python

    Python 3.13 launched today. I’ve done it. I’ve lived long enough to see a less-GIL’ed Python released to the public. Until now there’s been an unvirtuous cycle:

    Python isn’t good at running CPU-intensive threaded code. → No one writes code like that. → There was no pressure to remove the GIL because no one writes code that would benefit from it. → Repeat.

    I hope this is the first giant step toward good Python multithreading.

    Python 3.13 is removing more Amiga “dead batteries” modules, like chunk:

    The chunk module provides support for reading and writing Electronic Arts’ Interchange File Format. IFF is an old audio file format originally introduced for Commodore and Amiga. The format is no longer relevant.

    I’m sure that’s the right thing to do. It still saddens me.

    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.