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.

Комментарии (0)