
Show HN: dlinject.py – Inject a .so into a running Linux process, without ptrace - DavidBuchanan
https://github.com/DavidBuchanan314/dlinject
======
geofft
The tl;dr of the technique is to use /proc/$pid/mem to overwrite the stack.
(Since you don't have direct control of the instruction pointer this way,
there's some complexity in loading shellcode somewhere and having the process
return to it to get it to usefully execute your code, much like an actual
stack-corruption exploit.)

On a normal Linux system, /proc/$pid/mem is protected by the same kernel
permission check as ptrace, and a Linux security module like Yama, the thing
that disables ptracing unrelated processes on Ubuntu etc., will also block
this tool (which the README mentions). It seems like it's mostly useful for
cases where people are blocking the ptrace _syscall_ (like Docker's default
syscall filter, maybe?) and not loading an LSM.

Cool demonstration that blocking the ptrace syscall isn't sufficient. By the
way, blocking /proc too isn't sufficient either: there's the process_vm_writev
and process_vm_readv syscalls that work like writing/reading /proc/$pid/mem. I
think it's harder to write a robust tool using only those syscalls, but I
wouldn't bet on it being impossible.

If you really want to do syscall filtering to confine an untrusted process (as
opposed to reducing attack surface from potential bugs in otherwise-
permissible syscalls, which is I think Docker's goal), you need to start from
empty and allow syscalls instead of starting from full and blocking them.
Alternatively, maybe just run the untrusted code as another user account or
something.

~~~
saagarjha
> It seems like it's mostly useful for cases where people are blocking the
> ptrace syscall (like Docker's default syscall filter, maybe?) and not
> loading an LSM.

It’s a shot in the dark, but I’d guess this is designed to work with programs
that neuter themselves or commit suicide if you ptrace them, as is common for
mildly sophisticated malware or (less often) CTF challenges.

~~~
geofft
How do you detect that you're being ptraced? I see the link to
[https://www.aldeid.com/wiki/Ptrace-anti-
debugging](https://www.aldeid.com/wiki/Ptrace-anti-debugging) in the README,
but

a) the program has to actively run its check _while being ptraced_ to notice.
If you attach to the program (which pauses it), inject your code, run your
code, and then detach, the program's own code will not notice it's being
ptraced, no?

b) if you want to run the program's own code while being ptraced, can't you
just stop on the ptrace syscall and lie about its result? I think `strace -e
inject=ptrace:errno=0` will avoid the program in that wiki page from realizing
it's being traced. (You can use seccomp-bpf for this if you don't want to take
the performance overhead of stopping on every syscall.)

~~~
therein
> a) the program has to actively run its check while being ptraced to notice.
> If you attach to the program (which pauses it), inject your code, run your
> code, and then detach, the program's own code will not notice it's being
> ptraced, no?

Depends on if all threads were put into a paused state. Also, the program
that's being debugged might have spawned some additional processes that will
be checking if the parent is being ptraced.

Replace "self" with the PID you want to check if it is being traced:

    
    
      $ cat /proc/self/status  | grep Tracer                                                                                                                                                                                                                                                                                                                                                                       
      TracerPid: 0

~~~
geofft
Ahhh, yes, it's probably hard to track down a random subprocess somewhere
that's checking and then passing that info back to the original process. (I
think the subprocess could even just try to PTRACE_ATTACH the original process
and see if it works.)

------
frijolito
I'm new to the cyber world and have a few questions about how this works under
the hood since my exposure to memory management is really minimal. Why did we
have to mess with this shellcode within the injector (dlinject.py) rather than
just somehow identifying the PID of the target and loading the injected code
directly? Is it not possible via some simply python call to perform the
injection, and is that why we're playing with the x86?

Also, would this work on systems with ASLR?

Could malicious code be obfuscated, transformed, and then subsequently
injected into a process like explorer with this dlinject.py? What are our
limitations here?

~~~
saagarjha
> Why did we have to mess with this shellcode within the injector
> (dlinject.py) rather than just somehow identifying the PID of the target and
> loading the injected code directly? Is it not possible via some simply
> python call to perform the injection, and is that why we're playing with the
> x86?

Yes, there is no simple way to do this. Injecting arbitrary code into a
process is always finicky, and this project is even more so because it eschews
the API used for doing stuff like this (ptrace, which is still on the level of
registers and memory addresses) and going through other kernel interfaces to
try to perform similar things.

> Also, would this work on systems with ASLR?

It does, because it defeats ASLR by looking at the process’s memory mappings
and finding libc in that (which it uses for dlopen).

> Could malicious code be obfuscated, transformed, and then subsequently
> injected into a process like explorer with this dlinject.py? What are our
> limitations here?

None, it can inject pretty much anything. The only limitation is security
protections that the OS provides to stop you from touching other processes in
this way.

------
quotemstr
If you want to avoid the side effects of SIGSTOP, you can put the target
process in a freezer cgroup instead.

~~~
DavidBuchanan
This was recently suggested to me on Twitter, I'll have a look at implementing
it tomorrow.

[https://twitter.com/arnaud_lb/status/1212512382859251712](https://twitter.com/arnaud_lb/status/1212512382859251712)

------
s_Hogg
What are the circumstances in which someone would be doing something like
this?

~~~
matheusmoreira
Debugging a program that resists ptrace.

