-
Notifications
You must be signed in to change notification settings - Fork 1
passengers 1
- Категория: PWN
- Стоимость: 400
- Автор: Михаил Евдокимов aka merrychap
- Репозиторий
Корпорация Spacebury Craft, созданная известным бизнесменом и изобретателем Иваном Ланном, открыла набор экипажа на первый запуск пилотируемого корабля за пределы солнечной системы. Чтобы заявить о своём желании принять участие в экспедиции, достаточно воспользоваться системой регистрации с открытым исходным кодом.
Все заявки будут рассмотрены специальным комитетом Spacebury. Члены комитета выберут 50 человек, которые станут кандидатами на участие в полёте. Их ждёт череда серьёзных испытаний на пригодность к полёту и месяцы тренировок. По словам самого Ивана Ланна, итоговый экипаж будет состоять из 10 человек. Подробности испытаний, которые предстоит пройти 50 счастливчикам, пока не разглашаются.
Примечание: для доступа к системе следует использовать программу nc: nc passengers.contest.qctf.ru 50001
. При заполнении заявки нужно воспользоваться токеном Your_Token_Right_Here
Из условия мы знаем, что имеем дело с какой-то системой регистрации пассажиров.
Сразу заметим, что есть структура, представляющая пассажира:
struct passenger {
int id;
unsigned char is_frozen;
unsigned char age;
unsigned char is_crewman;
unsigned char token[7];
unsigned char country[100];
unsigned char first_name[100];
unsigned char last_name [100];
void (*on_unfreeze_ptr)(struct passenger*);
void (*on_freeze_ptr)(struct passenger*);
void (*info_ptr)(struct passenger*);
};
Тут есть пара важных полей: age
- 1 байт, is_crewman
- 1 байт и token
- 7 байтов. Также, указатели на 3 метода вывода информации (если вы не знаете, что такое указатели, то можете почитать о них, например, здесь).
При запуске исполняемого файла, запускается функция applicant_menu
. Здесь мы имеем возможность подать заявку, посмотреть список уже подавших, изменить информацию в анкете и показать созданную анкету. Заметим интересный кейс в коде, проверящим введённый номер действия:
switch (choice) {
case 31337:
if (applier != NULL && crewman_enter(applier))
crewman_menu();
break;
case 1:
...
case 2:
...
case 3:
...
case 4:
...
case 5:
...
}
При вводе 31337
происходит проверка подающего заявку на принадлежность к экипажу. Если всё пройдёт успешно, то мы попадём в меню для члена экипажа (важно понимать, что пассажир и член экипажа — это разные привилегии). Давайте посмотрим как происходит проверка в crewman_enter
.
int crewman_enter(struct passenger *pass) {
printf("\n[*] Entering as a member of the ship crew...\n");
printf("[*] Checking is running...\n");
sleep(2);
if (pass->is_crewman == 1) {
for (int i = 0; i < CREWMANS_COUNT; i++) {
if (ship[i]->is_crewman && !strcmp(used_tokens[i], pass->token))
printf("[+] Done. Welcome!\n");
return 1;
}
}
printf("[-] Invalid credentials.\n");
return 0;
}
Сперва проверяется, что выставлен флаг принадлежности экипажу, затем происходит итерирация по членам экипажа и проверка токенов. Если токен подающего заявку совпадает с чьим-либо, то выводится [+] Done. Welcome!
и считается, что он может войти в другой терминал. Стоит оговориться, что на сервере исполняемый файл не совпадал с приведённым исходным кодом. В частности, не было проверки на токен.
Посмотрим что проиходит в подаче заявки:
int input_age(long *real_age) {
char age[MAX_LEN];
input_string(age, AGE_LEN);
*real_age = parse_long(age);
if (*real_age <= 18) {
printf("[-] Invalid age, try again!\n\n");
return 0;
} else return 1;
}
struct passenger* apply() {
long real_age = 0;
......
......
......
printf(" Age: ");
if (!input_age(&real_age)) return NULL;
struct passenger *pass = create_passenger(firstname, lastname, country, real_age);
......
......
}
Из приведённого кода видно, что real_age
, который мы передаём, имеет размер 8 байт (т.к. его тип — long
).
Стоит посмотреть, как происходит инициализация пассажира:
struct passenger* create_passenger(char *first_name, char *last_name, char *country, long age) {
struct passenger *passng = malloc(sizeof(struct passenger));
.......
.......
.......
long *_age = (long *) &passng->age;
*_age = age;
return passng;
}
Теперь можно соединить всё в единое целое. Мы вводим число real_age
размером в 8 байт, которое отправляется в create_passenger
в качестве аргумента age
. В функции passng->age
— это указатель на char *
(то есть на 1 байт). Затем, passng->age
кастуется к указателю на long *
и после этого мы записываем 8 байт real_age
по этому указателю. Таким образом, мы заменяем 8 байт в структуре пассажира, тем самым изменяя значения полей age
, is_crewman
и token
. То есть, мы можем выставить is_crewman = 1
и токен одного из членов экипажа.
Используя приведённую выше уязвимость мы можем задать age = 8806289263452881168 = "\x10\x01rim96z"
, после этого is_crewman = 1; token = rim96z
. Затем введём 31337
в choice и попадём в терминал члена экипажа и выведем secret information
, в которой лежит флаг.