Перевод статьи «Debugging — you’re doing it wrong. 10 techniques to find a bug in your code».

Помните эти долгие, долгие часы, проведенные за отладкой? Когда вы всматриваетесь в код и не можете понять, что именно не в порядке? Вы не одиноки! Думаю, время от времени трудности с отладкой переживают все разработчики. В этой статье я расскажу вам о своих любимых подходах к поиску багов в коде.
Поискать информацию по сообщению об ошибке в Google
Я не буду сортировать свои подходы по степени их полезности, но поиск в Google занимает первое место совершенно заслуженно. Думаю, что когда, взглянув на сообщение об ошибке, вы даже не можете его понять, загуглить это самый быстрый способ разобраться, что к чему.
В большинстве случаев сообщение, с которым вы столкнулись, наверняка гуглил и кто-нибудь еще. Кроме того, у нас есть много таких прекрасных мест как StackOverflow и GitHub issues, где люди помогают друг другу. Благодаря этому вы можете найти не только ответ, но и советы о том, что нужно предпринять, чтобы подобная ошибка не возникала в дальнейшем. Так почему же не гуглить ошибки? Это самый простой способ из всех!
Console log
Я полагаю, это один из самых популярных способов поиска багов в кодовой базе. Мы добавляем предложения console. log(…) в код, заново запускаем приложение и пытаемся разобраться, что идет не так.
Я люблю этот способ и, как мне кажется, это подходящий вариант для поиска простых проблем, уже локализованных в нескольких классах. Но если вы вообще не представляете, что происходит и где притаился баг, начинать поиски при помощи console. log(…) будет плохой идеей. Потому что в этом случае можно пропустить что-то важное, что не попало в логи, и тогда вам придется многократно добавлять console log и перезапускать приложение, пока вы не найдете причину отказа.
Использовать отладчик
Не думаю, что здесь нужно что-то объяснять. Все разработчики знают, что такое отладчик и как им пользоваться. Вместо этого я расскажу вам о своем недавнем маленьком разговоре на эту тему с коллегой.
Несколько дней назад мы с товарищами по работе обсуждали разные подходы к отладке (что и привело к написанию этой статьи). И я сказал, что самый надежный способ найти корень проблемы – использовать отладчик. Нужно лишь установить точки прерывания, а затем, производя какие-то действия, продвигаться шаг за шагом по коду, наблюдая за изменениями в приложении. Что может быть проще?
Но один из моих коллег высказал потрясающую мысль: используя отладчик, вы не тренируете свои аналитические способности и критическое мышление. С отладчиком вы лишь смотрите в окно с переменными и ждете неправильного поведения. А если вы копаетесь в кодовой базе без отладчика, вы более сфокусированы. Вы пытаетесь понять, что происходит, и таким образом каждый раз открываете для себя что-то новое.

Локализация проблемы
Основная идея метода локализации проблемы в том, чтобы пошагово удалять или закрывать комментариями код, пока не поймете, в каком месте у вас ошибка. Это особенно полезно в тех случаях, когда вы довольно длительное время пишете алгоритм или какую-то бизнес-логику, не компилируя и не выполняя приложение.
В подобных случаях я всегда делаю что-то не так, и самый простой способ найти, где именно вкралась ошибка, это частично закомментировать код и проследить, исчезла ли ошибка. Затем процесс следует повторять до тех пор, пока все баги не будут найдены и исправлены.
В некоторых случаях бывает полезным использовать подход бинарного поиска: закомментировать половину кода (или удалить половину файлов) и посмотреть, осталась ли ошибка. Если да, то повторить процесс для половины этой половины, если нет – для другой половины кода.
Конечно, это работает лишь в определенных случаях, когда вы можете закомментировать или удалить часть кода таким образом, чтобы все остальное работало.
Создать несколько тестов
Да, это бредовый вариант, но и он может быть полезен в определенных случаях. С моей точки зрения, этот способ хорошо работает, когда у вас есть какой-то неверно работающий алгоритм, слишком сложный, чтобы писать console. log или проходить построчно с отладчиком.
В этом случае может быть полезным написание пары тестов для этого кода. Они могут помочь вам локализовать проблему в алгоритме.
Когда баг будет локализован, вы будете знать, где искать, и для дальнейшего поиска сможете использовать другой подход.
Анализ логов
Да, я знаю, что вы терпеть не можете анализировать все эти 10mb текстовых файлов с логами. Но довольно часто это позволяет сэкономить целые часы на отладке. Конечно, прежде всего следует адекватно настроить ведение логов. Файлы с логами должны собираться и храниться какое-то разумное количество времени. Но если соблюдены все условия – вам повезло. Это как использование console log, только у вас уже есть все console. log-и на своих местах, так что вы можете просто читать, какие действия выполняла система.
Но, к сожалению, довольно часто об этом остается лишь мечтать. Мы не всегда уделяем достаточно внимания ведению логов.

Спросить у друга
Это вполне очевидный вариант, но мы часто им пренебрегаем. Полагаю, у всех нас в офисе есть более опытные коллеги. А если это не так, можно найти нужных экспертов в интернете. Не стесняйтесь обращаться за помощью: люди всегда готовы вам помочь!
Но. Прежде, чем спрашивать у кого-то, сначала необходимо просмотреть все доступные ресурсы самостоятельно. Если будете спрашивать у людей какую-то легкотню, написанную на второй странице документации, – будьте готовы спасаться бегством!
Git bisect
Git не только помогает нам отслеживать историю изменений в приложении, но и предоставляет несколько инструментов для отладки. Одни из них – git bisect – инструмент для осуществления бинарного поиска по вашей git-истории. Это довольно полезно в случаях, когда вы некоторое время не работали с этой кодовой базой, а за это время в ней были добавлены сотни коммитов. И теперь вы обнаружили баг и понятия не имеете, когда именно он появился. Но вы помните, что, например, в версии 2.0.15 его не было.
В этом случае git bisect вам поможет. Идея тут довольно простая. Вы начинаете процесс отладки (git bisect start), затем нужно пометить текущую версию как плохую, потому что здесь у нас баг (git bisect bad). После этого нужно сообщить git, какую версию считать хорошей – git bisect good 2.0.15. На этой стадии настройка завершена и мы можем начинать поиск.
git bisect выбирает коммит на середине отрезка bad-good и осуществляет проверку. Нам нужно проверить, есть ли баг в этой версии. Если да – запускаем git bisect bad, если нет – git bisect good. Затем git выбирает новый коммит на оригинальном отрезке bad-good и мы повторяем процесс, пока не найдем коммит с багом.
git bisect это очень мощный инструмент, поэтому полностью описывать его здесь мы не будем. Если вам интересно, хорошее пояснение есть здесь.
Поговорить с уточкой

Это один из самых действенных методов понять, что происходит в коде. Суть его в следующем. Вы берете резиновую уточку (любую игрушку), ставите ее перед собой и объясняете ей всю вашу систему, начиная с общих концепций и продвигаясь строчка за строчкой. Мне нравится этот метод и я пользуюсь им регулярно.
Когда-то, на заре моей деятельности в качестве разработчика, я долго не мог разобраться с одной проблемой. Несколько часов я пялился в экран, а потом решил обратиться за помощью к коллеге. Я начал объяснять ему ситуацию и уже через минуту нашел решение! Тогда я еще не знал о методе утенка, но вполне ощутил, насколько этот метод действенный.
Танцы с бубном
Это мой любимый способ решения проблем с программами. Все, что вам нужно, это бубен. Танцуйте вокруг своего рабочего стола, ударяя в бубен. Большой плюс, если на бубне нанесен логотип технологии, с которой вы работаете. Например, у меня есть бубен для решения проблем с микросервисами:

Правда, для фронтенд-приложений он совершенно бесполезен.
Заключение
Наконец, для тех, кто дочитал до конца, отмечу, что самый лучший способ решения проблем с багами – писать код без багов. Вам это любой менеджер подтвердит:)
Но, если серьезно, будьте сфокусированы на проблеме и последовательны. Исследуйте свой код шаг за шагом, применяя различные подходы, и вы эффективно расправитесь со всеми вашими багами.
Роковые ошибки. Как искать логические уязвимости в веб-приложениях
Эта статья — конспект вебинара по веб‑уязвимостям, который я проводил для читателей «Хакера». В конце будет приведена его запись, где ты сможешь увидеть то, что не попало в текстовую версию. Если тебе интересны такие темы и ты бы хотел разобраться в них глубже, записывайся на мой курс по безопасности веб‑приложений!
Сразу предупрежу, что большинство задач, которые мы сегодня разберем, будут на языке PHP. Оно неспроста — подавляющее большинство сайтов и сервисов в интернете написаны именно на нем. Так что, несмотря на подпорченную репутацию этого языка, тебе придется разбираться в нем, чтобы хакерствовать серьезно. Сейчас же, в рамках этого занятия, знание PHP не является необходимостью, но, конечно, будет очень серьезным подспорьем.
Впрочем, большинство уязвимостей не привязаны к конкретному языку или стеку технологий, так что, узнав их на примере PHP, ты легко сможешь эксплуатировать подобные баги и в ASP. NET, и в каком‑нибудь Node. JS.
А еще предупрежу, что задачки, которые мы сегодня разберем, не совсем начального уровня и совсем уж «валенкам» тут делать нечего — сначала стоит почитать матчасть и хоть немного представлять, с чем хочешь иметь дело. Если же ты можешь отличить HTTP от XML и у тебя не возникает вопросов вида «а что за доллары в коде?», то добро пожаловать!
warning
Ни автор курса, ни редакция «Хакера» не несут ответственности за твои действия. Применение материалов этой статьи против любой системы без разрешения ее владельца преследуется по закону.
Сегодня мы разберем несколько задач, которые я решал сам в рамках тренировки. Возможно, они покажутся тебе сложными, но не пугайся — всегда есть возможность отточить свои навыки на сайтах правительств специализированных сайтах для хакеров. Я сейчас говорю о HackTheBox и Root-me, которыми пользуюсь сам и всячески советую другим. Две из сегодняшних задач взяты именно оттуда.
Задача 1
Сначала я приведу код, с которым мы сейчас будем работать.
По сути, тут всего три строки кода. Казалось бы, где тут может закрасться уязвимость?
Чтобы это понять, давай разберем алгоритм, который здесь реализован. Вообще, при аудите кода стоит уметь читать его построчно. Тогда проще понять, что именно может пойти не так.
В конце очищенное имя файла подставляется в путь и загружается файл с этим именем. Ничего плохого.
Итак, что может пойти не по плану?
Как ты уже, конечно, догадался — проблема в функции очистки ввода (которая preg_replace ). Давай обратимся к первой попавшейся шпаргалке по регулярным выражениям.
Шпаргалка
Тут прямо написан ответ, как обойти защиту (подсказка: ищи справа).
Видишь точку? А шапочку ( ^ )? Та строка читается как «если в начале строки находится любое количество любых символов, кроме переноса строки, и это заканчивается слешем, удалить соответствующую часть строки».
Ключевое тут «кроме переноса строки». Если в начале строки будет перенос строки — регулярка не отработает и введенная строка попадет в include( ) без фильтрации.
Результат
Задача 2
Это задачка с root-me, где ты, возможно, уже видел ее. Но мы все равно рассмотрим ее подробнее — она относится к реалистичным, и шансы встретить что‑то подобное в жизни немаленькие.
В задании нам дается простой файлообменник и просят получить доступ к панели админа.
Интерфейс файлообменника
Интерфейс крайне прост: есть кнопка загрузки файла на сервер и просмотр загруженных файлов по прямым ссылкам. Забегая вперед, скажу, что грузить скрипты на PHP, bash и прочие — бесполезно, проверки реализованы верно и ошибка в другом месте.
Обрати внимание на нижнюю часть страницы, а точнее — на фразу «frequent backups: this opensource script is launched every 5 minutes for saving your files». И приведена ссылка на скрипт, вызываемый каждые пять минут в системе.
Давай глянем на него пристальнее:
Казалось бы — что тут такого? На параметры ты влиять не можешь, а мантру призыва tar вообще знаешь как свои пять пальцев. А проблема в самой мантре: тут она написана не полностью. Точнее, не в том виде, как ее увидит сам tar.
Что делает звездочка? Вместо нее bash подставит имена всех файлов в текущей папке. Вроде ничего криминального.
А давай обратимся к мануалу на Tar, который нам любезно предоставлен вместе с условием задачи.
Интересности в Tar
Теперь вспомним про звездочку: вместо нее шелл (bash) подставит список всех файлов в текущей папке, при этом они могут иметь любые имена. В том числе такие, которые будут восприняты архиватором как специальные параметры.
Просто заголовок и команда копирования админской панели в текущую папку. Естественно, тут мог быть реверс‑шелл или еще что‑то, но для решения конкретно этой задачи такая «тяжелая артиллерия» не нужна.
Теперь дожидаемся выполнения нашего шелла — и увидим в окне файлообменника файл админ‑панели в виде простого текста. Осталось только открыть его и найти там пароль!
Пароль в чистом виде
Задача 3
Тут у нас плагин для WordPress, который позволяет запись аудио и видео.
Я не буду просить тебя найти уязвимость, а сразу покажу ее.
Уязвимое место
Как видно из строк 247–251 на скриншоте, не предусмотрено никаких проверок на тип или содержимое файла — это просто классическая загрузка!
Есть, правда, ограничение: файл грузится в стандартную директорию WordPress ( / wordpress/ wp-content/ uploads/< YEAR>/ < MONTH>). Это значит, что листинг содержимого нам по умолчанию недоступен. А в строке 247 генерируется случайный идентификатор, который подставляется в начало имени файла, то есть обратиться к / wordpress/ wp-content/ uploads/ 2021/ 01/ shell. php уже не выйдет. Непорядок!
Получает уникальный идентификатор с префиксом, основанный на текущем времени в микросекундах.
<…>
Внимание. Эта функция не гарантирует получения уникального значения. Большинство операционных систем синхронизирует время с NTP либо его аналогами, так что системное время постоянно меняется. Следовательно, возможна ситуация, когда эта функция вернет неуникальный идентификатор для процесса/потока. <…>
Так как PHP — проект открытый, мы можем подсмотреть исходники функций стандартной библиотеки. Открываем исходник uniqid( ) на GitHub, переходим к строке 76 и наблюдаем следующее:
Что тут происходит? А то, что возвращаемое значение зависит исключительно от текущего времени, которое в рамках одной планеты вполне предсказуемо.
Хоть выходная последовательность и выглядит случайной, она таковой не является. Чтобы не быть голословным, вот пример имени файла, сгенерированного таким алгоритмом:
Полученное значение легко можно конвертировать обратно в дату и время его генерации:
Конечно, брутить все 13 символов — вши заедят, но у нас есть способ получше: мы можем пробрутить варианты на основе времени загрузки плюс‑минус полсекунды, чтобы нивелировать разбежки часов на клиенте и сервере. А можно просто поверить, что часы у обоих хостов точные, а значит, можно проверить не миллион вариантов (1 секунду), а только варианты, возможные между временем отправки запроса и временем получения ответа. На шустром канале это будет порядка 300–700 мс, что не так и много.
Конечно, не все реальные кейсы требуют глубоких познаний в PHP или другом серверном языке. Многие ошибки можно найти, даже не открывая код — с помощью автоматических сканеров. Подробнее о них — в нашей статье об автоматическом взломе. Они здорово помогают, так что не грех иметь парочку под рукой для экспресс‑анализа!
Я набросал простой скрипт на Python для демонстрации такой возможности. Его код представлен ниже:
Нам нужно запустить его несколько раз, чтобы подобрать минимальное время между отправкой запроса и получением ответа — это позволит уменьшить время перебора.
Также нужно помнить, что разбежки все же могут быть, и чисто на всякий случай стоит проверить, насколько локальное время соответствует времени на сервере. Частенько оно возвращается сервером в заголовке Last-Modified и позволяет понять, какую величину коррекции внести в свои расчеты.
Как бы еще оптимизировать перебор?
Ну, во‑первых, питон сам по себе очень медленный и, конечно, не смог бы выполнить соединение, передачу заголовков, отправку файла и прочие мелкие накладные расходы в тот же момент. А интерпретатор PHP на стороне сервера едва ли моментально проверит права, запустит скрипт, отработает служебные функции и дойдет до собственно уязвимого места. Тут можно накинуть эдак тысяч сто микросекунд без малейших потерь.
Во‑вторых, выполнение uniqid( ) очевидно происходит не в самом конце функции. Еще нужно время на обработку загруженного файла, запись ответа (заголовков), отправку этого всего по сети и на обработку ответа интерпретатором Python. Тут тоже можно порядка 100 000 микросекунд вычесть.
Вот так на ровном месте мы сократили перебор на 200 000 запросов. Много это или мало? В моем случае это сократило количество запросов еще примерно на треть.
Осталось порядка 500 000 вариантов, которые можно перебрать в пределах часа или даже меньше — у меня это заняло минут 15.
Теперь давай напишем еще один скрипт, который и будет искать наш шелл с использованием этого алгоритма:
Вот и всё: запускаешь, через некоторое время получаешь путь, и хост захвачен!
Наверняка у тебя возник вопрос, нельзя ли как‑то еще усовершенствовать этот перебор, потому что 500 тысяч вариантов — это все равно как‑то многовато? Можно, но такого значимого ускорения, как раньше, уже не будет. Суть в том, что можно идти не от начала промежутка времени к концу, а от середины к краям. По опыту, это работает несколько быстрее.
Другой способ
Задача 4
Последняя на сегодня задачка — тоже с root-me и тоже из категории реалистичных, но заметно посложнее. Сервис Web TV — новейшая французская разработка в сфере интернет‑телевидения. Но нас интересует не новая дешевая трагедия, а админка.
Главная страница Web TV. Простите за мой французский
Только — вот незадача — Gobuster никаких признаков админки не обнаружил. Придется изучать, что нам доступно. А доступен логин (там форма авторизации) и ссылка на неработающий эфир.
Попробуем залогиниться и перехватить запрос на авторизацию с помощью Burp.
Буква З в слове «реальность» означает «защищенность»
Запрос отправляем в Repeater (повторитель). Пусть пока там полежит.
Взглянем еще разок на форму логина. Какие мысли тебя посещают, когда ты видишь форму для авторизации? Конечно, SQL-инъекция! А давай ткнем туда кавычку. Написали. Отправляем. Хм, ничего не поменялось. А как вообще узнать, что что‑то поменялось? Смотри на заголовок Content-Length в ответе: в нашем случае там приходит ровно 2079 байт, если инъекции не было, и, очевидно, придет сильно другой результат в противном случае. Я попробовал еще немного, и инъекция так просто не выявилась, так что давай поищем в другом месте, а потом вернемся к этому запросу.
Я попробовал перейти на страницу / page_index и получил ошибку как на скрине ниже.
Ошибка интерпретатора
/?action=../../index. php
Видно не все, но если открыть ответ в Burp или даже просто просмотреть код страницы браузером — открывается полный исходник. Вот тебе и directory traversal налицо.
Результат обхода каталога
Помнишь, мы не могли найти путь к админке? А на скриншоте он есть: именно на него будет редирект, когда скрипт проверит логин и пароль.
Давай, не отходя от кассы, сразу и его прочитаем — вдруг там что‑нибудь интересное есть.
Как находить баги в коде веб-приложений.
Сразу предупрежу, что большинство задач, которые мы сегодня разберем, будут на PHP. Это неспроста — на нем написано подавляющее большинство сайтов и сервисов в Интернете. Так что, несмотря на испорченную репутацию этого языка, вам придется его понять, чтобы серьезно взломать. Теперь, в рамках этого урока, знание PHP необязательно, но, конечно, это будет очень серьезным подспорьем.

Впрочем, большинство уязвимостей не привязаны к конкретному языку или стеку технологий, так что, узнав их на примере PHP, ты легко сможешь эксплуатировать подобные баги и в ASP. NET, и в каком‑нибудь Node. JS.
И еще предупреждаю, что задачи, которые мы сегодня будем разбирать, не совсем начального уровня и что к «валенкам» тут совершенно никакого отношения — надо сначала прочитать материал и хоть немного представлять, чем вы хотите заниматься. Если вы можете отличить HTTP от XML и у вас нет вопросов типа «для чего нужен код?», Милости просим!
warning
Применение материалов этой статьи против любой системы без разрешения ее владельца преследуется по закону.
Сегодня мы разберем несколько задач, которые я решил сам в рамках тренинга. Они могут показаться вам пугающими, но пусть это не пугает вас — всегда есть возможность отточить свои навыки на специализированных хакерских сайтах. Я сейчас говорю о HackTheBox и Root-me, которыми пользуюсь сам и всячески советую другим. Две из сегодняшних задач взяты именно оттуда.
Задача 1
Сначала я приведу код, с которым мы сейчас будем работать.
По сути, тут всего три строки кода. Казалось бы, где тут может закрасться уязвимость?
Чтобы это понять, давай разберем алгоритм, который здесь реализован. Вообще, при аудите кода стоит уметь читать его построчно. Тогда проще понять, что именно может пойти не так.
В конце очищенное имя файла подставляется в путь и загружается файл с этим именем. Ничего плохого.
Итак, что может пойти не по плану?
Как ты уже, конечно, догадался — проблема в функции очистки ввода (которая preg_replace ). Давай обратимся к первой попавшейся шпаргалке по регулярным выражениям.
Тут прямо написан ответ, как обойти защиту (подсказка: ищи справа).
Видишь точку? А шапочку ( ^ )? Та строка читается как «если в начале строки находится любое количество любых символов, кроме переноса строки, и это заканчивается слешем, удалить соответствующую часть строки».
Ключевое тут «кроме переноса строки». Если в начале строки будет перенос строки — регулярка не отработает и введенная строка попадет в include( ) без фильтрации.
Задача 2
Это задачка с root-me, где ты, возможно, уже видел ее. Но мы все равно рассмотрим ее подробнее — она относится к реалистичным, и шансы встретить что‑то подобное в жизни немаленькие.
В задании нам дается простой файлообменник и просят получить доступ к панели админа.
Пользовательский интерфейс очень прост: есть кнопка для загрузки файла на сервер и просмотра загруженных файлов по прямым ссылкам. Забегая вперед, скажу, что загрузка скриптов в PHP, Bash и др. бесполезна, проверки выполнены правильно, а ошибка в другом.
Обрати внимание на нижнюю часть страницы, а точнее — на фразу «frequent backups: this opensource script is launched every 5 minutes for saving your files». И приведена ссылка на скрипт, вызываемый каждые пять минут в системе.
Давай глянем на него пристальнее:
Интересности в Tar
Теперь вспомним про звездочку: вместо нее шелл (bash) подставит список всех файлов в текущей папке, при этом они могут иметь любые имена. В том числе такие, которые будут восприняты архиватором как специальные параметры.
Задача 3
Тут у нас плагин для WordPress, который позволяет запись аудио и видео.
Я не буду просить тебя найти уязвимость, а сразу покажу ее.
Как видно из строк 247–251 на скриншоте, не предусмотрено никаких проверок на тип или содержимое файла — это просто классическая загрузка!
Есть, правда, ограничение: файл грузится в стандартную директорию WordPress ( / wordpress/ wp-content/ uploads/< YEAR>/ < MONTH>). Это значит, что листинг содержимого нам по умолчанию недоступен. А в строке 247 генерируется случайный идентификатор, который подставляется в начало имени файла, то есть обратиться к / wordpress/ wp-content/ uploads/ 2021/ 01/ shell. php уже не выйдет. Непорядок!
Получает уникальный идентификатор с префиксом, основанный на текущем времени в микросекундах.
<…>
Внимание. Эта функция не гарантирует получения уникального значения. Большинство операционных систем синхронизирует время с NTP либо его аналогами, так что системное время постоянно меняется. Следовательно, возможна ситуация, когда эта функция вернет неуникальный идентификатор для процесса/потока
Так как PHP — проект открытый, мы можем подсмотреть исходники функций стандартной библиотеки. Открываем исходник uniqid( ) на GitHub, переходим к строке 76 и наблюдаем следующее:
Хоть выходная последовательность и выглядит случайной, она таковой не является. Чтобы не быть голословным, вот пример имени файла, сгенерированного таким алгоритмом:
Полученное значение легко можно конвертировать обратно в дату и время его генерации:
Конечно, не все реальные кейсы требуют глубоких познаний в PHP или другом серверном языке. Многие ошибки можно найти, даже не открывая код — с помощью автоматических сканеров.
Я набросал простой скрипт на Python для демонстрации такой возможности. Его код представлен ниже:
Нам нужно запустить его несколько раз, чтобы подобрать минимальное время между отправкой запроса и получением ответа — это позволит уменьшить время перебора.
Также нужно помнить, что разбежки все же могут быть, и чисто на всякий случай стоит проверить, насколько локальное время соответствует времени на сервере. Частенько оно возвращается сервером в заголовке Last-Modified и позволяет понять, какую величину коррекции внести в свои расчеты.
Как бы еще оптимизировать перебор?
Ну, во‑первых, питон сам по себе очень медленный и, конечно, не смог бы выполнить соединение, передачу заголовков, отправку файла и прочие мелкие накладные расходы в тот же момент. А интерпретатор PHP на стороне сервера едва ли моментально проверит права, запустит скрипт, отработает служебные функции и дойдет до собственно уязвимого места. Тут можно накинуть эдак тысяч сто микросекунд без малейших потерь.
Во‑вторых, выполнение uniqid( ) очевидно происходит не в самом конце функции. Еще нужно время на обработку загруженного файла, запись ответа (заголовков), отправку этого всего по сети и на обработку ответа интерпретатором Python. Тут тоже можно порядка 100 000 микросекунд вычесть.
Вот так на ровном месте мы сократили перебор на 200 000 запросов. Много это или мало? В моем случае это сократило количество запросов еще примерно на треть.
Осталось порядка 500 000 вариантов, которые можно перебрать в пределах часа или даже меньше — у меня это заняло минут 15.
Теперь давай напишем еще один скрипт, который и будет искать наш шелл с использованием этого алгоритма:
Вот и всё: запускаешь, через некоторое время получаешь путь, и хост захвачен!
Наверняка у тебя возник вопрос, нельзя ли как‑то еще усовершенствовать этот перебор, потому что 500 тысяч вариантов — это все равно как‑то многовато? Можно, но такого значимого ускорения, как раньше, уже не будет. Суть в том, что можно идти не от начала промежутка времени к концу, а от середины к краям. По опыту, это работает несколько быстрее.
Другой способ
Задача 4
Последняя на сегодня задачка — тоже с root-me и тоже из категории реалистичных, но заметно посложнее. Сервис Web TV — новейшая французская разработка в сфере интернет‑телевидения. Но нас интересует не новая дешевая трагедия, а админка.
Главная страница Web TV.
Только — вот незадача — Gobuster никаких признаков админки не обнаружил. Придется изучать, что нам доступно. А доступен логин (там форма авторизации) и ссылка на неработающий эфир.
Попробуем залогиниться и перехватить запрос на авторизацию с помощью Burp.
Буква З в слове «реальность» означает «защищенность»
Запрос отправляем в Repeater (повторитель). Пусть пока там полежит.
Взглянем еще разок на форму логина. Какие мысли тебя посещают, когда ты видишь форму для авторизации? Конечно, SQL-инъекция! А давай ткнем туда кавычку. Написали. Отправляем. Хм, ничего не поменялось. А как вообще узнать, что что‑то поменялось? Смотри на заголовок Content-Length в ответе: в нашем случае там приходит ровно 2079 байт, если инъекции не было, и, очевидно, придет сильно другой результат в противном случае. Я попробовал еще немного, и инъекция так просто не выявилась, так что давай поищем в другом месте, а потом вернемся к этому запросу.
Я попробовал перейти на страницу / page_index и получил ошибку как на скрине ниже.
Видно не все, но если открыть ответ в Burp или даже просто просмотреть код страницы браузером — открывается полный исходник. Вот тебе и directory traversal налицо.
Результат обхода каталога
Помнишь, мы не могли найти путь к админке? А на скриншоте он есть: именно на него будет редирект, когда скрипт проверит логин и пароль.
Давай, не отходя от кассы, сразу и его прочитаем — вдруг там что‑нибудь интересное есть.
https://techrocks. ru/2019/07/27/10-techniques-to-find-a-bug-in-your-code/
https://xakep. ru/2021/01/12/websec-errors/
https://cryptoworld. su/kak-naxodit-bagi-v-kode-veb-prilozhenij/