0

I'm trying to understand what information is left behind after an attacker exploits a vulnerable application leveraging a memory vulnerability (overflows, ROP, etc.).

I wanted to know:

  • What are the files that are looked at when trying to understand an attack?
    • Which logs (or other files) do you look at?
  • Are logs all the information you have?
    • Are there any other data that give useful information?
  • What do you mainly look for? That is, what pieces of information will help you determine the type of attack or where the vulnerability is?
  • I don't have full confidence but AFAIK there are no logs and really not much to find if they clean up after themselves and properly fork and detach from your program. I don't think you'll see that they got in by expecting the program image in memory but instead by all the nasty things they do after getting in. – Sirens Oct 17 '17 at 16:09
1

There are two places you could look for evidence of such an exploit: the syslog and coredumps.

System log

I'll assume you are talking about the record of the exploit attempt itself and nothing before it (such as remote access logs), and that you have not enabled any extra logging which may track file accesses or process creation. For the exploit itself, there is typically no trace left behind in log files if the attack succeeded. The only way there would be is if it failed at least once and triggered a segmentation fault, which may be logged to the kernel log buffer (shown via dmesg), which itself is likely logged by your choice of syslog daemon.

Core files

If you have coredumps enabled, then you may be able to get a lot more information about exactly what caused the segfault, assuming you have and know how to use gdb, and have the original binary which had crashed. as processes will log their core memory to the disk upon such a crash. But just like checking the system log, this only works if the exploit has failed at least once and triggered a crash. It also does not provide any information about who the attacker is, what their vector of exploitation was, what they are after, or what they used to attack. Also remember that, unless you have a special setup, coredumps are written to the disk with the same permissions as the program which crashed, so in some cases an attacker may have sufficient privileges to erase it (and examining that act goes into filesystem forensics, which I won't go into and isn't particularly related).

Compile-time hardening

As for what I do, I compile all programs locally, so I can use compile-time security features which attempt to trigger crashes on exploitation attempts. A few examples:

  • UBSAN (Undeined Behavior SANitizer) - Traps various types of undefined behavior to the ud2 instruction, which is an intentionally illegal instruction and crashes the program. Some types of undefined behavior can be used to exploit a program.

  • SSP (Stack Smashing Protector) - Creates a secret canary on the stack and aborts the program if it is ever overwritten. An attacker who only has an arbitrary write primitive will not be able to easily exploit a program which is utilizing SSP, unless they also have an arbitrary read primitive and can read the value of the canary (and put it in his overflow code).

  • FORTIFY_SOURCE - A collection of header files which wrap certain functions that expect a length as their argument, like strncpy(), to add runtime checks to ensure that the maximum length is not exceeded when it is known at compile-time.

  • PIE (Position Independent Executable) - Adds support to an executable for addressing libraries who's base is not known at compile-time. This allows a program to make use of ASLR, which loads libraries at random addresses to make it more difficult to exploit buffer overflows.

  • RELRO (RELocations Read Only) - In partial mode, makes ELF sections read-only after the linker has set everything up. In full mode, additionally makes the GOT read-only.

  • BINDNOW - Causes libraries to be resolved at executable load time, rather than when they are first referenced, which is necessary to benefit from the full potential of RELRO.

There are more security-related features which can be done at compile-time, like CFI and SafeStack. That was just a list of some of the most popular. You can use the updated checksec script to see whether or not a binary has bee compiled with various hardening flags:

$ checksec --file /bin/bash
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FORTIFY Fortified Fortifiable  FILE
Full RELRO      Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   Yes     13              33      /bin/bash

Log entry examples

Logs will generally only be created when the program is compiled with the correct hardening flags (with the exception of segfaults which will occur regardless of how you compile the binary). These hardening flags do not guarantee that an attacker will be unable to exploit a given binary, but it does make it more likely that an exploit will fail and possibly leave entries in logs. Some examples:

Segfault triggering SIGSEGV (in this case, a null pointer dereference):

$ gcc <<< 'void main() { char *a = 0; *a = 5; }' -x c - 2>/dev/null
$ ./a.out
a.out[24078]: segfault at 0 ip 000000720ab0b6f4 sp 000003c8ad38d170 error 6 in a.out[720ab0b000+1000]
Segmentation fault

UBSAN triggering SIGILL (in this case, an out of bounds index):

$ gcc <<< 'void main() { char a[2]; a[-1] = 5; }' -x c - -fsanitize=undefined -fsanitize-undefined-trap-on-error 2>/dev/null
$ ./a.out
traps: a.out[24130] trap invalid opcode ip:62a7e64673 sp:3be53c5fe20 error:0 in a.out[62a7e64000+1000]
Illegal instruction

Stack Smashing Protector (written only to stderr):

$ gcc <<< 'void main() { char a[2]; strcpy(a, "abcd"); }' -x c - -fstack-protector-all -include string.h 2>/dev/null
$ ./a.out
*** stack smashing detected ***: ./a.out terminated
Killed

FORTIFY_SOURCE (written only to stderr):

$ gcc <<< 'void main() { char a[2]; strncpy(a, "abcd", 4); }' -x c - -O -D_FORTIFY_SOURCE=2 -include string.h 2>/dev/null
$ ./a.out
*** buffer overflow detected ***: ./a.out terminated
Killed

But again, and I can't stress this enough, a successful exploit will not leave logs like these. You must check for signs of the original intrusion and not rely on the chance that their exploit fails.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.