개인적으로 코드 분석 능력이 최근 퇴화한 것처럼 크게 느껴지고 가끔 진행하는 CTF 에서도 웹 해킹이나 모바일 문제 이외 (리버싱 문제도 못푸는 것은 아니나 푼다고 보는 수준은 아닌거 같아서) 그나마 접점이 있는 포너블 문제를 풀어보고자 했다.
최근 안좋은 일들이 겹겹사로 내가 하는 것들에 대한 Refresh 가 필요하기도 했고... 그래서 하나씩 해보려 한다. (그게 끝까지 될 가능성...이 완벽하게 있진 않지만 시작하는게 어딘가 하니.)
각설하고 우선 해당 문제 내용을 살펴보자.
해당 문제는 리눅스에서 사용되는 파일 디스크립터에 대해 묻고 있다. (물론 그걸 물어보는게 아니겠지만.)
리눅스에서 이용되는 파일 디스크립터 라고 명시해 줬다는 것은 그것과 연관있기 때문에 작성했다 라는 것을 알 수 있다.
그럼 문제에 접속해서 한번 풀어보자.
해당 문제의 서버에 접근하여 접속된 서버 내 home 디렉터리를 확인해보면 우리가 얻어야하는 값이 존재하는 'flag' 파일과 접속된 계정명과 동일한 이름을 가진 'fd', 'fd.c' 이라는 파일이 있는 것을 확인할 수 있다. 해당 서버에 접근한 목적은 'flag' 값 확보이니 한번 확인해보자.
바이너리 파일인 'flag' 파일을 실행해보았으나 권한문제로 실행되지 않는다. 그래서 'flag' 파일의 권한을 확인해보면 소유계정 및 그룹에 포함하는 계정들만 읽기 권한만 있으며 이외는 모두 권한이 존재하지 않았다. 그렇다면 해당 문제 명과 동일한 이름의 바이너리 파일을 실행해보자.
바이너리를 그냥 실행하게 되면 pass argv[1] a number 이라는 문자열을 보내며 프로그램을 종료한다.
해당 문장에서 Argument Vector 값이 필요하다 알려주고 있으므로 해당 바이너리로 임의 정수 값이나 임의 문자열 값을 입력하여 결과 값 확인 결과 learn about Linux file IO 라는 문장만 확인 가능했다.
해당 바이너리가 어떻게 동작하고 어떤 방법으로 flag 값을 찾아야하는지 한번 소스코드를 확인해보자.
해당 소스코드를 확인해보자.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
if(argc<2){
printf("pass argv[1] a number\n");
return 0;
}
int fd = atoi( argv[1] ) - 0x1234;
int len = 0;
len = read(fd, buf, 32);
if(!strcmp("LETMEWIN\n", buf)){
printf("good job :)\n");
system("/bin/cat flag");
exit(0);
}
printf("learn about Linux file IO\n");
return 0;
}
전역 변수로 32바이트짜리 buf 라는 Character 형 배열을 선언해주고 main 에서 전달인자 (argv[]) 를 입력받는다. 대신 전달인자로 해당 파일 이름을 제외한 1개 이상의 인자가 필요하고 인자가 추가 인자가 존재하지 않는 경우 추가 인자 ( argv[1] ) 를 입력하여 지나가라고 문자열을 출력하며 해당 프로그램을 끝낸다. 만일 전달 인자가 1개 이상 입력되었다면 argv[1] 에 입력된 전달 인자 값을 정수 형으로 변환한 다음 16진수 '1234' 값을 뺀 결과를 정수 형 변수 fd 에 저장한다. 이때 16진수 1234 값은 다음과 같다.
이후 정수 형 변수인 len을 초기화한 후 read 을 실행하며 함수 인자로 fd 값으로 함수 모드를 설정, 32 바이트 만큼 사용자에게 입력을 받아 배열인 buf 에 저장한다.
이때 buf 내 입력된 값이 'LETMEWIN\n' 과 동일한 경우 문자열 'good job :0\n' 를 출력하며 폴더 내 존재하는 'flag' 값을 cat 으로 실행시키고 프로그램을 종료 시킨다.
하지만 해당 문자열이 일치하지 않으면 fd 바이너리 파일을 최초 실행하여 임의 전달 인자를 입력했을 경우 확인했던 'learn about Linux file IO\n' 를 출력한다.
프로그램 기준으로 fd 바이너리 파일의 실행 로직을 요약하자면
1. 프로그램 실행 시 명령 줄 인수 확인
2. 첫 번째 명령 줄 인수를 정수로 변환, 16진수 1234를 뺀 값을 fd 정수 형 인수로 저장
3. 해당 변수에 최대 32바이트를 읽어 'buf' 배열에 저장
4. 'buf' 내용이 문자열 'LETMEWIN\n' 과 일치하는지 확인
5. 일치하면 'good job :)' 을 출력하며 flag 파일 내용도 출력하고 종료하며, 이외 'learn about Linux file IO' 를 출력하고 프로그램을 종료
라고 볼 수 있다.
따라서 해당 프로그램 전달 인자로 위에서 0x1234 값의 10진수 반환 값 4660 을 입력하고 프로그램을 실행하게 되면 read 함수에 따라 사용자 입력을 받으므로 LETMEWIN 을 입력하면 (\n 값은 엔터 값에 해당하는 값으므로 입력하지 않는다.) flag 가 실행되어 flag 문자열을 얻을 수 있다.
문제 풀이 결과 ▼
'Training > Pwnable.kr' 카테고리의 다른 글
Collision (0) | 2024.07.18 |
---|