Palo Alto's exploited Python code
watchTowr Labs has a nice blog post dissecting CVE-2024-3400. It’s very readable. Go check it out.
The awfulness of Palo Alto’s Python code in this snippet stood out to me:
def some_function():
...
if source_ip_str is not None and source_ip_str != "":
curl_cmd = "/usr/bin/curl -v -H \"Content-Type: application/octet-stream\" -X PUT \"%s\" --data-binary @%s --capath %s --interface %s" \
%(signedUrl, fname, capath, source_ip_str)
else:
curl_cmd = "/usr/bin/curl -v -H \"Content-Type: application/octet-stream\" -X PUT \"%s\" --data-binary @%s --capath %s" \
%(signedUrl, fname, capath)
if dbg:
logger.info("S2: XFILE: send_file: curl cmd: '%s'" %curl_cmd)
stat, rsp, err, pid = pansys(curl_cmd, shell=True, timeout=250)
...
def dosys(self, command, close_fds=True, shell=False, timeout=30, first_wait=None):
"""call shell-command and either return its output or kill it
if it doesn't normally exit within timeout seconds"""
# Define dosys specific constants here
PANSYS_POST_SIGKILL_RETRY_COUNT = 5
# how long to pause between poll-readline-readline cycles
PANSYS_DOSYS_PAUSE = 0.1
# Use first_wait if time to complete is lengthy and can be estimated
if first_wait == None:
first_wait = PANSYS_DOSYS_PAUSE
# restrict the maximum possible dosys timeout
PANSYS_DOSYS_MAX_TIMEOUT = 23 * 60 * 60
# Can support upto 2GB per stream
out = StringIO()
err = StringIO()
try:
if shell:
cmd = command
else:
cmd = command.split()
except AttributeError: cmd = command
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, bufsize=1, shell=shell,
stderr=subprocess.PIPE, close_fds=close_fds, universal_newlines=True)
timer = pansys_timer(timeout, PANSYS_DOSYS_MAX_TIMEOUT)
It uses string building to create a curl
command line. Then it passes that command line down into a function that calls subprocess.Popen(cmd_line, shell=True)
. What? No! Don’t ever do that!
I fed that code into the open source bandit static analyzer. It flagged this code with a high severity, high confidence finding:
ᐅ bandit pan.py
[main] INFO profile include tests: None
[main] INFO profile exclude tests: None
[main] INFO cli include tests: None
[main] INFO cli exclude tests: None
[main] INFO running on Python 3.12.1
Run started:2024-04-16 17:14:52.240258
Test results:
>> Issue: [B604:any_other_function_with_shell_equals_true] Function call with shell=True parameter identified, possible security issue.
Severity: Medium Confidence: Low
CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
More Info: https://bandit.readthedocs.io/en/1.7.8/plugins/b604_any_other_function_with_shell_equals_true.html
Location: ./pan.py:14:26
13 logger.info("S2: XFILE: send_file: curl cmd: '%s'" % curl_cmd)
14 stat, rsp, err, pid = pansys(curl_cmd, shell=True, timeout=250)
15
--------------------------------------------------
>> Issue: [B602:subprocess_popen_with_shell_equals_true] subprocess call with shell=True identified, security issue.
Severity: High Confidence: High
CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
More Info: https://bandit.readthedocs.io/en/1.7.8/plugins/b602_subprocess_popen_with_shell_equals_true.html
Location: ./pan.py:49:8
48 bufsize=1,
49 shell=shell,
50 stderr=subprocess.PIPE,
51 close_fds=close_fds,
52 universal_newlines=True,
53 )
54 timer = pansys_timer(timeout, PANSYS_DOSYS_MAX_TIMEOUT)
--------------------------------------------------
Code scanned:
Total lines of code: 41
Total lines skipped (#nosec): 0
Run metrics:
Total issues (by severity):
Undefined: 0
Low: 0
Medium: 1
High: 1
Total issues (by confidence):
Undefined: 0
Low: 1
Medium: 0
High: 1
Files skipped (0):
From that we can infer that Palo Alto does not use effective static analysis on their Python code. If they did, this code would not have made it to production.