본문 바로가기
CTF 분야/SmashTheStack

IO SmashTheStack level10

by 웃는 얼굴, 친절한 말, 따뜻한 마음 2015. 3. 11.
728x90

 

>> 소스코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//written by andersonc0d3
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
 
 int main(int argc, char **argv){
        FILE *fp = fopen("/levels/level10_alt.pass""r");      // fopen 함수 내부에 malloc을 통해 heap영역 할당
        struct {char pass[20], msg_err[20]} pwfile = {{0}};    //구조체가 선언
        char ptr[0];         //ptr이라는 배열 포인터가 선언
 
         if(!fp || argc != 2)
                return -1;
 
         fread(pwfile.pass120, fp);    // 구조체의 1부터 20만큼의 데이터를 읽음
pwfile.pass[19= 0;
        ptr[atoi(argv[1])] = 0;   // 입력된 argv[1] 을 정수형태로 변경하여 ptr에서 argv[1]만큼 떨어진 위치의 값을 0으로 변경
        fread(pwfile.msg_err, 119, fp);   // 구조체의 21부터 19만큼을 읽음
        fclose(fp);
 
         if(!strcmp(pwfile.pass, argv[1]))
                execl("/bin/sh""sh"0);
        else
                puts(pwfile.msg_err);
 
 
         return 0;
}
 
cs

 

>> 문제분석

문제에서는 구조체에 저장된 패스워드와 에러메세지를 파일포인터를 이용하여 읽는다.

그리고 파일을 읽는 fread 함수 중간에 ptr(atoi(argv[1])=0; 을 통해 1byte를 0으로 변경할 수 있도록 하고 있다.

이 문제는 특정 위치의 값을 0으로 변경 또는 덮어써서 pwfile.pass의 값을 알아내고

파라미터로 pwfile.pass의 값을 전달하여 shell을 얻는 문제이다.

 

>> 알아야할 개념

파일포인터와 파일포인터 구조

 

File 구조체는 /usr/include/libio.h (which is included in stdio.h) and struct defintion 에 정의되어 있다.

아래 명령어를 치면 Command 창에서 확인 할 수 있다. File은 _IO_FILE 구조체의 형태를 가지고 있다.

위 그림을 보면 File 은 _IO_read_base를 기준으로 _IO_read_ptr 위치의 값을 읽는 것을 알 수 있고

Library Buffer를 사용함을 알 수 있다.

 

>> 풀이

ASLR이 적용되어 있는 않은 경우 함수의 주소가 거의 변경되지 않음을 이용하여

/tmp 에 프로그램을 복사하여 ptr과 fp 구조체에서 _IO_read_base, _IO_read_ptr의 주소를 확인해보자.

파일구조체와 동일한 형태의 파일도 생성하고 파일의 경로도 수정한다.

A : 20개, C : 19개

주소 확인을 위해 아래 라인을 추가하였다.

printf("%p %p %p %p\n\n", fp, ptr, fp->_IO_read_base, fp->_IO_read_ptr);

Fp의 주소는 0x804a008 이고, library buffer의 base는 0xb7fde000 이다.

level10_alt.pass의 내용이 39자리로 0x27이기 때문에 현재 ptr의 주소는 0xb7fde027 임을 알 수 있다.

라이브러리 버퍼의 base에 내용 확인을 위해 ptr을 조작하는 라인을 추가하여 실행하면

아래와 같이 라이브러리 버퍼의 base라인부터 출력이 된다.

즉 _IO_read_ptr의 마지막 자리를 0으로 변경하여 password를 알아내야 한다.

브레이크를 걸어 Fp의 구조체를 확인해볼 수 있다.

 

Gdb를 이용하여 파일포인터의 위치에서 우리가 변경할 값이 4byte 떨어진 _IO_read_ptr 임을 확인할 수 있다.

 

0으로 변경해야할 위치는 0x804a008 + 4 = 0x804a00c 이고 fp 시작위치에서의 거리는

ptr[atoi(argv[1])] = 0;

prt[0] + argv[1] = 0x804a00c

gdb를 이용하여 ptr의 주소를 찾아보자.

위 구문은 atoi 된 결과가 저장된 eax의 값만큼 ptr의 주소가 저장된 ebp-0x58에서 떨어진 1 바이트를 0으로 바꾸는 명령이다.

여기에서 ptr은 ebp-0x58 위치에 저장되어 있음을 확인할 수 있다.

 

0xbffffc86 + argv[1] = 0x804a00c

argv[1] = 0x804a00c-0xbffffc86

FP는 heap영역을 사용하기 때문에 Bash shell을 이용하여 bruteforce로 근사치의 값을 적용해보자

for((i=1208262558;i<1208263558;i++)); do ./level10 $i | grep -v ACCESS ; done;

 

결과값을 넣어서 실행하면 여전히 ACCESS DENY가 나타난다.

!! 가 shell 명령의 특수기호이기 때문에 파일에 넣어 읽거나, python으로 전달해야 한다.

 

참고 : http://stackoverflow.com/questions/16424349/where-to-find-struct-io-file

반응형