zznop / drow

Injects code into ELF executables post-build
MIT License
222 stars 37 forks source link

Segmentation fault on CentOS and Debian #2

Closed ghost closed 4 years ago

ghost commented 4 years ago

The tool works great on Ubuntu however we're experiencing crashes on CentOS and Debian. I've included 2 patched binaries one created under latest Debian and another created under Centos 6.10. Let us know if there is anything we can do to help. debian-ls-patched-crashing.gz centos-ls-patched-crashing.gz

[root@localhost drow]# cp /bin/ls ./ cp: overwrite `./ls'? y [root@localhost drow]# ./build/drow ls ./build/rappers_delight.bin ls-bd


(  _ \(  _ \(  _  )( \/\/ )
 )(_) ))   / )(_)(  )    (
(____/(_)\_)(_____)(__/\__)

[] Mapping file: ls [] Mapping file: ./build/rappers_delight.bin [] Finding last section in executable segment ... [+] Found executable segment at 0x00000040 (size:000001c0) [+] Found executable segment at 0x00000000 (size:0001851c) [+] Found .eh_frame at 0x00016540 with a size of 8156 bytes [] Expanding .eh_frame size by 8192 bytes... [] Adjusting Section Header offsets ... [] Adjusting Program Header offsets ... [] Adjusting ELF header offsets ... [] Modifying ELF e_entry to point to the patch at 0x0001851c ... [] Exporting patched ELF to ls-bd ... [] Writing first part of ELF (size: 99612) [] Setting old and new e_entry values in stager ... [] Writing stager stub (size: 49) ... [] Writing patch/payload (size: 289) [] Writing pad to maintain page alignment (size: 7854) [*] Writing remaining data (size: 9596) [+] ELF patched successfully! [root@localhost drow]# ./ls-bd Segmentation fault (core dumped)

zznop commented 4 years ago

Bummer, due to a limitations in the technique that drow uses there must be atleast a page (4096 bytes) of slack space between the end of the RX segment and the start of the next segment.

So, what is likely occuring is it is successfully injecting the code into the binary and adjusting the offsets. However, the .fini and the .rodata sections have overlapping virtual mappings (see below). The dynamic linker is likely trying to map the .fini section overtop of part of the .rodata section (or vice-versa). Causing it to crash.

image

This is likely not an issue with the binaries tested on Ubuntu because either the RX segment was the last segment in the binary, or there was plenty of slack space between where the RX segment is mapped relative to where the next segment is mapped in memory. I will modify drow to error out under these conditions.

zznop commented 4 years ago

Checkout 2-overlapping-mapping-check, build it, and try to run it against your version of ls again. I expect it to bail and not create the patched binary. Also, would you mind sharing the unmodified version of those binaries? I'd like to look into how I can still make it work. With position-independent binaries I could likely modify the DT_DYNAMIC entries to move around segments. Unfortunately you can't do that with position-dependent binaries.

ghost commented 4 years ago

Thanks for looking into this. I just checked out the new branch it does indeed throw an error and bails out. I've also attached the ls binaries from Centos 6.0 and latest Debian.

centos_ls.gz debian_ls.gz

[user@localhost build]$ ./drow ls ./rappers_delight.bin ls-bd


(  _ \(  _ \(  _  )( \/\/ )
 )(_) ))   / )(_)(  )    (
(____/(_)\_)(_____)(__/\__)

[] Mapping file: ls [] Mapping file: ./rappers_delight.bin [*] Finding last section in executable segment ... [+] Found executable segment at 0x00000040 (size:000001c0) [!] RX segment is not injectable. Must contain atleast 4096 bytes of slack space before next segment mapping

zznop commented 4 years ago

Backdoor Factory for example tries to fit within code caves without expanding the size of segments. I'll likely add a feature that first checks if the payload can fit in the slack space between two sections in the RX segment or at the end of the RX segment on the last page of memory. If it won't fit, then it will check if it can expand the size of the RX segment without overlapping with the next segment. Unfortunetely, I don't think I'll have time to look at this until atleast Friday. I'll let you know when I have something worth testing.

zznop commented 4 years ago

@EMCELLY I've just pushed code that works for your debian ls binary (I did not test with CentOS). Pull latest on 2-overlapping-mapping-check. See the commit here:

https://github.com/zznop/drow/pull/3/commits/7b8bd9c2ca8388f6f43e9cfd4d9a8e7cd164b47c

This adds a second technique. Drow now attempts to inject at the end of the last page of the RX segment without expanding. If the payload won't fit, it will determine whether or not it's safe to expand the segment. If it can safely expand the segment it will do so and inject in the newly added pages. Otherwise, it will bail.

I have not tested if there are any regressions with the Ubuntu binaries as a result of this change. Please try out the changes and let me know if you have any issues.

ghost commented 4 years ago

I did some quick testing here are the results.

Centos 8.1 - works as expected. Ubunutu 18 - segfault in drow Ubuntu 16 - segfault in drow Centos 7.-0 - segfault in drow Centos 6.0 - segfault in drow

Attaching some gdb logs and a core file since they all seem to be the same issue on line 103 of find_exe_seg_last_section function.

core.15948.gz ubuntu-18.crash.txt ubuntu-16.crash.txt centos-7.0.crash.txt centos-6.0-crash.txt