-
UAF - pwnable.xyzWrite-ups/pwnable.xyz 2020. 2. 13. 22:22
Prob Info
Prob Info Checksec int __cdecl main(int argc, const char **argv, const char **envp) { Game *v3; // rsi const char *v4; // rdi __int64 savedregs; // [rsp+10h] [rbp+0h] setup(); initialize_game(); printf("Name: ", argv); v3 = cur; v4 = 0LL; read(0, cur, 0x7FuLL); while ( 1 ) { print_menu(v4, v3); read_int32(); switch ( (unsigned int)&savedregs ) { case 0u: return 0; case 1u: ((void (*)(void))cur->f_calc)(); break; case 2u: save_game(); break; case 3u: delete_save(); break; case 4u: v3 = cur; v4 = "Save name: %s\n"; printf("Save name: %s\n", cur); break; case 5u: edit_char(); break; default: v4 = "Invalid"; puts("Invalid"); break; } } }
prev_size 0xa1 name: 0x80 bytes *data calc 00000000 Game struc ; (sizeof=0x90, align=0x8, copyof_13) 00000000 name db 128 dup(?) 00000080 data dq ? ; offset 00000088 f_calc dq ? ; offset 00000090 Game ends
cur, saves를 분석해 위와 같은 구조체를 만들었다.
Game *initialize_game() { Game *result; // rax cur = (Game *)malloc(0x90uLL); cur->data = malloc(0x90uLL); cur->f_calc = calc; result = cur; saves[0] = cur; return result; }
이 함수에서 cur를 초기화하고 saves를 세팅한다.
int save_game() { Game *v0; // rbx __int64 v1; // rdx Game *v2; // rax signed int i; // [rsp+Ch] [rbp-14h] for ( i = 1; ; ++i ) { if ( i > 9 ) { LODWORD(v2) = puts("No space."); return (signed int)v2; } if ( !saves[i] ) break; } saves[i] = (Game *)malloc(0x90uLL); v0 = saves[i]; v0->data = malloc(0x90uLL); if ( !saves[i]->f_calc ) saves[i]->f_calc = cur->f_calc; printf("Save name: "); read(0, saves[i], 0x80uLL); v1 = i; v2 = saves[v1]; cur = saves[v1]; return (signed int)v2; }
save_game에서 이름을 최대 0x80 바이트 저장할 수 있다.
int edit_char() { int result; // eax unsigned __int8 v1; // [rsp+6h] [rbp-Ah] char v2; // [rsp+7h] [rbp-9h] puts("Edit a character from your name."); printf("Char to replace: "); v1 = getchar(); getchar(); printf("New char: "); v2 = getchar(); result = getchar(); if ( v1 && v2 ) { result = (unsigned __int64)strchrnul(cur->name, v1); if ( result ) *(_BYTE *)result = v2; else result = puts("Character not found."); } return result; }
이 함수는 이름에서 한글자 바꾸는 기능을 하는데, strchrnul을 사용한다. 이 함수의 설명을 봐보자.
man strchrnul 이 함수는 문자열을 찾지 못하면 널을 반환하는 것이 아니라 문자열의 끝 ("\x00")을 가리키는 포인터를 반환한다. 그러므로 문자열을 찾든 못찾든 아래 조건을 만족한다.
if ( result ) *(_BYTE *)result = v2;
힙 구조상 name을 0x80바이트 입력한 뒤 널바이트를 특정한 값으로 변경할 수 있어 Game::data 부분과 Game::f_calc 부분을 변경할 수 있어 함수 포인터를 win함수로 덮으면 된다.
'Write-ups > pwnable.xyz' 카테고리의 다른 글
fclose - pwnable.xyz (0) 2020.02.15 message - pwnable.xyz (0) 2020.02.15 iape - pwnable.xyz (0) 2020.02.13 J-U-M-P - pwnable.xyz (0) 2020.02.13 SUS - pwnable.xyz (0) 2020.02.13