Решение задания с pwnable.kr 07 — input. Разбираемся с pwntools

от автора

image

В данной статье разберем решение многоуровнего задания с помощью библиотеки pwntools.

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

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

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

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

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

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

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

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

image

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

image

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

ls -l

image

Таким образом мы можем можем прочитать исходный код программы, так как есть право читать для всех, и выполнить с правами владельца программу fd (установлен sticky-бит). Давай просмотрим исход код.

Исходный код input.c

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h>  int main(int argc, char* argv[], char* envp[]){         printf("Welcome to pwnable.kr\n");         printf("Let's see if you know how to give input to program\n");         printf("Just give me correct inputs then you will get the flag :)\n");          // argv         if(argc != 100) return 0;         if(strcmp(argv['A'],"\x00")) return 0;         if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;         printf("Stage 1 clear!\n");          // stdio         char buf[4];         read(0, buf, 4);         if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0;         read(2, buf, 4);         if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0;         printf("Stage 2 clear!\n");          // env         if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;         printf("Stage 3 clear!\n");          // file         FILE* fp = fopen("\x0a", "r");         if(!fp) return 0;         if( fread(buf, 4, 1, fp)!=1 ) return 0;         if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0;         fclose(fp);         printf("Stage 4 clear!\n");          // network         int sd, cd;         struct sockaddr_in saddr, caddr;         sd = socket(AF_INET, SOCK_STREAM, 0);         if(sd == -1){                 printf("socket error, tell admin\n");                 return 0;         }         saddr.sin_family = AF_INET;         saddr.sin_addr.s_addr = INADDR_ANY;         saddr.sin_port = htons( atoi(argv['C']) );         if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){                 printf("bind error, use another port\n");                 return 1;         }         listen(sd, 1);         int c = sizeof(struct sockaddr_in);         cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c);         if(cd < 0){                 printf("accept error, tell admin\n");                 return 0;         }         if( recv(cd, buf, 4, 0) != 4 ) return 0;         if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0;         printf("Stage 5 clear!\n");          // here's your flag         system("/bin/cat flag");         return 0; }

Из кода следует, что нам необходимо пройти пять уровней. Будем проходить их по очереди.

image

На первом уровне проверяется, чтобы число аргументов программы равнялось 100. При этом 65-м элементом должна быть строка “\x00”, а 66-м — “\x20\x0a\x0d”. Создадим свою директорию в директории /tmp/ и там создадим питоновский скрипт.

image

Для решения первого уровня мы создадим массив из ста строк ‘A’. И присвоим нужным аргументам нужные значения. Запустить процесс с данными аргументами мы можем следующим образом:

from pwn import *  a = ['A']*100 a[0] = '/home/input2/input' a[ord('A')] = '\x00' a[ord('B')] = '\x20\x0a\x0d'  ex = process(argv=a)  ex.interactive()

image

Мы прошли первый уровень. Взглянем на второй.

image

На данном уровне происходит считывание двух строк, одной и стандартного ввода stdin, а другой — из stderr. Нам необходимо создать два файла, которые будут содержать эти строки.

image

Открыть потоки к этим файлам и указать в качестве дескриптора stdin дескриптор на поток одного файла, а в качестве дескриптора stderr — другого файла.

from pwn import *  a = ['A']*100 a[0] = '/home/input2/input' a[ord('A')] = '\x00' a[ord('B')] = '\x20\x0a\x0d'  fin = open('/tmp/ex/in.txt', 'r') ferr = open('/tmp/ex/err.txt', 'r')  ex = process(argv=a, stdin=fin, stderr=ferr)  fin.close() ferr.close()  ex.interactive()

image

Приступим к решению третьего уровня.

image

Функция getenv() возвращает значение переменной окружения, которое должно быть равно эталонному значению. Таким образом нужно создать переменную окружения с определенным значением.

from pwn import *  a = ['A']*100  a[0] = '/home/input2/input' a[ord('A')] = '\x00' a[ord('B')] = '\x20\x0a\x0d'  fin = open('/tmp/ex/in.txt', 'r') ferr = open('/tmp/ex/err.txt', 'r')  e={'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}  ex = process(argv=a, stdin=fin, stderr=ferr, env=e)  fin.close() ferr.close()  ex.interactive()

image

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

image

По коду можем сказать, что программа открывает файл с именем “\x0a” и читает из него 4 символа, после чего сравнивает их с байтами “\x00”. Так как и сами символы, и название файла состоит из непечатуемых символов, то используем python.

image

image

Остался последний уровень. Приступим.

image

Программа открывает сокет на порте, указанном в 66-м аргументе. Потом принимает по сети 4 байта и сравнивает с эталонной строкой. Нам необходимо добавить в программу еще один аргумент — номер порта, установить соединение и послать необходимые 4 байта.

from pwn import *  a = ['A']*100 a[0] = '/home/input2/input' a[ord('A')] = '\x00' a[ord('B')] = '\x20\x0a\x0d' a[ord('C')] = '1234'  fin = open('/tmp/ex/in.txt', 'r') ferr = open('/tmp/ex/err.txt', 'r')  e={'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}  ex = process(argv=a, stdin=fin, stderr=ferr, env=e)  fin.close() ferr.close()  ex.interactive()

image

image

Вот и все, получаем свои очки.

image

До встречи в следующих статьях!


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


Комментарии

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

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