天天看點

那些年我拿下的demo站之方維O2O

早前一個被我捂爛的漏洞,其實不是要捂的,當注入交到烏雲,稽核比較忙測試的時候沒複現,就給打回來了。我一直想寫個詳細的再交,結果沒時間就沒寫,這兩天上去一看雞毛都沒了,全補完了,shell也掉光了,背景也進不去……

想想算了不挖了。還好我有記筆記的習慣,拿出來分享一下。

以下是筆記。

今天挖了一陣子方維O2O的本地生活系統。這個系統是不開源的,偶然拿到了一個版本的源碼,于是有了這次挖洞之旅。

首先翻到了一個注入,拿下了管理者密碼。實際上,demo站已經給了一個低權限的管理者賬号demo/demo。

登入背景:

那些年我拿下的demo站之方維O2O

0x01 雞肋檔案包含漏洞

大概看了一下功能不多,很多敏感功能(如sql操作)demo管理者沒權限。

背景一般安全做的比較差,最容易出現的是檔案包含漏洞。我很快在源碼裡找到一處雞肋檔案包含。/admin/Lib/Action/ApiLoginAction.class.php

<?php
public function install()
{
    $class_name = $_REQUEST['class_name'];
    $directory = APP_ROOT_PATH."system/api_login/";
    $read_modules = true;

    $file = $directory.$class_name."_api.php";
    if(file_exists($file))
    {
        $module = require_once($file);
        $rs = M("ApiLogin")->where("class_name = '".$class_name."'")->count();
        if($rs > 0)
        {
            $this->error(l("API_INSTALLED"));
        }
    }
    else
    {
        $this->error(l("INVALID_OPERATION"));
    }           

複制

之是以叫雞肋,因為需要截斷:directory.class_name."_api.php";

但我看demo站的php是5.3.3,記得5.3.4以後才解決截斷問題。而且fanwe全局并沒有addslashes,不影響截斷。

是以簡單嘗試了一下……結果還真截斷不了:

那些年我拿下的demo站之方維O2O

可能是其他什麼原因,反正不能截斷。這個雞肋漏洞是用不了了。再繼續翻源碼。

0x02 解壓縮造成的雞肋檔案上傳漏洞

/admin/Lib/Action/FileAction.class.php,一個檔案管理的controller。(審計的時候嗅覺也比較重要,看到這個檔案名FileAction我就感覺這裡會出問題,因為是檔案操作)

實際上也沒我想的那麼糟糕,這裡是上傳的控制器,上傳的地方過濾的比較嚴,不能直接上傳php檔案。

但有個上傳壓縮包并解壓的函數:

<?php
/**
 * 圖示上傳
 */
public function do_upload_icon()
{
    require_once APP_ROOT_PATH."system/utils/zip.php";
    $archive  = new PHPZip();
    $font_dir = APP_ROOT_PATH."public/iconfont";

    $result = $archive->unZip($_FILES['file']['tmp_name'], $font_dir);
    if(empty($result)||$result==-1)
    {
        ajax_return(array("status"=>false,"info"=>"圖示庫更新失敗,請手動解壓後上傳檔案到".$font_dir));
    }

    if ( $dir = opendir( $font_dir."/" ) )
    {
        while ( $file = readdir( $dir ) )
        {
            $check = is_dir( $font_dir."/". $file );
            if ( !$check )
            {
                @unlink( $font_dir ."/". $file );
            }
        }
    }

    $result = $archive->unZip($_FILES['file']['tmp_name'], $font_dir);      
    //清空原檔案

    foreach($result as $k=>$v)
    {
        $file = APP_ROOT_PATH."public/iconfont/".$k;
        $file_arr = explode("/", $file);

        foreach($file_arr as $f)
        {
            if($f=="iconfont.css"||$f=="iconfont.eot"||$f=="iconfont.svg"||$f=="iconfont.ttf"||$f=="iconfont.woff")
            {
                //echo APP_ROOT_PATH."public/iconfont/".$f;
                @rename($file,APP_ROOT_PATH."public/iconfont/".$f);
            }
        }

    }

    foreach($result as $k=>$v)
    {
        $file = APP_ROOT_PATH."public/iconfont/".$k;
        @unlink($file);
    }
    foreach($result as $k=>$v)
    {
        $file = APP_ROOT_PATH."public/iconfont/".$k;
        @rmdir($file);
    }
    ajax_return(array("status"=>true,"info"=>""));
}           

複制

看過我去年寫的一篇文章:https://www.leavesongs.com/PENETRATION/after-phpcms-upload-vul.html 的同學應該記憶猶新,解壓這種操作有很多方法可以傳shell。

這裡也一樣,雖然解壓完成後删除了所有原有檔案。但其解壓了zip檔案後,它判斷了是否成功,不成功則直接退出了:

<?php
if(empty($result)||$result==-1)
{
    ajax_return(array("status"=>false,"info"=>"圖示庫更新失敗,請手動解壓後上傳檔案到".$font_dir));
}           

複制

那麼我可以構造一個“能夠解壓一半”的壓縮包,解壓出部分php檔案,然後出錯。這樣就執行不到“删除”的代碼了。

另外一個方法是,我解壓的時候修改檔案名為“../xxxx.php”,就能把php檔案解壓到上層目錄中,也能避免被删除的命運。

相對的,第二種方法更簡單。

于是我構造壓縮包,先寫好一個webshell,名字就叫aaaaaaaaaaaaaaaaaaaaaa.php。壓縮後用editplus編輯:

那些年我拿下的demo站之方維O2O

将前4個a替換成“/../”,這樣保證了整個檔案的長度不變。

再構造本地上傳單頁:

<form action="http://o2odemo.fanwe.net/m.php?m=File&a=do_upload_icon" method="post" enctype ="multipart/form-data"> 
<input name="file" type="file" />
<input type="submit" value="upload" />
</form>           

複制

選擇後傳上去。結果通路發現403:

那些年我拿下的demo站之方維O2O

又重新換檔案名試了一下,也403。試了一下不存在的.php檔案,也403。基本上就是這個規則:public目錄下,所有.php檔案都是403。

試了txt檔案,是可以上傳的,說明public目錄有寫權限:

那些年我拿下的demo站之方維O2O

另外嘗試了傳到其他目錄,比如根目錄、admin目錄,結果404,應該是沒寫權限。

是以,現在這個洞也是很悲劇很雞肋的:隻有public目錄有寫權限,但public目錄下不能執行php。

0x03 組合漏洞出奇迹

這是突然想到:之前挖的那個雞肋檔案包含,不就正好排上用場了嗎?

之前的檔案包含漏洞,雞肋就雞肋在隻能包含php檔案,一般情況下如果我們能寫入php檔案其實就已經getshell了。

但這裡不一樣,我解壓出來一個xxxx_api.php,雖然在public目錄下不能執行,但通過檔案包含的方法包含之,即可執行我的webshell了。

于是将檔案名改成aaaaaaaaaaaa_api.php

那些年我拿下的demo站之方維O2O

上傳後直接包含,成功:

那些年我拿下的demo站之方維O2O

菜刀連接配接:

那些年我拿下的demo站之方維O2O

0x04 法2:apache解析漏洞的逆襲

平時用apache用的不多,一直覺得apache的那個解析漏洞是很老的版本才會有的。但這次還真被我碰上了。

通過上一個漏洞getshell後,再回來思考還有沒有别的方法。

這個時候應該換位思考,如果我是運維,我一般會怎樣禁用一個目錄中的php檔案?

很可能是一個正則:^/public/.*.php$,隻要HTTP請求符合這個正則,就傳回403。

我曾經寫過一篇文章:https://www.leavesongs.com/PENETRATION/nginx-deny-exec-php-file.html,講的是nginx如何正确禁用執行。

文中被繞過的方法實際上就是這個正則:

那些年我拿下的demo站之方維O2O

這樣通過字尾去禁止執行的方式是很不可靠的,文中我通過pathinfo的方式(xxx.php/xxx)來繞過了這個正則。

這裡我也試了用pathinfo,可惜還是傳回403 。那麼針對這個正則:“^/public/.*.php$”,真的沒有辦法了嗎?

思路就是:有沒有其他字尾可以被解析,如果有就能繞過這個正則了。

試了一下phtml、php3/4/5,都不能解析。這個時候我想到apache的解析漏洞了:當apache不識别最後一個字尾時,會向前尋找直到找到一個能夠識别的字尾。

于是将名字改成xxx.php.phi:

那些年我拿下的demo站之方維O2O

上傳發現已經可以解析了:

那些年我拿下的demo站之方維O2O

一個解析漏洞的逆襲。

0x05 拿到shell以後的反思

限制執行這個問題,看了看配置檔案。果然和我想的一樣,是限制了public目錄下幾個字尾,不允許執行:

<Directory "/fanwe/www/o2odemo/public">
    <FilesMatch ".(php|asp|jsp)$">
     Deny from all
    </FilesMatch>
</Directory>           

複制

并且檢視了整個web目錄的權限,web目錄所有者是root,隻有public是777,其他檔案全部是755和644,好家夥和我想的一樣。看來我自己的直覺現在也是越來越準拉,哈哈~(完)