烏雲1000個PHP代碼審計案例(1)


前兩天發現的寶藏網站:https://php.mengsec.com/

在github上面找到了源代碼:https://github.com/Xyntax/1000php,可以在自己的服務器上面搭建

跟數據結構學習一樣,每天跟着上面前輩們的案例進行審計,打基礎+學習思路

 

1,BlueCMS v1.6 sp1 ad_js.php SQL注入漏洞

使用seay自動審計工具

 

 可以看到第一條可能出現的漏洞點就是ad_js.php

我們跟進去看,

 

 經過getone函數在數據庫里面查詢出結果並賦值給$ad變量,同時可以看到$ad_id沒有包裹引號

 

 getone函數是在數據庫里面進行查詢的函數

mysqli_fetch_array() 函數從結果集中取得一行作為關聯數組,或數字數組,或二者兼有。也就是從數據庫中查詢。

我們往上看$ad_id變量的輸入位置是否有過濾。

 

 可以看到對於GET方法輸入的ad_id變量僅僅是使用trim函數去除了輸入值兩邊的空格,此外就沒有另外的過濾了,所以該點確實存在SQL注入漏洞。

 

真實環境下利用很簡單,使用union聯合注入即可出數據

修復方法:$ad_id = !empty($_GET['ad_id']) ? intval($_GET['ad_id']) : '';

將輸入的數據強行轉換成整型

 

2,Discuz!7.2/X1 第三方插件SQL注入及持久型XSS漏洞

這里分析SQL注入漏洞,插件是由Discuz!認證的(http://addons.discuz.com/workroom.php)第三方開發團隊“潮流少年工作室 Teen Studio”出品的心情牆插件(http://www.discuz.net/forum.php?mod=viewthread&tid=1632898)

因為去找漏洞插件太麻煩了,我這里直接貼上漏洞源代碼吧

elseif($action == 'edit_mood' && moodid) {


          //moodid未初始化,直接代入sql查詢


	  $check = $db->result_first("SELECT * FROM {$tablepre}moodwall WHERE id='$moodid' AND uid='$discuz_uid'");





	  if(!$check || !$moodid) {


		  showmessage('moodwall:moodwall_inc_php_2', 'plugin.php?id=moodwall&action=user_mood');


	  }


	  $sql = "SELECT * FROM {$tablepre}moodwall WHERE id='$moodid'";


	  $query = $db->query($sql);


	  $moodlist_edit = array();


	  while($mood_edit = $db->fetch_array($query)) {


		  $moodlist_edit[] = $mood_edit;


	  }

  可以看到$moodid變量在單引號包裹下代入$sql語句進行查詢

$sql = "SELECT * FROM {$tablepre}moodwall WHERE id='$moodid'";

  但是在這里漏洞的發現者也說了,需要網站magic_quotes_gpc=off,在magic_quotes_gpc=On的情況下,如果輸入的數據有單引號(’)、雙引號(”)、反斜線()與 NULL(NULL 字符)等字符都會被加上反斜線。

不過實際上還可以有寬字節注入等特殊情況對網站進行注入

 

3,ecshop SQL注射漏洞

因為沒有詳細的ecshop版本信息,所以同樣不進行源碼的下載,在wooyun大大的基礎上直接進行分析

在include_libcommon.php中存在如下函數,get_package_info函數

function get_package_info($id)
{
    global $ecs, $db,$_CFG;
    $now = gmtime();
    $sql = "SELECT act_id AS id,  act_name AS package_name, goods_id , goods_name, start_time, end_time, act_desc, ext_info".
           " FROM " . $GLOBALS['ecs']->table('goods_activity') .
           " WHERE act_id='$id' AND act_type = " . GAT_PACKAGE;
  $package = $db->GetRow($sql);
  /* 將時間轉成可閱讀格式 */
  if ($package['start_time'] <= $now && $package['end_time'] >= $now)   {     $package['is_on_sale'] = "1";    }   else   {     $package['is_on_sale'] = "0";   }   $package['start_time'] = local_date('Y-m-d H:i', $package['start_time']);   $package['end_time'] = local_date('Y-m-d H:i', $package['end_time']);   $row = unserialize($package['ext_info']);   unset($package['ext_info']);   if ($row)   {     foreach ($row as $key=>$val)     {       $package[$key] = $val;     }   } $sql = "SELECT pg.package_id, pg.goods_id, pg.goods_number, pg.admin_id, ".     " g.goods_sn, g.goods_name, g.market_price, g.goods_thumb, g.is_real, ".     " IFNULL(mp.user_price, g.shop_price * '$_SESSION[discount]') AS rank_price " .     " FROM " . $GLOBALS['ecs']->table('package_goods') . " AS pg ".     " LEFT JOIN ". $GLOBALS['ecs']->table('goods') . " AS g ".     " ON g.goods_id = pg.goods_id ".     " LEFT JOIN " . $GLOBALS['ecs']->table('member_price') . " AS mp ".     "ON mp.goods_id = g.goods_id AND mp.user_rank = '$_SESSION[user_rank]' ".     " WHERE pg.package_id = " . $id. " ".     " ORDER BY pg.package_id, pg.goods_id";   $goods_res = $GLOBALS['db']->getAll($sql);   $market_price = 0;

  可以看到$sql語句里面代入了輸入的$id,我們再定位誰調用了get_package_info函數,在系統的lib_order.php中。

function add_package_to_cart($package_id, $num = 1)
{
    $GLOBALS['err']->clean();
  /* 取得禮包信息 */
  $package = get_package_info($package_id);
  if (empty($package))
  {
    $GLOBALS['err']->add($GLOBALS['_LANG']['goods_not_exists'], ERR_NOT_EXISTS);
    return false;
  }

  可以看到調用get_package_info函數賦值給了$package變量,而$package_id變量是傳遞給add_package_to_cart函數的,在該函數中也沒有看到有對變量的過濾,尋找調用add_package_to_cart函數的文件

在flow.php中存在可控的輸入源

$package = $json->decode($_POST['package_info']);
    /* 如果是一步購物,先清空購物車 */
     if ($_CFG['one_step_buy'] == '1')
    {
     clear_cart();
  }
  /* 商品數量是否合法 */
  if (!is_numeric($package->number) || intval($package->number) <= 0)
  {
    $result['error']   = 1;
    $result['message'] = $_LANG['invalid_number'];
  }
  else
  {
  /* 添加到購物車 */
    if (add_package_to_cart($package->package_id, $package->number))
    {
      if ($_CFG['cart_confirm'] > 2)

  而在這里$package->package_id是我們可以控制的輸入源,根據剛才函數調用的過程,對於我們的這個輸入並沒有其余的過濾,可知存在SQL注入漏洞

 

4,Discuz!漫游插件API本地包含漏洞

先上代碼 manyou/api/class/MyBase.php

128: function parseRequest() {

131: $request = $_POST;

132: $module = $request['module'];

133: $method = $request['method'];

...

174: return $this->callback($module, $method, $params);

175: }



177: function callback($module, $method, $params) {

...

191: @include_once DISCUZ_ROOT.'./manyou/api/class/'.$module.'.php';

  在parseRequest函數里面,可以看到最后return調用了callback函數,並且傳入的三個參數都是我們可以控制的輸入,而callback函數里面使用include_once函數包含了當前路徑下的傳遞的$module.php文件

PHP中文件包含函數有以下四種:

    require()

    require_once()

    include()

    include_once()

 include和require的區別在於include在包含的時候出現錯誤的時候,會拋出一個警告,然后程序繼續運行,而require函數在出現錯誤的時候,會直接報錯並且退出程序的執行

require_once和include_once這兩個函數與前兩個不同的地方在於,這兩個函數只包含一次,適合於腳本執行期間同一個文件可能被包括一次以上的情況時,你想確保它只包含一次以避免函數重定義,變量重新賦值等問題

參考自:https://www.freebuf.com/articles/web/182280.html

在這里我們可以控制$module變量,但是這里是有限制的文件包含,因為在后面加上了.php的后綴,當我們想包含當前文件夾任意文件的時候,如果magic_quotes_gpc = Off php版本<5.3.4,我們可以使用%00截斷的方式,當然還有其他很多的方式可以繞過這個限制,如果在這里還存在目錄穿越漏洞的話,我們就可以包含服務器任意文件了,而不是限制在這個文件夾下。

 

5,PHPCMS通殺XSS

這里是一個反射型XSS漏洞,在我要報錯功能頁下,過濾不嚴格

http://**.**.**.**/error_report/error_report.php?title=1&contentid=1"><script>alert(/xss/)</script>

  反射型XSS漏洞比較容易通過掃描器黑盒直接發現,只需要將尖括號,單雙引號等提交到web服務器,檢查返回的HTML頁面里面有沒有保留原來的特殊字符即可判斷。

在白盒審計中,我們只需要尋找帶有參數輸出函數,然后根據輸出函數對輸出內容回溯輸入參數,觀察有沒有進行經過過濾

 

6,phpcms 2008 sp4 爆路徑及任意文件刪除漏洞

漏洞文件是corpandresize/config.inc.php

$tmp = $_COOKIE['tmp'];


define("TMP_PATH", $tmp);

  在cookie參數中獲取tmp,同時定義TMP_PATH等於變量tmp的值

使用到TMP_PATH的位置是corpandresize/process.php

@unlink(TMP_PATH.'/'.$thumbfile);

  這里使用unlink函數刪除文件,我們可以使用%00截斷刪除我們想要刪除的任意文件

如在cookie中添加tmp=../index.php%00,按照上面的分析過程,就可以刪除首頁文件

 

7,phpcms2008本地文件包括及利用(執行任意SQL腳本)

漏洞文件存在於wap/index.php

代碼如下:

<?php

include '../include/common.inc.php';

include './include/global.func.php';

$lang = include './include/lang.inc.php';

if(preg_match('/(mozilla|m3gate|winwap|openwave)/i', $_SERVER['HTTP_USER_AGENT']))

{

header('location:../');

}

wmlHeader($PHPCMS['sitename']);



$action = isset($action) && !empty($action) ? $action : 'index';

if($action)

{

include './include/'.$action.'.inc.php';

}



$html = CHARSET != 'utf-8' ? iconv(CHARSET, 'utf-8', $html) : $html;

echo str_replace('<br/>', "<br/>\n", $html);

wmlFooter();

?>

  可以看到在這一段代碼里有:

$action = isset($action) && !empty($action) ? $action : 'index';
if($action)
{
    include './include/'.$action.'.inc.php';
}

  isset() 函數用於檢測變量是否已設置並且非 NULL。在這里即檢測$action是否設置,同時檢測$action是否為空,空的話就賦值為index,非空即傳入值,接着就是包含傳入的$action.inc.php文件,對於輸入的$action沒有進行過多的檢測

可以包含存在sql語句的php文件,執行任意sql語句來對漏洞進行利用

例如包含目錄include\fields\areaid 下任一文件,執行任意SQL腳本。

比如field_add.inc.php文件,我們只需要傳入的$action=fields/areaid/field_add,field_add.inc.php文件代碼為:

<?php

if(!$maxlength) $maxlength = 255;

$maxlength = min($maxlength, 255);

$sql = "ALTER TABLE `$tablename` ADD `$field` VARCHAR( $maxlength ) NOT NULL DEFAULT '$defaultvalue'";

$db->query($sql);

?>

  在這里我們只需要傳入$tablename變量就可以執行$sql語句進行查詢

最后的payload為:http://www.phpcms.cn/wap/index.php?action=../../include/fields/areaid/field_add&tablename=xx

 

8,Ecshop2.7.2持久型XSS(可獲得管理員帳號)

 

 可以看到當POST傳輸的數據extend_field_index沒有任何的過濾就進入數據庫進行更新了。

這里的輸入是密碼保護問題這一項,我們可以在密碼保護問題里輸入XSS,但是后台查看會員資料是不顯示密碼保護問題的,所以這里必須要網站后台添加了新的 “會員注冊項”時,后台查看資料就會顯示了,顯示了之后就可以進行XSS攻擊了。

這里不懂的地方是原漏洞作者導入外部js的時候,js文件里面的內容是Ajax:

Ajax.call('privilege.php?act=update','id=1&user_name=heihei&email=10001@**.**.**.**','',"POST","JSON");

 

 

 

 而不是寫入外部js盜取管理員cookie,以后明白了再補充這里。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM