ENPM809V
Injection
What we will be covering
- What is Injection
- LD_Preload
- ELF Poisoning
- /proc/pid/mem injection
- Ptrace
What is Injection?
What is Injection?
Injection is the art of inserting code/data to manipulate the program. Particularly we will focus on code injection.
Why is it useful?
- Stealing Data:
- Encryption Keys, Session Token, etc.
- Modify Behavior:
- Execute shellcode
- Execute code at a higher privilege level (through stealing handles)
- Dubugging - Yes Deubgging
- Inject breakpoints
Why is it hard?
- Not a straightforward API
- Some require system calls
- Utilize compiler/linking features
- Some features require higher privileges
- ptrace requires permissions to utilize the system call, and additional privileges to use all of the features.
- /proc/pid/mem injection requires the ability to write to the /proc filesystem
Why are we learning this?
- Malware use this technique for avoiding detection
- Hide in another process
- Blending in with the noise
- Exploits might use this to be able to launch things
- Why we must have proper sandboxing...
- And only give the minimum number of privileges necessary for a program to function
LD_PRELOAD
What is LD_PRELOAD
LD_PRELOAD is an environment variable that will tell the linker to loader symbols in a shared library before executing a program
This has the side effect of potentially overwriting functions that the program depends on (e.g. other shared libraries).
Example: LD_PRELOAD=./sharedobj.so ./my_prog
Modes and Helper-Variables
- LD_LIBRARY_PATH - Defines a path to look for shared libraries for the program to use
- Secure Mode - Ignores slashes in LD_PRELOAD (making it harder to look in a different directory)
Pros and Cons
- Pros:
- Non-invasive - Doesn't modify a running program
- Low-overhead - Doesn't create extra
- Cons:
- Can only replace dynamically-linked functions
- Doesn't work all the time with setuid binaries
- Needs to be done before executing the binary
- Requires ability to influence the environment
How to do this
- Reverse engineer the binary, see what function are present.
- Determine the function you want to overwrite
- Compile a shared object that contains a function with the same name (with whatever code you want)
- Run the binary with LD_PRELOAD
- LDPRELOAD=</path/to/your/so> /path/to/original/binary
Your Turn!
Class Assignment
- We have a binary called waiter...
- It waits way too long before completing
- Use LD_PRELOAD to make it go faster
ELF Poisoning
What is it?
- A method for inserting code to a binary, which will then be executed when the binary is started.
- Point the binary to your code, before calling the original
How do we get it to work?
- Re-Link the binary
- Take over an existing section of the binary that isn't necessary
- Take over existing headers that aren't needed
- Fit it in the cracks
What doe this actually look like?
R--
R--
R-E
RW-
R--
RW-
R-E
ELF Header
Header Table
Code
Data
What doe this actually look like?
R--
R--
R-E
RW-
R--
RW-
R-E
ELF Header
Header Table
Code
Data
Injected Code
How can this be done?
- C and Python have libraries for parsing ELF files
- Insert your code into the binary
- Use Write or Ptrace to write code into PLT, GOT, or even .txt file
- Manipulate the ELF header to point to your code
/proc/pid/mem injection
What is it?
- Overwrite the /proc/<pid>/mem file with your own data/code
- This allows manipulation of code and data while an application is running.
- Change code flow
- Manipulate data
- Insert new code into another process
- Requires root privileges
Process 1
Process 2
Process 1
Process 2
- open(/proc/2/mem)
- Seek to address
Write(fd, shellcode, size)
New Code
Breaking it Down
- Determine what process we want to inject into
- Find where we want to inject into.
- Is there a place where we can put code?
- Can we manipulate data?
- This needs to be reverse engineered
- Create the data that will be injected into the other process
- shellcode
- value/data
Breaking it Down
- Open the /proc/<pid>/mem file
- Make sure to retrieve the file descriptor we get
- Seek to the address we want to go to
- This is from what we reverse engineered
- Use lseek for this
- Write the data we would like into the file (at the position we seeked to).
Breaking it Down
/* What this would look like if it was a C program */
int main()
{
//This would be actual shellcode
char *shellcode = {0x1, 0x2, 0x3, ...};
size_t shellcodeSize = 0x20;
//Note O_WRONLY and SEEK_SET are #defines to integer values
fd = open(fd, /proc/<insert_pid_num_here>/mem, O_WRONLY);
lseek(fd, offset, SEEK_SET);
write(fd, shellcode, shellcodeSize);
return 0;
}
Note: you may need to convert this to shellcode too
Helpful Resources
- Utilize GDB to figure out where in the code you want to jump to (be careful if the binary is PIE/PIC)
- Utilize /proc/pid/mmap to figure out where data/code is laid out.
PTrace Injection
Applications Using PTrace
- GDB
- strace/ltrace
- code-coverage tools
Why is this useful for injection?
We can manipulate the program's memory, registers, and file-descriptors. In a sense, we can patch the program dynamically using the ptrace system call.
How PTrace work?
long ptrace (enum __ptrace_request request, pid_t pid, void *addr, void *data);
- PTRACE_ATTACH - Sends the SIGSTOP Signal to the thread and attaches tracer
- PTRACE_PEEKTEXT/DATA - Read data from the tracee
- PTRACE_POKETEXT/DATA - Write data to the tracee
- PTRACE_GET/SETREGS - Set the registers of the trace
- PTRACE_TRACEME - Request that the current process should be the tracee
- PTRACE_CONT - Continue execution
More in man ptrace
How would we inject a shared library via PTrace?
- Attach to a tracee (wait until it receives SIGTRAP)
- Find a safe spot in .text (code section) to overwrite
- Write in code to map memory for path string and new stack
- Update registers to call code and continue
- Add code to call dlopen
- Restore, cleanup, detach
Lets see an example of this
Note: there are multiple ways, but this is just one way.
See It In Action
Tracer:
- Atttach to target (PTRACE_ATTACH)
- Save State
- PTRACE_PEEKTEXT (introspect memory)
- PTRACE_GETREGS (to manipulate later)
libc.so
./target
Stack
See It In Action
Tracer:
- Inject Opcodes
- PTRACE_POKETEXT - glibc generally has e_entry unused, so it's safe to clobber
- Not necessarily the only place to do this
- Ex: 0x0F, 0x05, 0xCC = syscall; int3
- PTRACE_POKETEXT - glibc generally has e_entry unused, so it's safe to clobber
libc.so
./target
Stack
0x0F 0x05 0xCC
See It In Action
Tracer:
- Update registers to point to e_entry
- PTRACE_SETREGS
- rip -> location of glibc e_entry
- rax -> mmap syscall number
- Use calling convention to fill out mmap arguments
- PTRACE_SETREGS
libc.so
./target
Stack
0x0F 0x05 0xCC
See It In Action
Tracer:
- PTRACE_CONT
- Wait PID - wait to hit breakpoint
libc.so
./target
Stack
0x0F 0x05 0xCC
New Allocated Memory
See It In Action
Tracer:
- Inject indirect call
- Where do we put this?
- glibc unused e_entry
- 0xFF 0xD0 0xCC - call rax; int3
libc.so
./target
Stack
0xFF 0xDO 0xCC
New Allocated Memory
See It In Action
Tracer:
- Inject indirect call
- Where do we put this?
- glibc unused e_entry
- 0xFF 0xD0 0xCC - call rax; int3
- How do we do this?
- PTRACE_POKETEXT
libc.so
./target
Stack
0xFF 0xDO 0xCC
New Allocated Memory
See It In Action
Tracer:
- PTRACE_POKETEXT path to injectable shared library into Allocated memory
libc.so
./target
Stack
0xFF 0xDO 0xCC
New Allocated Memory
./inject.so
See It In Action
Tracer:
- PTRACE_SETREGS - For call to dlopen
- rip -> glibc_e_entry
- rax -> address to dlopen
- rsp->Newly allocated stack
- dlopen arguments
- rdi->.so path
- rsi->RTLD_LAZY - This is generally how shared libraries are loaded. It means that it will only fill the GOT/PLT when needed.
libc.so
./target
Stack
0xFF 0xDO 0xCC
New Allocated Memory
./inject.so
See It In Action
Tracer:
- PTRACE_CONT
- waitpid - Let's see what happens
libc.so
./target
Stack
0xFF 0xDO 0xCC
New Allocated Memory
./inject.so
See It In Action
Tracer:
- Cleanup
- munmap
- un-clobber glibc->e_entry
- put registers back
- PTRACE_DETACH
- Continue program as normal
- Ensure that we do not set PTRACE_O_EXITKILL or we will kill the process (very very very bad)
libc.so
./target
Stack
Ways to protect against this
- Disable ptrace if it's not a development machine
- Sandbox with seccomp
- Disable it system wide
- Do not have users with root privileges
- Not full-proof, but reduces risk
Your Turn!
Homework
We have an updated version of limited resources. Instead of having the write system call, we now have ptrace available. Figure out how to use ptrace and /proc/pid/mem injection in order to get the flag.
ENPM809V Injection
By Ragnar Security
ENPM809V Injection
- 118