这次NCTF的Web方向让我印象最深的不是某个Java反序列化题,也不是复杂的密码学配合,而是一道看起来没什么攻击面的小题目:N-RustPICA。题面简单到只有一个图片上传框和格式转换按钮,但正是这种“没什么可打”的感觉,差点让我把它当成二进制题跳过。实际上,这道题用Rust重写Web服务,表面走的是图片处理流程,漏洞点却藏在最容易被忽视的fmt参数里。如果你对Rust系Web题目还比较陌生,这篇文章应该能帮你省下不少比赛中的试错时间。
1. 从题目名和首页探测看出一道题的“体检报告”
1.1 题目名里的暗示
N-RustPICA这个名称可以拆成三块:N、Rust、PICA。N没什么好说的,多半是题目编号或者NCTF的缩写;Rust直接说明这是一道基于Rust语言开发的后端题;PICA则是Nintendo 3DS平台上常见的图形纹理格式,PICA200是那颗GPU的核心代号。看到PICA的时候,我最先想到的是“这题可能涉及图像解析漏洞”,于是第一反应是找解析器版本、找CVE。但接下来的实际操作推翻了这一判断。
这类命名在CTF里很常见:出题人喜欢用看起来很专业的格式名当幌子,实际漏洞往往不在格式解析本身,而在调用解析器前后的业务逻辑缝隙里。所以拿到题目先别被名字带偏,老老实实把Web入口的每一个参数摸一遍才是正事。
1.2 首页和基础请求
打开题目环境,页面非常朴素:一个文件上传控件,一个下拉选择框(输出格式),一个Convert按钮。页面标题写着“PICA Image Converter”。上传一张普通的PNG图片,选PNG输出,点击转换,几秒后返回一个图片下载链接,功能正常。
我用Burp盯了一遍完整请求,发现转换请求不是通过上传接口直接完成的,而是分为两步:
http复制POST /upload HTTP/1.1
Content-Type: multipart/form-data
{file: test.png}
响应里返回了一个文件ID,类似uploads/tmp_abc123.png。然后前端拿这个ID再请求:
http复制POST /convert HTTP/1.1
Content-Type: application/x-www-form-urlencoded
file=uploads%2Ftmp_abc123.png&fmt=png
这里出现了两个核心参数:file和fmt。file是服务端返回的上传路径,fmt是用户选择的输出格式。第一眼看上去,fmt只有一个枚举值可选(png、jpg、bmp),似乎没什么操作空间。但比赛经验告诉我,凡是能被用户控制、又要拼进后端逻辑里的参数,都值得挨个做变形测试。
1.3 框架指纹和目录探测
先确认后端技术栈。响应头里没有明显的Server: axum之类的标记,但404页面的响应体很有辨识度:只有一行纯文本Not Found,没有HTML模板、没有图标,这是axum默认的404行为。换几个不存在的路径,返回都是同样的纯文本。再结合响应头里的content-type: text/plain; charset=utf-8,基本可以确定是axum/hyper那一套Rust生态。
Rust的Web框架不像PHP那样好扫。PHP项目通常路由不严谨,很多路径都能解析;axum这类框架是精确匹配路由,扫目录工具扫出来的大多是404或者405,信息量很低。我快速跑了几个常见路径:/admin、/source、/.git、/backup、/robots.txt、/static/,全部404或302到首页。唯一有价值的是/source返回了405 Method Not Allowed,说明这个路由存在,但只接受特定方法。
到这里,我的判断是:这道题不存在简单粗暴的源码泄露或者隐藏后台,攻击面收敛到了/upload和`/c
