Information Security
Python
Programming
CTF
July 16

Решение задания с 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

Таким образом мы можем можем прочитать исходный код программы, так как есть право читать для всех, и выполнить с правами владельца программу input (установлен 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

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

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

+10
1k 14
Leave a comment