Хакер - VPN для всех! Снимаем ограничения с OpenVPN Access Server
hacker_frei
Denis Simonov
В последнее время ко мне все чаще обращаются друзья с просьбами помочь получить доступ к Facebook, Instagram, Telegram, YouTube и прочим недоступным сайтам, где могла остаться важная информация. Чтобы помочь им, я всегда использовал OpenVPN Access Server. Его бесплатная версия имеет ряд лицензионных ограничений. В этой статье я покажу, как с ними можно справиться.
Access Server — прекрасное ПО, которое устанавливается на сервере в пару команд и позволяет удобно работать с клиентами: редактировать подсети, профили и пароли и так далее. Делать все это вручную было бы намного тяжелее.
Но вот незадача — в бесплатной версии есть ограничение на число подключений: работать одновременно могут лишь два клиента. Платная лицензия стоит дорого, к тому же ее сейчас никак не оплатишь с российской кредитки. Увы, та же участь постигла многие программы и полезные хакерские ресурсы. Вчера, например, я не смог оплатить Hack The Box, что меня очень расстроило. Что ж, будем вместо прохождения тачек с HTB упражняться в старинном русском ремесле — крякинге!
WARNING
На момент написания этого материала законы, запрещающие использование нелицензионного ПО, по‑прежнему имеют силу. Возможность разрешить использовать софт уходящих из России компаний без лицензии только обсуждается. Мы в этой статье лишь демонстрируем уязвимость, благодаря которой возможен обход проверки. Применяя полученные знания, помни, что ты действуешь на свой страх и риск. Не забывай следить за актуальным законодательством, особенно если действуешь в коммерческих целях.
Чтобы не изобретать велосипед, давай глянем, что ответят поисковики на запрос «OpenVPN Access Server license unlimited». Любопытно, но есть один репозиторий, удаленный с GitHub за несоблюдение авторских прав. Находим зеркало и смотрим описание: требуется версия CentOS 7. В установочном скрипте после инсталляции самого пакета openvpn-as идет еще и замена файла pyovpn-2.0-py2.7.egg по пути /usr/local/openvpn/python/sites-enabled.
Такой важный элемент системы, отвечающий в том числе за веб‑фронтенд, я бы не стал менять вслепую и тебе тоже не советую. Тем более в софте, связанном с конфиденциальностью. Поэтому перед использованием изучим, что там внутри. Сравним хакнутые варианты с исходными файлами.
Распакуем оригинальный RPM в поисках pyovpn-2.0-py2.7.egg. Распаковать .rpm можно обычным tar:
$ tar xf openvs.rpm
А .egg — это, в свою очередь, обычный ZIP. Находим его по адресу pyovpn-2.0-py2.7/usr/local/openvpn_as/lib/python/.
Далее распаковываем хакнутый pyovpn-2.0-py2.7.egg из комплекта:
$ unzip pyovpn-2.0-py2.7.egg
Поищем файлы, в которых что‑то менялось.
$ diff -rq ./pyovpn-2.0-py2.7_hacked ./pyovpn-2.0-py2.7_original
Files ./pyovpn-2.0-py2.7_hacked/pyovpn/lic/uprop.pyo and ./pyovpn-2.0-py2.7_original/pyovpn/lic/uprop.pyo differ
Only in ./pyovpn-2.0-py2.7_hacked/pyovpn/lic: uprop2.pyo
Files ./pyovpn-2.0-py2.7_hacked/pyovpn/production.pyo and ./pyovpn-2.0-py2.7_original/pyovpn/production.pyo differ
Теперь сравним сами файлы, но сначала их нужно декомпилировать, поскольку .pyc — это байт‑код. Воспользуемся утилитой decompile6, которая отлично работает с версиями Python 2.7, 3.7, 3.8. Я буду делать все на macOS, но в Linux команды вряд ли отличаются.
$ pip install decompyle6
$ uncompyle6 /Users/n0a/Work/openvpn_decompile/test_diff/pyovpn-2.0-py2.7_hacked/pyovpn/lic/uprop.pyo > uprop.py
$ cat uprop.py
Смотрим содержимое декомпилированного файла:
$ cat uprop.py
import uprop2
old_figure = None
def new_figure(self, licdict):
ret = old_figure(self, licdict)
ret['concurrent_connections'] = 1024
return ret
for x in dir(uprop2):
if x[:2] == '__':
continue
if x == 'UsageProperties':
exec 'old_figure = uprop2.UsageProperties.figure'
exec 'uprop2.UsageProperties.figure = new_figure'
exec '%s = uprop2.%s' % (x, x)
Интересно! Цикл for — это перебор всех атрибутов uprop2. Те атрибуты, названия которых начинаются с двух подчеркиваний, пропускаются. Функция old_figure становится ссылкой на метод figure класса UsageProperties, а функция в классе UsageFigure ссылается на new_figure. Сложно сказать, зачем это сделано. Могу предположить, что класс UsageProperties используется где‑то еще, и, чтобы не менять везде, сделали такой неочевидный трюк.
Декомпилируем uprop2 и понимаем, что это оригинальный uprop, в котором и выполняется проверка лицензии.
...
class UsageProperties(object):
def figure(self, licdict):
proplist = set(('concurrent_connections',))
good = set()
ret = None
if licdict:
for key, props in licdict.items():
if 'quota_properties' not in props:
print 'License Manager: key %s is missing usage properties' % key
continue
proplist.update(props['quota_properties'].split(','))
good.add(key)
for prop in proplist:
v_agg = 0
v_nonagg = 0
if licdict:
for key, props in licdict.items():
if key in good:
if prop in props:
try:
nonagg = int(props[prop])
except:
raise Passthru('license property %s (%s)' % (prop, props.get(prop).__repr__()))
v_nonagg = max(v_nonagg, nonagg)
prop_agg = '%s_aggregated' % prop
agg = 0
if prop_agg in props:
try:
agg = int(props[prop_agg])
except:
raise Passthru('aggregated license property %s (%s)' % (prop_agg, props.get(prop_agg).__repr__()))
v_agg += agg
if DEBUG:
print 'PROP=%s KEY=%s agg=%d(%d) nonagg=%d(%d)' % (prop,
key,
agg,
v_agg,
nonagg,
v_nonagg)
apc = self._apc()
v_agg += apc
if ret == None:
ret = {}
ret[prop] = max(v_agg + v_nonagg, bool('v_agg') + bool('v_nonagg'))
ret['apc'] = bool(apc)
if DEBUG:
print "ret['%s'] = v_agg(%d) + v_nonagg(%d)" % (prop, v_agg, v_nonagg)
return ret
Разработчик кряка использовал подмену объекта, не трогая основной файл. Принцип понятен.
Теперь попробуем провернуть то же самое, только для актуальной версии OpenVPN Aсcess Server. Экспериментировать буду на VPS с актуальной Debian 11 Bullseye.
Скачиваем последнюю версию с сайта разработчика. Устанавливаем или распаковываем архив .deb и смотрим версию Python, для которой написан pyovpn:
$ ls /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.9.egg
Ага, 3.9, что не очень подходит, так как декомпиляция для этой версии Python пока не реализована.
INFO
Ты можешь поддержать разработчика декомпилятора и помочь в работе над проектом. Подробнее — в его обращении.
Что‑то мне подсказывает, что версия для Debian 10 будет подходящей, так как, изучив вики, узнаем, что в 11-й версии (Bullseye) уже Python 3.9, а в 10-й (Buster) — 3.7.
УСТАНОВКА В DEBIAN 10
Поскольку VPS новая, просто поменяю ОС на десятую версию и посмотрю доступные версии OpenVPS-AS:
$ apt update && apt -y install ca-certificates wget net-tools gnupg
wget -qO - https://as-repository.openvpn.net/as-repo-public.gpg | apt-key add -
echo "deb http://as-repository.openvpn.net/as/debian buster main">/etc/apt/sources.list.d/openvpn-as-repo.list
$ apt update
$ apt policy openvpn-as
openvpn-as:
Installed: (none)
Candidate: 2.10.1-d5bffc76-Debian10
Version table:
2.10.1-d5bffc76-Debian10 500
500 http://as-repository.openvpn.net/as/debian buster/main amd64 Packages
2.10.0-ca1e86b5-Debian10 500
500 http://as-repository.openvpn.net/as/debian buster/main amd64 Packages
2.9.6-1090f6b3-Debian10 500
500 http://as-repository.openvpn.net/as/debian buster/main amd64 Packages
2.9.5-82d54e5b-Debian10 500
500 http://as-repository.openvpn.net/as/debian buster/main amd64 Packages
2.9.4-8b3ce898-Debian10 500
...
Отлично, версия последняя, как в 11-й, а значит, актуальная. Устанавливаем и смотрим версию Python, которая используется в pyovpn. Должно быть 3.7.
$ apt -y install openvpn-as
$ ls /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.7.egg
/usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.7.egg
Так и есть: это последняя версия (2.10.1) и она использует Python 3.7. Все складывается. Давай проверим, многое ли изменилось по сравнению с версией 2.0.5, которая была изначально найдена похеканной. Чтобы не таскать файлы туда‑сюда, ставлю на сервере python-decompile3:
$ git clone https://github.com/rocky/python-decompile3
$ cd python-decompile3
$ pip3 install -e .
И снова распаковываем .egg:
$ mkdir /opt/ovpn && cd ovpn
$ cp /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.7.egg ./
$ cp /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.7.egg pyovpn-2.0-py3.7.zip
$ unzip pyovpn-2.0-py3.7.zip && rm pyovpn-2.0-py3.7.zip
$ ls
EGG-INFO pyovpn
Затем декомпилируем:
$ cd ./pyovpn/lic
$ decompyle3 uprop.pyc > uprop.py
$ cat uprop.py
Смотрим отличия и видим, что все то же самое, кроме небольших расхождений в синтаксисе. Но что имел в виду автор хака, когда использовал два файла? Почему просто не указать явно в конце функции figure количество соединений, минуя все проверки?
Давай попробуем более простой вариант. Добавляем ret['concurrent_connections'] = 1337 перед возвратом (ret):
$ nano uprop.py
...
apc = self._apc()
v_agg += apc
if ret == None:
ret = {}
ret[prop] = max(v_agg + v_nonagg, bool('v_agg') + bool('v_nonagg'))
ret['apc'] = bool(apc)
if DEBUG:
print("ret['%s'] = v_agg(%d) + v_nonagg(%d)" % (prop, v_agg, v_nonagg))
ret['concurrent_connections'] = 1337
return ret
def _apc(self):
...
Сохраняем файл, компилируем:
$ python3 -m compileall uprop.py
$ rm uprop.pyc uprop.py
$ cp __pycache__/uprop.cpython-37.pyc ./uprop.pyc
$ rm -Rf __pycache__
Архивируем и заменяем пакет .egg:
$ cd /opt/ovpn
$ zip -r *
$ sudo rm /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.7.egg
$ sudo cp common.zip /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.7.egg
И перезагружаем openvpn-as. Я удалял логи, так как ловил ошибки во время экспериментов. Это делать необязательно.
$ sudo service openvpnas stop
$ sudo rm /var/log/openvpnas.log
$ sudo touch /var/log/openvpnas.log
Запускаем сервис openvpnas:
$ service openvpnas start
Убеждаемся, что все в порядке и ошибок нет:
cat /var/log/openvpnas.log
Идем в админку на порте 943/admin и видим, что нам доступно 1337 подключений.

INFO
Если ты вдруг потерял пароль и не можешь попасть в админку, напиши passwd openvpn.
Тестирование показало отличную работу с двумя и более устройствами.
ВЫВОДЫ
Многие VPN сейчас под блокировками или не могут принять оплату, поэтому Access Server — неплохой способ быстро развернуть собственный аналог с возможностью управлять профилями пользователей. Пользоваться ли пиратским методом его активации — решать тебе, но это, как оказалось, совсем не сложно.
Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei