3. 버퍼 오버플로 (bof) - Pwnable.kr 해설

2020. 3. 9. 21:44문제연습/Pwnable.kr (PWN)

반응형

 

티스토리 블로그는 PC 환경에 최적화되어 있습니다.

모바일 유저분들은 아래 네이버 블로그를 이용해 주세요.

 

3. 버퍼 오버플로 (bof)

네이버 블로그는 모바일 환경에 최적화되어 있습니다.PC 유저분들은 아래 티스토리 블로그를 이용해 주세...

blog.naver.com

 

문제 내용

Nana told me that buffer overflow is one of the most common software vulnerability.

Is that true?

 

Download : http://pwnable.kr/bin/bof

Download : http://pwnable.kr/bin/bof.c

 

Running at : nc pwnable.kr 9000

 

들어가기 전

What is Buffer Overflow?

 

1. Buffer? (from wiki)

버퍼(buffer,문화어: 완충기억기)는 데이터를 한 곳에서 다른 한곳으로 전송하는 동안 일시적으로 그 데이터를 보관하는 메모리의 영역이다.

 

2. Buffer Overflow? (from wiki)

버퍼 오버플로(영어:buffer overflow) 또는버퍼 오버런(buffer overrun)은 메모리를 다루는 데에 오류가 발생하여 잘못된 동작을 하는 프로그램 취약점이다. 컴퓨터 보안과 프로그래밍에서는 프로세스가 데이터를 버퍼에 저장할 때 프로그래머가 지정한 곳 바깥에 저장하는 것을 의미한다.

 

벗어난 데이터는 인접 메모리를 덮어쓰게 되며 이때 다른 데이터가 포함되어 있을 수도 있는데, 손상을 받을 수 있는 데이터는 프로그램 변수와 프로그램 흐름 제어 데이터도 포함된다. 이로 인해 잘못된 프로그램 거동이 나타날 수 있으며, 메모리 접근 오류, 잘못된 결과, 프로그램 종료, 또는 시스템 보안 누설이 발생할 수 있다.

 

버퍼 오버플로가 코드를 실행시키도록 설계되거나 프로그램 작동을 변경시키도록 설계된 입력에 의해 촉발될 수 있다. 따라서 이는 많은 소프트웨어 취약점의 근간이 되며 악의적으로 이용될 수 있다. 경계 검사로 버퍼 오버플로를 방지할 수 있다.

 

3.principle of attack(from wiki)

아래 사진을 예시로 들겠습니다.

A는 8바이트 문자열을 저장하는 스트링 버퍼, B는 2바이트 정수를 저장하는 정수로 정의를 하겠습니다.

이때 A 스트링 버퍼에 8바이트가 넘는 문자열인"sexybacke"를 저장하면 다음과 같이 메모리가 변경됩니다.

스트링의 끝을 알리기 위해 sexybacke 문자열 뒤에 널(NULL) 값인 0이라는 1바이트 값이 자동적으로 저장됩니다.

그러면 프로그래머가 의도하지 않아도 B의 변수값이 문자열 "e"로 변경되어 세그먼테이션 오류를 발생시킵니다.(단 리틀 엔디언 환경에 한정입니다.)

 

만약 공격자가 버퍼 오버플로를 이용하여 함수 포인터나 리턴 주소에 자신이 원하는 주소를 집어넣는 경우 공격자의 공격 코드를 실행시키는 방식으로 사용될 수 있습니다.

오버플로 공격 기법을 막기 위한 여러 가지 기법들이 존재하는데, 그 부분은 다음에 설명을 드리도록 하겠습니다.

 

그럼 오늘의 문제 bof를 풀어보도록 하겠습니다.

 

 

문제 해설

우선 리눅스 터미널에 들어가지 말고 브라우저 창을 띄운 후 주소창에http://pwnable.kr/bin/bof.c을 입력합니다.

 

위 주소로 들어가면 다음과 같은 소스코드가 보일 것입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
    char overflowme[32];
    printf("overflow me : ");
    gets(overflowme);    // smash me!
    if(key == 0xcafebabe){
        system("/bin/sh");
    }
    else{
        printf("Nah..\n");
    }
}
int main(int argc, char* argv[]){
    func(0xdeadbeef);
    return 0;
}
 

현재 func라는 함수에 0xdeadbeef라는 값을 넣고 이 값이 0xcafebabe와 같으면 system 함수를 이용해 flag를 출력합니다.

 

흠, 그런데 저번 시간처럼 기존의 입력하는 방식으로는 값을 일치시킬 수가 없습니다.

그러면 실행 파일을 분석해 보도록 하겠습니다.

 

브라우저에 http://pwnable.kr/bin/bof를 입력하여 실행 파일을 다운로드하고 파일을 gdb로 분석하도록 하겠습니다.

 

우선 cd 명령어로 다운로드한 파일이 있는 경로로 이동하시고, gdb bof(실행 파일 이름)을 입력하여 분석해 보겠습니다.

 

-What is GDB? How to use GDB?

보통은GDB라고 부르는GNU 디버거(GNU Debugger)는GNU소프트웨어 시스템을 위한 기본디버거이다.

아래에 GDB 디버거의 기본 명령어에 관한 링크를 참고해 주세요.

 

GDB 명령어 정리

(1) 시작과 종료 - 시작 : gdb [프로그램명][core 파일명][PID] - 종료 : q or ctrl + d (2) 소스보기 ( list or l ) - list : main 함수 기점으로 소스 출력 - list 10 : 10행을 기준으로 출력 - list func : fun..

mintnlatte.tistory.com

 

앞서 bof.c 파일에서 보았듯 bof 파일은 크게 main, func 함수로 이루어져 있습니다.

각각의 함수 메모리 구조를 알아보도록 하겠습니다.

터미널 창에 disas main, disas func를 입력하여 main, func 함수의 어셈블리 코드를 출력해 보도록 하겠습니다.

 

여기서 왜 어셈블리 코드를 보는지 그 이유는 아래 링크를 이용해 주세요.

 

[C언어] 컴파일 과정(Compile Process) 4단계 자세한 설명

컴파일 과정 Visual Studio에서 우리는 실행할때 F5(또는 Ctrl+F5)를 눌러서 우리가 만든 소스코드를 실행시켜봤죠? 우리는 너무 쉽게 프로그램을 실행시킨다고 생각할 수 있지만 의외로 몇몇 단계를 거치고 있습..

reakwon.tistory.com

 

명령어를 입력하시면 아래 코드가 보일 것입니다.

이제 추측을 해보도록 하겠습니다.

 

문제의 힌트는 Buffer Overflow 기법을 이용하는 것이며 소스 코드에서는 gets 함수를 이용해서 공격해라는 주석문이 달려 있습니다.

 

gets 함수 얼마나 많은 문자를 읽어들일 것인지에 대해 지정하지 않기 때문에 사용자가 초과하여 값을 입력하는 오버플로 취약점을 가진 함수입니다.

 

main 함수에서 func 함수가 호출되는 주소가 0x0000069a입니다.

그리고 func 함수에서 printf 함수 호출 후 gets 함수가 호출되므로 gets 함수의 call 위치는 0x0000064f입니다.

 

-만약 여기까지 보고 방법이 떠오르셨다면 아래 내용을 보지 말고 한 번 더 다시 풀어보세요!!!

그럼 bof 문제 공략을 세워 보도록 하겠습니다.

 

 

1. gets 함수를 통해 입력받는char overflowme[32] 공간의 위치를 찾는다.

위 사진에서 처음 call 한 함수는 printf 함수이고 그다음 0x0000064f에서 call된 함수는 gets 함수라고 말했습니다.

call 다음에 위치한 -0x2c에 우리가 찾던 char overflowme[32]가 위치할 것입니다.

 

2. gets 함수를 통해 오버플로 하여 입력된 key(0xdeadbeef)을 0xcafebabe로 덮어 버립니다.

char overflowme[32] + 쓰레기 값(dummy) + 0xcafebabe(덮어버릴 값) = 52 + 4. 여기서 char overflowme[32] + 쓰레기 값(dummy)는 위 사진에서 -0x2c ~ 0x8입니다.

 

 

 

+2020/03/20 풀이 내용 추가

위 내용을 처음 디버깅하는 분은 의문점이 들것입니다.

 

1. 어째서 overflowme[32]의 배열 시작 주소가 %ebp-2c인 것을 알았을까???

 

2. 32byte 크기의 배열의 위치와 key 변수의 거리를 알아내서 gets 함수로 overflow 해서 덮어 씌우는 건 알겠는데, key 변수가 위치한 곳이 $ebp+0x8인 것은 어떻게 알았지???

 

3. eax가 함수의 리턴 값으로 사용된다는데, disas func에서 mov eax, gs:0x14 과정은 뭐지??

 

차근차근 설명해 드리도록 하겠습니다.

(gdb로 분석을 하려고 하였으나 표기에 오류를 범할 가능성이 있어 gdb peda로 분석을 진행합니다.)

 

1. overflowme[32]의 배열 시작 주소(gets 함수의 인자, 리턴 값은 아래 링크를 이용해 주세요.)

 

gets 함수 – 언제나 휴일

char *gets(char *buffer); 표준 입력 스트림의 버퍼에서 문자열을 읽는 함수 입력 매개 변수 리스트 buffer: 문자열을 보관할 버퍼 주소 반환 값 성공 시 입력 인자로 받은 str 반환, 실패 시 0 반환 gets 함수는 입력 버퍼의 크기를 전달받지 않아 버퍼의 크기를 모릅니다. 이는 최종 사용자가 버퍼의 크기보다 많이 입력했을 때 버퍼 오버플로우 문제가 발생할 수 있습니다. C11에서는 gets 함수 대신 버퍼 오버플로우 문제를 개

ehpub.co.kr

gets 함수는 일단 인자로 문자열을 저장할 버퍼 주소가 들어가야 합니다.

즉, call gets을 하기 전에 먼저 인자를 정리하는 부분이 반드시 존재하게 되는데, 그 부분이 바로 mov -0x2c(%ebp), eax입니다.

 

(puts 함수도 문자열 인자를 받는 과정이 존재합니다. mov 0x78c, (%esp) 한번 gdb 열고 x/s 0x78c를 입력해 보세요. 그러면 0x78c에 저장되어 있는 값이 문자열로 표현이 되는데 그 결과가 "overflow me : "입니다!!!)

 

그래서 -0x2c(%ebp) 즉 %ebp-0x2c가 배열의 시작 주소라고 추측했습니다.

 

2. key 변수 데이터의 위치

이건 위의 내용보다 간단합니다.

cmpl $0xcafebabe, 0x8(%ebp)를 하고 있습니다. (gdb 기준)

0x8(%ebp)은 ebp+0x8 위치의 주소에 저장한 값을 가지고 오겠다는 의미입니다. (포인터와 비슷하지요???)

아까 어셈블리 코드와 C 소스코드를 비교할 때 key 변수의 데이터가 ebp+0x8 위치의 주소에 0xdeadbeef가 있다는 것입니다.

(참고로 gdb peda를 사용하면 dword ptr [ebp+0x8]로 표기되는데, 풀이하면 ebp+0x8의 주소에 저장되어 있는 값중 4byte을 불러온다는 뜻입니다.)

 

16진수 1개가 4비트, 즉 0xdeadbeef는 총 4*8=32bit=4byte이므로 논리에 맞음을 알 수 있습니다.

정리하면 다음과 같습니다.

-0x2c(%ebp) 지점 ~ 32byte 떨어진 지점: overflowme[32]

0x8(%ebp) : 0xdeadbeef가 저장된 곳(key 변수의 데이터)

 

즉, 값을 덮어 씌우기 위해서는 0x2c+0x8 = 0x34 = 52, (gdb peda 기준으로) dword ptr [ebp+0x8]는 ebp+0x8을 기준으로 4byte 데이터(0xdeadbeef)를 가지고 오는 것이니 +4byte(0xcafebabe)가 되는 것입니다.

 

즉, eax에 ebp-0x2c 주소가 저장되기에 gets 함수를 통해 eax로 ebp-0x2c~ebp+0x8을 쓰레기 값으로 채우고 ebp+0x8에서 4바이트 만큼의 주소에 저장된 0xdeadbeef라는 값을 0xcafebabe으로 덮어쓰기 하면 dword ptr [ebp+0x8] 결과가 0xcafebabe가 되겠지요???

 

3. mov eax, gs:0x14 과정

이것은 stack 메모리 오염을 방지하기 위한 메모리 보호 기법 중 하나인 Stack Canary 기법입니다.

정확한 내용은 아래 링크를 이용해 주세요.

 

유저 영역 Stack Canary 분석

1. 서론 32/64bit 환경에서 Stack Canary 기법이 걸려있는 것을 많이 볼 수 있다. 여러 pwnable 문제만 봐도 Stack Canary + Heap Exploit이 거의 대다수를 이루고 있다. 근데 여태까지 Stack Canary가 Mitigation..

nekoplu5.tistory.com

 

gets 함수를 통해 56바이트 데이터를 덮어버리면 if 문에서 사용되는 key 값이 0xcafebabe로 변조되어 if 문의 조건을 충족시키게 되고, system 함수를 통해 쉘 권한을 얻을 수 있습니다.

 

그렇게 되면 단순히 cat flag를 통해 flag를 볼 수 있을 것입니다.

 

인자는 파이썬으로 리틀엔디언 방식을 준수하며 넘겨줍시다.

(단 문제에서 nc pwnable.kr 9000에서 실행해라고 했으니 파이프도 사용하 도록하겠습니다.)

 

 

최종적으로 다음과 같이 입력하시면 됩니다.

(python -c "print('A'*52+'\xbe\xba\xfe\xca')";cat) | nc pwnable.kr 9000

cat을 사용하는 이유, 파이프( | ), pwntools을 이용한 파이썬 코드는 아래 링크를 이용하시면 보다 자세히 알 수 있습니다.

 

시스템 해킹

cd80 ? ~ python2 -c 'print "\xbe\xba\xfe\xca"' | xxd - 00000000: beba feca 0a ..... ? ~ python3 -c 'print("\xbe\xba\xfe\xca")' | xxd - 00000000: c2be c2ba c3be c38a 0a ......... ? ~ 위 차이때문에 안되나보네요 cat을 붙여주는 이유는 cat을 그냥 실행해보면 ➜ ~ strace -if /bin/cat 2>&1 |

www.hackerschool.org

 

pwnable.kr [bof] 풀이

bof - 5 pt [writeup] Nana told me that buffer overflow is one of the most common software vulnerability. Is that true? Download : http://pwnable.kr/bin/bof Download : http://pwnable.kr/bin/bof.c Run..

mandu-mandu.tistory.com

 

[Linux, Unix]다중명령어(세미콜론(;), 파이프pipe(|), 더블 엔퍼센트 &&, ||)의미,사용법과 차이점

[리눅스 Linux] 리눅스 완전 정복 : 리눅스 목차 안녕하세요 오늘은 다중 명령어에 대해서 알아볼거예요 명렁어가 짧아도 매번 한줄에 한 명령어씩 치면서 연습했었죠? 하지만 리눅스는 한 라인(셸 프롬프트)에 여..

jhnyang.tistory.com

 

쉘 권한을 따고 cat 명령어를 통해 flag를 출력한 모습입니다.

 

오늘 내용은 여기까지입니다.

다음 시간에는 flag을 풀어보도록 하겠습니다.

이상 ICMP였습니다!!

반응형

'문제연습 > Pwnable.kr (PWN)' 카테고리의 다른 글

5. passcode - Pwnable.kr 해설  (0) 2020.03.12
4. flag - Pwnable.kr 해설  (0) 2020.03.11
2. 충돌 (collision) - Pwnable.kr 해설  (1) 2020.03.08
1. 파일 디스크립터(FD) - Pwnable.kr 해설  (0) 2020.03.08
Pwnable.kr 준비  (0) 2020.02.22