오늘은 pwnable.kr의 첫 번째 문제 fd를 풀어보도록 하겠습니다!
먼저 pwnable.kr에 접속합니다.
첫 번째 문제인 fd를 누릅니다.
프로토콜은 ssh, ID는 fd, HOST는 pwnable.kr, PORT는 2222, PW는 guest로 접속하는 것 같네요.
칼릴 리눅스를 켜고 접속합니다.
먼저 해당 디렉토리에 어떤 파일이 있는지 ls-al 명령으로 확인합니다.
숨김 처리된 파일을 제외하면 fd, fd.c, flag 파일이 있음을 확인할 수 있습니다.
여기서 ls의 -l 옵션은 파일과 디렉토리의 내용을 자세히 출력해 줍니다.보는 방법은 다음과 같습니다.예를 들어 내용이 아래와 같을 때 맨 앞이 -이면 파일, d이면 디렉토리를 의미하고 rw-r—는 퍼미션 정보를 나타냅니다. rw-r–r—소유자 권한 그룹 소유자 권한 일반 사용자 권한
rootroot은 소유권, 소유 그룹을 나타내고 418은 디렉토리(또는 파일) 용량, Jun11 2014는 생성 날짜와 시간, fd.c는 디렉토리(또는 파일)의 이름을 의미합니다.
현재 저는 fd계정이므로 가능한 행위는 fd파일을 읽고 실행하는 것, fd.c파일을 읽는 정도죠.그리고 fd.c 파일과 fd 파일이 있기 때문에 fd 파일은 fd.c를 컴파일 한 실행 파일이 아닐까 추측할 수 있습니다.
한번 fd 파일을 실행시켜 보겠습니다.덧붙여서 Linux 터미널에서의 파일 실행은 마침표(./)와 슬래시(./)를 입력하고 나서 파일명을 입력해 주세요.
실행해보니 passargv[1]anumber라는 문자를 출력하네요.그럼 fd.c 파일을 읽어볼게요.
C 언어의 코드가 출력되는 것을 볼 수 있습니다.그럼 지금부터 차근차근 분석해볼게요.
전역 변수 buf를 선언합니다.main 함수에서는 argc, argv를 매개 변수로 받고 있는 모습입니다.
argc와 argv는 각각 intargc:main 함수에 전달되는 정보의 개수를 의미합니다.char*argv [ ] : main 함수에 전달되는 실질적인 정보로 문자열 배열을 의미합니다.첫 번째 문자열은 프로그램 실행 경로로 항상 고정되어 있습니다.
예를 들어볼까요?만약 fd Hello World 명령을 실행하면 fd가 실행되면서 Hello World가 main 함수에 전달됩니다.이때 argv는 다음과 같습니다.argv[0]: 실행 경로 argv[1]: ‘Hello’ argv[2]: ‘World’ 그리고 argc에는 argv의 크기가 들어갑니다. 위와 같은 예에서는 3이 들어갑니다.
main 함수 내부에서는 if문을 먼저 볼 수 있었습니다. 만약 argc<2라면, “passargv[1]anumber”를 출력하여 프로그램을 종료합니다.처음 fd를 실행했을 때 passargv[1] anumber를 출력하신 이유를 알 수 있습니다.
다음은 정수형 변수 fd를 선언하고 atoi(argv[1])-0x1234 값으로 초기화하는 모습입니다.먼저 atoi함수를 보겠습니다.atoi는 ASCIIstring tointeger의 약자로 10진법으로 표기된 문자열을 정수로 바꿀 수 있습니다.(atoi 함수는 stdlib.h 헤더 파일에 선언되어 있습니다.) 따라서 atoi(argv[1])-0x1234란 argv[1]를 정수로 바꾸고 0x1234를 뺀 값을 의미합니다.argv[1] 값은 실행 시 넘겨주기 때문에 만약 실행 시./fd1을 입력했다면 atoi(argv[1])-0x1234 값은 1-0x1234가 됩니다.참고로 0x1234는 10진수로 4660입니다.
다음으로 read ( )함수가 표시됩니다.read ( )함수의 원형은 다음과 같습니다.여기서 fd는 데이터를 전송할 대상을 가리키는 파일 디스크립터, buf는 수신한 데이터를 저장하는 버퍼를 가리키는 포인터, nbytes는 수신할 최대 바이트 수를 의미합니다.
File Descriptor(파일 디스크립터)에 대해서도 알아보겠습니다.프로세스가 파일에 액세스할 때 우리는 파일을 open한다고 합니다.파일을 open할 때 파일 디스크립터는 0을 포함하는 양의 정수를 할당할 수 있습니다.
그 중 0, 1, 2는 기본적으로 할당되는 파일 디스크립터입니다.0은 표준 입력, 1은 표준 출력, 2는 표준 오류를 의미하는데 표준 입력은 키보드로, 표준 출력과 오류는 모니터를 이용합니다.
만약 read 함수의 fd 값이 0이라면 키보드로 입력된 값을 읽고 저장한다는 의미입니다.
다음은 익숙한 strcmp함수가 보입니다!! (수업시간에 배웠죠?) strcmp는 문자열 비교함수입니다. 두 인자의 값이 같으면 0, 다르면 1을 반환합니다.여기서는 함수 앞에!가 붙어 있기 때문에 두 인자의 값이 같으면 1, 다르면 0이 됩니다.만약 buf에 저장된 문자열이 “LETMEWIN”
“이면 goodjob:)을 출력하고 fd_pwn 권한으로 flag 파일을 읽고 종료합니다.
우리의 목표는 flag 값을 찾는 것이기 때문에 결과적으로 fd에 0을 넣고 키보드에서 LETMEWIN
을 입력하여 buf에 저장하고 flag 값을 불러와야 한다는 것입니다.
fd 값은 atoi(argv[1])-0x1234이므로 이 값이 0이 되려면 argv[1]에 0x1234에 해당하는 10진수의 값을 넣어야 합니다.0x1234 는 10진수로 4660 이므로 파일 실행 시. /fd4660 을 입력해 주셔야 합니다.
그리고 read 함수의 파일 디스크립터 값이 0이므로 buf에 넣을 값을 키보드로 입력할 수 있습니다.목표가 flag 값을 불러오기 때문에 buf에 LETMEWIN을 입력해 줍니다.
성공입니다! flag 값은 mommy! Ithink Iknow whatafiledescriptoris!! 이었군요!
fd 클리어!