【作者投稿】宽字符注入详解与实战

宽字节注入源于程序员设置MySQL连接时的错误配置,如下:

set character_set_client=gbk

,这样的配置会引发编码转换从而导致绕过某些防护实现注入漏洞。具体分析一下原理:

  1. 正常情况下GPC开启或者使用addslashes函数过滤GET或POST提交的参数时,我们测试输入的',就会被转义为\';
  2. 若存在宽字节注入,输入%df%27时,经过单引号的转义变成了%df%5c%27,之后再数据库查询语句进行GBK多字节编码,即一个中文占用两个字节,一个英文同样占用两个字节且在汉字编码范围内两个编码为一个汉字。然后MySQL服务器会对查询语句进行GBK编码即%df%5c转换成汉字"運",单引号逃逸出来,从而绕过转义造成注入漏洞。

现在基本都会将mysql的连接配置设置为:

[set character_set_client=binary]

来解决这个问题,这篇博客将介绍php中因为编码或字符编码转换导致的注入问题。

mysql中的宽字符注入

测试搭建学习的环境利用了phithon内容管理系统,看代码

SQL语句是SELECT * FROM news WHERE tid='{$id}',根据文章的id把文章从news表中提取出来,在$sql之前,我们只用了限制函数addslashes函数,对$id进行转义,只要我们输入参数在单引号中,就逃逸不出单引号的限制,从而无法注入。

我们这里利用的是mysql的一个特性,mysql在使用GBK编码的时候,会认为两个字节是一个汉字(前一个ascii码要大于128,才到汉字范围),我们测试输入%df'

已经报错,看到报错,说明sql语句出错,看到出错说明可以注入。报错的原因就是多了一个单引号,而单引号前面的反斜杠不见啦。这就是mysql的特性,因为gbk是多字节编码,它认为两个字节代表一个字符,所以%df和后面的%5c变成了汉字“運”,而’逃逸了出来。

因为是两个字节代表一个汉字,我们尝试%df%df%27:

不报错了,因为%df%df组成了汉字"哌",%5c%27不是汉字,仍然是\'

mysql如何判断一个字符是不是一个汉字,根据gbk编码,第一个字节的ascii码大于128,基本上就行,若不用%df而用%a1也可以

%a1%5c虽然不是一个汉字,但一定会被mysql认为是一个宽字符,所以就能让后面的%27逃逸出来,构造一个exp,查询管理人员的账号密码。

GB12和GBK的区别

gb2312和gbk都是宽字节家族医院,但是当把数据库编码设置为关闭gb2312时,结果就不能注入

这主要是gb2312编码取值范围的事情,它高位范围0xA1~0xF7,低位范围是0xA1~0xFE,\是%5c,是不在低范围中的,即其根本不是gb2312遍吗,故其不会被吃掉。故只要低位的范围中含有0x5c的编码,就可以进行宽字节的注入

利用mysql_real_escape_string解决问题

一些cms把addslashes替换为mysql_real_escape_string来防止宽字节的注入

我们若解决需要做的指定php连接mysql的字符集。我们需要在执行sql语句之前调用一下mysql_set_charset函数,设置当前的字符集为gbk,来避免问题

宽字节注入修复

character_set_client='binary'设置为binary(二进制),只需要在所有的sql语句前指定一下连接的形式为二进制:mysql_query("SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary", $conn);,当我们的mysql接受到客户端的数据后,会认为他的编码是character_set_client,然后会将换成character_set_connection的编码,然后在进入具体表和字段后,再转换成字段对应的编码,然后当查询结果产生后,会从表和字段编码,转换成character_set_results编码,返回给客户端。

这个方法避免宽字节的注入还是有效的,但是如果开发者画蛇添足的增加一些东西,会让前期的努力前功尽弃。

iconv造成的严重后果

很多cms会将接收到的数据,调用这样一个函数,转换其编码:iconv('utf-8','gbk',$_GET['id']);,目的一般是避免乱码,特别是搜索框的位置

可以发现,在sql语句执行前,将character_set_client设置成了binary,所以避免宽字节的注入问题。但之后其调用了iconv将已经过滤的参数$id给转换了一下,测试一下

报错说明我们錦被iconv从utf-8转换成gbk后,变成了%e5%5c,而后面的'被addslashes变成了%5c%27,这样组合起来就是%e5%5c%5c%27,两个%5c就是\,正好把反斜杠转义了,导致'逃逸出单引号,产生注入。利用的是将\转移掉。

利用iconv将gbk转换成utf-8,则可以直接用宽字节注入的姿势来。gbk汉字2字节,utf-8汉字是3字节,若把gbk转换成utf-8,则php会每两个字节一转换。所以,如果\’前面的字符是奇数的话,势必会吞掉\,’逃出限制。

总结

  1. gbk编码造成的宽字符注入问题,解决方法是设置character_set_client=binary。
  2. 矫正人们对于mysql_real_escape_string的误解,单独调用set name=gbk和mysql_real_escape_string是无法避免宽字符注入问题的。还得调用mysql_set_charset来设置一下字符集。
  3. 谨慎使用iconv来转换字符串编码,很容易出现问题。只要我们把前端html/js/css所有编码设置成gbk,mysql/php编码设置成gbk,就不会出现乱码问题。不用画蛇添足地去调用iconv转换编码,造成不必要的麻烦。

代码审计实战

对骑士cms审计时发现在plus/ajax_street.php

在之前配置文件设置的是mysql_query("SET character_set_connection=" . $dbcharset . ", character_set_results=" . $dbcharset . ", character_set_client=binary", $this->linkid);,其中利用了iconv函数造成致命的错误,同时分析发现页面将查询结果回显回来,构造一些union的查询语句即可获取数据库的敏感信息

漏洞的利用

测试有几个字段,发现category表一共有9个字段,所以可以构造获取数据库用户和先关信息的exp

然后利用union的查询语句爆出可利用的列为4,8,exp:

http://localhost/74cms/upload/plus/ajax_street.php?act=key&key=-%e9%8c%a6' union select 1,2,3,4,5,6,7,8,9-- -),

然后是爆出数据库和用户名等相关信息

补充

GBK编码中的两个字符是一个汉字,第一个字符需要大于128

原文发布于微信公众号 - 信安之路(xazlsec)

原文发表时间:2017-09-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java面试笔试题

Session的save()、update()、merge()、lock()、saveOrUpdate()和persist()方法分别是做什么的?有什么区别?

Hibernate的对象有三种状态:瞬时态(transient)、持久态(persistent)和游离态(detached),如第135题中的图所示。瞬时态的实...

1543
来自专栏orientlu

FreeRTOS 任务调度 任务创建

FreeRTOS 的任务调度在 Source/include/task.c 中实现,包含了任务的创建、切换、挂起、延时和删除等所有功能。涉及到的链表组织见文章 ...

2974
来自专栏Java架构沉思录

分布式ID常见解决方案

在分布式系统中,往往需要对大量的数据如订单、账户进行标识,以一个有意义的有序的序列号来作为全局唯一的ID。

6292
来自专栏大内老A

ASP.NET MVC Model元数据及其定制:一个重要的接口IMetadataAware

在介绍用于自定义Model元数据属性的AdditionalMetadataAttribute特性时我们提到了它实现的接口IMedataAware,我们说这是一个...

2106
来自专栏后端技术探索

php中常见编码问题

PHP程序设计中中文编码问题曾经困扰很多人,导致这个问题的原因其实很简单,每个国家(或区域)都规定了计算机信息交换用的字符编码集,如美国的扩展 ASCII 码,...

2272
来自专栏闻道于事

Java常用工具类之删除文件

package com.wazn.learn.util; import java.io.File; /** * 删除文件工具类 * @author ...

3438
来自专栏JavaEdge

单例模式(Singleton Pattern)百媚生1 动机2 定义结构分析优点缺点适用场景应用总结实现方式1、懒汉式(非线程安全)2、懒汉式(线程安全)4、双重检验锁模式(double checke

39110
来自专栏有趣的django

SQLAlchemy SQLAlchemy

4970
来自专栏我有一个梦想

vc++ 在程序中运行另一个程序的方法

在vc++ 程序中运行另一个程序的方法有三个: WinExec(),ShellExcute()和CreateProcess() 三个SDK函数: WinExec...

4559
来自专栏帘卷西风的专栏

关于lua扩展库lpack的使用指南

lpack的具体用法 1、打包接口pack的使用,全局名字容易混淆lua本身函数unpack,使用string.pack好些,也可以修改源码修改函数名。

1683

扫码关注云+社区

领取腾讯云代金券