打开靶机后发现有三个网页链接

先用diersearch扫描看看能不能找出源代码

什么也没发现
点开第一个(Hello World)

提示我们这里用的是Perl语言
Perl 是一门解释型编程语言,无需编译,直接由 Perl 解释器执行。用于早期 Web 开发,是 CGI 脚本的「首选语言」(比 PHP 更早普及),很多老服务器、运维脚本、漏洞场景都基于 Perl CGI;
点开第二个(Forms)

提示这里用的是Perl的CGI 模块
并且能够把输入的信息回显出来
猜测这里存在Perl CGI 模块的 param() 函数
Perl 编写的 CGI 脚本,通常会用 CGI.pm 模块处理 Web 请求,而 param() 是该模块的核心函数,作用是 获取客户端(浏览器 / 攻击者)提交的请求参数(比如表单数据、URL 参数、文件上传数据)。
param()的作用
- 接收 GET/POST 请求的参数(比如 http://xxx/cgi-bin/file.pl?name=test 中的 name 参数);
- 接收文件上传的相关数据(比如上传文件的文件名、文件内容);
- 无需区分 GET/POST 方法,直接通过参数名获取值,简化 CGI 脚本开发。
换句话说,param()函数就是一个接收文件并传到后台的函数
点开第三个(Files)

发现有一个文件上传页面
但不知道上传文件的路径

随便传入一个文件,发现它能把我们传入的文件内容打印出来
猜测这里存在一个句柄,脚本会读取这个句柄的内容并回显
并且我们从第二个网页猜测到这里存在Perl CGI 模块的 param() 函数
那么思路来了
这个句柄也许就是ARGV文件
我们可以传入一个文件,并修改文件名为 ARGV
如果我们传入一个ARGV的文件,那么Perl会将传入的参数作为文件名读出来。如果对正常的上传文件内容进行修改,可以达到读取任意文件的目的。
ARGV 是 Perl 内置的文件句柄,专门用于处理命令行参数传入的文件。当 Perl 脚本运行时,若命令行后跟文件名(如perl script.pl file.txt),ARGV 会自动关联file.txt,脚本中<> 运算符默认读取 ARGV 对应的文件内容。
换句话说,修改文件名为ARGV后可快速让目标服务器上的 Perl 脚本读取该文件,对正常的上传文件进行修改,可以达到读取任意文件的目的。
抓包读取源代码验证测试一下我们的猜想
抓取文件上传的包并发到repeater

直接把fliename参数改为ARGV并在文件内容上写入路径
------WebKitFormBoundaryRFagHVAG215Cy6BJ
Content-Disposition: form-data; name="file"; filename="ARGV"
Content-Type: application/octet-stream
var/www/cgi-bin/file.pl
发现它会直接把我的输入打印到页面上,而不会执行语句

换种方法
在抓取的包上再写入一个ARGV文件包,把filename删去,在url上传入查询路径
成功找到

CGI 脚本有一个核心特性:URL 中的查询参数(?后面的内容),会被 Perl 自动存入@ARGV数组(相当于命令行参数)。
后端也许存在文件检测,必须要检测到一个真实的文件才能放行
所以
第一个file字段用于注入语句
第二个file字段用于欺骗后端满足后端逻辑,让脚本继续跑下去
一般CGI脚本的标准部署目录都是/var/www/cgi-bin/,结合原来的url,我们不难猜测这个网站CGI的源码就在/var/www/cgi-bin/file.pl
源码如下
if ($cgi->upload('file')) {
my $file = $cgi->param('file');
while (<$file>) {
print "$_";
}
}
$file = $cgi->param('file')
param('file') 不仅能接收 “上传文件的句柄”,还能接收 用户指定的任意句柄名(比如 ARGV)—— 只要攻击者上传时,让 param('file') 返回 ARGV 句柄,<$file> 就会变成 <ARGV>,触发 Perl 的特殊读取逻辑。
并且我们发现<$file> 读取逻辑没有任何过滤
当 $file 是 ARGV 句柄时,<ARGV> 的读取优先级是:
先读 URL 参数 / 命令行参数(比如 file.pl?cmd=xxx 中的 cmd=xxx);
再读当前目录下名为 ARGV 的文件;
最后读标准输入。
且 <ARGV> 会把读取到的 “字符串” 当作 “数据源指令”—— 如果字符串是系统命令,Perl 会直接调用系统解释器(bash)执行该命令,再把执行结果通过 print "$_" 回显。
换句话说,现在我们可以直接在url中写入bash指令读取数据
先从根目录下寻找flag
构造payload
?/bin/bash -c ls /
传入后发现被过滤了

用${IFS}替代空格
?/bin/bash -c ls${IFS}/
还是被过滤了
IFS中存储的值可以使空格、制表符、换行符或者其他自定义符号,可以在linux中使用${IFS}代替空格

用%20替代空格并加上管道符试试
?/bin/bash%20-c%20ls${IFS}/|
成功了
发现flag就在根目录下
管道符 | 的核心功能是将前一个命令的输出,作为后一个命令的输入
在末尾加 |,会让命令变成 ls${IFS}/|,相当于改变了命令的结尾字符(从 / 变成 |),绕过 “禁止以 / 结尾” 的规则并且伪装成 “管道命令” 的格式,满足目标系统对命令结构的校验,让 bash 能正常解析执行。
并且管道符右端无指令并不会让bash报错,只会让它执行左边的ls${IFS}/

抓取flag
构造payload
?cat%20/flag%20|

9002

被折叠的 条评论
为什么被折叠?



