Skip to content

Instantly share code, notes, and snippets.

@p1a2l3o4a5l6t7o8
Last active July 17, 2024 17:10
Show Gist options
  • Save p1a2l3o4a5l6t7o8/7447ed2c119f30333471c4834e551508 to your computer and use it in GitHub Desktop.
Save p1a2l3o4a5l6t7o8/7447ed2c119f30333471c4834e551508 to your computer and use it in GitHub Desktop.
Vulnerable scripts for exploiting CVE-2024-3400 without device telemetry

pan_logexport_ftp

Globbing in the tar command results in parameter injection, resulting in code execution.

$ diff ./11.2.0/usr/local/bin/pan_log_export_ftp ./11.2.1/usr/local/bin/pan_log_export_ftp
5a6
> import tarfile
125c126
<     shellCmd = "cd /opt/pancfg/mgmt/mdm/enterprise_appstore; tar -czf /opt/pancfg/mgmt/mdm/tmp/entappstore.tgz *"
---
>     shellCmd = "cd /opt/pancfg/mgmt/mdm/enterprise_appstore"
126a128,130
>     sourceDir = "/opt/pancfg/mgmt/mdm/enterprise_appstore"
>     with tarfile.open(localTarFile, "w:gz") as tar:
>         tar.add(sourceDir, arcname=os.path.basename(sourceDir))

pan_logexport_scp

Basically the same as pan_logexport_ftp. Parameter injection thanks to tar *.

$ diff ./11.2.0/usr/local/bin/pan_log_export_scp ./11.2.1/usr/local/bin/pan_log_export_scp
6a7
> import tarfile
313c314
<             shellCmd = "cd /opt/pancfg/mgmt/mdm/enterprise_appstore; tar -czf /opt/pancfg/mgmt/mdm/tmp/entappstore.tgz *"
---
>             sourceDir = "/opt/pancfg/mgmt/mdm/enterprise_appstore"
324c325,326
<             os.system(shellCmd)
---
>             with tarfile.open(localTarFile, "w:gz") as tar:
>                 tar.add(sourceDir, arcname=os.path.basename(sourceDir))

genindex.sh

If I had a nickel for every time genindex.sh could be used in an RCE chain involving attacker-controlled filenames I'd have two nickels. Which isn't a lot, but it's weird that it happened twice. This is the same script that was used for most CVE-2017-15944 exploits. The newly fixed vulnerabilty is that the overly permissive * in the find command and the lack of quoting $logfile in md_batch calls allow injecting command line parameters, which can turn into code execution.

$ diff ./11.2.0/usr/local/bin/genindex.sh ./11.2.1/usr/local/bin/genindex.sh
410c410
<       for logfile in `find "$day" -mmin +5 -name pan.[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]*.log | sort -r`
---
>       for logfile in `find "$day" -mmin +5 -name pan.[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].log | sort -r`
416,418c416,418
<           idxfile=$logfile.$field.idx
<           bdxfile=$logfile.$field.bdx
<           upfile=$logfile.$field.updatefile
---
>           idxfile="${logfile}".$field.idx
>           bdxfile="${logfile}".$field.bdx
>           upfile="${logfile}".$field.updatefile
421c421
<           if [ \( ! -e $idxfile \) -a \( ! -e $bdxfile \) ]; then
---
>           if [ \( ! -e "${idxfile}" \) -a \( ! -e "${bdxfile}" \) ]; then
423c423
<           elif [ \( $logfile -nt $idxfile \) -a \( $logfile -nt $bdxfile \) ]; then
---
>           elif [ \( "${logfile}" -nt "${dxfile}" \) -a \( "${logfile}" -nt "${bdxfile}"\) ]; then
425c425
<           elif [ \( $upfile -nt $idxfile \) -a \( $upfile -nt $bdxfile \) ]; then
---
>           elif [ \( "${upfile}" -nt "${idxfile}" \) -a \( "${upfile}" -nt "${bdxfile}" \) ]; then
441,443c441,443
<             echo $DATE "generating indexes for $db db on $to_index : $logfile"
<             /usr/local/bin/md_batch -s -a -i -p 2 -o /var/log/pan/indexgen.log log_index $INDEXER -l $logfile -t $db -f "$to_index" -i
< #            $INDEXER -l $logfile -t $db -f "$to_index" -i
---
>             echo $DATE "generating indexes for $db db on $to_index : "${logfile}""
>             /usr/local/bin/md_batch -s -a -i -p 2 -o /var/log/pan/indexgen.log log_index $INDEXER -l "${logfile}" -t $db -f "$to_index" -i
> #            $INDEXER -l "${logfile}" -t $db -f "$to_index" -i
450c450
<           mergefile=$logfile.$field.bdx.merge
---
>           mergefile="${logfile}".$field.bdx.merge
462,463c462,463
<             echo $DATE "Merging bdx for $db db on "$to_merge": $logfile"
<             /usr/local/bin/md_batch -s -a -i -p 2 -o /var/log/pan/indexgen.log log_index $INDEXER -l $logfile -t $db -f "$to_merge" -m
---
>             echo $DATE "Merging bdx for $db db on "$to_merge": "${logfile}""
>             /usr/local/bin/md_batch -s -a -i -p 2 -o /var/log/pan/indexgen.log log_index $INDEXER -l "${logfile}" -t $db -f "$to_merge" -m

logfile_cleanup.py

Command injection in logfile names.

$ diff ./11.2.0/usr/lib64/python3.6/site-packages/logfile_cleanup/logfile_cleanup.py ./11.2.1/usr/lib64/python3.6/site-packages/logfile_cleanup/logfile_cleanup.py
5a6
> import tarfile
93c94,96
<         os.system('gzip -f -c %s > %s_%s.gz 2>&1' % (abs_name, abs_name, backup_suffix))
---
>         tarfile_name = str(abs_name + "_" + backup_suffix + ".gz")
>         with tarfile.open(tarfile_name, "w:gz") as tar:
>             tar.add(abs_name, arcname= os.path.basename(tarfile_name))
96,97c99,104
<     def compress_remove(self, root, name): 
<         os.system('gzip -f %s -S _%s.gz > /dev/null 2>&1' % (os.path.join(root, name), backup_suffix))
---
>     def compress_remove(self, root, name):
>         abs_name = os.path.join(root, name)
>         tarfile_name = str(abs_name + "_" + backup_suffix + ".gz")
>         with tarfile.open(tarfile_name, "w:gz") as tar:
>             tar.add(abs_name, arcname= os.path.basename(tarfile_name))
>             os.remove(abs_name)
99c106
<     # tar compress the files 
---
>     # tar compress the files
101c108,117
<         os.system('cd %s; tar -zcvf %s_%s.tgz %s.*_%s --remove-files > /dev/null 2>&1' % (root, name, backup_suffix, name, backup_suffix))
---
>         abs_name = os.path.join(root, name)
>         tarfile_name = str(abs_name + "_" + backup_suffix + ".tgz")
>         file_list=[]
>         for file in os.listdir(root):
>             if file.endswith(backup_suffix):
>                 file_list.append(file)
>         with tarfile.open(tarfile_name, "w:gz") as tar:
>             for file_name in file_list :
>                 tar.add(file_name, arcname= os.path.basename(tarfile_name))
>                 os.remove(file_name)

pan_trigger_syd_event

File globbing without filtering results in command injection.

$ diff ./11.2.0/usr/local/bin/pan_trigger_sysd_event ./11.2.1/usr/local/bin/pan_trigger_sysd_event
101,104c101,102
< 
<             pstr = "f = open('%s', 'w'); f.write('%s'); f.close()" % (item, event)
<             output = self.pansys.dosys('/usr/bin/python -c \"%s\" ' % pstr, shell=True, timeout=5)
< 
---
>             with open(item, "w") as f:
>                 f.write(event)

vm_license_check

f comes from globbing. Easy command injection.

$ diff ./11.2.0/usr/local/bin/vm_license_check ./11.2.1/usr/local/bin/vm_license_check
6a7
> import base64
120,131c121,144
<     cmd = 'cat %s | /usr/bin/openssl base64 -d | /usr/bin/openssl rsautl -verify -pubin -inkey %s' % (f, pub_pem)
<     sts, o, e = dosys(cmd, timeout=60, shell=True, quiet=True, allow_error=True)
<     if sts != 0:
<         try:
<             cmd = 'cat %s | /usr/bin/openssl base64 -d | /usr/bin/openssl rsautl -verify -pubin -inkey %s' % (f, alt_pub_pem)
<             sts, o, e = dosys(cmd, timeout=60, shell=True,quiet=True, allow_error=True)
<             if sts != 0:
<                 logit(log, 'Failed to decrypt license file: {}'.format(e))
<         except Exception as e:
<             logit(log, 'Failed to write decrypt license file: %s' % str(e))
<     else:
<         logit(log, 'Failed to decode license file: {}'.format(e))
---
>     try:
>       with open(f, "rb") as keyfile:
>         key = keyfile.read()
>         decoded = base64.b64decode(key)
>       with open(TMP_B64_LIC, "wb") as decfile:
>         decfile.write(decoded)
>       cmd = ['/usr/bin/openssl', 'rsautl', '-verify', '-pubin', '-inkey', pub_pem, '-in', TMP_B64_LIC]
>       sts, o, e = dosys(cmd, timeout=60, shell=False, quiet=True, allow_error=True)
>       if sts != 0:
>           try:
>               cmd = ['/usr/bin/openssl', 'rsautl', '-verify', '-pubin', '-inkey', alt_pub_pem, '-in', TMP_B64_LIC]
>               sts, o, e = dosys(cmd, timeout=60, shell=False,quiet=True, allow_error=True)
>               if sts != 0:
>                   logit(log, 'Failed to decrypt license file: {}'.format(e))
>           except Exception as e:
>               logit(log, 'Failed to write decrypt license file: %s' % str(e))
>       else:
>           logit(log, 'File decrypted')
>     except Exception as e:
>       logit(log, 'Failed to  decrypt license file: %s' % str(e))
>     try:
>       os.remove(TMP_B64_LIC)
>     except:
>       pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment