주어진 파일들이 꽤 많다. 대충 하나씩 열어보니 서버 실행환경이 우분투 20.04이고 도커이미지가 어떻게 구성되는지 알수있었음.
그리고 중요한것은
여기에 플레그가 있다.
원격서버에 접속해서 sp22.txt 파일에 있는 플레그를 읽으면 될듯.
보호기법을 확인해보면
64비트에 바이너리이며, 보호기법이 죄다 걸려있다.
주어진 소스코드를 확인해보았다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Course {
char name[0x20];
char instructor[0x20];
int is_staff;
};
static void find_instructor(struct Course *courses, int course_count);
static void find_course(struct Course *courses, int course_count);
static void handle_option(int choice, struct Course *courses, int course_count);
#define NUM_COURSES 50
int main(int argc, char** argv) {
struct Course courses[NUM_COURSES];
setvbuf(stdout, NULL, _IONBF, 0); // turn off buffered output
FILE *fin = fopen("sp22.txt", "r");
if (!fin) {
printf("Failed to open sp22.txt\n");
return 0;
}
// Read in courses and instructors
int course_count = 0;
while (course_count < NUM_COURSES) {
char *s = fgets(courses[course_count].name, 0x20, fin);
if (!s) break;
s[strcspn(s, "\n")] = 0; // remove newline
s = fgets(courses[course_count].instructor, 0x20, fin);
if (!s) break;
s[strcspn(s, "\n")] = 0; // remove newline
courses[course_count].is_staff = 1; // always hide instructors from students until the week before classes
course_count++;
}
while (1) {
printf("What would you like to do?\n1. Search for a course\n2. Search for an instructor\n> ");
int choice;
if (scanf("%d", &choice) != 1) break;
getc(stdin); // get newline
handle_option(choice, courses, course_count);
}
}
static void handle_option(int choice, struct Course *courses, int course_count) {
switch (choice) {
case 1:
find_course(courses, course_count);
break;
case 2:
find_instructor(courses, course_count);
break;
default:
printf("bad choice\n");
break;
}
}
static void find_instructor(struct Course *courses, int course_count) {
char instructor[0x20];
char course[0x20];
printf("What instructor would you like to look up?\n> ");
char *s = fgets(instructor, 0x20, stdin);
if (!s) return;
s[strcspn(s, "\n")] = 0; // remove newline
if (strcmp(instructor, "Staff") == 0) {
printf("There were %d results, please be more specific in your search.\n", course_count);
} else {
int i;
for (i=0; i<course_count; i++) {
if (strcmp(instructor, courses[i].instructor) == 0) {
strncpy(course, courses[i].name, 0x20);
break;
}
}
if (i == course_count) {
printf("We couldn't find your instructor.\n");
return;
}
}
printf("Professor %s will teach %s, but we'll probably change our minds the week before classes start.\n", instructor, course);
}
static void find_course(struct Course *courses, int course_count) {
char course[0x20];
char instructor[0x20];
printf("What course would you like to look up?\n> ");
char *s = fgets(course, 0x20, stdin);
if (!s) return;
s[strcspn(s, "\n")] = 0; // remove newline
int i;
for (i=0; i<course_count; i++) {
if (strcmp(course, courses[i].name) == 0) {
strncpy(instructor, courses[i].instructor, 0x20);
break;
}
}
if (i == course_count) {
printf("Sorry, we couldn't find your course.\n");
return;
}
if (courses[i].is_staff) {
printf("This course will be taught by: Staff\n");
} else {
printf("This course will be taught by: %s\n", instructor);
}
}
코드를 보니 메인에서 sp22.txt파일을 읽고 한줄씩 읽어서courses 배열에 각 코스이름, 강사명을 넣어준다.
courses[0].name = CSE 2221
courses[0].instructor = Bucci
이런식으로 값이 들어갈듯.
그리고 while문으로 무한루프 돌면서 메뉴출력, 입력값으로 1 또는 2를 받아서 handle_option 함수에서 switch문으로 각각 1일때 find_course함수, 2일때 find_instructor 함수를 호출한다.
그리고 각 find 함수에서는 사용자의 입력을 받아서 입력값을 courses 배열에서 찾아서 해당하는 값에 대응하는 name또는 instructor 값을 출력해 주는것 같다.
바이너리를 실행해 보면
이렇게 대화형으로 입력을 받는다.
1번 find_course 에서 sp22.txt 파일에 있는 CSE 2221을 넣어보았다.
근데 CSE 2221에 대응하는 instructor 값은 Bucci 인데 Staff 로 나온다.
그래서 코드를 보니
while (course_count < NUM_COURSES) {
char *s = fgets(courses[course_count].name, 0x20, fin);
if (!s) break;
s[strcspn(s, "\n")] = 0; // remove newline
s = fgets(courses[course_count].instructor, 0x20, fin);
if (!s) break;
s[strcspn(s, "\n")] = 0; // remove newline
courses[course_count].is_staff = 1; // always hide instructors from students until the week before classes
course_count++;
}
if (courses[i].is_staff) {
printf("This course will be taught by: Staff\n");
} else {
printf("This course will be taught by: %s\n", instructor);
}
이 두 부분이 문제인것 같다.
courses[course_count].is_staff = 1; 이부분에서 모든 course 들의 is_staff값을 1로 설정해주기때문에
아래 find 함수의 if 문에서 instructor 대신 Staff를 무조건 띄우게 되어있다.
flag값이 있는 course name인 FLAG 1337 을 넣어도 마찬가지로 Staff가 나온다.
이래서 문제이름이 Staff인것같다.
보호기법도 다 걸려있고 코드에서 bof가 일어나거나 is_staff 값을 조작할만한 함수가 딱히 안보여서 한참 삽질을 했다..
flag는 instructor 부분에 들어가기때문에 find_course 함수로 찾아야 될것같은데 find_instructor 함수는 는 어따쓰나 싶어서 자세히 보니
if (strcmp(instructor, "Staff") == 0) {
printf("There were %d results, please be more specific in your search.\n", course_count);
} else {
int i;
for (i=0; i<course_count; i++) {
if (strcmp(instructor, courses[i].instructor) == 0) {
strncpy(course, courses[i].name, 0x20);
break;
}
}
if (i == course_count) {
printf("We couldn't find your instructor.\n");
return;
}
}
printf("Professor %s will teach %s, but we'll probably change our minds the week before classes start.\n", instructor, course);
이부분에서 입력값이 Staff이면 이상한걸 출력하고 아니면 입력값을 courses배열에서 찾아주는걸 보고 일단한번 직접 넣어봤다.
ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
Staff를 입력하면 There were~~ 뭐시기를 출력하고 리턴하지않고 맨아래
printf("Professor %s will teach %s, but we'll probably change our minds the week before classes start.\n", instructor, course);
이걸 출력한다. 근데 else문으로 안들어가서 course 배열을 건드리지 않아서 원래있던 값을 그대로 출력해주는것 같다.
즉 이전에 1번에서 CSE 2221을 한번 해서
char instructor[0x20];
char course[0x20];
여기다가 값을 넣어줬는데
2번 find_instructor에서 똑같이
char instructor[0x20];
char course[0x20];
char 배열을 선언하지만 초기화는 하지않아서 이전값이 그대로 들어가있고 그걸 출력하는것같다.
즉 1번에서 FLAG 1337을 넣어주고 2번에서 Staff 를 넣어주면?
매우허무하게 flag가 출력된다...
포스팅하는 시점은 CTF가 종료된 후라 원격서버를 사용할수 없어서 로컬에서 돌린 이미지로 대체했지만 원격서버에서 정상적인 FLAG를 얻을 수 있었다.
'Pwnable > CTF' 카테고리의 다른 글
[BuckeyeCTF] ret4win Write Up (0) | 2021.11.07 |
---|