1. 파일 디스크립터(FD) - Pwnable.kr 해설

2020. 3. 8. 03:51문제연습/Pwnable.kr (PWN)

반응형

 

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

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

 

1. 파일 디스크립터(FD)

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

blog.naver.com

 

이번 시간에는 포너블 kr의 첫 번째 문제인 fd를 풀어보도록 하겠습니다.

 

문제 내용

Mommy! what is a file descriptor in Linux?

 

* try to play the wargame your self but if you are ABSOLUTE beginner, follow this tutorial link:

https://youtu.be/971eZhMHQQw

 

ssh fd@pwnable.kr -p2222 (pw:guest)

 

 

들어가기 전

What is File Descriptor???

리눅스는 물리적 장치들을 일종의 파일로 관리합니다.

특히 리눅스 셸은 작업에 필요한 파일들을 일종의 번호를 붙여서 관리하는 데 이를 '파일 디스크립터'라고 부릅니다

 

표준 입출력 장치의 파일 디스크립터는 다음과 같습니다

파일 디스크립터

FD 대신 사용하는 이름

목적

0

stdin

표준 입력

1

stdout

표준 출력

2

stderr

표준 에러

 

FD에 대한 정확한 정보는 아래 링크를 참고해 주세요.

 

리눅스 - 파일 디스크립터

File Descriptor (파일 디스크립터) [출처: http://dev.plusblog.co.kr/22] 1. 파일 디스크립터 - 시스템으로부터 할당 받은 파일을 대표하는 0이 아닌 정수 값 - 프로세스에서 열린 파일의 목록을 관리하는 테이..

dev-ahn.tistory.com

 

 

문제 해설

리눅스 터미널 화면을 열어서 ssh fd@pwnable.kr -p2222로 접속하고 pw인 guest를 입력하여 시작합니다.

ls 명령어를 이용해서 디렉터리 내용을 확인해보니 (터미널 창에 ls 입력 후 엔터) fd fd.c flag 세 가지가 존재합니다.

우선 확장자가. c인 fd.c 파일을 읽어 봅시다. (터미널 창에 cat fd.c를 입력 후 엔터)

 

리눅스 명령어에 관한 내용은 아래 링크를 이용해 주세요.

 

[linux] 리눅스 기본 명령어/자주 쓰는 명령어

리눅스 기본 명령어

itholic.github.io

 

다음과 같은 소스코드가 나올 것입니다. (주석은 제가 설명상 편의를 위해 추가하였습니다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){ //1
    if(argc<2){  //2
        printf("pass argv[1] a number\n");
        return 0;
    }
    int fd = atoi( argv[1] ) - 0x1234//3
    int len = 0;
    len = read(fd, buf, 32);  //4
    if(!strcmp("LETMEWIN\n", buf)){ //5
        printf("good job :)\n");
        system("/bin/cat flag");
        exit(0);
    }
    printf("learn about Linux file IO\n");
    return 0;
 
}
 
 

 

1
int main(int argc, char* argv[], char* envp[]){ //1
 

//1

main 함수에 argc, ...의 인자가 들어갑니다.

각각의 인자를 간단히 설명드리겠습니다.

 

argc : main 함수에 전달되는 인자의 개수

argv : main 함수에 전달되는 인자

 

예를 들어 리눅스 터미널에 아래와 같이 입력했다고 가정하겠습니다.

./fd 1234 ICMP

 

이렇게 입력하면 인자는 ./fd(파일의 경로), 1234, ICMP 3개가 main 문에 전달됩니다.

그러면 argc = 3, argv[0] = ./fd, argv[1] = "1234", argv[2] = "ICMP"가 됩니다.

아래 그림과 링크를 이용하면 좀 더 자세한 내용을 알 수 있습니다.

 

C언어 main 함수에게 인자 전달(argc, argv)

메인 함수를 보면 int main(int argc, char* argv[]) 이렇게 되어있는 것을 보셨을 겁니다. argc argument count argv argument vector 라고 하며 변수명은 변경되어도 상관없습니다. #include int main..

qzqz.tistory.com

다시 코드로 돌아오도록 하겠습니다.

 

1
2
3
4
 if(argc<2){  //2
        printf("pass argv[1] a number\n");
        return 0;
    }
 
 

//2

위 조건문을 다시 해석하자면 main 문에 전달되는 인자의 개수(argc)가 2보다 작으면 pass argv[1] a number을 출력하고 프로그램을 종료해 버립니다.

 

우리는 flag를 찾는 것이 최종 목표니 프로그램이 종료되지 않으려면 main 인자 개수가 2개 이상이어야겠지요??

다음 포인트로 넘어가 보겠습니다.

 

 

1
int fd = atoi( argv[1] ) - 0x1234;  //3
 

//3

int형 변수인 fd에 값을 저장하고 있습니다. 위 코드에서 보이는 atoi 함수는 문자열을 정수 타입으로 바꿔주는 함수입니다. 예를 들어서 "1234"라는 문자열을 atoi 함수에 넣으면 int형의 데이터인 1234로 바뀝니다.

 

정리하자면 main 함수에 전달된 2번째 인자를 atoi 함수로 int형으로 자료를 변환하고 16진수인 0x1234를 뺀 값을 fd에 저장합니다.

 

1
len = read(fd, buf, 32);  //4
 

//4

이 부분이 fd 문제의 가장 핵심인 부분입니다.

우선 read 함수 인자를 간단히 설명드리도록 하겠습니다.

ssize_t read(X, Y, Z)

X에는 파일 디스크립터 , Y에는 읽은 데이터를 저장할 버퍼를, Z에는 얼마큼 읽을지를 전달해 주는 인자입니다.

파일을 정상적으로 읽는데 성공하면 0보다 큰 수를 리턴하고, 반대로 파일을 읽는데 실패하면 0을 리턴합니다.

 

예를 들어 fd의 값이 0이면 사용자로부터 표준 입력(키보드)을 받은 데이터를 buf에 저장하게 될 겁니다.

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

read 함수에 대한 정확한 설명은 아래 링크를 참고해 주세요.

 

[파일입출력] 2. read()로 파일 읽기

지난 번 파일을 열었다면, 이번엔 열려있는 파일을 읽어보겠습니다. 가장 대표적인 저수준 파일 입출력에서는 POSIX에 정의된 read() 시스템 콜을 사용하여 파일을 읽어 들이는데요, 파일 오픈 시 사용했던 파일..

sonseungha.tistory.com

 

 

1
2
3
4
5
if(!strcmp("LETMEWIN\n", buf)){  //5
        printf("good job :)\n");
        system("/bin/cat flag");
        exit(0);
   }
 

//5

if 조건문에서 buf의 내용이 LETMEWIN\n과 일치하면 아래 문자열을 출력하고 system 함수를 통해서 flag를 출력해 줄 것입니다.

 

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

1. fd 값을 0으로 만들어 read 함수의 첫 번째 인자인 파일 디스크립터를 0으로 인식하도록 한다.

 

int fd = atoi( argv[1] ) - 0x1234; //3

0x1234를 10 진수로 바꾸면 4660입니다. main 함수에 전달될 2번째 인자를 "4660"으로 전달하면 fd의 값이 0으로 저장될 겁니다

 

2. 표준 입력을 이용하여 buf에 LETMEWIN\n을 저장하여 if 문을 만족시킨다.

이건 따로 설명이 필요 없습니다. 그냥 터미널 화면에 그대로 입력하면 됩니다.

 

3. system 함수를 통해 출력된 flag를 얻는다.

 

터미널에 입력 후 flag가 출력된 모습입니다.

 

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

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

이상 ICMP였습니다!!

 

반응형

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

5. passcode - Pwnable.kr 해설  (0) 2020.03.12
4. flag - Pwnable.kr 해설  (0) 2020.03.11
3. 버퍼 오버플로 (bof) - Pwnable.kr 해설  (0) 2020.03.09
2. 충돌 (collision) - Pwnable.kr 해설  (1) 2020.03.08
Pwnable.kr 준비  (0) 2020.02.22