https://www.chery666.cn/blog/2017/12/11/Code-audit.html
当该配置项为 ON 时,会把用户通过 GET、POST 提交的参数自动注册成全局变量。当代码中存在有未初始化的变量时,可能会导致变量覆盖的问题;
(PS: 其中参数覆盖的顺序受到配置文件中variables_order
的参数影响,默认是EGPCS
。按顺序,右边的参数来源会覆盖左边的的参数来源)
当该配置项为 ON 时,可以通过 include、require 等函数进行远程文件包含
其中有个类似的配置项是 allow_url_fopen,这个参数配置为 on 的时候可以函数中例如 file_get_contents 中打开 url。
当两个配置项都为 ON 的时候,可以直接使用 url 进行远程包含,当 include 为 ON,fopen 为 OFF 时,只能通过 php 伪协议进行包含
此配置项为 ON 的时候会对 GET、POST、COOKIE 变量中的单引号 (')、双引号(")、反斜杠()、空字符(NULL) 前添加反斜杠进行转义,注意:这个配置并不会对 SERVER 变量里的特殊字符进行转义,因此可能会导致 referer、client-ip 存在注入等漏洞
这个配置和 magic_quato_gpc 的区别就在于 runtime 是对从数据库或者文件中取出的数据进行转义,因此只对例如 file()、fgets()、fread()、mysql_fetch_array()等很多对数据库查询和文件读取的函数产生影响
这个配置和 magic_quato_gpc 的区别在于,sybase 只会转义空字符,把单引号转为双引号,并且这个配置如果为 ON 会覆盖 gpc 的配置
这个配置用来设置限定 php 程序只能访问哪些目录。在 windows 下,多个目录用分号(;)分割,linux 下用冒号 (:) 进行分割。注意的是配置的目录需要用斜杠(/)进行封尾,否则就变成了前缀匹配。例如,配置 / var/test,那么 / var/test 和 / var/test123 都是可以进行访问的,如果指定一个确定的目录就要写成 / var/test/
一般程序都是通过判断 install 文件下有没有安装过程中生成的以 lock 为后缀的文件或者 config 配置文件来判断有没有安装。
例如 PHPSHE B2C 商城 1.6(wooyun 2014-062047)
例如 frcms (wooyun 2014-073244)
foreach(Array('_GET','_POST','_COOKIE') as $_request){
foreach($$_request as $_k => $_v) ${$_k} = _runmagicquotes($_v);
}
他会把你从 GET、POST、COOKIE 中的变量注册为全局变量,因此我们直接通过 GET 参数提交 $insLockfile 变量即可绕过
例如 startbbs (wooyun-2013-045143)
class Install extends Install_Controller
{
function __construct ()
{
parent::__construct();
$this->load->library('myclass');
$file=FCPATH.'install.lock';
if (file_exists($file)){
$this->myclass->notice('alert("系统已安装过");window.location.href="'.site_url().'";');
}
}
可以看到其中判断 install.lock 文件存在后直接使用 js 代码将用户进行重定向,但是并没有 die 程序,直接从前端删除返回的 js 代码即可重装
例如: 用 thinkphp 改造的 hdcms (wooyun-2015-092061)
我们先跟着框架走一遍,首先查看入口文件 index.php
首先定义了一些基本的框架目录,然后就直接开始引入框架文件,我们进入框架初始化文件 hdphp.php
可以看到 module_path 常量是通过将 get 形式提交的 var_group 参数进行拼接的,然后又将 module_path 拼接入 module_config_path,最后使用 require 进行了文件包含。(PS: 因为这里后面制定了 config.php,所以需要用到 %00 进行截断)
但是我们分析到目前为止只能说是疑似存在文件包含漏洞,我们还要看 GET 参数接收时有没有进行过滤,于是我们进入之前的解析路由方法 route::parseurl(),代码较长我就不贴图了,里面就是将 url 中的参数进行截取解析,没有进行任何的过滤和检测,因此可以确定此处存在文件包含漏洞。因此我们接下来需要确定输入点,可以发现变量是通过 thinkphp 中获取参数的 C 方法进行获取的,而 C 方法获取的变量在 config.php 中,于是我们查看文件中可以看到 var_group 对应的变量是 g
之前的文件包含漏洞我们是通过 index.php 这个入口文件一步步搞懂 cms 框架然后进行审计。除了这种方法,我们还可以直接定位数据库查询语句或者功能附近,看看传入的数据有没有被进行清洗。
注入漏洞这里我们就用两个有意思的骚操作来分析一下
其实在 ECshop 中的 init.php 中对用户输入的参数进行了全局转义
$order_sn = str_replace($_GET['subject'], '', $_GET['out_trade_no']);
其中代码对用户提交的 out_trade_no 参数中将 subject 替换为空,然后送入 check_money 函数中的 sql 查询语句.
[\-->\\,"-->\","-->\",null-->\0]
out_trade_no=%00' ————>经过全局gpc转义————> out_trade_no=\0\'————>送入str_replace函数处理,将0替换————>out_trade_no=\\'
也就是等于了',最终也就成功在 sql 语句中引入了一个单引号,从而可以进行注入
再来分析一下前段时间出来的 wordpress 格式化字符导致的注入
具体的代码分析在这就不贴图了,我们直接来分析一下格式化字符串漏洞的核心原理,其中一个关键点就是 sprintf 的 padding 特性
printf()和 sprintf()函数中可以通过使用 % 接一个字符来进行 padding 功能
例如 %10s 字符串会默认在左侧填充空格至长度为 10,还可以 %010s 会使用字符 0 进行填充,但是如果我们想要使用别的字符进行填充,需要使用'单引号进行标识,例如 %’#10s 这个就是使用 #进行填充(百分号不仅会吃掉’单引号,还会吃掉 斜杠)
同时 sprintf()可以使用指定参数位置的写法
% 后面的数字代表第几个参数,$ 后代表格式化类型
于是当我们输入的特殊字符被放到引号中进行转义时,但是又使用了 sprintf 函数进行拼接时, 例如 %1$’%s’ 中的 ‘% 被当成使用 % 进行 padding,导致后一个’逃逸了
还有一种情况就是’被转义成了 \’, 例如输入 %’ and 1=1# 进入,存在 SQL 过滤,’被转成了 \’
于是 sql 语句变成了select * from user where username = '%\' and 1=1#’;
如果这个语句被使用 sprintf 函数进行了拼接,% 后的被吃掉了,导致了’逃逸
"select * from user where username = '%\' and 1=1#';";
$args = "admin";
echo sprintf( $sql, $args ) ;
//result: select * from user where username = '' and 1=1#'
?>
不过这样容易遇到 PHP Warning: sprintf(): Too few arguments 的报错
这个时候我们可以使用 %1$ 来吃掉转移添加的 \
"select * from user where username = '%1$\' and 1=1#' and password='%s';";
$args = "admin";
echo sprintf( $sql, $args) ;
//result: select * from user where username = '' and 1=1#' and password='admin';
?>