Решение задания с pwnable.kr 11-coin1, 12-blackjack, 13-lotto. Ошибки в логике приложений

от автора

image

В данной статье рассмотрим решения 3-х заданий с сайта pwnable.kr.

Организационная информация

Специально для тех, кто хочет узнавать что-то новое и развиваться в любой из сфер информационной и компьютерной безопасности, я буду писать и рассказывать о следующих категориях:

  • PWN;
  • криптография (Crypto);
  • cетевые технологии (Network);
  • реверс (Reverse Engineering);
  • стеганография (Stegano);
  • поиск и эксплуатация WEB-уязвимостей.

Вдобавок к этому я поделюсь своим опытом в компьютерной криминалистике, анализе малвари и прошивок, атаках на беспроводные сети и локальные вычислительные сети, проведении пентестов и написании эксплоитов.

Чтобы вы могли узнавать о новых статьях, программном обеспечении и другой информации, я создал канал в Telegram и группу для обсуждения любых вопросов в области ИиКБ. Также ваши личные просьбы, вопросы, предложения и рекомендации рассмотрю лично и отвечу всем.

Вся информация представлена исключительно в образовательных целях. Автор этого документа не несёт никакой ответственности за любой ущерб, причиненный кому-либо в результате использования знаний и методов, полученных в результате изучения данного документа.

Решение задания coin1

Нажимаем на иконку с подписью coin1, и нам предоставляют адрес и порт для подключения.

image

После подключения нам предлагают сыграть в игру и предоставляют правила игры. А также нам дают на прохождение 60 секунд, поэтому придется все автоматизировать.

image

По правилам игры нам дают N монет, каждая весом 10, кроме одной — ее вес 9. Нам дают количество шансов(раундов) C для одной игры. На каждом раунде мы посылаем индексы монет, и нам вовращают и суммарный вес. Таким образом, применяя бинарный поиск, мы найдем нужную монету.

Напишем код. Для начала установим соединение с сервером, примем и распарсим числа N и C.

from pwn import *  r = remote('pwnable.kr', 9007) r.recv()  s = r.recv() print(s) n = int(s.split(' ')[0][2:]) c = int(s.split('=')[2].split('\n')[0]) print(n, c)

image

Отлично. Теперь напишем часть, для прохождения одного уровня. Для этого нам понадобится массив значений от 1 до N+1 и цикл из С шагов, на каждой итерации которого будет отправлять половину массива. Если возвращенный в ответе вес будет делиться на 10 без остатка, то наша монета в другой части массива. Таким образом, мы снова разделим другую половину и проделаем то же самое с ней, и т.д. пока не будет обнаружена та самая монета.

mas = range(1,n+1) for i in range(c): 	s = "" 	if len(mas)==1: 		mas.append(mas[0]) 	for j in mas[:len(mas)/2]: 		s += (str(j)+" ") 	print(s) 	r.send(s+"\n") 	nr = r.recv() 	print(nr) 	if int(nr) % 10: 		mas = mas[:len(mas)/2] 	else: 		mas = mas[len(mas)/2:] r.send(str(mas[0])+"\n") print(r.recv())

image

Теперь добавим это решение в цикл для прохождения всех уровней.

from pwn import *  r = remote('pwnable.kr', 9007) r.recv()  for level in range(1, 101): 	s = r.recvline() 	n = int(s.split(' ')[0][2:]) 	c = int(s.split('=')[2].split('\n')[0])  	mas = range(1,n+1) 	for i in range(c): 		s = "" 		if len(mas)==1: 			mas.append(mas[0]) 		for j in mas[:len(mas)/2]: 			s += (str(j)+" ") 		r.send(s+"\n") 		nr = r.recv() 		if int(nr) % 10: 			mas = mas[:len(mas)/2] 		else: 			mas = mas[len(mas)/2:] 	r.send(str(mas[0])+"\n") 	r.recvline() 	if level%5==0: 		print("Check "+str(level)+"/100") print(r.recv())

image

Сдаем флаг и получаем очки.

Решение задания blackjack

Нажимаем на первую иконку с подписью coin1, и нам предоставляют адрес и порт для подключения. Еще сообщают, что нужно выиграть миллион.

image

После подключения нам предлагают сыграть в игру и спрашивают о готовности.

image

После нашего ответа, выходим в меню, начать игру, узнать правила или выйти из игры.

image

Начинаем новую игру.

image

Такие задания часто встречаются в CTF и о них полезно знать. Скорее всего отсутствует обработчик отрицательных чисел. Таким образом если ввести -999500 и проиграть, то из нашего банка вычтут отрицаельное число, то есть прибавлено положительное (500 — (-500) = 500 + 500 = 1000). Введем -1000000.

image

image

Сдаем флаг и получаем еще одно очко.

Решение задания lotto

Нажимаем на первую иконку с подписью lotto, и нам говорят, что нужно подключиться по SSH с паролем guest.

image

При подключении мы видим соответствующий баннер.

image

Давайте узнаем, какие файлы есть на сервере, а также какие мы имеем права.

image

Давай просмотрим исход код.

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h>  unsigned char submit[6];  void play(){          int i;         printf("Submit your 6 lotto bytes : ");         fflush(stdout);          int r;         r = read(0, submit, 6);          printf("Lotto Start!\n");         //sleep(1);          // generate lotto numbers         int fd = open("/dev/urandom", O_RDONLY);         if(fd==-1){                 printf("error. tell admin\n");                 exit(-1);         }         unsigned char lotto[6];         if(read(fd, lotto, 6) != 6){                 printf("error2. tell admin\n");                 exit(-1);         }         for(i=0; i<6; i++){                 lotto[i] = (lotto[i] % 45) + 1;         // 1 ~ 45         }         close(fd);          // calculate lotto score         int match = 0, j = 0;         for(i=0; i<6; i++){                 for(j=0; j<6; j++){                         if(lotto[i] == submit[j]){                                 match++;                         }                 }         }          // win!         if(match == 6){                 system("/bin/cat flag");         }         else{                 printf("bad luck...\n");         }  }  void help(){         printf("- nLotto Rule -\n");         printf("nlotto is consisted with 6 random natural numbers less than 46\n");         printf("your goal is to match lotto numbers as many as you can\n");         printf("if you win lottery for *1st place*, you will get reward\n");         printf("for more details, follow the link below\n");         printf("http://www.nlotto.co.kr/counsel.do?method=playerGuide#buying_guide01\n\n");         printf("mathematical chance to win this game is known to be 1/8145060.\n"); }  int main(int argc, char* argv[]){          // menu         unsigned int menu;          while(1){                  printf("- Select Menu -\n");                 printf("1. Play Lotto\n");                 printf("2. Help\n");                 printf("3. Exit\n");                  scanf("%d", &menu);                  switch(menu){                         case 1:                                 play();                                 break;                         case 2:                                 help();                                 break;                         case 3:                                 printf("bye\n");                                 return 0;                         default:                                 printf("invalid menu\n");                                 break;                 }         }         return 0; }

В функции main() нет ничего интересного. Интереспредставляет функция play(), разобрав которую мы поймем логику работы программы. Сперва мы вводим 6 значений, потом программа псевдослучайно генерирует еще 6 в диапазоне (1-45), после чего эти две последовательности сравниваются. Флаг получаем приналичии 6 совпадений. Но проверка выполнена неправильно. Таким образом в цикле каждый символ введенной последовательности сравнивается с каждым символом сгенерированной.

image

Таким образом будем вводить 6 одинаковых символов каждый раз, пока не получим флаг. Я вводил !!!!!!, и на 7 раз получил флаг.

image

Сдаем флаг и получаем два очка. До встречи в следующих статьях!

Мы в телеграм канале: канал в Telegram.


ссылка на оригинал статьи https://habr.com/ru/post/461641/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *