How to Remove OpenVPN Access Server Connection Limits

VPN for Everyone! Removing OpenVPN Access Server Restrictions

Recently, more and more friends have been asking me for help accessing Facebook, Instagram, Telegram, YouTube, and other blocked sites where important information might still be available. To help them, I’ve always used OpenVPN Access Server. However, its free version comes with some licensing restrictions. In this article, I’ll show you how to get around them.

What Is OpenVPN Access Server?

Access Server is excellent software that can be installed on a server with just a couple of commands. It allows you to conveniently manage clients: edit subnets, profiles, passwords, and more. Doing all this manually would be much harder.

But here’s the catch—the free version limits the number of simultaneous connections: only two clients can connect at the same time. The paid license is expensive, and right now, it’s impossible to pay for it with a Russian credit card. Unfortunately, many programs and useful hacker resources are facing the same fate. For example, yesterday I couldn’t pay for Hack The Box, which was very disappointing. So, instead of solving HTB challenges, let’s practice the old Russian craft—cracking!

Legal Disclaimer

At the time of writing, laws prohibiting the use of unlicensed software are still in effect. The possibility of allowing the use of software from companies leaving Russia without a license is only being discussed. In this article, we’re only demonstrating a vulnerability that allows bypassing the check. If you use this knowledge, remember you do so at your own risk. Always keep up with current legislation, especially if you’re acting for commercial purposes.

Finding a Solution

Instead of reinventing the wheel, let’s see what search engines say about “OpenVPN Access Server license unlimited.” Interestingly, there’s a repository that was removed from GitHub for copyright violations. We find a mirror and look at the description: it requires CentOS 7. In the installation script, after installing the openvpn-as package, it also replaces the pyovpn-2.0-py2.7.egg file at /usr/local/openvpn/python/sites-enabled.

This is an important system element responsible, among other things, for the web frontend. I wouldn’t recommend blindly replacing it, especially in software related to privacy. So, before using it, let’s see what’s inside. We’ll compare the hacked files with the originals.

Unpacking and Analyzing the Files

Let’s unpack the original RPM in search of pyovpn-2.0-py2.7.egg. You can unpack .rpm files with tar:

$ tar xf openvs.rpm

An .egg file is just a ZIP archive. Find it at pyovpn-2.0-py2.7/usr/local/openvpn_as/lib/python/.

Next, unpack the hacked pyovpn-2.0-py2.7.egg from the package:

$ unzip pyovpn-2.0-py2.7.egg

Let’s look for files that have been changed.

Decompiling and Comparing

Now, let’s compare the files, but first, we need to decompile them since .pyc files are bytecode. Use the decompyle6 utility, which works well with Python 2.7, 3.7, and 3.8. I’ll do everything on macOS, but the commands should be similar on 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

Here’s the decompiled file’s content:

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)

Interesting! The for loop iterates over all attributes of uprop2. Attributes starting with two underscores are skipped. The old_figure function becomes a reference to the figure method of the UsageProperties class, and the class’s figure method is replaced with new_figure. This trick likely avoids changing the class everywhere it’s used.

Decompiling uprop2 shows it’s the original uprop where the license check happens.

Applying the Hack to a Modern Version

Let’s try the same approach for the latest OpenVPN Access Server version. I’ll experiment on a VPS running Debian 11 Bullseye.

Download the latest version from the developer’s site. Install or unpack the .deb archive and check the Python version used by pyovpn:

$ ls /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.9.egg

It’s Python 3.9, which isn’t supported by decompilers yet.

Tip: You can support the decompiler developer and help the project progress. More details are in their announcement.

Debian 10 uses Python 3.7, which is supported. Let’s switch the VPS to Debian 10 and check available OpenVPN-AS versions:

$ 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

The latest version is available, just like in Debian 11. Install it and check the Python version used by pyovpn. It should be 3.7.

$ apt -y install openvpn-as
$ ls /usr/local/openvpn_as/lib/python/pyovpn-2.0-py3.7.egg

Perfect: it’s the latest version (2.10.1) and uses Python 3.7. Let’s see if much has changed compared to version 2.0.5, which was originally found hacked. To avoid moving files around, install python-decompile3 on the server:

$ git clone https://github.com/rocky/python-decompile3
$ cd python-decompile3
$ pip3 install -e .

Unpack the .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

Then decompile:

$ cd ./pyovpn/lic
$ decompyle3 uprop.pyc > uprop.py
$ cat uprop.py

The code is almost the same, with minor syntax differences. But why did the hacker use two files? Why not just set the number of connections directly at the end of the figure function, bypassing all checks?

Let’s try a simpler approach. Add ret['concurrent_connections'] = 1337 before the return statement:

$ 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):
    ...

Save the file and compile:

$ python3 -m compileall uprop.py
$ rm uprop.pyc uprop.py
$ cp __pycache__/uprop.cpython-37.pyc ./uprop.pyc
$ rm -Rf __pycache__

Archive and replace the .egg package:

$ 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

Restart openvpn-as. I deleted the logs while troubleshooting errors during experiments, but this step is optional.

$ sudo service openvpnas stop
$ sudo rm /var/log/openvpnas.log
$ sudo touch /var/log/openvpnas.log

Start the openvpnas service:

$ service openvpnas start

Make sure everything is working and there are no errors:

cat /var/log/openvpnas.log

Go to the admin panel at port 943/admin and you’ll see that 1337 connections are now available.

Five active connections out of 1337 possible

Tip: If you’ve lost your password and can’t access the admin panel, run passwd openvpn.

Testing showed excellent performance with two or more devices.

Conclusion

Many VPNs are currently blocked or can’t accept payments, so Access Server is a good way to quickly deploy your own alternative with user profile management. Whether to use this “pirate” activation method is up to you, but as it turns out, it’s not difficult at all.

Leave a Reply