Решение задания с 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.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

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

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое