tklengyel / drakvuf

DRAKVUF Black-box Binary Analysis
https://drakvuf.com
Other
1.05k stars 253 forks source link

process injection on ubuntu #1456

Open cvcachagua opened 2 years ago

cvcachagua commented 2 years ago

I would like to experiment with process injection on Ubuntu. What stable versions would work best together of drakvuf, and of Ubuntu? Is there a test suite that is used for process injection that I can look at to learn more about process injection, and perhaps to run a few test cases? Tks.

cvcachagua commented 2 years ago

I tried to hack-ishly add to syscalls linux.h if a certain syscall was detected use: drakvuf-master/src/libinjector/debug_helpers.h: void print_registers(drakvuf_trap_info_t* info); in functions linux_cb(), and linux_ret_cb() But I could not get the function to link -- ld Does anyone know how this can be done?

I tried to create my own plugin, but I always had linking issues, and I am not sure why.

I appreciate any ideas?

tklengyel commented 2 years ago

FYI https://github.com/tklengyel/drakvuf/wiki/DRAKVUF-Developers-Guide

cvcachagua commented 2 years ago

it was an error on my part why debug_helpers did not link -- sorry

I am trying to access what was rdi execve for a user within-vm command "/usr/bin/bash /usr/bin/a.out"

I believe that prior to 2018-ish you could just do: pathandname = vmi_read_str_va(vmi, info->regs->rdi, info->proc_data.pid); This method no longer works for execve, and I see in src/plugin/syscall/linux.h a reference to 2018 and that rdi is now in a struct pt_regs. I see further in linux.cpp file that pt_regs is copied and accessed.

It seems that ACCESS_CONTEXT() must be performed first, something like (for syscall): ACCESS_CONTEXT(ctx, .translate_mechanism = VMI_TM_PROCESS_DTB, .dtb = info->regs->cr3 ); size_t value_rdi; then ctx.addr = info->regs->rsi + s->offsets[PT_REGS_RDI]; // PT_REGS_RDI enumeration from linux.h vmi_read_64(vmi, &ctx, &value_rdi)

But for some reason I cannot get the value_rdi.

I am also confused about the num_args value -- I see zero as the args, but I was thinking that I should see at least one argument for the execve, perhaps I am confusing exec vs sys_execve.

cvcachagua commented 2 years ago

I am trying to fix a plugin that someone wrote around early 2018, where the plugin would write into rdi: vmi_write_va(vmi, info->regs->rdi, info->proc_data.pid, strlen(fakepath)+1, fakepath, c); I assume that the rdi register is now in pt_regs, but where?

tklengyel commented 2 years ago

But for some reason I cannot get the value_rdi.

What do you mean "cannot get"?

So your approach assumes that when you are trapping RSI will point to a valid struct pt_regs. You probably want to verify that's the case and that rdi in the struct points to what you think it should. The easiest way to do that is to use GDB on your target kernel (either with qemu/kvm or gdbsx with Xen). Set a breakpoint at what you would with DRAKVUF and poke around using gdb to verify the kernel state matches your assumptions.

cvcachagua commented 2 years ago

Does anyone know what would need to be done (familiar with drakvuf make files) in order to use say src/libinjector/linux/methods/linux_read_file.h functions from within a plugin?

cvcachagua commented 2 years ago

Ok, after looking somewhat at the code, what I put in my last post cant work.

Kind of hoping for some clarification of Linux with drakvuf injector. So in using src/libinjector/injector.c There are a lot of parameters, some not too clear. having an overall picture of what is being done (looks like multiple things) I see in src/libinjector/linux/methods/*.c explanations of each process of injection. Which methods will really work for linux? read_file, write_file, shellcode, and exec are there, do they all work? My assumption right now is they do work.

I know this is not supposed to be done, but I hacked into the syscall plugin and I can determine when execve will be used so I know when my executable is being worked on by the kernel, yet injector uses syscall too -- can I use injector inside syscall launching it when execve shows my executable on the kernel? In this way I know the PID, but will injector work in this manor? It seems to be just another trap, that should come up immediately.

Functionality of tasks: Functionality of read_file -- it looks like this just reads a file on the guest system into another file on the guest system, I think both files must already exist. This will occur in a process from within the system (executed by a hijacked process that the user supplies, originating from vdso, but it does not stop original execution, just adds this task, and not sure when the extra task will happen, this should be stealthy as it is a vdso function).

Functionality of write_file -- it looks like this program writes to a file on the guest system from another file on the guest system. Both files must already exist. This will occur in a process from within the system (executed by a hijacked process that the user supplies, originating from vdso, but it does not stop the original execution, just adds this task, and not sure when the extra task will happen, this should be stealthy as it is a vdso function).

Functionality of shellcode -- (not too sure about this one) the provided by usr process when vdso is accessed shell execution will occur (executed by a hijacked process that the user supplies, originating from vdso, but it does not stop the original execution of the process, just adds this task, and not sure when the extra task will happen, but this should be stealthy as it is a vdso function). Shell code refers to an executable run from the shell ( something like /usr/bin/bash /usr/bin/a.out -- not too sure what is meant by shell code -- not sure about this, will ps/ptrace show that a new process was forked from the original?).

Functionality of exec -- the usr supplies an executable in the guest, when the executable is run by sys_execve, the original process will stop execution, and instead the executable provided on the guest (path and name) will be executed instead, and the original guest executable will not be run, after the execution of the injected process runs, the system will return like normal (not sure what ps/ptrace will show during this execution, and what the full path of the actual executed process will show during execution of the injected process -- in other words would it be stealthy?).

Some terminology (this perhaps may change according to the task at hand -- not sure about this): pid -- the target process to inject? tid -- target tid process to inject? app -- the name of the guest application, like: "/usr/bin/a.out" cwd -- for only exec/shell-exec? method -- I think this is enumerated found somewhere in one of the files; 0=create-proc (win), 1=term-proc (win), 2=shellexec (win), 3=shellcode (both), 4=read-file (both), 5=write-file (both), 6=exec-proc (lin) output_format -- totally confused, still looking for the enumeration (used =1 and it did not complain) binary_path -- for exec/shell-exec location of ... I think the to be injected binary.. not sure target_process -- what would be displayed by drakvuf_get_process_name(), or info->proc_data.name, when execve, which is the path to the executable break_loop_on_detection -- I think it ok to be false as I dont use, but? (injector_t*) injector2Bfreed -- originating trap from info->trap->data ? global_search -- used false, but it did not work wait4exit -- used false, but now I think it should be true, but not sure what it is for yet args_count -- for exec and shell-exec required args -- for exec and shell-exec required injected_pid -- I think this is just a variable that needs to be entered so that injector can show me the to be determined pid

-- I guess this is all documentation, sorry for the length, but please help.

tklengyel commented 2 years ago

Documentation is spotty unfortunately and the injection techniques aren't actively tested for Linux. They have worked in the past but right now the CI doesn't check for regressions in them when PRs are merged. Your best bet is to experiment with it and check which works for you.

cvcachagua commented 2 years ago

Thanks for getting back to me. I will update, if I find something I think may be useful to others. BTW -- shell code refers to exec under a shell, or is it bourn shell code?

tklengyel commented 2 years ago

Neither, it refers to shellcode .

cvcachagua commented 1 year ago

I am in a situation where the code seems to indicate that injection worked, but injection does not seem to have worked, though there is a very brief, but noticeable pause. I created a simple executable on the VM that would output "hello you", and another executable on the VM different location and different name that standard output was "hello world", and even though the code says it worked, the executable outputs to standard out "hello you". I used: const char* arg[2]; arg[0] = fullPathAndNameToExec-helloyou retvalue = injector_start_app( drakvuf, (vmi_pid_t) info->proc_data.pid, (uint32_t) info->proc_data.tid, the app -- was the full path to the executable on the VM, cwd -- was NULL, injection method -- was 6, output format type -- was 1 (not sure what type stands for), binary path -- fullPathAndNametoExec-helloworld, target process file -- NULL, break loop -- NULL [used linux, windows only parameter (WOP)], injector to be freed -- NULL (WOP), global search -- NULL (WOP), wait for exit -- NULL (WOP), number of args for i -- 0, arg -- arg, injected pid -- NULL)

I also tried to use injection method=5 and tried to write a file, but again, the code seems to indicate that it worked, but no file is created.

I think perhaps my: methodology is incorrect, my use case may not be correct, or there is a bug.

I would really appreciate help.