<?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[CSG @ GovTech Singapore - Medium]]></title>
        <description><![CDATA[We’ve moved! New cybersecurity technical stories are now published under @GovTechSingapore’s official technical Medium blog. Discover new stories ➡️ (https://medium.com/govtechsingapore). - Medium]]></description>
        <link>https://medium.com/csg-govtech?source=rss----ebb6b6f1ad4e---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>CSG @ GovTech Singapore - Medium</title>
            <link>https://medium.com/csg-govtech?source=rss----ebb6b6f1ad4e---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 14:45:25 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/csg-govtech" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Testing Enterprise Browsers — Part 3]]></title>
            <link>https://medium.com/csg-govtech/testing-enterprise-browsers-part-3-d431a4bd0557?source=rss----ebb6b6f1ad4e---4</link>
            <guid isPermaLink="false">https://medium.com/p/d431a4bd0557</guid>
            <category><![CDATA[enterprise-software]]></category>
            <category><![CDATA[security-testing]]></category>
            <category><![CDATA[csg]]></category>
            <category><![CDATA[product-security]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <dc:creator><![CDATA[Yu Ann]]></dc:creator>
            <pubDate>Mon, 03 Aug 2026 03:56:35 GMT</pubDate>
            <atom:updated>2026-08-03T04:12:23.229Z</atom:updated>
            <content:encoded><![CDATA[<p>Enterprise browsers have emerged as a critical security control for organisations seeking to protect sensitive data and enforce security policies in cloud-first environments. This is Part 3 of our 3-part series on our security assessment of Palo Alto Networks’ Prisma Access Browser (PAB). Read more about our discoveries in <a href="https://medium.com/csg-govtech/testing-enterprise-browsers-a-security-assessment-of-prisma-access-browser-e5ad639c4924">Part 1</a> and <a href="https://medium.com/csg-govtech/testing-enterprise-browsers-part-2-6fe4b9282e4c">Part 2</a>.</p><h3><strong>Understanding Design Trade-offs and Security Implications</strong></h3><p>The findings in Part 3 did not arise from implementation flaws or logic errors in the traditional sense. Instead, they stem from design decisions in PAB that, when examined from an attacker’s perspective, create security consequences that may not have been apparent at the time those decisions were made. Both findings reflect the tension between building a product that meets customer needs and ensuring that the resulting design does not introduce exploitable conditions.</p><h4>Finding 7: Bypassing security patches via forced version downgrades</h4><p>PAB is distributed through an installer package that functions as a bootstrapper, downloading the full browser binary during setup rather than containing it directly.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*2PcrILW8cQj-iOL0FnqIXQ.png" /><figcaption><strong>Figure 13 — Default PAB installer package</strong></figcaption></figure><p>We investigated the mechanism by which this installer package determines which version to retrieve, and observed that it queries a server to obtain the download URL for the full installation binary (i.e. PAB standalone installer). The request is structured as an XML document containing multiple fields that specify the desired browser version, which we investigated for opportunities to manipulate the retrieval process.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*06A0tOwLdi-9_D80vAhuGg.png" /><figcaption><strong>Figure 14 — HTTP request sent by PAB’s default installer package to retrieve the URL for the standalone installer</strong></figcaption></figure><p>The critical parameter in this request is the “&lt;updatecheck targetversionprefix=” field, which accepts a version number as input. We discovered that by modifying this field to specify any valid PAB version number, we could retrieve the download URL for the corresponding standalone installer, including older versions.</p><p>This allowed an attacker to install previous PAB versions that may contain known, patched vulnerabilities. We tested this by installing an earlier version of PAB and confirmed that previously patched security issues could be re-exploited.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*qvMTwCfymKozhmRj4m7g1A.png" /><figcaption><strong>Figure 15 — Version-specific, “standalone” PAB installer package</strong></figcaption></figure><p>On Windows systems, when the standalone installer runs as part of the normal installation flow, several configuration options are automatically included that provide additional security controls. When the standalone installer is downloaded and run manually, these options can be omitted. Doing so disables PAB’s automatic update mechanism, creating a persistent downgrade scenario in which the browser does not update to secure versions, allowing known vulnerabilities to be exploited indefinitely. Other techniques, such as redirecting the update server at the network or host level, can achieve the same effect.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/944/1*UqeurdWhZrRI8j6JOY7SXA.png" /><figcaption><strong>Figure 16 — When installed using the standalone installer, PAB cannot auto-update</strong></figcaption></figure><p><strong>Addressing Version Control Challenges</strong></p><p>When we disclosed this finding, the PAB team explained that the ability to install older versions was an intentional design decision to accommodate various customer requirements, particularly around legacy compatibility and controlled rollout scenarios.</p><p>Following our disclosure, Palo Alto Networks committed to implementing an enhanced update control mechanism that would provide administrators with more robust controls to prevent the use of outdated versions. As of April 2025, this enhanced mechanism has been deployed.</p><p><strong>Finding 8: Accessing obfuscated extension source code via public hosting endpoints</strong></p><p>During our analysis of PAB’s installation process, we identified that the Prisma Access Browser Extension (“PAB Extension”) is hosted on publicly accessible endpoints, where it is available for download and analysis despite being distributed in obfuscated format.</p><p>While examining network traffic during installation, we observed URLs pointing to extension files hosted on the “extensions.talon-sec.com” domain.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/904/1*jtri-TlTzk0xhUr3Vo_4uw.png" /><figcaption><strong>Figure 17 — HTTP requests sent by PAB to download the PAB extension</strong></figcaption></figure><p>Further investigation revealed that these URLs follow a predictable pattern in which the version number is embedded directly in the URL:</p><pre>https://extensions.talon-sec.com/[extension-id]-ext-v[version].crx</pre><p>By modifying the version parameter in the URL, we could download any version of the PAB Extension in CRX format.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/900/1*TkzfLiOEkEz7anScxFsQDg.png" /><figcaption><strong>Figure 18 — Downloading PAB extension version 1.358.0</strong></figcaption></figure><p>CRX files are standard Chromium extension packages that can be extracted using common archive tools on Linux systems:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/900/1*14uYm_L_1-dIZkNkSAnSIQ.png" /><figcaption><strong>Figure 19 — Unpacking PAB extension (.crx file)</strong></figcaption></figure><p>Once extracted, the extension’s source code was accessible in obfuscated form, which nonetheless permitted reverse engineering and static analysis:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/790/1*0fYMdtW8RVhjHM_5QohGNQ.png" /><figcaption><strong>Figure 20 — Downloaded PAB extension is in obfuscated format</strong></figcaption></figure><p>The obfuscated JavaScript files, once reverse engineered, provided detailed information about PAB’s internal security mechanisms, which facilitated the discovery of other vulnerabilities during our assessment (both findings in <a href="https://medium.com/csg-govtech/testing-enterprise-browsers-a-security-assessment-of-prisma-access-browser-e5ad639c4924">Part 1</a>). This is notable because the on-disk version of the PAB Extension that accompanies standard installations is encrypted — a protection that is effectively negated by the availability of obfuscated but analysable versions on publicly accessible endpoints:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/786/1*kNP50s8gDhqjsB_5oiNsgg.png" /><figcaption><strong>Figure 21 — On-disk copy of PAB extension is encrypted</strong></figcaption></figure><p><strong>Vendor Response</strong></p><p>Following our disclosure, Palo Alto Networks clarified that the PAB Extension is made available in obfuscated format as part of their design approach. The on-disk encryption of the extension serves performance optimisation purposes rather than security objectives.</p><p>Palo Alto Networks acknowledged the security considerations around publicly hosting the obfuscated extension code. In response, they have implemented additional measures to increase the difficulty of reverse-engineering the extension and are exploring further mitigations to protect sensitive API calls within the JavaScript files.</p><p><strong>Recommendations</strong></p><p>For organisations deploying PAB, the findings in Part 3 point to controls that sit outside the browser itself — in how it is deployed and maintained over time.</p><p>Finding 7 is a reminder that version control is an operational responsibility, not just a vendor one. Organisations should verify that PAB is installed through the standard installation flow rather than via a manually-run standalone installer. Administrators should also confirm that PAB’s update control mechanism is active and enforced in their deployments.</p><p>Finding 8 has no straightforward remediation on the organisation’s side, since the extension hosting is a vendor-side decision. The practical implication for defenders is awareness: PAB’s internal mechanisms should not be assumed to be opaque to a determined attacker. Organisations should treat the browser’s security controls as one layer among several.</p><p><strong>Conclusion</strong></p><p>The findings in this part share a common thread: they did not arise from implementation bugs, but from design decisions that made sense in their original context. The ability to install older versions served real customer needs. Hosting the extension publicly simplified distribution. Neither decision was made carelessly, but both created security implications that were worth surfacing.</p><p>This is perhaps the most important lesson of the series as a whole. Security assessments that only look for traditional vulnerabilities will miss a significant class of risk. Understanding how a product is designed, how it is distributed, and how its components interact with the broader environment is just as important as finding bugs in its code.</p><p><strong>Summary</strong></p><p>Through this assessment, we identified several findings — of which five were recognised as vulnerabilities by Palo Alto Networks — that could allow attackers to circumvent PAB’s security controls, extract sensitive data, and bypass protective mechanisms. Our findings fell across three broad categories.</p><p>The first category, security control bypass, covered methods to circumvent PAB’s configured security policies through policy bypass and signature replay attacks. The second, environmental exploitation, covered techniques that leveraged operating system primitives to extract sensitive data and bypass data protection controls. The third, logical findings, covered design decisions that created potential security considerations, including version downgrade capabilities and the public availability of extension code for analysis.</p><p>Collectively, these findings demonstrate that while enterprise browsers provide valuable security controls, they require careful implementation and ongoing independent validation. Security guarantees that have not been tested should not be assumed to hold.</p><p>Security advisories have been published for the resolved issues:</p><p>● <a href="https://security.paloaltonetworks.com/CVE-2025-0129">https://security.paloaltonetworks.com/CVE-2025-0129</a></p><p>● <a href="https://security.paloaltonetworks.com/CVE-2025-4233">https://security.paloaltonetworks.com/CVE-2025-4233</a></p><p>● <a href="https://security.paloaltonetworks.com/CVE-2025-4616">https://security.paloaltonetworks.com/CVE-2025-4616</a></p><p>● <a href="https://security.paloaltonetworks.com/CVE-2025-4617">https://security.paloaltonetworks.com/CVE-2025-4617</a></p><p>● <a href="https://security.paloaltonetworks.com/CVE-2025-4618">https://security.paloaltonetworks.com/CVE-2025-4618</a></p><p>We appreciate Palo Alto Networks’ professional and collaborative approach throughout the disclosure process. Their responsiveness in addressing these findings reflects a strong commitment to securing their products together with the security research community.</p><p><em>Click to check out </em><a href="https://medium.com/csg-govtech/testing-enterprise-browsers-a-security-assessment-of-prisma-access-browser-e5ad639c4924"><em>Part 1</em></a><em> and </em><a href="https://medium.com/csg-govtech/testing-enterprise-browsers-part-2-6fe4b9282e4c"><em>Part 2</em></a><em> of this series.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d431a4bd0557" width="1" height="1" alt=""><hr><p><a href="https://medium.com/csg-govtech/testing-enterprise-browsers-part-3-d431a4bd0557">Testing Enterprise Browsers — Part 3</a> was originally published in <a href="https://medium.com/csg-govtech">CSG @ GovTech Singapore</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Testing Enterprise Browsers — Part 2]]></title>
            <link>https://medium.com/csg-govtech/testing-enterprise-browsers-part-2-6fe4b9282e4c?source=rss----ebb6b6f1ad4e---4</link>
            <guid isPermaLink="false">https://medium.com/p/6fe4b9282e4c</guid>
            <category><![CDATA[product-security]]></category>
            <category><![CDATA[security-testing]]></category>
            <category><![CDATA[enterprise-software]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <category><![CDATA[csg]]></category>
            <dc:creator><![CDATA[Zhang Bosen]]></dc:creator>
            <pubDate>Mon, 03 Aug 2026 03:56:23 GMT</pubDate>
            <atom:updated>2026-08-03T04:04:05.496Z</atom:updated>
            <content:encoded><![CDATA[<p>Enterprise browsers have emerged as a critical security control for organisations seeking to protect sensitive data and enforce security policies in cloud-first environments. This is Part 2 of our 3-part series on our security assessment of Palo Alto Networks’ Prisma Access Browser (PAB). Read more about our discoveries in <a href="https://medium.com/csg-govtech/testing-enterprise-browsers-a-security-assessment-of-prisma-access-browser-e5ad639c4924">Part 1</a> and <a href="https://medium.com/csg-govtech/testing-enterprise-browsers-part-3-d431a4bd0557">Part 3</a>.</p><h3><strong>Exploiting Environmental Blind Spots</strong></h3><p>Where <a href="https://medium.com/csg-govtech/testing-enterprise-browsers-a-security-assessment-of-prisma-access-browser-e5ad639c4924">Part 1</a> examined how PAB’s own policy enforcement logic could be subverted, Part 2 examines how the environment PAB runs within can be made to work against it. A recurring theme across these findings is the challenge of building application-level security controls on top of a platform — in this case, Chromium — whose default behaviours were not designed with enterprise data protection in mind.</p><h4>Finding 3: Extracting secrets via Chromium sub-process memory dumps (CVE-2025–4618)</h4><p>PAB is designed to protect against data exfiltration by preventing users from extracting sensitive information from the browser, including application session tokens and downloaded files (when configured to block such downloads). We investigated whether these protections could be circumvented through direct access to browser process memory.</p><p>As PAB is built on the Chromium platform, it follows Chromium’s multi-process architecture, dividing its operations across multiple sub-processes responsible for functions such as handling network traffic and managing storage.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/944/1*QoRGte6FWAgOOyfPC-V_Aw.png" /><figcaption>Figure 10 — PAB’s NetworkService sub-process, which handles network traffic</figcaption></figure><p>We hypothesised that these sub-processes, which handle sensitive data during normal browser operation, would retain that data in memory in extractable form.</p><p>Our testing confirmed this hypothesis. Sensitive data was recoverable from PAB sub-process memory by a local user, without any exploitation of PAB’s own code. This is not a flaw unique to PAB; it reflects a fundamental challenge in client-side security. Protecting data from users with local system access is difficult to achieve through application-level controls alone, since those controls cannot prevent direct memory access by a sufficiently privileged user. Addressing this class of attack typically requires hardware-based protections such as confidential computing techniques — mechanisms that protect data even from the operating system itself — which operate beyond the scope of browser-level security controls.</p><h4>Finding 4: Bypassing screenshot restrictions via Windows Display Affinity manipulation (CVE-2025–4617)</h4><p>PAB implements data loss prevention controls that prevent users from capturing screenshots of browser content.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/904/1*0H8_FGzrq28bAw58vm2k2w.png" /><figcaption>Figure 11 — What a user sees when PAB blocks screen sharing/screenshots</figcaption></figure><p>We investigated whether this protection could be bypassed by directly manipulating the underlying Windows API property that governs display affinity — the mechanism through which applications signal to the operating system that their content should not be captured.</p><p>Our testing confirmed that this protection could be bypassed through a direct Windows API call, allowing browser content to be captured despite the restriction being active. This finding illustrates how security controls that rely on operating system cooperation can be undermined when an attacker has sufficient access to interact with the OS directly.</p><p>Palo Alto Networks mitigated CVE-2025–4617 and CVE-2025–4618 by introducing a kernel driver (Prisma Browser’s Self-Protection feature) that enhances Prisma Browser’s security at runtime within its designated operational scope. Additional findings relating to the kernel driver’s protections have been responsibly disclosed to Palo Alto Networks and are currently under review. For secure deployment please refer to <a href="https://docs.paloaltonetworks.com/prisma-access-browser/administration/prisma-browser-self-protection-fpr-windows">Prisma Browser Self-Protection for Windows</a>.</p><h4>Finding 5: Extracting secrets via Chromium LevelDB cache access</h4><p>We also investigated whether sensitive data could be accessed through the file system. As a Chromium-based browser, PAB follows Chromium’s standard data storage patterns, including the use of LevelDB databases for local storage. These databases are stored on disk in a well-documented location and can be read using publicly available tools.</p><p>Our analysis confirmed that sensitive data stored in PAB’s local storage was accessible to a user with file system access, without any need to interact with the browser itself. This finding reinforces the broader point that data protection controls implemented at the browser interface level do not protect data that has already been written to disk in a readable format.</p><p>This finding has been addressed in Prisma Browser versions beyond 137.16.2.69.</p><h4>Finding 6: Bypassing download restrictions via Chromium disk cache extraction (CVE-2025–4233)</h4><p>PAB can be configured to prevent file downloads as part of its data loss prevention capabilities. We investigated whether files blocked from download might still be accessible through Chromium’s disk cache, which stores fetched resources for performance optimisation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/944/1*lsizWfZsmR80bo83n4Btcw.png" /><figcaption>Figure 12 — What a user sees when PAB blocks a file download</figcaption></figure><p>Our testing confirmed that files blocked from download were in some cases still recoverable from the disk cache using standard Chromium cache analysis tools. This means that a user denied the ability to download a file through PAB’s interface could in some cases retrieve it from the cache, effectively bypassing the download restriction without exploiting any vulnerability in PAB’s own code.</p><p>This finding highlights a tension inherent in building data loss prevention controls on top of a general-purpose browser platform. Chromium’s caching behaviour is a feature, not a flaw, but it creates a gap that security controls implemented above the platform layer cannot easily close.</p><p>This finding has been addressed in Prisma Browser versions beyond 137.16.2.69.</p><h4>Vendor Response and Mitigations</h4><p>Palo Alto Networks responded constructively to these findings and worked with us to implement appropriate mitigations. The vendor has released patches addressing these vulnerabilities, demonstrating their commitment to product security.</p><p>Security advisories detailing the fixes are available at:</p><p>● <a href="https://security.paloaltonetworks.com/CVE-2025-0129">https://security.paloaltonetworks.com/CVE-2025-0129</a></p><p>● <a href="https://security.paloaltonetworks.com/CVE-2025-4233">https://security.paloaltonetworks.com/CVE-2025-4233</a></p><p>● <a href="https://security.paloaltonetworks.com/CVE-2025-4616">https://security.paloaltonetworks.com/CVE-2025-4616</a></p><p>● <a href="https://security.paloaltonetworks.com/CVE-2025-4617">https://security.paloaltonetworks.com/CVE-2025-4617</a></p><p>● <a href="https://security.paloaltonetworks.com/CVE-2025-4618">https://security.paloaltonetworks.com/CVE-2025-4618</a></p><h4>Recommendations</h4><p>For organisations deploying PAB, the findings in this part suggest several concrete actions.</p><p>The most immediate action is to ensure PAB is updated to the latest version. Findings 5 and 6 have been addressed in versions 137.16.2.69 and beyond. For Findings 3 and 4, the mitigation introduced by Palo Alto Networks requires deliberate enablement — organisations should confirm that Prisma Browser’s Self-Protection feature is active, as it is not enabled by default.</p><p>Beyond patching, these findings underscore that browser-level controls cannot substitute for endpoint security. Organisations should ensure that endpoint privilege management is in place to limit the ability of users to interact directly with process memory or OS-level APIs. Where possible, users should not have administrative privileges on their endpoints.</p><p>Data written to disk by the browser should be treated as potentially accessible to any user with file system access to that machine. Organisations handling particularly sensitive data should consider whether strict file system access controls on browser profile directories, combined with endpoint monitoring for unusual file access patterns, provide sufficient coverage, or whether additional controls are warranted.</p><p>Finally, these findings are a useful reminder that the threat model for an enterprise browser assumes a degree of trust in the endpoint itself. If that endpoint is compromised or in the hands of a malicious insider, browser-level controls will not hold. Defence-in-depth — combining PAB with endpoint detection and response, network monitoring, and access controls — remains the appropriate posture.</p><h4>Conclusion</h4><p>The findings in this part share a common thread: they are not primarily about flaws in PAB’s own code, but about the gap between what a browser-level security control can enforce and what the underlying platform and operating environment make possible. An enterprise browser can restrict what a user can do within the browser interface, but it cannot fully control what a sufficiently privileged local user can do outside of it, whether that means reading from disk, inspecting process memory, or manipulating OS-level APIs.</p><p>This does not mean enterprise browsers are ineffective. It means their threat model has limits, and organisations should understand those limits when deciding what complementary controls are needed alongside them.</p><h4>Part 3: <a href="https://medium.com/csg-govtech/testing-enterprise-browsers-part-3-d431a4bd0557">Logical findings arising from design decisions</a></h4><p>In <a href="https://medium.com/csg-govtech/testing-enterprise-browsers-part-3-d431a4bd0557">Part 3</a>, we turn to findings that arose not from implementation flaws or environmental blind spots, but from design decisions, and the security considerations that emerge when balancing security requirements with usability and business needs.</p><p><em>Or check out </em><a href="https://medium.com/csg-govtech/testing-enterprise-browsers-a-security-assessment-of-prisma-access-browser-e5ad639c4924"><em>part 1</em></a><em> if you haven’t done so.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6fe4b9282e4c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/csg-govtech/testing-enterprise-browsers-part-2-6fe4b9282e4c">Testing Enterprise Browsers — Part 2</a> was originally published in <a href="https://medium.com/csg-govtech">CSG @ GovTech Singapore</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Testing Enterprise Browsers: A Security Assessment of Prisma Access Browser]]></title>
            <link>https://medium.com/csg-govtech/testing-enterprise-browsers-a-security-assessment-of-prisma-access-browser-e5ad639c4924?source=rss----ebb6b6f1ad4e---4</link>
            <guid isPermaLink="false">https://medium.com/p/e5ad639c4924</guid>
            <category><![CDATA[product-security]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <category><![CDATA[security-testing]]></category>
            <category><![CDATA[csg]]></category>
            <category><![CDATA[enterprise-software]]></category>
            <dc:creator><![CDATA[Tan Inn Fung]]></dc:creator>
            <pubDate>Mon, 03 Aug 2026 03:56:06 GMT</pubDate>
            <atom:updated>2026-09-02T09:30:41.240Z</atom:updated>
            <content:encoded><![CDATA[<h3>Introduction</h3><p>Enterprise browsers have emerged as a critical security control for organisations seeking to protect sensitive data and enforce security policies in cloud-first environments. As more organisations adopt them as a cornerstone of their security architecture, the question is not just about their operational capabilities, but how they withstand targeted scrutiny from a sophisticated, determined local attacker.</p><p>As part of our ongoing efforts to evaluate security solutions used within the Singapore Government, GovTech’s Cyber Security Group (CSG) conducted a security assessment of Palo Alto Networks’ Prisma Access Browser (PAB). Our goal was not simply to run automated scans or only rely on LLMs, but to think like an attacker — reasoning across system boundaries, chaining together observations, and probing the interactions between components that are easy to overlook. This kind of assessment requires curiosity, persistence, and a willingness to question assumptions.</p><h3>Executive Summary</h3><p>We identified several findings, five of which have since been acknowledged and addressed by Palo Alto Networks. Through these findings, we found that an attacker with access to a device with PAB installed could install unauthorised extensions, replay captured policy configurations to weaken security controls, and extract sensitive data. Note that the latest versions of Prisma Browser mitigate our findings when installed and deployed securely following PAB’s documentation at <a href="https://docs.paloaltonetworks.com/prisma-access-browser/administration/prisma-browser-self-protection-fpr-windows">Prisma Browser Self-Protection for Windows</a>.</p><p>These findings do not mean that enterprise browsers are ineffective as a security control. Rather, they highlight that no security solution should be treated as a silver bullet. Enterprise browsers require careful implementation, ongoing validation, and layered defences to ensure their protections hold up against determined attackers.</p><p>We are sharing these findings not only to disclose responsibly, but because we care about whether users and organisations are protected. At the end of each part of this series, we include recommendations on patching, secure configuration, and compensating controls where relevant. Following the resolution of these findings with Palo Alto Networks, we are now sharing them with the broader security community.</p><h3>Responsible Disclosure</h3><p>We thank Palo Alto Networks for responding professionally and promptly addressing the vulnerabilities identified through the responsible disclosure journey. Security advisories have been published for the resolved issues:</p><p>● <a href="https://security.paloaltonetworks.com/CVE-2025-0129">https://security.paloaltonetworks.com/CVE-2025-0129</a></p><p>● <a href="https://security.paloaltonetworks.com/CVE-2025-4233">https://security.paloaltonetworks.com/CVE-2025-4233</a></p><p>● <a href="https://security.paloaltonetworks.com/CVE-2025-4616">https://security.paloaltonetworks.com/CVE-2025-4616</a></p><p>● <a href="https://security.paloaltonetworks.com/CVE-2025-4617">https://security.paloaltonetworks.com/CVE-2025-4617</a></p><p>● <a href="https://security.paloaltonetworks.com/CVE-2025-4618">https://security.paloaltonetworks.com/CVE-2025-4618</a></p><h3>About This Series</h3><p>Given the breadth of our findings, we are publishing them across three parts:</p><p>● <strong>Part 1, Security control bypasses</strong> (this article): Policy bypass and signature replay attacks that circumvent PAB’s configured security policies.</p><p>● <strong>Part 2</strong>, <a href="https://medium.com/csg-govtech/testing-enterprise-browsers-part-2-6fe4b9282e4c"><strong>Environmental Exploitation</strong></a>: Operating system-level primitives for extracting secrets and bypassing data protection controls.</p><p>● <strong>Part 3, </strong><a href="https://medium.com/csg-govtech/testing-enterprise-browsers-part-3-d431a4bd0557"><strong>Logical Findings</strong></a>: Security considerations arising from design decisions, including version downgrade capabilities and availability of obfuscated extension code for analysis</p><p>We hope these findings contribute to the broader security community’s understanding of enterprise browser security and inform both vendors and organisations in strengthening their security postures.</p><p><strong>Finding 1: Bypassing security controls via partial policy bypass (CVE-2025–0129)</strong></p><p>We found that by manipulating certificate trust during the login flow, we could bypass the policy before security restrictions were applied. This policy bypass was relevant when a PAB policy to restrict extension installation was in place, and enabled an attacker to install Chrome extensions that persist beyond the restricted session. The extensions were not removed upon the next browser startup, allowing them to remain active in normal PAB sessions where they can be used to bypass browser security controls, exfiltrate sensitive data, or modify browser-handled information. Note that the extensions would be removed upon an additional browser restart when the extension restriction policy is active, hence the attacker likely has to re-exploit this vulnerability to consistently reap the benefits of the attacker-installed Chrome extensions.</p><p>The vulnerability was triggered by inducing a Chromium SSL/TLS verification failure during authentication. We intercepted the certificate installation flow using a proxy tool (such as PortSwigger’s Burp Suite) and intercepted traffic through to the initial login page. At this point, we removed the interceptor’s root certificate from the Trusted Root Certification Authorities store. After entering the email address and clicking Continue, the authentication flow produced a certificate error page that presented a “Back to safety” option in the browser UI, as shown below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/944/1*xtTEz_WAMXtPx2K877Y7tQ.png" /><figcaption><strong>Figure 1 — “Back to safety” option was presented in the browser’s page after altering the certificate trust</strong></figcaption></figure><p>Clicking the “Back to safety” option yielded a restricted PAB instance, which constitutes a policy bypass.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/877/1*XIlGqNhcKWpqCYxkeMI4_Q.png" /><figcaption><strong>Figure 2 — A restricted PAB browser instance was obtained after the “Back to safety” option was clicked</strong></figcaption></figure><p>The restricted PAB instance limits user capabilities significantly — context-menu actions (right-click), browser settings, and the address bar are all disabled. However, the initial “Google search” bar remains available for navigation. We discovered that this search bar, along with the Web Store shortcut on the PAB homepage, provides access to the Chrome Web Store, where extensions can be installed despite the restrictions.</p><p>To demonstrate the security impact, we installed a Chrome extension while in the restricted instance.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/944/1*NRF9cN1lq9fa2rOoJTVlvg.png" /><figcaption><strong>Figure 3 — Installing an extension through the Chrome Web Store</strong></figcaption></figure><p>After restarting PAB and logging in normally, we observed that the Chrome extension persisted in the fully functional PAB session. However, these Chrome extensions would be removed upon an additional browser restart when the extension restriction policy is active.</p><p>The persistence of an unauthorised extension in a normal PAB session undermines the trust model that enterprise browsers are built on. An organisation deploying PAB typically does so with the expectation that the browser environment is controlled and auditable — but a persisted, unauthorised extension breaks that assumption. Any security guarantee that depends on the integrity of that environment can no longer be relied upon, and the organisation may have no visibility into what the extension is doing or has done.</p><p>This finding has been addressed in the latest versions of Prisma Browser.</p><p><strong>Finding 2: Bypassing security controls via signature replay attack (CVE-2025–4616)</strong></p><p>To enforce its security control, PAB distributes a security policy to the client after the user successfully completes the login process. The policy is protected from tampering through a signature verification mechanism. However, we identified a flaw in the signature verification logic — the freshness check and the signature validation were performed separately. This allowed an attacker to substitute a different policy and its accompanying valid signature while reusing a captured freshness value, effectively bypassing the intended integrity protection, effectively bypassing the intended integrity protection. Nonetheless, carrying out this attack requires overcoming several obstacles.</p><p>Under normal circumstances, PAB’s use of certificate pinning prevents interception of policy downloads via proxy tools. However, during our testing, we observed that PAB could be launched with Chromium’s “--ignore-certificate-errors” command-line flag, which disables certificate validation and allows policy traffic to be intercepted.</p><p>Note: Latest versions of Prisma Browser block the “--ignore-certificate-errors” flag.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/900/1*ukgplI9Q0-BIVPyPYuVysA.png" /><figcaption><strong>Figure 4 — Launching PAB by ignoring the certificate errors</strong></figcaption></figure><p>With certificate validation disabled, we were able to capture the security policy from the server’s response after completing the login process.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/944/1*o3hu3ydvAn5OgtK1-Vczqw.png" /><figcaption><strong>Figure 5 — Successfully captured the security policy from the server response</strong></figcaption></figure><p>Examining the response headers, we observed that integrity protection was implemented through two separate values: X-Talon-Freshness (ensuring policy currency) and X-Talon-Signature (verifying policy authenticity). The response body contained the security policy in JSON format. In our test environment, the policy restricted access to “developerTools” and “extensionsList” options.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/443/1*ou3Oi3AbmyHTcW65xHeZ-A.png" /><figcaption><strong>Figure 6 — The “Developer Tools” and “ExtensionsList” options were restricted in the PAB security policy</strong></figcaption></figure><p>We verified these restrictions by confirming that the current PAB instance blocked access to developer tools and prevented side-loading of extensions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/744/1*wkw9xcTHxUvpgbDrvYXUTg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/944/1*sVyQHNXM3OpHYz9ULj95kA.png" /><figcaption><strong>Figure 7 — PAB blocks users from accessing the developer tool and blocks side-loading extensions (no option to “Load unpacked extensions”) due to current PAB security policy</strong></figcaption></figure><p>To demonstrate the vulnerability, we use a previously captured PAB security policy that permits both developer tools and extension side-loading. This is a reasonable assumption — organisations often deploy less restrictive policies during a pilot phase before tightening controls for production. We substituted this permissive policy into the server response along with its valid signature (X-Talon-Signature), while retaining the freshness value (X-Talon-Freshness) from the original response.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/904/1*x-2PaN_u4POn4JVP02IVyA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/473/1*LaXwqeWj5VlvEmtdxT53hw.png" /><figcaption><strong>Figure 8 — Key chunks of previously-captured PAB security policy that allows side-loading extensions and developer tools</strong></figcaption></figure><p>After launching PAB with the substituted policy, we confirmed that both developer tools and extension side-loading were successfully enabled. This demonstrated that an attacker could replay a previously valid policy to reinstate permissions that have since been revoked, bypassing the organisation’s current intended security restrictions. Note that this replay attack is constrained to policies previously assigned to the same user within the same tenant.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/944/1*SYUDsmN_-i7dcQOyVLDTuA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/944/1*P-_S5n9GB40pP-bIIGIY8A.png" /><figcaption><strong>Figure 9 — Both Developer Tools and side-loading extension options were enabled</strong></figcaption></figure><p>This vulnerability has been addressed in the latest versions of Prisma Browser.</p><h3>Recommendations</h3><p>For organisations deploying PAB, the findings in this part suggest several concrete actions.</p><p>The most immediate action is to update to the latest version of Prisma Browser and follow Palo Alto Networks’ secure deployment guidance at <a href="https://docs.paloaltonetworks.com/prisma-access-browser/administration/prisma-browser-self-protection-fpr-windows">Prisma Browser Self-Protection for Windows</a>. Both findings have been addressed in versions 142.15.2.60 and beyond, though some mitigations are version-dependent, so organisations running older versions remain exposed.</p><p>Beyond patching, Finding 1 highlights the importance of monitoring for unauthorised browser extensions. Even where PAB’s extension restriction policy is active, organisations should consider whether they have visibility into what extensions are present in user sessions. Endpoint detection tooling that can alert on unexpected extension installations would provide an additional layer of defence.</p><p>Finding 2 is a reminder that policy integrity controls are only as strong as their implementation. Organisations should not assume that a signed policy is tamper-proof without understanding how the signature and freshness checks interact. Where PAB is used to enforce critical security boundaries, periodic validation that the expected policies are actually in effect, rather than a previously-captured permissive version, is a worthwhile operational check.</p><p>More broadly, both findings required the attacker to have some degree of access to the device and the ability to intercept or manipulate traffic. Organisations should treat endpoint security and network monitoring as essential complements to PAB, not optional additions. An enterprise browser operating on a compromised or poorly managed endpoint provides weaker guarantees than its configuration might suggest.</p><h3>Conclusion</h3><p>The findings in Part 1 illustrate a recurring theme in security research: the most impactful vulnerabilities are often not found in isolation, but at the seams between components, where one system’s edge case becomes another system’s entry point. Bypassing a policy enforcement mechanism during initialisation, or replaying a valid signature with a stale policy, are not brute-force attacks. They require an attacker to understand how a system is designed, where its assumptions lie, and how those assumptions can be turned against it.</p><p>This is why independent validation matters. Security controls should not be assumed to work simply because they are configured — they should be tested against the kinds of reasoning an attacker would apply.</p><p><strong>Part 2: </strong><a href="https://medium.com/csg-govtech/testing-enterprise-browsers-part-2-6fe4b9282e4c"><strong>Environmental exploitation techniques for data extraction</strong></a></p><p>In <a href="https://medium.com/csg-govtech/testing-enterprise-browsers-part-2-6fe4b9282e4c">Part 2</a>, we turn to a different class of findings: techniques that exploit the operating environment itself to extract sensitive data, bypassing protections that the browser cannot fully control. These findings reveal how even a well-hardened browser can be undermined by the complexity of the systems it runs within.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e5ad639c4924" width="1" height="1" alt=""><hr><p><a href="https://medium.com/csg-govtech/testing-enterprise-browsers-a-security-assessment-of-prisma-access-browser-e5ad639c4924">Testing Enterprise Browsers: A Security Assessment of Prisma Access Browser</a> was originally published in <a href="https://medium.com/csg-govtech">CSG @ GovTech Singapore</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What Four Months at GovTech Taught Me About Cybersecurity]]></title>
            <link>https://medium.com/csg-govtech/what-four-months-at-govtech-taught-me-about-cybersecurity-b3fb58b30334?source=rss----ebb6b6f1ad4e---4</link>
            <guid isPermaLink="false">https://medium.com/p/b3fb58b30334</guid>
            <category><![CDATA[csg]]></category>
            <category><![CDATA[internships]]></category>
            <category><![CDATA[internship-experience]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <category><![CDATA[sharing]]></category>
            <dc:creator><![CDATA[Jodie]]></dc:creator>
            <pubDate>Tue, 12 May 2026 05:44:29 GMT</pubDate>
            <atom:updated>2026-05-12T05:44:28.268Z</atom:updated>
            <content:encoded><![CDATA[<h4>Reflections on my internship at GovTech, from simulated DDoS Testing, building AI-assisted security tools, to agentic pen-testing, and why speed in cybersecurity matters more than ever.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eUAgWb1-m0DifM7RsyVa_A.jpeg" /></figure><p>Four months isn’t a long time. But it was enough to shift how I think about what it actually means to work in cybersecurity. In this (slightly more than) 4-month internship, I got to watch simulated DDoS attacks, build AI tools using LLMs, and perform web penetration tests (both manually and through an agentic tool that did it faster than any human could). It was a lot to take in.</p><p>This reflection is my attempt to make sense of what I built, why it mattered, and what surprised me most along the way.</p><h3>AI as a Productivity Tool, Not a Replacement</h3><p>One of the biggest takeaways from my internship was understanding how AI fits into cybersecurity work in practice. It is easy to think of AI as something that replaces jobs, but that was not what I observed. Instead, it showed up as a way to speed up parts of the workflow that are structured, repetitive, and time-consuming.</p><p>A lot of cybersecurity work follows the same pattern of analysis applied to different problems, which takes time to repeat manually at scale. The projects I worked on were attempts to shift that balance. By using AI to handle the repetitive parts, the goal was not to remove the human, but to free up time for deeper analysis, better judgement, and more meaningful work.</p><h4><strong>1. DDoS Reconnaissance</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/512/1*LmukwZvse-cVqMnICxLCwA.png" /><figcaption>Home page</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/512/1*549LG3hQ3w9ra7ucCyYvcw.png" /><figcaption>Results of a Scan</figcaption></figure><p>The first major project I worked on was a tool for automating reconnaissance in simulated Distributed Denial-of-Service (DDoS) testing. A DDoS attack is a malicious attempt to disrupt a server, service, or network by overwhelming it with a flood of Internet traffic from multiple sources. To find out how resilient a system is against DDoS attacks, it needs to be tested deliberately, under controlled conditions.</p><p>Before the actual testing is done, we should start by understanding the system. Which endpoints exist? What do they do? How much traffic can they handle? By answering these questions, we would be able to find the weakest point to attack. And these questions are answered during the reconnaissance phase. Done manually, reconnaissance is painstaking. To visit the system page by page, crawl through endpoints endpoint by endpoint, trying to analyse the responses, it takes time.</p><p>So, I built a tool to automate this. It crawls the target system, collects all discoverable endpoints, and then uses an AI model to analyse each one. Based on that analysis, my tool recommends which endpoints are the most valuable to test against, and which attack vectors are the most relevant to use.</p><h4>2. CVSS AutoScorer</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/512/1*Xe5eUWe8Qr3Wlux6h43RYw.png" /><figcaption>Home page</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/512/1*31xBfYGJWQLywbdHcs3Dzg.png" /><figcaption>Output score of a vulnerability</figcaption></figure><p>Early in my internship, I got introduced to vulnerability scoring, where you assign a score to a newly discovered security vulnerability. Common Vulnerability Scoring System (CVSS) is the industry standard for this. You discover a vulnerability, identify eight specific metrics including the attack vector, complexity, required privileges, and impact on confidentiality, integrity, and availability, then feed them into a formula that outputs a number between 0 and 10. The higher the score, the more urgent it needs to be fixed.</p><p>The methodology is rigorous and well-defined. But doing it by hand, for every vulnerability, at scale, is tedious and slow. A skilled tester has to read through dense specifications, pull out the relevant signals, map them to metric values, and compute the score. And many of the times, there are different interpretations of the specifications, hence I often see people debating (really) over the metric value (e.g whether Confidentiality is “Low” or “High”). This makes the process both time-consuming and inconsistent.</p><p>So I built the CVSS AutoScorer. My tool takes a raw vulnerability description, sends it to an LLM asking it to identify each of the eight metrics and justify each choice, then parses the output and computes the final CVSS score. The justification part was important to get right. One of the real concerns is that LLMs may hallucinate or give inaccurate results. By having the model show its reasoning for every metric, anyone can quickly spot whether the metric value is correctly allocated. Automation doesn’t mean removing people from the loop. It means putting them in the right place in the loop.</p><p>Here’s the part I still find a little surprising: I built the whole thing in two days, using Claude Code. Not as a shortcut, but as something that genuinely sped up the thinking and building. What two days taught me is that the hard part of building useful tools isn’t really technical anymore. It’s knowing what to build and why. Once that’s clear, the rest moves faster than you’d expect.</p><p>Looking at both projects that I’ve built (or Claude Code built), the key insight here is that AI isn’t replacing the tester. It replaces the part of the job that’s rote and slow, so that the tester can focus on the part that actually requires expertise (like interpreting results and making judgement calls about risk). The role of AI isn’t to automate away the human, but to move the human upstream and away from the tedious and repetitive tasks.</p><h3>What it means when AI does the attacking</h3><p>I had the opportunity to experiment with agentic web penetration testing tools capable of autonomously discovering and exploiting vulnerabilities in web applications. Using it genuinely felt like catching a glimpse of what the future of penetration testing may look like.</p><p>What struck me most was the speed. Traditional pen-testing is methodical. A human probes an application, follows a lead, documents a finding, then moves on. The agentic tool ran multiple attack chains in parallel, adapted based on what it found, and covered in minutes what would take a person hours. It did not get tired or lose focus (unlike me lol). It just kept going.</p><p>That was genuinely impressive to watch. It was also a little unsettling. Because such a powerful tool would be dangerous in the wrong hands. Attackers are not sitting around doing manual hacking anymore. They have access to tools that scale, and those tools are only getting better. The uncomfortable truth is that if offensive capabilities are operating at machine speed, defensive capabilities have to as well. That realisation stuck with me more than any specific finding from the testing itself.</p><h3><strong>The New Pace of Cybersecurity</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OBjR7qXfxceTpM5bF5Ny0w.jpeg" /></figure><p>Near the end of my internship, I attended the STACKx Cybersecurity Conference. The phrase that kept coming up, across talk after talk, was “force multiplier.” AI as a force multiplier for attackers. AI as a force multiplier for defenders. The implication was always the same. The game has not changed, but the speed of the game has. And if you are not thinking about how to scale your capabilities, you are already falling behind.</p><p>Sitting in those sessions, the tools I had spent four months building suddenly felt like part of something larger. The CVSS AutoScorer was not just a way to save analyst time. It was a small attempt at a bigger question, which is how do we maintain quality at the pace the modern threat landscape actually demands. The DDoS reconnaissance tool was not just a convenience. It was an attempt to make structured, deliberate security testing move as fast as the ad-hoc probing an attacker would do without a second thought.</p><p>None of it is finished. These are early tools, first attempts, proofs of concept. But the direction feels right. Using AI not to replace human judgment, but to free the right people up to focus on the right problems, without getting buried in the manual work that surrounds them.</p><h3><strong>What I’m taking away</strong></h3><p>Looking back on this internship, I think the biggest thing I gained was a clearer understanding of what working in cybersecurity actually feels like beyond the classroom. Technical knowledge matters, but so does the ability to adapt quickly, communicate clearly, and stay curious when facing problems you have never seen before.</p><p>It also changed the way I think about technology itself. Over the course of the internship, I started to see how deeply it is beginning to reshape cybersecurity, both for defenders and attackers. Whether it was experimenting with agentic penetration testing tools, or observing how quickly workflows can now be automated, I realised that the field is changing far faster than I expected. More importantly, I learnt that understanding technology alone is not enough. Knowing when and why to apply it matters just as much.</p><p>But honestly, what I am taking away most from these four months is not a tool I built or a concept I finally understood. It is the people I met along the way. The conversations over lunch, the advice shared during casual discussions, the patience that never made me feel like my questions were out of place. I am genuinely grateful to everyone who guided and supported me throughout this journey. The memories, friendships, and connections I built over these months are what I will carry forward the most.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*51mydx2QAjIU4PPWcuvG8Q.jpeg" /></figure><blockquote>To everyone at GovTech who made this experience what it was:</blockquote><blockquote>Thank you! I came in not entirely sure what to expect, and I am leaving with a much clearer sense of the kind of work I want to do and the kind of person I want to be in this field.</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b3fb58b30334" width="1" height="1" alt=""><hr><p><a href="https://medium.com/csg-govtech/what-four-months-at-govtech-taught-me-about-cybersecurity-b3fb58b30334">What Four Months at GovTech Taught Me About Cybersecurity</a> was originally published in <a href="https://medium.com/csg-govtech">CSG @ GovTech Singapore</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deobfuscating, Decrypting and Detecting Xenomorph Malware]]></title>
            <link>https://medium.com/csg-govtech/deobfuscating-decrypting-and-detecting-xenomorph-malware-5b1e63c59416?source=rss----ebb6b6f1ad4e---4</link>
            <guid isPermaLink="false">https://medium.com/p/5b1e63c59416</guid>
            <category><![CDATA[csg]]></category>
            <category><![CDATA[malware-analysis]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <category><![CDATA[sharing]]></category>
            <category><![CDATA[reverse-engineering]]></category>
            <dc:creator><![CDATA[cano32]]></dc:creator>
            <pubDate>Wed, 28 May 2025 04:22:44 GMT</pubDate>
            <atom:updated>2025-05-28T04:22:44.308Z</atom:updated>
            <content:encoded><![CDATA[<p>I came across a highly obfuscated Xenomorph APK malware that was like a Matryoshka doll, with layers upon layers of hidden complexity. Known for its ability to target banking and cryptocurrency apps, the Xenomorph malware aims to steal credentials by employing some interesting techniques. This includes the creative and unconventional use of a chat platform (Telegram) to decode more information, the simulation of user interactions to remotely “touch” the application, and the bypassing of multi-factor authentication to perform fraudulent transactions.</p><p>The unique techniques used by the Xenomorph malware were unlike anything I had seen before and it immediately caught my attention, so I decided to dig deeper to analyze how it works.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*BwzGKQU4gzyuh3vRgV4pWw.jpeg" /><figcaption><a href="https://www.youtube.com/watch?v=DBhs676nka4"><em>Source</em></a></figcaption></figure><p>In this article, I will uncover the above techniques through static analysis on the Xenomorph sample to deobfuscate it, decrypt the 2nd stage payload and finally malware analysis. Lastly, I will share how I compared a few samples to identify the patterns and eventually crafted a script to fully automate the whole process.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7GvbUURbZe6WEJuJs21RuQ.png" /><figcaption>Overview</figcaption></figure><h3>Introducing Xenomorph</h3><p>Discovered back in 2022, Xenomorph is an advanced malware that mimics other applications, such as banking and cryptocurrency applications, and steals Personally Identifiable Information (PII).</p><p>These articles should get you up to speed on the background of Xenomorph.</p><ul><li><a href="https://www.threatfabric.com/blogs/xenomorph">Xenomorph Malware Strikes Again: Over 30+ US Banks Now Targeted (threatfabric.com)</a></li><li><a href="https://www.threatfabric.com/blogs/xenomorph-v3-a-new-variant-with-ats-targeting-more-than-400-institutions">Xenomorph v3: a new variant with ATS targeting more than 400 institutions</a></li></ul><p>The first sample we will be touching on can be found on <a href="https://www.virustotal.com/gui/file/259e88f593a3df5cf14924eec084d904877953c4a78ed4a2bc9660a2eaabb20b/community">Virustotal</a>, <a href="https://tria.ge/230926-g58vnafb4y">Triage</a>. This sample is from the latest Xenomorph campaign in 2023.<br>SHA256 hash: 259e88f593a3df5cf14924eec084d904877953c4a78ed4a2bc9660a2eaabb20b</p><h3>Static Analysis</h3><p>I worked on the samples on a Kali virtual machine (VM), and mainly used tools like jadx Dex to Java decompiler. Other tools I used include Python and Java for scripting, and GHex hex editor.</p><p>We will obtain the deobfuscated strings and decrypted files before proceeding with the malware analysis.</p><h4>Decompiling APK file</h4><p>The package names 😭</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/666/1*ft439TJnzoH1j1rnqdgHgQ.png" /><figcaption>Decompiled content in jadx</figcaption></figure><p>Using jadx Dex to Java decompiler, we first search for the class containing attachBaseContext, which sets up the app’s environment at the beginning before it starts running. This is typically found in the Application class or Activity class. The whole class makes multiple references to another class, cetdg, for its strings.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7HjPorfKpr_zuMVznYx2Ow.png" /><figcaption><em>Use of class cetdg’s functions in class flo.nvslppd.jewhqtssl.lemhslt</em></figcaption></figure><h4>Deobfuscating Strings</h4><p>The strings were all heavily encoded by the function sqqpnjwojnscqq.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*34sJ1ijoL4m8iLK3mYXC3w.png" /><figcaption>Use of function sqqpnjwojnscqq in class ecd.retbpse.slefdiurp.ffgh.cetdg</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/987/1*by4qserc4MnjZHhZxWubNg.png" /><figcaption>Deobfuscation — function sqqpnjwojnscqq in class hhb.lsivkws.qtrcisvkq.etghw.djllmwdqjps</figcaption></figure><p>I reviewed the code in the above function to locate where the deobfuscation was performed. From there, I re-created these functions in Python and obtained the deobfuscated strings.</p><p>[1] Code decode_cetdg_strings.py — Python script to decode strings in sample SHA256 259e88f593a3df5cf14924eec084d904877953c4a78ed4a2bc9660a2eaabb20b</p><ul><li>URL: <a href="https://github.com/GovTech-CSG/xenomorph-config-extractor/blob/main/article_scripts/decode_cetdg_strings.py">GovTech-CSG/xenomorph-config-extractor/article_scripts/decode_cetdg_strings.py</a></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IrYBnEmNSfMvlYYhLZ3Jqw.png" /><figcaption>Code decode_cetdg_strings.py</figcaption></figure><ol><li>In class fefuwlrrh:</li></ol><p>We derive that there is a Dex file, which ends with ‘.gvo’ and is in the directory ‘sqvgk’, thus the path ‘sqvgk/&lt;dexName that ends with ‘.gvo’&gt;’. This brings us to another function qepfhiwqjjpmg that has this path as an argument.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*skEwBnVuKhKmEsIJLETgHg.png" /><figcaption>Use of function qepfhiwqjjpmg in class qeh.focrjqv.vcfumjvbt.qnpew.fefuwlrrh to form file path</figcaption></figure><p>2. In class mbiehswbbbng:</p><p>We derive that there is another assets file, ‘rnscus.huv’. Again, function qepfhiwqjjpmg is called.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/867/1*pnjW1YS82f9Yf8MnwxV1-A.png" /><figcaption>Use of function qepfhiwqjjpmg in class eur.ptlnwtw.jsbflnerb.vnmgg.mbiehswbbbng to form file path</figcaption></figure><p>3. In class tlbcnpufb:</p><p>The function qepfhiwqjjpmg accepts either file stream of ‘sqvgk/&lt;dexName that ends with ‘.gvo’&gt;’ or ‘rnscus.huv’, and a file stream of a new file (presumably the encrypted file stream) as an argument. The functions that follow (jgqf, kkvicc, sswg) are used for the unpacking and writing of the input file. For example, in function kkvicc, the key is defined and comes from class cetdg again.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/803/1*b73CXmUQPlmd2rSM1wCNbg.png" /></figure><p>…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/958/1*GTrJY7D_gWIYCv_4a7fgZA.png" /><figcaption><em>Forming file path and payload decryption — class kuf.hggkvuo.rsrrmrlju.hfsle.tlbcnpufb</em></figcaption></figure><p>Scouring through the APK’s resources/assets directory, we find the 2 files ‘rnscus.huv’ and ‘sqvgk/lkfupve.gvo’. Surprisingly, ‘rnscus.huv’ is just 22 bytes long, compared to ‘lkfupve.gvo’ at 1.3MB. It is thus unlikely that ‘rnscus.huv’ is our file of interest, leading us to ‘lkfupve.gvo’ being the encrypted Dex file.</p><h4>Decrypting Payload</h4><p>Then, I extracted out all the functions required for decryption of the encrypted Dex file ‘lkfupve.gvo’ (i.e., functions qepfhiwqjjpmg, jgqf, kkvicc, sswg) and developed an additional script to decrypt the encrypted Dex file.</p><p>[2] Code — DecryptXenoDexPayload.java: Extended script to decrypt payload in sample SHA256 259e88f593a3df5cf14924eec084d904877953c4a78ed4a2bc9660a2eaabb20b</p><ul><li>URL: <a href="https://github.com/GovTech-CSG/xenomorph-config-extractor/blob/main/article_scripts/DecryptXenoDexPayload.java">GovTech-CSG/xenomorph-config-extractor/article_scripts/DecryptXenoDexPayload.java</a></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UKqQP8YDU9MM8DBqP-Q9mw.png" /><figcaption>DecryptXenoDexPayload.java</figcaption></figure><p>The file is decrypted into a Dex file, as seen from the Dex file signature of bytes 64 65 78 0A 30 33 35 00 (SHA256: b1315eb69aa70d923637cf5a797224611c26580bd40fdd31019798d43a694194).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/749/1*WyNWlnec6oPz-4bW9EjWhg.png" /><figcaption><em>SHA256 of decrypted file</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/793/1*wQetOVM61sVMuq2z2OQyOA.png" /><figcaption>Original file</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/795/1*3lR2VtkG2MXCmgRO0VZuMg.png" /><figcaption>Decrypted file</figcaption></figure><h3>Malware Analysis</h3><p>Upon reviewing the earlier steps, the original APK appears to drop a Dex file, which performs the main functionalities of the Xenomorph malware family.</p><p>There are also various JSON files in the original APK’s resources. Among them are the encrypted overlay similar to the ones in <a href="https://www.threatfabric.com/blogs/xenomorph">Xenomorph Malware Strikes Again: Over 30+ US Banks Now Targeted (threatfabric.com)</a>, found in the original APK’s resources (res/raw/rum_modules.json, res/raw/a). These overlays are used to pose as login pages for target applications.</p><h4>C2 servers… and Telegram</h4><p>Malware configurations are the embedded settings and parameters that determine how a malware sample behaves, including the Command and Control (C2) servers and encryption keys. In our case, part of the malware configuration comes in the form of a configuration file, which can be found here in the original APK’s resources (res/raw/c.json), containing the following C2 servers:</p><ul><li>fobocontentplus[.]online</li><li>fobocontentplus[.]top</li><li>fobocontentplus[.]site</li><li>fobocontentplus[.]hk</li></ul><p>It also contained another URL:</p><ul><li>hxxps://t[.]me/n3w3rras</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZxiTVnowSTJ90SkyN9sKUQ.png" /><figcaption><em>Malware configuration — Original APK resource file res/raw/c.json</em></figcaption></figure><p>I was intrigued by the use of Telegram as a C2 server, but I was not expecting it to be used to deliver an additional C2 server! With reference to <a href="https://www.zscaler.com/blogs/security-research/rise-banking-trojan-dropper-google-play">this article</a>, the Telegram URL from the original APK is used by taking the string in between hearts emoji in the description to form the C2. In the JSON file above, “begin” and “end” are the unicode representation of the 3 hearts emoji at the start and end of the description.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/780/1*Mran3G07T9KKdIYGvIlyTw.png" /><figcaption><em>Obfuscated string — Description of the Telegram channel</em></figcaption></figure><p>We can see “externalMnemonicUrl” being mentioned above as “type”, along with the Telegram URL and the “begin” and “end”. As the word “mnemonic” could refer to a pattern of letters or associations to aid in remembering something, I figured the line of random words between the hearts emoji could be deobfuscated to something else.</p><p>In the decrypted Dex file, I identified the class com.mtnyrvojt.qtbxtwjnq.api.url.ExternalMnemonicResourceApiUrlSource which decrypts the line of random words. Then, I made used of the short decoding routine observed at the start of the class (explained in the next section, Obfuscation) to decode the strings in the class, and finally reconstructed the functions to decrypt the line of random words.</p><p>[3] Code — DecryptTelegramC2.java: to decrypt the Telegram strings in sample SHA256 259e88f593a3df5cf14924eec084d904877953c4a78ed4a2bc9660a2eaabb20b</p><ul><li>URL: <a href="https://github.com/GovTech-CSG/xenomorph-config-extractor/blob/main/article_scripts/DecryptTelegramC2.java">GovTech-CSG/xenomorph-config-extractor/article_scripts/DecryptTelegramC2.java</a></li><li>The payload will locate the string between the heart emojis from the Telegram channel description for decryption.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_uy1mHc3KwggSfj_TmS2DA.png" /><figcaption>DecryptTelegramC2.java — Function to obtain Telegram channel description</figcaption></figure><ul><li>The important function is the function decrypt as it takes the Telegram description and decrypts it, forming a URL.</li><li>The function caeDecrypt from the class UtilEncryption was used in the function decrypt.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GSLD_Ln6utx0CAba07ZfmQ.png" /><figcaption>DecryptTelegramC2.java — Function for decryption</figcaption></figure><p>Running the script, we get a new C2 server Bkp[.]HaD0[.]live, where information like commands is obtained and sent to. The endpoints for this C2 server are ping and metrics, which are also mentioned in the configuration file c.json.</p><p>Endpoints:</p><p>ping</p><ul><li>Used for initial verification step.</li><li>A request is sent to the C2 to register and verify itself.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/605/1*hQ3TYG-NHBaQBDJe0pI9mw.png" /><figcaption><em>Request fields in decrypted Dex’s class com.mtnyrvojt.qtbxtwjnq.api.tdp.ApiVerifyRequestPayload</em></figcaption></figure><ul><li>The message is encrypted using an AES key as seen in the original APK’s resources above (res/raw/c.json) (ref: <a href="https://pl.linkedin.com/pulse/apkdetect-opencti-%C5%82ukasz-cepok">Apkdetect &amp; OpenCTI (linkedin.com)</a>, <a href="https://security-garage.com/index.php/es/investigaciones/analisis-del-protocolo-de-comunicacion-del-troyano-para-android-xenomorph">Xenomorph Android Trojan Communication Protocol Analysis (security-garage.com)</a>).</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-2bbXrc0Ui2-WxRv-HqaDA.png" /><figcaption><em>Key from original APK resources res/raw/c.json</em></figcaption></figure><ul><li>A request is sent to the C2 server again and the following is obtained.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/529/1*P3sddLuzFA3-TQB02XbwJw.png" /><figcaption><em>Response fields in decrypted Dex’s class com.mtnyrvojt.qtbxtwjnq.api.bodies.ApiVerifyResponseBody</em></figcaption></figure><p>metrics</p><ul><li>Used to retrieve commands from the C2 and send information.</li></ul><h4>Obfuscation</h4><p>There is a ton of obfuscation (again) in the decrypted Dex file and we are unable to determine the intent of each function simply by eyeing it. Let’s analyze it further to locate how it is deobfuscated.</p><p>We can observe that at the start of most classes, a decoding routine is called. This takes in a different array $ for every class and decodes it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/521/1*A4GEUlp4zQS28ub6nn_blg.png" /><figcaption><em>Decoding routine</em></figcaption></figure><p>We also observed the variable settingsFilename, which is the name of the SharedPreferences file (seen obfuscated in com.mtnyrvojt.qtbxtwjnq.utilities.UtilGlobal). Using the decoding routine, it is decoded to ‘thom_yorke-a_rats_nest’. SharedPreferences can be used to store key-value pairs such as user preferences and app settings that can be accessed across app sessions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/596/1*TO8B0AHYjYwiu8Ph4MyKJQ.png" /><figcaption><em>settingsFilename in class com.mtnyrvojt.qtbxtwjnq.utilities.UtilGlobal</em></figcaption></figure><p>Other notable strings decoded to a list of app button IDs, Android permissions, and a curious “appInfoWindowHashList” which could be a list of hashes associated with different app windows. I listed the deobfuscated strings in the appendix [a].</p><p>Obfuscated commands used in Xenomorph malware can be found in class com.mtnyrvojt.qtbxtwjnq.api.tdp.ApiGetCommandsResponsePayload, and also some in class com.mtnyrvojt.qtbxtwjnq.utilities.UtilGlobal (i.e., sms_ic_disable, sms_ic_enable).</p><p>I listed the deobfuscated commands from this sample in the appendix [b]. ThreatFabric provides a full list of the commands across samples and their description: <a href="https://www.threatfabric.com/blogs/xenomorph-v3-a-new-variant-with-ats-targeting-more-than-400-institutions">Xenomorph v3: a new variant with ATS targeting more than 400 institutions</a>. Nevertheless, I went on a more in depth analysis with 2 commands that I found most interesting.</p><h4>Command 1 — “clickOnPoint”</h4><p>Out of the various commands, I find it interesting that the command “clickOnPoint” is able to allow the threat actor to remotely “touch” the device. In class com.mtnyrvojt.qtbxtwjnq.utilities.UtilAccessibility, x and y coordinates are supplied to the click function as integers.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/902/1*zQvZ6h4MfCJns5h73rKGcw.png" /><figcaption><em>Use of function clickAtPosition in class com.mtnyrvojt.qtbxtwjnq.utilities.UtilAccessibility</em></figcaption></figure><p>Back to class com.mtnyrvojt.qtbxtwjnq.api.tdp.ApiGetCommandsResponsePayload:</p><ul><li>Here, $(572, 584, -2390) decodes to “clickOnPoint”, $16 to x coordinate and $17 to y coordinate.</li><li>clickOnPoint allows the threat actor to simulate a touch on specific coordinates.</li><li>The x and y coordinates are then passed to UtilAccessibility.click, as seen above in UtilAccessibility. clickAtPosition will then be called.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/871/1*Rc1rweMkBhUQRPrswa70Ug.png" /><figcaption><em>Mapping x and y coordinates in class com.mtnyrvojt.qtbxtwjnq.api.tdp.ApiGetCommandsResponsePayload</em></figcaption></figure><ul><li>This can be compared with the code in <a href="https://www.threatfabric.com/blogs/xenomorph">Xenomorph Malware Strikes Again: Over 30+ US Banks Now Targeted (threatfabric.com)</a>.</li></ul><p>To demonstrate the above:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UzK9dYBwASugUVBeeql64A.png" /><figcaption>Pseudocode — use of clickOnPoint</figcaption></figure><h4>Command 2 — “execute_rum”</h4><p>There is also the Automated Transfer Systems (ATS), which is run from the command “execute_rum”. It allows threat actors to complete fraudulent transactions on the device without needing human interaction.</p><p>When “execute_rum” is called, a module is added. This module consists of the following fields, which are taken and converted to operations to be executed on the device.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/865/1*cKR1T1TfF_NOALEUq0X4Sw.png" /><figcaption><em>Module in class com.mtnyrvojt.qtbxtwjnq.accessibility.runtime.models.RuntimeAccessibilityModule</em></figcaption></figure><p>These fields found in classes in com.mtnyrvojt.qtbxtwjnq.accessibility.runtime.models also form part of the modules to further customize the operations (i.e., commands), requires (i.e., required conditions), triggerConditions (i.e., when this module is run). This is derived from RuntimeAccessibilityModule in the screenshot above, where RuntimeAccessibilityStep object is used for those fields.</p><ul><li>RuntimeAccessibilityStep<br>– name<br>– type</li><li>RuntimeAccessibilityConditionalOperation<br>– conditions<br>– operations</li></ul><p>[4] Code — DecryptRumStrings.java</p><ul><li>URL: <a href="https://github.com/GovTech-CSG/xenomorph-config-extractor/blob/main/article_scripts/DecryptRumStrings.java">GovTech-CSG/xenomorph-config-extractor/article_scripts/DecryptRumStrings.java</a></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FRo8IRy4Dxt_2_qmWUihHA.png" /><figcaption>DecryptRumStrings.java</figcaption></figure><p>I was also able to deobfuscate and obtain the operations from com.mtnyrvojt.qtbxtwjnq.accessibility.runtime.base.RuntimeAccessibilityDefinedOperations. I listed the deobfuscated operations from this sample in the appendix [c].</p><p>With these operations, a script can be crafted to complete transactions without needing human interaction, for example to evade multi-factor authentication requests by reading authentication codes from authenticator apps. This is demonstrated in the article by ThreatFabric, which also provides a nice list of the operations and their description: <a href="https://www.threatfabric.com/blogs/xenomorph-v3-a-new-variant-with-ats-targeting-more-than-400-institutions">Xenomorph v3: a new variant with ATS targeting more than 400 institutions</a></p><h3>Comparison with other samples</h3><p>I then made use of the other samples that were available at the time of analysis for this comparison.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/288b812bd312a6325765ebcd956b968f/href">https://medium.com/media/288b812bd312a6325765ebcd956b968f/href</a></iframe><p>The first sample we analysed above (SHA256: 259e88f593a3df5cf14924eec084d904877953c4a78ed4a2bc9660a2eaabb20b) was encrypted in a different manner from the other samples, requiring scripts just for its deobfuscation and decryption, as shown in the previous section.<br>For all of the other samples, the encrypted file can be found in the APK resources assets/*.json, and they can all be decrypted using the <a href="https://github.com/cryptax/misc-code/blob/1dbc90c901a205d494a9b71093ecf5a4f18d716b/jsonpacker/jsondecrypt.py">code in Cryptax’s GitHub for JsonPacker-packed files</a> but we would still need the key for decryption.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cvAc0pWQvbm51J5ahGIZuA.png" /><figcaption><em>Encrypted file in 2877b27f1b6c7db466351618dda4f05d6a15e9a26028f3fc064fa144ec3a1850</em></figcaption></figure><p>Follow the detailed steps in this excellent article by Cryptax: <a href="https://cryptax.medium.com/unpacking-a-jsonpacker-packed-sample-4038e12119f5">Unpacking a JsonPacker-packed sample | by @cryptax | Medium</a>.</p><ul><li>Identify the class with attachBaseContext which sets up the app’s environment at the beginning before it starts running to obtain the encrypted payload filename.</li><li>Identify the class with DexClassLoader, which dynamically loads the decrypted payload Dex file, and its references.</li><li>Identify where payload decryption occurs.</li><li>Identify where the key is read.</li><li>Decrypt the payload.</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f8b874c0bf7ee40d76a8228f085931db/href">https://medium.com/media/f8b874c0bf7ee40d76a8228f085931db/href</a></iframe><p>Once we obtain the decrypted payload, the rest is much simpler as the strings are not obfuscated. All of the decrypted payloads are also similar to each other and to that in our first sample (other than obfuscated strings and new features).</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bc695148c75d008b9cf0fb64825a6aed/href">https://medium.com/media/bc695148c75d008b9cf0fb64825a6aed/href</a></iframe><p>The first sample and its decrypted file has obfuscation techniques for many of its strings. The same function for the deobfuscation can be found in every Java class file that contains such obfuscated strings. Its configuration file is different from the rest, and it is in plaintext in its original file as well.</p><p>Other than the first sample, we observe a pattern from the analysis of the samples — by following references to the Java functions containing attachBaseContext and DexClassLoader, we are able to identify the encrypted payload filename and the payload decryption routine. After identifying the decryption key, we can easily decrypt the payload with the Python script by Cryptax.</p><p>Additionally, after unpacking the file, the decrypted payloads (also except the first sample) seem to have the same Java class where the commands are found, and where endpoints, settingsFilename, permissions, hex string and C2 servers strings are found. Most of these strings are similar.</p><p>Catered towards the majority of the samples I have encountered, I decided to create a script to automate the following:</p><ul><li>Decompile APK sample</li><li>Identification of the encrypted payload and keys</li><li>Decryption of the JsonPacker-packed payload using Cryptax’s script</li><li>Deobfuscation of strings</li><li>Detection of Xenomorph malware family</li><li>Extraction of Xenomorph malware configuration (e.g., C2 servers, encryption keys)</li></ul><p>Here is an overview of how the script works:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vEN5a2UQF1kyUumYaFRXeA.png" /><figcaption>Xenomorph Config Extractor Overview</figcaption></figure><p>The script goes through a series of conditions to run various codes to determine whether the sample belongs to the Xenomorph malware family, and eventually extract the malware configuration.</p><p>From the comparison table above, we obtained strings that were seen across the various APKs. I made use of these strings, regardless of whether it is obfuscated, to determine whether the APK belongs to the Xenomorph family.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Gp6yK7-_PZVRfx-7cJzEZA.png" /><figcaption>Pseudocode — xenoAnalyze.py match section</figcaption></figure><p>The script works for both JsonPacker-packed samples, and the already decrypted files. You may find the script in the GitHub repository here: <a href="https://github.com/GovTech-CSG/xenomorph-config-extractor">GovTech-CSG/xenomorph-config-extractor | GitHub</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/775/1*uzWNHofe0dmQLLHJLPS4bQ.png" /></figure><h3>Conclusion</h3><p>In this walkthrough, we leveraged on the traits of android applications to quickly zoom into the entry point of the malware. As with all highly obfuscated malware, it is necessary to deobfuscate and decrypt any encrypted data before discovering its malicious properties.</p><p>We were able to demystify the sample and obtain the decrypted Dex payload responsible for the main functionality of the Xenomorph malware. We eventually obtained the C2 server and commands through malware analysis, and also a whole bunch substantial number of deobfuscated strings duringin the process.</p><p>To effectively triage Xenomorph malware samples, some level of automation is required. We compared a few other samples and the similar patterns observed were used to craft construct a script to automate the deobfuscation and decryption process, and to extract the Xenomorph malware configuration. This automation not only streamlines the analysis workflow but also enables faster detection and response to new variants of the Xenomorph malware family.</p><p>Understanding the inner workings of the Xenomorph malware through this analysis has revealed sophisticated obfuscation techniques. While this article focused on Xenomorph malware, I hope that the methodology and techniques demonstrated here can also prove be useful for analyzing other Android malware families that employ similar obfuscation tactics :-)</p><p>Here’s the full GitHub repository link for all the code mentioned in this article:<br><a href="https://github.com/GovTech-CSG/xenomorph-config-extractor">GovTech-CSG/xenomorph-config-extractor | GitHub</a></p><h3>Appendix</h3><pre>[a] Notable strings from sample SHA256 259e88f593a3df5cf14924eec084d904877953c4a78ed4a2bc9660a2eaabb20b:<br>allowButtonsIdList<br>– com.android.packageinstaller:id/permission_allow_button<br>– com.android.settings:id/restricted_action<br>– com.miui.securitycenter:id/intercept_warn_allow<br>– com.miui.securitycenter:id/accept<br>– com.android.permissioncontroller:id/permission_allow_button<br>– com.android.settings:id/action_button<br>– com.android.settings:id/button1<br>– android:id/button1<br>appInfoWindowHashList<br>– 4e7d36521f246327efffde4e5f3d1705bacff85a7d3cf1836d31714196434d79<br>– f82847a89d3e776505ab6af6cf2d0298455b52f9e9741cd0d9d3714451a96aff<br>– d83fa5b262824b544ed5565164a1791e29d015bdd325461ed9344a9a5b60c9b5<br>– 34a6a777402003a51fa70f4184ec8340c4dda695849309bbf5647648b2c3c62d<br>permissions<br>– android.permission.READ_SMS<br>– android.permission.RECEIVE_SMS<br>– android.permission.WAKE_LOCK<br>– android.permission.RECEIVE_BOOT_COMPLETED<br>– android.permission.ACCESS_NETWORK_STATE<br>– android.permission.INTERNET<br>– android.permission.WRITE_EXTERNAL_STORAGE<br>– android.permission.READ_PHONE_STATE<br>– android.permission.USE_FULL_SCREEN_INTENT<br>– android.permission.FOREGROUND_SERVICE<br>– android.permission.READ_PHONE_NUMBERS<br><br>[b] Commands from sample SHA256 259e88f593a3df5cf14924eec084d904877953c4a78ed4a2bc9660a2eaabb20b, with the ones from the latest Xenomorph campaign in red:<br>app_list<br>– inj_enable<br>– inj_disable<br>– inj_list<br>– inj_update<br>– fg_enable<br>– fg_disable<br>– notif_ic_enable<br>– notif_ic_disable<br>– notif_ic_list<br>– notif_ic_update<br>– sms_log<br>– sms_ic_enable<br>– sms_ic_disable<br>– socks_start<br>– socks_stop<br>– sms_ic_list<br>– sms_ic_update<br>– app_kill<br>– app_delete <br>– app_clear_cache<br>– self_kill <br>– self_cleanup<br>– app_start<br>– show_push<br>– cookies_handler<br>– send_sms<br>– make_ussd<br>– call_forward<br>– start_mimic<br>– stop_mimic<br>– execute_rum<br>– clickOnPoint<br>– sms_ic_disable<br>– sms_ic_enable<br><br>[c] Operations from sample SHA256 259e88f593a3df5cf14924eec084d904877953c4a78ed4a2bc9660a2eaabb20b:<br>– clickCancelButton<br>– setActionPassedThrough<br>– findNodesByParameters<br>– findFirstNodeByParameters<br>– findNodesByClass<br>– findFirstNodeByClass<br>– findNodesByViewId<br>– findFirstNodeByViewId<br>– findFirstNodeByText<br>– getNodeByIndex<br>– getFirstChildOfClass<br>– sleepForMilliseconds<br>– API_SEND_SIMPLE_STATE_WITH_OBJECT<br>– CONTROL_MODULE_FINISH_SUCCESSFULLY<br>– CONTROL_MODULE_ABORT<br>– CONTROL_GO_HOME<br>– CONTROL_GO_BACK<br>– CONTROL_GO_BACK5<br>– CONTROL_MODULE_RETURN<br>– CONTROL_PUT_ACTION_RESULT<br>– CONTROL_RUN_MODULE_STRAIGHT<br>– CONTROL_GLOBAL_VALUE_SET_FROM_TEXT_DATA_ENTRY<br>– CONTROL_GLOBAL_VALUE_SET<br>– CONTROL_SET_UNPROCESSED<br>– CONTROL_CLEAR_UNPROCESSED<br>– CONTROL_FLUSH_ALL_CONTEXT_DATA_ENTRIES<br>– DATA_JOIN_TUPLE_LIST<br>– DEBUG_LOG_CONTEXT<br>– DEBUG_LOG_WHOLE_CONTEXT<br>– DEBUG_LOG_MODULE_REGISTERED_ACTIONS<br>– DEBUG_LOG_CURRENT_DATA<br>– DEBUG_PRINT<br>– DEBUG_LOG_CONTEXT<br>– DEBUG_LOG_CURRENT_DATA<br>– STATE_EVENT_CLEAR</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5b1e63c59416" width="1" height="1" alt=""><hr><p><a href="https://medium.com/csg-govtech/deobfuscating-decrypting-and-detecting-xenomorph-malware-5b1e63c59416">Deobfuscating, Decrypting and Detecting Xenomorph Malware</a> was originally published in <a href="https://medium.com/csg-govtech">CSG @ GovTech Singapore</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Reverse Engineering a Smartwatch]]></title>
            <link>https://medium.com/csg-govtech/reverse-engineering-a-smartwatch-a7cec52b29c8?source=rss----ebb6b6f1ad4e---4</link>
            <guid isPermaLink="false">https://medium.com/p/a7cec52b29c8</guid>
            <category><![CDATA[csg]]></category>
            <category><![CDATA[iot]]></category>
            <category><![CDATA[reverse-engineering]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <category><![CDATA[hardware]]></category>
            <dc:creator><![CDATA[Benjamen Lim]]></dc:creator>
            <pubDate>Thu, 27 Jun 2024 03:44:33 GMT</pubDate>
            <atom:updated>2025-09-11T07:03:04.064Z</atom:updated>
            <content:encoded><![CDATA[<p>Some time ago, I was assigned a consignment of smart watches with geolocating capabilities that were being mothballed after a trial. I was determined to find some use for them and thus began my journey of<br>reverse engineering a smartwatch!</p><p>In this article, I will share the reverse engineering process by first highlighting some initial observations on the watch surface and circuitry, before going into detail as to how I reprogrammed the smartwatch,<br>and the final step of patching the firmware so it could be repurposed.</p><h3>Initial Observations</h3><p>The watches as delivered were bare-bones and had a single page of instructions on how to charge and use them. Each box contained a single charger and a watch. There were no READMEs, websites, or developer portals. The watches themselves only had a single capacitive sensor on the surface that turned on the screen and allowed the user to view their heart rate, some debug information, and the time on different watch faces.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ccPfu97XFU7OIe-lZWyYgw.png" /><figcaption>Watch faces</figcaption></figure><p>The debug information provided useful information for identifying the watch. I observed that the IP address on the debug watch face was common across different watches of the same model. My initial guess was that it referred to the IP address of the server that these watches were communicating with.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5CT8G5rxPHW8TyITBvpYMw.png" /><figcaption>Charging the watch</figcaption></figure><p>The watch had an IP67 rating, i.e. they waterproof, so there was no way I could access these watches (with my limited tools) without damaging them. In the spirit of exploration, I took a pair of pilers to a watch, tearing it down to determine what lay within. It wasn’t easy, but after an hour of careful prying and cutting, I managed to remove the outer casing of the watch.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*3FTjuWdGm2d6EdP4" /><figcaption>Cracking the device open</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/844/0*6nn6PzZt8vA_rANu" /><figcaption>Viewing the innards</figcaption></figure><p>Inside was a single PCB with a LIPO battery. There were two antennas embedded in the case itself and were connected to u.FL connectors on the board.</p><h3>Reverse Engineering the PCB</h3><p>The SmartWatch was driven by a Bluetooth-enabled nRF52832 chip and had two other major ICs: a WiFi-capable ESP8285 and a cellular IC from SIMCOM. I was initially confused by the presence of the WiFi microcontoller, because there was no indication that the watch had WiFi capabilities. However, I found out later that it was used for urban triangulation. Because the GPS’s accuracy is generally quite bad in an urban environment, an approximation of the location of the watch can be determined by using the WiFi AP and cellular data combined with historical GPS data. From the layout, the nRF52832 was the device’s main IC, and used the WiFi chip to scan for local WiFi Access Points (APs). The nRF52832 also communicated with the SIMCOM device over UART and issued commands to communicate with the mobile network. Knowing that, I focused my efforts on I was looking for any UART or exposed programming pins on the nRF52832, as it was main IC and those connections are commonly used to interact with the microcontroller.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/935/0*muPhaZ3zBU9menZl" /></figure><p>In this case, quite a few exposed UART and programming pins were on the board. One very interesting trace that I found was that the round gold contacts on the right side of the PCB were connected to SWDIO and SWCLK on the nRF52832 IC in addition to the connections that I expected for charging the battery of the watch. SWDIO and SWCLK are JTAG programming pins that are used to program the chip. There were also pogo pins, spring loaded electrical contacts, attached on the underside of the watch’s front cover that would make contact with these pads when the watch was closed.</p><p>These pogo pins were exposed to the copper-plated contacts on the front of the watch’s face, meaning that the programming pins for the main chip were exposed on the device’s face. Having exposed programming pins was unusual because in general, the firmware for the device would be flashed into the PCB at the factory and would be subsequently updated over WiFi or Bluetooth. There is usually no need to expose the programming pins. This feature turned out to be a very helpful feature for later as it meant that I didn’t need to open the watch to gain access to the firmware. Needing to open the watch would have rendered useless my intention to repurpose the watch as evidenced by my attempt at opening the watch earlier: it wasn’t something I was going to put back together anytime soon.</p><p>What was <em>more interesting</em> was that on the charging adapter, the pogo pins that connected to the SWCLK and SWDIO contacts on the watch’s face were connected to D+ and D- pins on the microUSB port. The D+ and D- pins are usually used for data transfer. This meant that I didn’t even need to build a custom programming platform to reprogram the watch: all I had to do was to splice a microUSB wire to expose the programming pins to any watch connected to the charger. Very nice.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Br8cMqOcCAkcppsO" /><figcaption>Contacts</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*H44mIWVM_O1TWpVb" /><figcaption>Splicing the cable</figcaption></figure><p>After I connected the watch to my JLink debugger through the charger, the first thing I noticed was that the watch was producing debug output using the JLink RTT viewer. Success! While being able to observe the debug output was very useful, however, as there was no input configured for the RTT module, so there was no way to send commands to the watch. However, the output confirmed my earlier assumptions about how the watch was connected internally.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*rFrzfAKaJMx3qloM" /><figcaption>UART communications being sent to the SIMCOM module from the nRF52832 IC</figcaption></figure><p>After a few exploratory attempts at sending commands over JLink, I decided to take a look at the firmware. With my JLink attached, I was able to dump the firmware using nrfjprog with the --readcode and --readram flags.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/592/0*C_0WqG3Oky8U9KV8" /><figcaption>Raw firmware dump</figcaption></figure><p>The firmware was not read or write protected so I was able to obtain the full firmware dump. At this point I had two options for repurposing the watch: 1) I could reprogram it completely by flashing a new firmware into the device, or 2) I could patch the IP address and port in the firmware so that the watch would send data back to a server I control. Since reprogramming the watch was likely a huge undertaking, I decided to try patching the watch.</p><h3>Enter the Ghidra</h3><p>I needed to dump both the RAM and the flash to get a proper mapping of the functions and the variables in Ghidra. Since this is bare metal code from a Cortex M0+ device, I had to decompile the firmware as ARM little-endian and Ghidra was able to produce semi-readable pseudocode.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*GXsOBGPds9KhLO5e" /></figure><p>My objective at this point was to investigate the location of the IP address, which I believed was stored somewhere in the code. The watch sends data over a cellular connection to a server, the address of which was displayed on the watch’s debug face. If I can update the IP address, I will be able to redirect the data that the watch was sending to a server I control.</p><p>However, my initial attempts to find a hardcoded string that corresponded to the IP address that I observed on the face of the watch were not successful.</p><p>My next attempt was to find the closest string that occurred in the debug output that I observed in the RTT viewer. In this case, it was AT+CIPOPEN=0 which is the serial command sent from the nRF52832 to the SIMCOM module, instructing it to open a connection to an IP address which is specified as an argument. By finding this string, because it uses the IP address of the server that the watch has to connect to, I would be able to find the location in memory where the server’s IP address is stored.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/805/0*4jNbfm_uU_R0Rjo2" /><figcaption>String found in memory</figcaption></figure><p>I was more successful at finding a format string. This match had a string pattern that matched that of an IP address. It was being called in a function that looked suspiciously like a sprintf function.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/961/0*E8-Ewqji0ntYkI3s" /><figcaption>Reference to string being called in a sprintf function, which calls four other variables</figcaption></figure><p>The sprint function referenced an array that was stored in DAT_20000887, which corresponds to the Data RAM section of the nRF52832 datasheet as seen in the memory mapping below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/946/0*N9EvQIRhDaDGzrEc" /><figcaption>Memory map of nRF52832</figcaption></figure><p>Going to the RAM’s location in memory, I found that it was being written to in two functions. Only the first location was of interest because it was where the hardcoded IP address was. The second location allows the IP address to be updated later over the air.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/777/0*JO9J4GZQkgb0-kJm" /></figure><p>Jumping to the first function, I found these hardcoded variables hex values in memory that was written to the array as part of the main function, and these variables corresponded to the IP address that I observed on the watches.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*rROBllLwSEQ9NQ3H" /><figcaption>Location in memory for patching</figcaption></figure><p>These locations in memory were the six bytes of data in the firmware that I needed to patch for the IP address and port.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/507/0*WPF4sW3or-Cr_sv3" /><figcaption>6 bytes to patch, 4 bytes for IP, 2 bytes for port</figcaption></figure><p>One small wrinkle that emerged was that the original port number was 38899, which was saved as two bytes: 0x0c and 0x68. The compiled program used a movn instruction, which applied a Boolean not operation to the hardcoded values before they were put into RAM. Technically, the instruction could be patched to remove the not operation, but I was trying to minimise the number of bytes to change, therefore, I added an additional operation when converting the desired port number to the appropriate hex values.</p><h3>Reprogramming the Smartwatch</h3><p>With this knowledge, I was able to write a simple script that patched the firmware with any IP address and port that I wanted. The script also updated the checksum of each patched line to ensure that the firmware matched the expected format, and once the patching was complete, I flashed the firmware to the watch using Nordic Semiconductor’s Programmer utility program.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/843/0*W8efS7vLUfhH2py5" /><figcaption>Firmware patching script</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Uivaoji7UgN1_uF9" /><figcaption>Firmware Flashing</figcaption></figure><p>I am pleased to say that it worked! By updating the firmware to communicate with the server, we were able to get data from the phone and process it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/870/0*p_Lwqzgzr6MxPymZ" /><figcaption>Patched firmware</figcaption></figure><p>That concludes the reverse engineering portion of this project. There was a lot of learning for me as I came in with limited knowledge of reverse engineering ARM devices. However, I had a good idea of how I would have developed the device and what it was capable of, so that significantly helped to narrow down what I was looking for.</p><p>An interesting takeaway was how the programming interface was exposed to the USB port which I have not observed in other watches. Not having any read-write protection on the firmware is also uncommon as most other IoT devices tend to enforce firmware protections once they are in production to prevent trivial cloning of the firmware.</p><p>Nevertheless, it was great fun, and being able to revive and reuse electronics that would have either sat in a warehouse for a long time or thrown away gave this project additional meaning.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a7cec52b29c8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/csg-govtech/reverse-engineering-a-smartwatch-a7cec52b29c8">Reverse Engineering a Smartwatch</a> was originally published in <a href="https://medium.com/csg-govtech">CSG @ GovTech Singapore</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Cache me if you can — Local Privilege Escalation in Zscaler Client Connector]]></title>
            <link>https://medium.com/csg-govtech/catch-me-if-you-can-local-privilege-escalation-in-zscaler-client-connector-7ad997bd7058?source=rss----ebb6b6f1ad4e---4</link>
            <guid isPermaLink="false">https://medium.com/p/7ad997bd7058</guid>
            <category><![CDATA[devsecops]]></category>
            <category><![CDATA[zscaler]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <category><![CDATA[zero-trust]]></category>
            <category><![CDATA[sharing]]></category>
            <dc:creator><![CDATA[Winston Ho]]></dc:creator>
            <pubDate>Mon, 27 May 2024 05:55:26 GMT</pubDate>
            <atom:updated>2024-05-29T03:55:42.561Z</atom:updated>
            <content:encoded><![CDATA[<h3>Cache me if you can — Local Privilege Escalation in Zscaler Client Connector</h3><p>GovTech’s Cybersecurity Group (CSG) recently collaborated with CSIT to evaluate products in the Zscaler suite for Zero Trust Network Access. During our research, several vulnerabilities were discovered within the Zscaler Client Connector application (prior to version 4.2.1) that were ultimately assigned CVEs by Zscaler:</p><ul><li>Revert password check incorrect type validation (CVE-2023–41972)</li><li>Lack of input santisation on Zscaler Client Connector enables arbitrary code execution (CVE-2023–41973)</li><li>ZSATrayManager Arbitrary File Deletion (CVE-2023–41969)</li></ul><p>By chaining together several low-level vulnerabilities and bypasses, we (Eugene Lim and Winston Ho) were able to escalate a standard user’s privileges to execute arbitrary commands as the high-privileged <strong>NT AUTHORITY\SYSTEM</strong> service account on Windows.</p><p>In this article, we will share our methodology used, from vulnerability discovery to developing proof-of-concept exploits.</p><h3>Overview of Zscaler Client Connector and the Zscaler Ecosystem</h3><p>Zscaler is a global cloud-based information security company that enables secure digital transformation for mobile and cloud environments. The Zscaler Client Connector is a lightweight agent for user endpoints, enabling hybrid work through secure, fast, reliable access to any app over any network. It also encrypts and forwards user traffic to the Zscaler Zero Trust Exchange — the world’s largest inline security cloud, that acts as an intelligent switchboard to securely connect users directly to applications.</p><p>The ZScaler Client Connector application consists of two main processes: ZSATray and ZSATrayManager. ZSATrayManager is the service that runs as the <strong>NT AUTHORITY\SYSTEM</strong> user and handles high-privileged actions needed such as network management, configuration enforcement, and updates. ZSATray, on the other hand, is the user-facing frontend application, built on the .NET Framework.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/569/1*W8FSrGTwICCKdFnOwNCmCQ.png" /></figure><p>Like most client-server software on Windows, ZSATray and ZSATrayManager communicate using Microsoft Remote Procedure Call (RPC). For example, when a user requests to dump logs from the user interface, ZSATray makes an RPC call to ZSATrayManager using the native sendZSATrayManagerCommand method from <em>ZSATrayHelper.dll</em> with serialised inputs.</p><pre>public bool dumpLogs(ZSATrayManagerConfigDumpLog configData) =&gt; this.sendZSATrayManagerCommandHelper(ZSCALER_APP_RPC_COMMAND.DUMP_LOGS, (object) configData) == 0;<br><br>private int sendZSATrayManagerCommandHelper(<br>  ZSCALER_APP_RPC_COMMAND commandCode,<br>  object configData = null)<br>  {<br>    ZSATrayManagerCommand structure = new ZSATrayManagerCommand();<br>    structure.commandCode = (int) commandCode;<br>    if (configData != null)<br>      structure.configJson = JsonConvert.SerializeObject(configData);<br>    IntPtr num1 = Marshal.AllocCoTaskMem(Marshal.SizeOf((object) structure));<br>    Marshal.StructureToPtr((object) structure, num1, false);<br>    int num2 = NativeMethods.sendZSATrayManagerCommand(num1);<br>    ZSALogger.zsaLog(“sendZSATrayManagerCommandHelper retVal: “ + num2.ToString());<br>    Marshal.FreeCoTaskMem(num1);<br>    return num2;<br>  }</pre><p>Accepting RPC calls from any process without validation is a significant security risk, especially when some of the RPC calls supported by ZSATrayManager involve the execution of high-privileged actions.</p><p>Most software, including ZScaler Client Connector, implements checks to ensure the RPC calls made originate from trusted processes. Thus began our quest to bypass these checks.</p><h3>Bypassing the RPC Connection Check via Cache Grooming and Collision</h3><p>Since <em>CVE-2020–11635</em>¹, ZScaler Client Connect has added additional validation checks for RPC connections to ZSATrayManager. The checks are executed in the <em>IfCallbackFn</em> function and consists of the following:</p><ol><li><strong>Process ID (PID Validation)</strong>: The PID of the caller must match a process whose image path name belongs to an executable that is signed by Zscaler (Authenticode check).</li><li><strong>Caller Process Validation</strong>: The caller process must be either:<br>a. A high-privileged SYSTEM owned process; or<br>b. <em>ZSATray.exe</em></li></ol><p>The ZSATrayManager determines whether the PID belongs to ZSATray by checking a cache in memory. It keys this cache using a Fowler–Noll–Vo hash function (FNV-1a) and stores the process name, allowed status, and last access timestamp.</p><pre>2023–08–05 14:54:53.960564(+0800)[8528:17868] DBG ZSATrayManager: addRpcCallerInCache: — — — — — — entries — — — — — — — — -<br>2023–08–05 14:54:53.960564(+0800)[8528:17868] DBG PID | name | is_allowed | last_access_ts<br>2023–08–05 14:54:53.960564(+0800)[8528:17868] DBG 37352 | C:\Program Files\Zscaler\ZSATray\ZSATray.exe | true | 1691247282094 ms<br>2023–08–05 14:54:53.960564(+0800)[8528:17868] DBG 39296 | C:\Program Files\Zscaler\ZSATray\ZSATray.exe | true | 1691244684011 ms<br>2023–08–05 14:54:53.960564(+0800)[8528:17868] DBG 39144 | C:\Program Files\Zscaler\ZSATray\ZSATray.exe | true | 1691246922202 ms</pre><p>When ZSATrayManager first starts ZSATray, it stores its PID in the cache of the ZSATrayManager. In addition, every time ZSATrayManager successfully validates an RPC connection, it stores the hashed PID of the calling process in this cache. In future requests, if the hashed caller PID exists in the cache, it can skip the Authenticode and caller process checks.</p><p>Unfortunately, because ZSATrayManager does not regularly prune this cache, it is possible to brute force a cached PID since PIDs are non-random. An attacker can cache numerous allowed PIDs by repeatedly killing the ZSATray process and triggering ZSATrayManager to launch a new ZSATray process that adds a new PID to the cache after making a successful connection to ZSATrayManager. This creates numerous allowed PIDs that the attacker can brute force. By repeatedly starting and killing an exploit binary, the attacker can cause a cache collision when Windows assigns a reused PID that exists in the cache.</p><p>The attacker-controlled binary can thus make arbitrary RPC connections to ZSATrayManager that bypasses the validation checks. Since ZSATray already includes an implementation of the RPC connection client in <strong>sendZSATrayManagerCommandHelper</strong>, we can reuse that to make the call from a custom .NET binary for exploitation.</p><h4>Process Injection</h4><p>An alternative means to bypass this check is by injecting the user-owned <em>ZSATray.exe</em> process to run arbitrary code. The process will pass all the necessary checks but is somewhat more complex due to ZSATray being a .NET assembly with managed code. The injection can also fail if ZScaler Client Connector’s anti-tampering feature is enabled.</p><h3>Exploiting the Revert Password Check Incorrect Type Validation (CVE-2023–41972)</h3><p>Having achieved the ability to make arbitrary RPC calls to ZSATrayManager, our next step was to explore which supported RPC functions could be exploited to achieve privilege escalation.</p><p>Interestingly, ZScaler has added additional authentication for some of these functions, such as <em>PERFORM_APP_REVERT</em>. As the name suggests, the function reverts ZScaler Client Connector to a previous version by executing an older version’s installer. The function accepts <em>previousInstallerName</em>, <em>pwdType</em>, and <em>password</em> as arguments. The latter two are used when an administrator has <em>set a password</em>² for this action and only allow the function to execute if a correct password has been provided.</p><p>Unfortunately, ZSATrayManager does not check if <em>pwdType</em> matches <em>PASSWORD_TYPE.ZCC_REVERT_PWD</em> (7), meaning that the password check function will trust whichever <em>pwdType</em> is passed via the RPC and perform the corresponding password check. For example, if <em>ZIA_DISABLE_PWD</em> is provided for <em>pwdType</em>, ZSATrayManager will check that the <em>password</em> matches the password set for Zscaler Internet Access instead of the password for reverting the application.</p><pre>case 90: // PERFORM_APP_REVERT<br> v66 = sub_1400949C0(v294, (__int64)v371);// Note: there is no check on pwdType e.g. if ( pwdType == 4 ) like in other cases<br> if ( (unsigned __int8)PasswordCheck(v67, pwdType, v66, 1) )</pre><p>Some of the password types including <em>ZCC_REVERT_PWD</em> return true by default if no password has been specified.</p><pre>case 6u:<br>sub_14025D9B0(a1);<br>  LOBYTE(isCorrectPassword) = 0;<br>  if ( passwordConfigured )<br>  {<br>    …<br>  }<br>  else<br>  {<br>    v8::internal::wasm::ErrorThrower::CompileError(<br>    (v8::internal::wasm::ErrorThrower *)&amp;LogHandle,<br>    “Skip password check — ZAD is not enabled”); // Password check passes since isCorrectPassword is still 0<br>  }</pre><p>As such, even if a password has been set for <strong>PERFORM_APP_REVERT</strong>, an attacker can bypass this by setting <em>pwdType</em> in the RPC to <em>SHOW_ADVANCED_SETTINGS</em> (6).</p><h3>Exploiting the Lack of Input Santisation on Zscaler Client Connector (CVE-2023–41973)</h3><p>At this juncture, however, the hallowed <strong>NT AUTHORITY\SYSTEM</strong> privilege escalation has not been achieved yet. We continued to dig further into <strong>PERFORM_APP_REVERT</strong>.</p><p>As mentioned earlier, <strong>PERFORM_APP_REVERT</strong> accepts a <em>previousInstallerName</em> argument. This argument is appended to <em>C:\Program Files\ZScaler\RevertZcc\</em> and is typically set to <em>{VERSION NUMBER}.exe</em>. ZSATrayManager executes the file at this path as <strong>NT AUTHORITY\SYSTEM</strong>. However, since this is controllable from the <em>previousInstallerName</em> parameter, an attacker can send a path traversal string such as <em>..\..\..\{ATTACKER-CONTROLLED PATH}</em> to execute their payload.</p><p>Unfortunately for us, there are still additional checks on the executable at the path, such as Microsoft Authenticode signature verification using the <em>WinVerifyTrust </em>function. This performs an OS-level trust verification to ensure that the executable was properly signed by ZScaler. This verification appears to be done properly as it specifically checks the SHA-2 hash of the signer and issuer thumbprints:</p><pre>if ( CertCompareIntegerBlob(&amp;v19, (PCRYPT_INTEGER_BLOB)(v6 + 24)) )<br>  {<br>    initString(v28, “92c1588e85af2201ce7915e8538b492f605b80c6”, 0x28ui64);<br>    initString(v26, “83fe2a3586d483fd75c0b0abdb89697a56ad0b41”, 0x28ui64);<br>    if ( (unsigned __int8)validateSignerAndIssuerThumbprints(v26, v28, a2) )<br>    {<br>      LogInfo(&amp;LogHandle, 1i64, “Signer matches Zscaler SHA2 02/28/2018”);<br>LABEL_20:<br>      v4 = 1;<br>    }<br>  }</pre><p>Here is a snapshot of the log output when we tried to get it to launch Microsoft Word.</p><pre>INF validateSignerAndIssuer Thumbprints returned true<br>INF Signer matches Zscaler SHA2 March 1, 2021<br>INF Signer trust released.<br>INF Process executable is signed by Zscaler.<br>INF UserSID: “0, 0, 0, 0, 0, 5”, SECURITY_LOCAL_SYSTEM_RID: “0, 0, 0, 0, 0, 5”<br>INF SID matched with SECURITY_LOCAL_SYSTEM_RID<br>INF ZSAService RPC: Accepting RPC from a SYSTEM owned Zscaler process<br>INF ZSAService RPC command: PERFORM_APP_REVERT<br>INF Starting revert<br>DBG Running zscaler executable: C:\Program Files\Zscaler\RevertZcc\..\..\..\Program Files\Microsoft Office\root\Office16\WINWORD.EXE — revertzcc 1 — mode unattended<br>ERR Signer does not match Zscaler<br>INF Signer trust released.<br>ERR Executable [C:\Program Files\Zscaler\RevertZcc\..\..\..\Program Files\Microsoft Office\root\Office16\WINWORD.EXE] is not Zscaler binary.<br>INF Done with ZSAService RPC command: PERFORM_APP_REVERT with return value:0</pre><p>As such, we needed to find another link in the chain.</p><h3>Achieving Arbitrary Code Execution via DLL Hijacking with ZSAService</h3><p>DLL hijacking is often <em>not deemed as a vulnerability</em>³ for good reasons, but it can still shine when chained in specific scenarios like this one. Two conditions elevate the humble DLL hijacking to a privilege escalation gadget:</p><ol><li>The process that is hijacked is executed by a higher-privileged process than the attacker, so a security boundary can be crossed.</li><li>The DLL hijack path is in a low-privileged attacker-writable location, so no additional privileges are required to execute the attack.</li></ol><p>One of the ZScaler Client Connector binaries, ZSAService, is vulnerable to DLL hijacking because its search path starts with the current directory. One of the DLLs that could be hijacked is <em>userenv.dll</em>. This is a straightforward DLL hijacking that can be exploited with one of the many DLL hijacking payload templates out there.</p><pre>#include “pch.h”<br>#include &lt;iostream&gt;<br><br>BOOL APIENTRY DllMain(HMODULE hModule,<br>  DWORD ul_reason_for_call,<br>  LPVOID lpReserved<br>)<br>{<br>  switch (ul_reason_for_call)<br>  {<br>  case DLL_PROCESS_ATTACH:<br>    system(“whoami &gt; C:\\hacked.txt”);<br>    //WinExec(“cmd.exe”, SW_SHOW);<br>    //WinExec(“powershell.exe”, SW_SHOW);<br>  case DLL_THREAD_ATTACH:<br>  case DLL_THREAD_DETACH:<br>  case DLL_PROCESS_DETACH:<br>    break;<br>  }<br>  return TRUE;<br>}<br><br>extern “C” __declspec(dllexport) void DestroyEnvironmentBlock()<br>{<br>  return;<br>}<br><br>extern “C” __declspec(dllexport) void LoadUserProfileW()<br>{<br>  return;<br>}<br><br>extern “C” __declspec(dllexport) void UnloadUserProfile()<br>{<br>  return;<br>}<br><br>extern “C” __declspec(dllexport) void LoadUserProfileA()<br>{<br>  return;<br>}<br><br>extern “C” __declspec(dllexport) void CreateEnvironmentBlock()<br>{<br>  return;<br>}</pre><p>By compiling this as a DLL and placing the DLL (renamed to <em>userenv.dll</em>) in the same directory as <em>ZSAService.exe</em>, launching <em>ZSAService.exe</em> will cause the arbitrary commands in the malicious <em>userenv.dll</em> to be executed.</p><p>Thus, the final link in our chain was complete:</p><ol><li>Attacker brute forces cached PIDs to make RPC calls to ZSATrayManager.</li><li>Attacker bypasses password protection for the <strong>PERFORM_APP_REVERT </strong>function.</li><li>Attacker sends path traversal payload in <em>previousInstallerName</em> argument.</li><li>ZSATrayManager executes DLL-hijacked <em>ZSAService.exe</em> that passes the Authenticode check.</li><li>Hijack DLL causes the attacker’s commands to be executed as <strong>NT AUTHORITY\SYSTEM</strong>.</li><li>Pwned!</li></ol><h3>Exploiting the ZSATrayManager Arbitrary File Deletion (CVE-2023–41969)</h3><p>We also examined another RPC function <strong>REPORT_ISSUE_REQUEST</strong>, which as its name suggests, facilitates issue reporting. The function accepts the following parameters: <em>requestJson </em>which contains information pertaining to the issue, <em>zipFilePath</em>, the location of the ZIP archive to upload, and <em>mobileSupportUrl</em> the URL endpoint to connect to.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DukZRr1C3Q9BwarRuNMEQg.png" /></figure><p>Diving deeper into the function logic, we also noticed that the file path specified with the <em>zipFilePath </em>parameter will be encrypted with a <strong>.enc2 </strong>suffix. When the function exits, the encrypted <strong>.enc2 </strong>archive that was created by ZSATrayManager was subsequently deleted. Both the creation and deletion of this file are done by the ZSATrayManager service, which runs as <strong>NT AUTHORITY\SYSTEM</strong>.</p><p>This behaviour appears to be susceptible to privileged file operation abuse, so we turned to the venerable <em>symboliclink-testing-tools</em>⁴ suite developed by James Foreshaw from the Project Zero team and proceeded with symbolic link testing. Interestingly, the delete function seems to be capable of deleting both files and directories, as shown in the following IDA screenshot:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/360/1*FV7Im7dlcxV00Ui9axoH7w.png" /></figure><p>We now have enough information to develop an exploit PoC. To demonstrate this, we will first create a copy of <em>cmd.exe</em> to be used as a target for deletion. The <strong>REPORT_ISSUE_REQUEST</strong> function will be invoked with the following arguments: <em>zipLocation = “C:\Tools\arb_delete\POC\deleteme.bin”, requestJson = {}, mobileSupportURL = “http://localhost:8080&quot;</em>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*31g198GivyaHepngP0mBCQ.png" /></figure><p>The full exploit chain to complete the arbitrary file deletion PoC is as follows:</p><ol><li>Attacker creates an empty file at the location <em>C:\Tools\arb_delete\POC\deleteme.bin</em>.</li><li>Attacker brute forces cached PIDs to make RPC calls to ZSATrayManager.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Rm-7DKIjUssKzNd8VSq2bg.png" /></figure><p>Looking at the logs, we observe that the PID of our payload executable exists in the cache (PID 10084).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WMYiXpW8Vt83YIl-YUr2Aw.png" /></figure><p>3. Attacker creates a web server that listens at <a href="http://127.0.0.1:8080."><strong>http://127.0.0.1:8080</strong></a>.</p><p>4. Attacker sends the malicious issue report request to the <strong>REPORT_ISSUE_REQUEST </strong>function.</p><p>5. ZSATrayManager creates an encrypted ZIP archive of <em>C:\Tools\arb_delete\POC\deleteme.bin</em> at <em>C:\Tools\arb_delete\POC\deleteme.enc2</em>.</p><p>6. ZSATrayManager attempts to connect to <a href="http://127.0.0.1:8080/poc."><strong>http://127.0.0.1:8080/poc</strong></a>. Attacker receives the incoming connection, holds on to the connection and leaves it hanging.</p><p>7. While the attacker is hanging onto the connection, the attacker proceeds to delete the <em>C:\Tools\arb_delete\POC</em> directory.</p><p>8. Attacker creates a directory junction at <em>C:\Tools\arb_delete\POC</em> which points to <em>\RPC Control</em>.</p><p>9. Attacker then creates a symlink object at the junction <em>\RPC Control\deleteme.enc2</em> to point to the target file that we want to delete (the <em>C:\Windows\System32\cmd.secret.exe</em> that was created).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OgeiDW7W-q9mael1KEyptQ.png" /></figure><p>10. Attacker now closes the hanging connection and let ZSATrayManager proceed to delete the target file via the symlink.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qDza3z1wobC_i2rJwwAwIQ.png" /></figure><p>We can verify that the deletion worked without a hitch by looking through the logs of the ZSATrayManager service.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0e1gOWBZ8U5XIc5r6mE5rQ.png" /></figure><p>Using this privileged file deletion primitive, we can potentially chain it up with other vulnerabilities on the Windows machine, such as deleting the <em>C:\Config.Msi</em> directory to perform a local privilege escalation as mentioned by <em>Zero Day Initiative</em>⁵ and <em>Mandiant</em>⁶.</p><h3>Conclusion</h3><p>This was an extremely fun vulnerability chain that took the greater part of a Friday night, highlighting how multiple small vulnerabilities can add up with enough persistence. One of the biggest challenges in client-server process architectures is authentication and authorisation, making it a ripe hunting ground for vulnerability researchers. Our findings prove that even with proper validation of the calling process, the RPC inputs should be properly sanitised and validated as well.</p><h3>Disclosure timeline</h3><ul><li>15 August 2023 — Reported the Password Check bypass and Path Traversal vulnerabilities to the Zscaler team.</li><li>31 August 2023 — Zscaler team acknowledged the findings.</li><li>28 August 2023 — Reported the Arbitrary File Deletion vulnerability to the Zscaler team.</li><li>01 September 2023 — Zscaler Client Connector 4.2.0.209 / 4.3.0.121 was released that fixes <strong>CVE-2023–41972</strong> and <strong>CVE-2023–41973</strong>.</li><li>06 December 2023 — Zscaler Client Connector 4.2.1 / 4.3.0.151 was released that fixes <strong>CVE-2023–41969</strong>.</li><li>11 January 2024 — Zscaler team informed the team that CVEs have been reserved.</li><li>26 March 2024 — Zscaler team publicly disclosed the CVEs (<a href="https://trust.zscaler.com/private.zscaler.com/posts/18226">https://trust.zscaler.com/private.zscaler.com/posts/18226</a>)</li></ul><p>[1] <a href="https://www.cve.org/CVERecord?id=CVE-2020-11635">https://www.cve.org/CVERecord?id=CVE-2020-11635</a></p><p>[2] <a href="https://help.zscaler.com/client-connector/reverting-zscaler-client-connector">https://help.zscaler.com/client-connector/reverting-zscaler-client-connector</a></p><p>[3] <a href="https://itm4n.github.io/windows-dll-hijacking-clarified/">https://itm4n.github.io/windows-dll-hijacking-clarified/</a></p><p>[4] <a href="https://github.com/googleprojectzero/symboliclink-testing-tools">https://github.com/googleprojectzero/symboliclink-testing-tools</a></p><p>[5] <a href="https://www.zerodayinitiative.com/blog/2022/3/16/abusing-arbitrary-file-deletes-to-escalate-privilege-and-other-great-tricks">https://www.zerodayinitiative.com/blog/2022/3/16/abusing-arbitrary-file-deletes-to-escalate-privilege-and-other-great-tricks</a></p><p>[6] <a href="https://www.mandiant.com/resources/blog/arbitrary-file-deletion-vulnerabilities">https://www.mandiant.com/resources/blog/arbitrary-file-deletion-vulnerabilities</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7ad997bd7058" width="1" height="1" alt=""><hr><p><a href="https://medium.com/csg-govtech/catch-me-if-you-can-local-privilege-escalation-in-zscaler-client-connector-7ad997bd7058">Cache me if you can — Local Privilege Escalation in Zscaler Client Connector</a> was originally published in <a href="https://medium.com/csg-govtech">CSG @ GovTech Singapore</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[STF Secret of Meow Olympurr: Creating a CTF Challenge with Cat Pictures Part 2]]></title>
            <link>https://medium.com/csg-govtech/stf-secret-of-meow-olympurr-creating-a-ctf-challenge-with-cat-pictures-part-2-a153b51e618f?source=rss----ebb6b6f1ad4e---4</link>
            <guid isPermaLink="false">https://medium.com/p/a153b51e618f</guid>
            <category><![CDATA[ctf-writeup]]></category>
            <category><![CDATA[csg]]></category>
            <category><![CDATA[cloud]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <dc:creator><![CDATA[Glenice]]></dc:creator>
            <pubDate>Tue, 05 Sep 2023 03:10:20 GMT</pubDate>
            <atom:updated>2023-09-05T03:10:20.304Z</atom:updated>
            <content:encoded><![CDATA[<p>The Secret of Meow Olympurr was a challenge created for GovTech’s second edition of STACK the Flags (STF) in 2022. I had a lot of fun creating this challenge and thought it would be good to note down some of my thoughts behind it.</p><p>Apart from utilising the cat pictures, there were three design principles in mind when creating the challenge:</p><ol><li>To host the challenge in a multi-cloud environment</li></ol><p>There are several cloud service providers available in the market, such as Amazon Web Service (AWS) and Microsoft Azure. While there are similarities in the types of cloud computing services provided (e.g., AWS EC2 and Azure Virtual Machine), the behavior and implementation could be relatively different among different cloud environments.</p><p>To push the participants, and myself, a little more, I wanted the challenge to span across AWS and Microsoft Azure.</p><p>2. To explore a different exploitation path from the <a href="https://medium.com/csg-govtech/tisc-cloudynekos-creating-a-ctf-challenge-with-cat-pictures-6f126c7770db">CloudyNekos</a> challenge</p><p>In 2022, we submitted a challenge to CSIT’s “The InfoSecurity Challenge” (TISC), which consists of horizontal and vertical privilege escalation within the Amazon Web Service (AWS) cloud environment with resource creation permissions.</p><p>For STF, I wanted to create something different and leverage mainly read-only permissions for lateral movement.</p><p>3. To focus on enumeration</p><p>Being stuck in a rabbit hole is frustrating — sometimes it feels like there is no light at the end of the tunnel until we realize that we missed important information that may open a new path for exploration or help eliminate unviable exploits. Since this challenge leverages on read-only privileges, enumeration is key!</p><p>While the Secret of Meow Olympurr is no longer live, you may read the rest of the article to experience and learn more about it!</p><h4><strong>Challenge statement</strong></h4><p>Jaga reached Meow Olympurr and met some native Meows. While cautious at first, they warmed up and shared that they have recently created a website to promote tourism!</p><p>However, the young Meows explained that they are not cy-purr security trained and would like to understand what they might have misconfigured in their environments. The young Meows were trying to get two different environments to work together, but it seemed like something was breaking….</p><p>Log a cy-purr security case by invoking the mysterious function and retrieve the secret code!</p><h4><strong>Technical walkthrough</strong></h4><p>At the start of the challenge, a CloudFront URL was provided to the participants.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*tUO37rZbYlnj90bvySl7_Q.png" /></figure><p>Upon browsing to the error page, a broken image link was shown.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*873zvaMxeAipvCOQX5xHtQ.png" /></figure><p>From the page source, it was observed that the developers were attempting to retrieve the image via a Cross-Origin Resource Sharing (CORS) server.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*2JEkXRwhidfX-I5tZxWXpA.png" /></figure><p>Since CloudFront is an AWS service, the CORS server might be hosted on an AWS EC2.</p><p>Server-side Request Forgery (SSRF) attack is a popular method to steal <a href="https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/">AWS EC2 metadata credentials</a>:</p><blockquote><em>SSRF vulnerabilities allow attackers to make unauthorized requests from web applications. Since these requests come from the application itself, they can be used to access internal resources that the application has access to but that was not intended to be accessible to outsiders.</em></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*51bcAUn5wUMkho6_n904PA.png" /></figure><p>We can try accessing the AWS EC2 Instance Metadata Service (IMDS) via the IP address: 169.254.169.254.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*9q5bzfUTxVgb6ftIXaNIww.png" /></figure><p>When interacting with IMDS, it was observed that the <em>iam </em>path was not present.</p><p>Another set of credentials can be found at <em>/identity-credentials/ec2/security-credentials/ec2-instance</em>, which was created <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-identity-roles.html">regardless of the use of instance identity roles</a>. Since the <em>iam </em>path was not present, it was safe to conclude that IAM role was not attached to the EC2.</p><p>Moving on to the next clue — the <a href="https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website">Azure static website</a> URL.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*j_VAu_rnrSkjV4ncrS1ZRw.png" /></figure><p>This time, the image on the error page loaded successfully.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*22xZPKUdsjYw98Qm6_0LEg.png" /></figure><p>A <a href="https://learn.microsoft.com/en-us/rest/api/storageservices/create-account-sas">shared access signature (SAS)</a> token was found on the HTML page, which allows access to the source codes.</p><blockquote><em>A shared access signature is a token that is appended to the URI for an Azure Storage resource. The token contains a special set of query parameters that indicate how the resources may be accessed by the client.</em></blockquote><p>We can use Azure Storage Explorer to connect to the storage account.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/961/1*0SlifdDei5ShoVWrAeTfyA.png" /><figcaption>$web and $log are default containers created by Azure.</figcaption></figure><p>A <em>readme.md</em> file was found in the <em>dev </em>container.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*P2fXd9J0HfXig3ojEXPwYg.png" /></figure><p>Given the instructions and details, there are three key areas to explore:</p><ol><li>Interact with the function app.</li><li>Authenticate using the service principal credentials and tenant ID.</li><li>Extract the function app’s source code using the SAS token. However, we are missing the storage account name to use the SAS token.</li></ol><p>Let us explore in sequence — first up, interacting with the function app.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*mG6LSf-Lwkk0ic5AwN_a3Q.png" /></figure><p>The instructions on how to interact with the function app are included on the home page.</p><p>Upon supplying the URL parameter, it was noted that the function app queries and returns the page source.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*M1d7NcNOQl2Be321NsLfsg.png" /></figure><p>Here, we may eliminate leveraging SSRF to communicate with Azure IMDS, as it requires an HTTP Request header. On a side note, SSRF may also be eliminated as an attack path in AWS if the EC2 is configured to use only <a href="https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/">IMDSv2</a>.</p><p>Moving on to the next item — authenticating with Azure CLI using the service principal credentials and tenant ID.</p><p>We can list all resources in the current subscription using the <em>Get-AzResource</em> command.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/917/1*JBO02dWLhqTnBCdAeLBeiQ.png" /></figure><p>From the results, we can identify a second storage account, which has a similar name to the function app.</p><p>The <a href="https://learn.microsoft.com/en-us/azure/ai-services/translator/document-translation/how-to-guides/create-sas-tokens?tabs=Containers">blob container SAS URL</a> can be formed using the storage account name, container name and SAS token:</p><p><em>https://meowvellousappstorage.blob.core.windows.net/scm-releases?sv=2018-11-09&amp;sr=c&amp;st=2022-09-01T00%3A00%3A00Z&amp;se=2022-12-12T00%3A00%3A00Z&amp;sp=rl&amp;spr=https&amp;sig=jENgCFTrC1mYM1ZNo%2F8pq1Hg9BO1VLbXIk%2FpABrK4Eo%3D</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*KeIh3nkmK-NEV8XkY7odJw.png" /></figure><p>Inspect the zip file and note that it stores the source codes of the function app.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*rLTRerVVIfxABujOBCVvXw.png" /></figure><p>It is time for some source code analysis!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*f5EfTNsMS_-qRCiz0A2Cqw.png" /></figure><p><a href="https://aws.amazon.com/sdk-for-python/">Boto3</a> is the AWS Software Development Kit (SDK) for Python and provides an API for AWS infrastructure services. The AWS credential was retrieved from the Azure key vault upon each API call to invoke the AWS Lambda function.</p><p>Since the service principal has the same privileges as the function app, we can retrieve the AWS secret access key using the vault name (in the <em>vault_url</em>) and key name.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*uOakF_fWgo_ulVcwHOVixw.png" /></figure><p>We have gained access to AWS!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/943/1*l4QT65Rt4ubZOURf6muPPA.png" /></figure><p>With that, it is time to enumerate our current access in AWS.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MLwZ9CLCn7r4X9JFyPqcMQ.png" /></figure><p>The IAM user entity is attached with two policies and a permission boundary. The two policies are:</p><ol><li>azure-policy</li></ol><pre>{<br>    &quot;Version&quot;: &quot;2012-10-17&quot;,<br>    &quot;Statement&quot;: [            <br>      {<br>        &quot;Action&quot;: [<br>          &quot;iam:GetPolicy&quot;,<br>          &quot;iam:GetPolicyVersion&quot;<br>        ],<br>        &quot;Resource&quot;: &quot;*&quot;,<br>        &quot;Effect&quot;: &quot;Allow&quot;<br>      },<br>      {<br>        &quot;Effect&quot;: &quot;Allow&quot;,<br>        &quot;Action&quot;: [<br>            &quot;iam:ListAttachedUserPolicies&quot;<br>        ],<br>        &quot;Resource&quot;: &quot;arn:aws:iam::&lt;ACCOUNT_ID&gt;:user/azure_user&quot;<br>      },<br>      {<br>        &quot;Action&quot;: [<br>          &quot;lambda:Invoke*&quot;,<br>          &quot;lambda:ListFunctions&quot;,<br>          &quot;lambda:CreateFunction&quot;,<br>          &quot;logs:CreateLogGroup&quot;,<br>          &quot;logs:CreateLogStream&quot;,<br>          &quot;logs:PutLogEvents&quot;,<br>          &quot;lambda:UpdateFunctionCode&quot;<br>        ],<br>        &quot;Resource&quot;: &quot;*&quot;,<br>        &quot;Effect&quot;: &quot;Allow&quot;<br>      }<br>    ]<br>  }</pre><p>2. azure-policy-extended</p><pre>{<br>    &quot;Version&quot;: &quot;2012-10-17&quot;,<br>    &quot;Statement&quot;: [            <br>      {<br>        &quot;Action&quot;: [<br>          &quot;iam:GetPolicy&quot;,<br>          &quot;iam:GetPolicyVersion&quot;,<br>          &quot;iam:AddUserToGroup&quot;,<br>          &quot;iam:AttachUserPolicy&quot;,<br>          &quot;iam:CreateRole&quot;,<br>          &quot;iam:AttachRolePolicy&quot;,<br>          &quot;iam:PassRole&quot;<br>        ],<br>        &quot;Resource&quot;: &quot;*&quot;,<br>        &quot;Effect&quot;: &quot;Allow&quot;<br>      },<br>      {<br>        &quot;Effect&quot;: &quot;Allow&quot;,<br>        &quot;Action&quot;: [<br>            &quot;iam:ListAttachedUserPolicies&quot;,<br>            &quot;iam:GetUser&quot;<br>        ],<br>        &quot;Resource&quot;: &quot;arn:aws:iam::&lt;ACCOUNT_ID&gt;:user/azure_user&quot;<br>      },<br>      {<br>        &quot;Action&quot;: [<br>          &quot;lambda:Invoke*&quot;,<br>          &quot;lambda:ListFunctions&quot;,<br>          &quot;lambda:CreateFunction&quot;,<br>          &quot;logs:DescribeLogGroups&quot;,<br>          &quot;logs:DescribeLogStreams&quot;,<br>          &quot;logs:GetLogEvents&quot;,<br>          &quot;lambda:UpdateFunctionCode&quot;<br>        ],<br>        &quot;Resource&quot;: &quot;*&quot;,<br>        &quot;Effect&quot;: &quot;Allow&quot;<br>      },<br>      {<br>        &quot;Action&quot;: [<br>          &quot;cloudformation:CreateStack&quot;<br>        ],<br>        &quot;Resource&quot;: &quot;*&quot;,<br>        &quot;Effect&quot;: &quot;Allow&quot;<br>      }<br>    ]<br>  }</pre><p>Interestingly, the permissions seemed to allow privilege escalation; for example, using the <em>iam:CreateRole, lambda:CreateFunction, iam:PassRole</em> and <em>lambda: InvokeFunction</em> permissions.</p><p>However, these actions were denied — because of the permission boundary:</p><pre>{<br>    &quot;Version&quot;: &quot;2012-10-17&quot;,<br>    &quot;Statement&quot;: [   <br>      {<br>        &quot;Action&quot;: [<br>          &quot;iam:*&quot;,<br>          &quot;lambda:*&quot;<br>        ],<br>        &quot;Resource&quot;: &quot;*&quot;,<br>        &quot;Effect&quot;: &quot;Allow&quot;<br>      },<br>      {<br>        &quot;Action&quot;: [<br>          &quot;lambda:*&quot;<br>        ],<br>        &quot;Resource&quot;: &quot;arn:aws:lambda:ap-southeast-1:&lt;ACCOUNT_ID&gt;:function:amplify-*&quot;,<br>        &quot;Effect&quot;: &quot;Deny&quot;<br>      },<br>      {<br>        &quot;Action&quot;: [<br>          &quot;lambda:InvokeAsync&quot;,<br>          &quot;iam:Generate*&quot;,<br>          &quot;iam:Add*&quot;,<br>          &quot;iam:Create*&quot;,<br>          &quot;iam:Put*&quot;,<br>          &quot;lambda:InvokeFunctionUrl&quot;,<br>          &quot;iam:Delete*&quot;,<br>          &quot;logs:Put*&quot;,<br>          &quot;iam:Deactivate*&quot;,<br>          &quot;iam:Enable*&quot;,<br>          &quot;logs:Create*&quot;,<br>          &quot;iam:Pass*&quot;,<br>          &quot;lambda:List*&quot;,<br>          &quot;iam:Attach*&quot;,<br>          &quot;lambda:Create*&quot;,<br>          &quot;lambda:Update*&quot;,<br>          &quot;cloudformation:*&quot;<br>        ],<br>        &quot;Resource&quot;: &quot;*&quot;,<br>        &quot;Effect&quot;: &quot;Deny&quot;<br>      },<br>      {<br>        &quot;Action&quot;: [<br>          &quot;logs:Describe*&quot;<br>        ],<br>        &quot;Resource&quot;: &quot;*&quot;,<br>        &quot;Effect&quot;: &quot;Allow&quot;<br>      },<br>      {<br>        &quot;Action&quot;: [<br>          &quot;logs:*&quot;<br>        ],<br>        &quot;Resource&quot;: &quot;arn:aws:logs:ap-southeast-1:&lt;ACCOUNT_ID&gt;:log-group:/aws/lambda/*-webservice:*&quot;,<br>        &quot;Effect&quot;: &quot;Allow&quot;<br>      }<br>    ]<br>  }</pre><p>When evaluating IAM policy logic, deny statements take precedence. The effective permissions works out to be:</p><pre>{<br>    &quot;Version&quot;: &quot;2012-10-17&quot;,<br>    &quot;Statement&quot;: [            <br>      {<br>        &quot;Action&quot;: [<br>          &quot;iam:GetPolicy&quot;,<br>          &quot;iam:GetPolicyVersion&quot;<br>        ],<br>        &quot;Resource&quot;: &quot;*&quot;,<br>        &quot;Effect&quot;: &quot;Allow&quot;<br>      },<br>      {<br>        &quot;Effect&quot;: &quot;Allow&quot;,<br>        &quot;Action&quot;: [<br>            &quot;iam:ListAttachedUserPolicies&quot;,<br>            &quot;iam:GetUser&quot;<br>        ],<br>        &quot;Resource&quot;: &quot;arn:aws:iam::&lt;ACCOUNT_ID&gt;:user/azure_user&quot;<br>      },<br>      {<br>        &quot;Action&quot;: [<br>          &quot;lambda:InvokeFunction&quot;,<br>          &quot;logs:DescribeLogGroups&quot;,<br>          &quot;logs:DescribeLogStreams&quot;,<br>          &quot;logs:GetLogEvents&quot;,<br><br>        ],<br>        &quot;Resource&quot;: &quot;*&quot;,<br>        &quot;Effect&quot;: &quot;Allow&quot;<br>      }<br>    ]<br>  }</pre><p>Going back to the challenge description, the objective was to invoke a secret function. While <em>lambda:ListFunction</em> is not allowed, we have <a href="https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html"><em>logs:DescribeLogGroups</em></a>!</p><blockquote><em>Lambda automatically integrates with CloudWatch Logs and pushes all logs from your code to a CloudWatch Logs group associated with a Lambda function, which is named /aws/lambda/&lt;function name&gt;.</em></blockquote><p>Bingo — we can infer the function name from the log group name!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jDQm3UawkB1jesHNGlnT-A.png" /></figure><p>Invoke the Lambda function and the flag is returned in the response!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*5EgpOevXusYwCogK-If6vw.png" /></figure><h4><strong>Conclusion</strong></h4><p>As we move to the cloud and/or hybrid environment, it is important to note that cloud service providers adopt a <a href="https://aws.amazon.com/compliance/shared-responsibility-model/">shared responsibility model</a>.</p><p>Customers are responsible for ensuring that the permissions assigned follow the principle of least privileges. This challenge also demonstrates the ability to gain meaningful insights and lateral moves between cloud environments with read-only privileges!</p><p>If you are looking for more Cloud challenges and adorable cat pictures, fret not, <a href="https://www.csit.gov.sg/events/tisc/tisc-2023">TISC</a> is back for its next run. Who knows, we might meet there again ;)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/359/1*ECOM8pPmrgAMAORtJgjROw.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a153b51e618f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/csg-govtech/stf-secret-of-meow-olympurr-creating-a-ctf-challenge-with-cat-pictures-part-2-a153b51e618f">STF Secret of Meow Olympurr: Creating a CTF Challenge with Cat Pictures Part 2</a> was originally published in <a href="https://medium.com/csg-govtech">CSG @ GovTech Singapore</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Pwning Kubernetes]]></title>
            <link>https://medium.com/csg-govtech/pwning-kubernetes-b526d14aafc0?source=rss----ebb6b6f1ad4e---4</link>
            <guid isPermaLink="false">https://medium.com/p/b526d14aafc0</guid>
            <category><![CDATA[cloud-security]]></category>
            <category><![CDATA[csg]]></category>
            <category><![CDATA[sharing]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <category><![CDATA[kubernetes]]></category>
            <dc:creator><![CDATA[Yu Pengfei]]></dc:creator>
            <pubDate>Thu, 02 Feb 2023 08:33:41 GMT</pubDate>
            <atom:updated>2023-02-02T09:30:49.583Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Written by: </em><a href="https://medium.com/@pengfeiyu1"><em>Yu Pengfei</em></a><em>, </em><a href="https://medium.com/@chiahui.govtech"><em>Mah Chia Hui</em></a></p><h3>Introduction</h3><p>The explosive usage growth of Kubernetes over the last decade was a huge spectacle. However, Rome was not built in a day, and just like every other major technology stack in use today, it took several decades for containerisation technology to mature before Kubernetes presented itself as the best articulation at its current stage.</p><p>As more applications are containerised, orchestration engines such as Kubernetes have become extremely popular. As an orchestration engine, Kubernetes helps to manage, scale and deploy containerised applications. That said, there are tons of different orchestration engines (such as Docker Swarm) — so why does Kubernetes remain so popular? A quick answer lies in Kubernetes being feature-rich, packed with capabilities such as self-healing and auto-scaling. On top of that, Kubernetes is implemented as a managed service by most cloud service providers (CSP). This cements its position as the “Gold standard” for orchestration engines. Kubernetes adoption is still growing, with 96% of organisations using or evaluating Kubernetes. (<a href="https://thenewstack.io/is-kubernetes-adoption-slowing/">https://thenewstack.io/is-kubernetes-adoption-slowing/</a>)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/904/1*cmk3_N9qFu5pPXiAZQ_LxA.png" /></figure><h3>Mo’ Honey Mo’ Problems</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/904/1*eBQ2gI370bN0yYHTexTTkQ.png" /></figure><p>With the widespread usage of Kubernetes, it is no surprise that it is also a prized attack surface that attackers are actively trying to sniff out and exploit. With its new-ish ascendance, developers also make mistakes in its implementation, configuration or deployment.</p><p>This article is the first in our series regarding Kubernetes security. It will provide an overview of some common attacks, and basic security best practices for Kubernetes. Subsequent articles will dive deeper into more complex attacks, such as HostPath Mounting, trampoline pods/nodes, etc, and their remediations.</p><p>To understand the attacks, we must first understand the Kubernetes architecture, the high-level diagram below is a good starting point. Feel free to skip to “Attack Methodology” if you are already familiar with the Kubernetes architecture and its fundamentals.</p><h3>Kubernetes Architecture Diagram</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/904/1*WuWeLGnpUHZCoscwV7FNPA.png" /></figure><h3>Pods</h3><p>Kubernetes comprises of many moving parts, and its lowest working unit is known as a <strong><em>POD</em></strong><em>. </em>A pod can host multiple different containers and storage volumes. Each pod also has its unique IP address. It is also ultra-scalable as it is based on deployment blueprints (yaml file).</p><h3>Nodes</h3><p>To keep things simple for everyone, we will just breakdown the type of nodes to:</p><p>● Worker Nodes</p><p>● Master Node</p><p>A set of nodes forms a cluster.</p><p>Worker Nodes:</p><p>Pods reside on these worker nodes that can either be virtual or physical machines. You can think of them as host machines for pods.</p><p>Master Node:</p><p>A Master Node is the ultimate administrator over the entire cluster. It controls and manages all worker nodes, which include the following components: Kube-API server, Kube-Controller-Manager, Kube Scheduler, etc.</p><h3>Namespace</h3><p>A Kubernetes namespace is an isolation mechanism that isolates resources within a cluster, generally used when different teams are working on the same cluster. In the cybersecurity context, each namespace object/resource is unique to the namespace. Cross-namespace invocation of object/resource is impossible as these objects/resources are isolated within their namespaces.</p><p>Now with all this new knowledge, you should have a fundamental understanding of the Kubernetes environment. Let’s move on to the juicier side of things, how does one attack Kubernetes?</p><h3>Attack Methodology</h3><p>A sophisticated attacker’s methodology can be broken down into three stages.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/904/1*9g75r8Q_GC05m5paOUWHZg.png" /></figure><h3>Stage 1: Initial foothold</h3><p>The primary way to gain an initial foothold is to compromise a web application or microservice hosted on the cluster. The attacker’s objective is to gain remote code execution on the application by enumerating the application and identifying exploitable vulnerabilities (e.g. log4shell, apache struts).</p><h3>Stage 2: Privilege Escalation</h3><p>Upon gaining a foothold into the Kubernetes cluster, an attacker would be inside a pod, which, as explained, is simply a docker container. An attacker could sniff the container for sensitive data but is limited to what is available. . As such, the attacker will look to escalate to either a pod with more privileges or to a node.</p><p>The following are some common techniques an attacker would employ:</p><ul><li>Sensitive Files</li></ul><p>Upon compromising a pod, an attacker would look for credentials such as SSH keys or passwords that would enable lateral movement to other pods that may have higher privileges. Another file an attacker would go for is the service account token, which is typically found in pods that host a long-running service. This service account token is mounted on the pod for the application to access the Kubernetes API server. An attacker can use the service account token to perform malicious operations, such as creating pods in different namespaces or even reading sensitive information from the secrets resource.</p><p>Remediation:</p><p>In addition to the standard practice of not leaving sensitive credentials unencrypted in a pod, a Kubernetes administrator should adhere to the principles of least privilege when providing privileges to the service account token.</p><ul><li>Reachable Applications</li></ul><p>The attacker would enumerate the network with tools like ping or nmap and get a sense of what other applications are reachable. From there, an attacker will identify and exploit the vulnerabilities in the newly discovered applications. The aim here is to gain remote code execution on applications that exist in other namespaces.</p><p>Remediation:</p><p>To manage which pods, namespaces or even external servers can reach a particular Kubernetes resource, a Kubernetes administrator can use the <a href="https://kubernetes.io/docs/concepts/services-networking/network-policies/">Kubernetes network policy resource</a>.</p><ul><li>Cloud Metadata Services</li></ul><p>Access to the cloud metadata service (e.g. <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html">AWS instance metadata</a>) can be a major find for an attacker. The attacker would be able to extract credentials from the metadata service, which can be used to interact with other sensitive cloud services such as AWS secrets manager. The credentials could also have further access into the cluster, such as gaining access to another pod with a different namespace.</p><p>Remediation:</p><p>The Kubernetes administrator can utilise the aforementioned <a href="https://kubernetes.io/docs/concepts/services-networking/network-policies/">Kubernetes network policy resource</a> to prevent an attacker from reaching the cloud metadata API and other applications hosted within the cluster not required for core business functions.</p><ul><li>Pod breakout</li></ul><p>An attacker can break out of a pod by exploiting misconfiguration in the Kubernetes cluster. Some common misconfigurations we have identified over numerous penetration tests are overly permissive capabilities (e.g. SYS_ADMIN), pod running in privileged mode and allowing host path mounts. An attacker can utilise the misconfigurations to break out of the pod into other pods or, worse, to the worker node on which the pod is running..</p><p>Remediation:</p><p>The Kubernetes administrator can utilise the admission controller resource, a Kubernetes resource that validates every resource being created in the Kubernetes cluster by a defined ruleset. A resource that does not comply with the defined ruleset would fail to be created, resulting in an error.</p><h3>Stage 3: Getting the crown jewels</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/900/1*NugO6d47HTUyNuNqozettQ.png" /></figure><p>In the Kubernetes ecosystem, the crown jewels refer to the master node. An attacker who has gained full access to the master node would essentially have complete control of the Kubernetes cluster. The attacker would repeat the techniques detailed in stages 1 and 2, to laterally move from an infected pod to the underlying node and then finally to the master node. To prevent this worst-case scenario from occurring, a Kubernetes administrator must ensure that the cluster is securely configured with some of the remediations mentioned above.</p><h3>Is that all?</h3><p>For a more comprehensive look at your organisation’s Kubernetes security posture, we recommend that you utilise Microsoft’s Kubernetes attack matrix. The matrix comprises major tactics that are relevant to Kubernetes security. Each one of them contains several techniques attackers can use to achieve different goals.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/904/1*bBURmeR2kx7xm8XZ4LOfDw.png" /></figure><h3>Conclusion</h3><p>As more organisations adopt Kubernetes as the de facto tool for managing container orchestration, more sensitive data will end up on Kubernetes clusters. There is an even greater need for system administrators looking to set up their cluster to get it securely configured. An attacker with full access to the master node of a Kubernetes cluster is the last thing a CISO would want to see.</p><p>Watch out for our upcoming articles, where we will share more in-depth and specific attacks that we commonly see in the field today.</p><p>Take care and catch you on the FLIPPETY-FLIP.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/908/1*fMGNlj0BTHXKfXy-y96YZA.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b526d14aafc0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/csg-govtech/pwning-kubernetes-b526d14aafc0">Pwning Kubernetes</a> was originally published in <a href="https://medium.com/csg-govtech">CSG @ GovTech Singapore</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Meet The Final Boss— Clearing each level of Multi-Stage Malware]]></title>
            <link>https://medium.com/csg-govtech/meet-the-final-boss-clearing-each-level-of-multi-stage-malware-8779c5272ec7?source=rss----ebb6b6f1ad4e---4</link>
            <guid isPermaLink="false">https://medium.com/p/8779c5272ec7</guid>
            <category><![CDATA[csg]]></category>
            <category><![CDATA[reverse-engineering]]></category>
            <category><![CDATA[malware-analysis]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <category><![CDATA[sharing]]></category>
            <dc:creator><![CDATA[Darrel]]></dc:creator>
            <pubDate>Wed, 18 Jan 2023 06:39:48 GMT</pubDate>
            <atom:updated>2023-01-18T06:39:48.778Z</atom:updated>
            <content:encoded><![CDATA[<h3>Introduction</h3><p>For a long time, malware authors have built and deployed multi-stage malware to evade detection mechanisms and frustrate malware analysis efforts. When you think you have cleared the final hurdle to find the malware’s core functionality, another level of obfuscation presents itself.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/597/1*oCygs-MEotHKXzWTsS_sXg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/598/1*ITpeI3JC5H8C6azyD32I0g.png" /></figure><p>In this post, we will go through a couple of cool, reliable tools and techniques that help us discover the inner workings of one such malware. These will be useful and can be applied across analysis or reverse engineering of other malware.</p><p><strong>Link to the malware via VirusTotal can be accessed here:</strong> <a href="https://www.virustotal.com/gui/file/564cbc7e47b7a758c8590dfe0aec63d8ff8ab465dea054292ab1027fbd70fb20">https://www.virustotal.com/gui/file/564cbc7e47b7a758c8590dfe0aec63d8ff8ab465dea054292ab1027fbd70fb20</a></p><h3>Delivery</h3><p>Email remains the most popular way to deliver malware. Archive File Formats such as ZIP and ACE files, ISO image files, and HTML files are the most common avenues used to smuggle nasty payloads when opened by their victims. Phishing emails, for instance, can be cleverly crafted to entice recipients to open these files, executing their contents. This typically leads to the start of the multi-stage execution of the malware. Since an adversary may need to rely on follow-on behaviour by a victim upon receiving the phishing email to achieve execution of their malware, a common method to build a payload for the victim to execute is to package the malware using NSIS into a single, easy-to-click executable.</p><h3>NSIS Installers</h3><p>NSIS (Nullsoft Scriptable Install System) is a script-driven open-source system to create Windows installers. Its ubiquity, flexibility and extensive capabilities make it a great pick for malware authors seeking to package their malware into one executable file.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/597/1*30ylOsiFe4bqvcp7Ski2tg.png" /></figure><p>At first glance, this may look like a normal PE Executable. But with the “Detect It Easy” tool, we quickly find that it is an NSIS Installer. We can then use a tool like 7-Zip to unzip and inspect all files packaged, including the main NSI script, which runs when the NSIS Installer is clicked.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/515/1*6jE8IXGpcYlQRmvDTFd89Q.png" /></figure><p>Upon opening the NSIS installer with 7-zip, we see a couple of files contained within and a [NSIS].nsi file. The [NSIS].nsi file is the NSI script controlling what happens when the installer is clicked. We can inspect its contents using any text editor software.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/483/1*wQthOpsVn2XQJDihuvrEdQ.png" /><figcaption>Contents of the NSIS Installer</figcaption></figure><p>We want to focus on what the malware is doing here. It sets the output path to $INSTDIR, which was set to the $temp environment variable in an earlier part of the script. The “File” command then extracts each file to the output path. After the files are copied to the Windows temp directory, ExecWait executes the “eiwshq.exe” executable with “xnntpm.au3” as its argument. Both these files are found within the NSIS Installer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/537/1*ZRena3LZL9K5c_p3g5MinA.png" /><figcaption>NSIS Script — .oInit Function</figcaption></figure><h3>AutoIt v3</h3><p>AutoIt is another open-source project with powerful capabilities for automating the Windows GUI and general scripting. Malware authors widely use it in the infection execution chain due to its capabilities.</p><p>We quickly identify that “eiwshq.exe” turns out to be the AutoIt Application.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/402/1*BlaNvfgeZUMNiu51ZyjVTA.png" /></figure><p>The AutoIt script, “xnntpm.au3”, will be passed to the application for execution. At first glance, we can see that $A30diehlk seems to be an alias for the Chr function. The Chr function returns a character corresponding to its ASCII code, which is what we observe here, albeit another layer of obfuscation using the subtraction of two integers to prevent us from calculating the ascii code easily.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/816/1*FldhaOMgC07Zlz6X0g3n6Q.png" /><figcaption>Contents of “xnntpm.au3”</figcaption></figure><p>A simple python script will help make the integer calculations, convert the ascii code to its associated character and replace in-place. This should do the trick to make the code human-readable and allow us to understand what the AutoIt Script is programmed to do.</p><pre>import re</pre><pre>def readfilelines(filename):<br>    file1 = open(filename, &#39;r&#39;)<br>    Lines = file1.readlines()   <br>    return Lines<br>    <br>    <br>def deobfuscate_chr(txt):<br>    if txt == &quot;&quot;:<br>        return<br>        <br>    while True:<br>        current_char = re.search(&quot;Chr\(.*?\)&quot;,txt)<br>        if current_char == None:<br>            break<br>        else:<br>            current_char_arithmetic = chr(eval(current_char.group()[4:-1]))</pre><pre>try:<br>                txt = re.sub(&quot;Chr\(.*?\)&quot;, current_char_arithmetic, txt, 1)<br>            except:<br>                if current_char_arithmetic == &quot;\\&quot;:<br>                    txt = re.sub(&quot;Chr\(.*?\)&quot;, &quot;\\\\&quot;, txt, 1)<br>                else:<br>                    txt = re.sub(&quot;Chr\(.*?\)&quot;, &quot;&lt;ERROR&gt;&quot;, txt, 1)<br>    print(txt.replace(&quot; &amp; &quot;, &quot;&quot;))<br>    <br>def main():<br>    lines = readfilelines(&quot;xnntpm.au3&quot;)<br>    for line in lines:<br>        #replace all $A30diehlk with Chr<br>        line = line.replace(&quot;$A30diehlk&quot;,&quot;Chr&quot;)<br>        deobfuscate_chr(line)<br>    <br>main()</pre><p>After running the deobfuscation script, we can manually replace the rest of the alias to make the code look more readable.</p><p>We can see that the script essentially reads the “yecrxn.ke” file and removes all instances of “448782054140” in the file. It uses Windows API functions such as VirtualAlloc to create a region of memory, and EnumTimeFormatsA to execute the decoded shellcode in “yecrxn.ke”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/824/1*L5tk-RU2ctxtks6cgPChEA.png" /><figcaption>Deobfuscated Autoit Script</figcaption></figure><p>We can do the same now that we know what the AutoIt script was doing. Remove all instances of “448782054140” in the next stage shellcode file and then use a scripting language like python to read from the file and write its bytes into a binary format file. We can then use IDA or Ghidra to analyse the shellcode.</p><h3>Shellcode Analysis</h3><p>Once the shellcode file opens in IDA, we can reverse engineer the disassembly to make sense of what this stage of the payload is doing. Upon execution,we see that the shellcode reads the “zkhlbwzloru.xft” file, which is also contained in the NSIS installer. The filesize is checked, and the address pointer to the data buffer containing the contents of the file is passed to a decryption function using the EDI register.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wphF6J4YnaPh0bDYr4Lvrg.png" /><figcaption>Disassembly of the next stage shellcode, with comments added during reversing.</figcaption></figure><p>We identify a long looping subroutine that decrypts the “zkhlbwzloru.xft” file contents.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/811/1*VUVQy22dDEh3zJ6ZD00TBw.png" /></figure><p>Faced with this issue, we realised that there was no way we could decrypt and run the next stage code contained in “zkhlbwzloru.xft” manually. Unicorn to the rescue!</p><h3>Unicorn</h3><p><strong>Unicorn</strong> is a lightweight multi-platform, multi-architecture CPU emulator framework. In our case, we can use it to emulate the execution of the decryption subroutine and give us the decrypted final payload.</p><p>The python script below achieves that by first reading bytes from the “zkhlbwzloru.xft” file and storing it in an emulated memory region. The decryption function is then imported into our python script in its hex form, and will be the opcodes to be run by the emulated CPU.</p><p>Just as how it was in the actual shellcode, we will set the address pointer to the memory region containing the encrypted file data through the EDI register. The other registers used with the subroutine will also have to be properly initialised.</p><p>Once the decryption function from the malware runs in the emulator, the bytes from the original “zkhlbwzloru.xft” file will be decrypted. We will then use the binascii python module to format the decrypted data and write it into a new file named “zkhlbwzloru-decrypted.bytes”</p><pre>from unicorn import *<br>from unicorn.x86_const import *<br>import binascii</pre><pre>#read bytes from file<br>with open(&quot;zkhlbwzloru.xft&quot;, &quot;rb&quot;) as f:<br>    X86_ENCRYPTED_SHELLCODE = f.read()</pre><pre># code to be emulated (Decryption Function)<br>X86_CODE32 = b&quot;\x8A\x04\x1F\xB1\xB3\x2A\xC3\x34\x5F\xC0\xC0\x02\x2C\x56\x32\xC3\xF6\xD8\xC0\xC0\x02\x34\x5F\xF6\xD0\x2C\x4D\x32\xC3\x34\xB3\xC0\xC0\x03\x2A\xC3\x34\x54\xF6\xD8\x32\xC3\xC0\xC8\x02\x04\x3D\xC0\xC0\x02\x2C\x4E\xD0\xC0\x2A\xC8\xB0\x95\x32\xCB\x80\xE9\x43\x80\xF1\xE2\x02\xCB\xF6\xD1\x32\xCB\xD0\xC9\x2A\xCB\x80\xF1\xB2\x80\xC1\x1C\xF6\xD1\x80\xC1\x49\x80\xF1\x52\xC0\xC1\x02\x2A\xC1\xB1\xA7\x34\x02\x2C\x31\xD0\xC8\xF6\xD0\x2A\xC3\x32\xC3\x2A\xC8\x2A\xCB\x32\xCB\x80\xE9\x40\x32\xCB\xF6\xD1\x80\xE9\x7D\xC0\xC9\x02\xF6\xD9\xC0\xC9\x02\x80\xF1\x03\x80\xE9\x2A\xC0\xC1\x03\x88\x0C\x1F\x43\x3B\xDE\x0F\x82\x66\xFF\xFF\xFF&quot; # Decoding Function</pre><pre># memory address where emulation starts<br>ADDRESS = 0x1000000<br># memory address where encrypted shellcode starts<br>ADDRESS_EncryptedShellcodeBuffer = 0x2000000</pre><pre>print(&quot;Emulate i386 code&quot;)<br>try:<br>    # Initialize emulator in X86-32bit mode<br>    mu = Uc(UC_ARCH_X86, UC_MODE_32)</pre><pre># map 2MB memory for this emulation<br>    mu.mem_map(ADDRESS, 2 * 1024 * 1024)<br>    mu.mem_map(ADDRESS_EncryptedShellcodeBuffer, 2 * 1024 * 1024)<br>    <br>    # write machine code to be emulated to memory<br>    mu.mem_write(ADDRESS, X86_CODE32)<br>    mu.mem_write(ADDRESS_EncryptedShellcodeBuffer, X86_ENCRYPTED_SHELLCODE)<br>        <br>    # initialize machine registers<br>    mu.reg_write(UC_X86_REG_ECX, 0x0)<br>    mu.reg_write(UC_X86_REG_ESI, len(X86_ENCRYPTED_SHELLCODE))<br>    #mu.reg_write(UC_X86_REG_EDX, 0x7890)<br>    mu.reg_write(UC_X86_REG_EDI, ADDRESS_EncryptedShellcodeBuffer)</pre><pre># emulate code in infinite time &amp; unlimited instructions<br>    mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32))</pre><pre># now print out some registers<br>    print(&quot;Emulation done. Below is the CPU context&quot;)<br>    r_eax = mu.reg_read(UC_X86_REG_EAX)<br>    r_ecx = mu.reg_read(UC_X86_REG_ECX)<br>    r_edx = mu.reg_read(UC_X86_REG_EDX)<br>    r_ebx = mu.reg_read(UC_X86_REG_EBX)<br>    r_edi = mu.reg_read(UC_X86_REG_EDI)<br>    r_esi = mu.reg_read(UC_X86_REG_ESI)<br>    <br>    # read the decrypted shellcode into variable<br>    mem_EncryptedShellcodeBuffer = mu.mem_read(r_edi,len(X86_ENCRYPTED_SHELLCODE))</pre><pre># write the decrypted shellcode into a new file &quot;zkhlbwzloru-decrypted.bytes&quot;<br>    with open(&#39;zkhlbwzloru-decrypted.bytes&#39;, &#39;wb&#39;) as fout:<br>        for b in mem_EncryptedShellcodeBuffer:<br>            <br>            fout.write(<br>                binascii.unhexlify(&quot;{:02x}&quot;.format(b))<br>            )<br>            <br>    <br>except UcError as e:<br>    print(&quot;ERROR: %s&quot; % e)</pre><h3>The Final Boss — Formbook Infostealer</h3><p>After decrypting the final payload, we can continue to run our analysis, or do a simple hash check against OSINT sources such as VirusTotal.</p><p>Based on information from VirusTotal, the final payload, “zkhlbwzloru-decrypted.bytes” that we decrypted earlier is a Windows Portable Executable many AV vendors categorise as Formbook Infostealer malware.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/692/1*FVt3JHDOGjTg303Kp82FRw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*vtjlF_QXzrjvmJMQIZl3VA.png" /><figcaption>Virustotal Automated Dynamic Analysis</figcaption></figure><p>Some of the Network IOCs returned by VirusTotal</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*LsEr7aVJfR3Hv0EcODIrJQ.png" /></figure><p><strong>Link to the Formbook malware via VirusTotal can be accessed here:</strong> <a href="https://www.virustotal.com/gui/file/076f6d91e7dd2dcbcae3c16aae2438a058eb95458720e2ad3f6bde0f567134d0">https://www.virustotal.com/gui/file/076f6d91e7dd2dcbcae3c16aae2438a058eb95458720e2ad3f6bde0f567134d0</a></p><h3>Conclusion</h3><p>Multi-stage malware is challenging to analyse for cyber defence and incident response teams everywhere.</p><p>The tactics deployed by the malware author discussed in this post are by no means the only ones. There are countless methods and edge cases where Cyber Adversaries continually push boundaries and increase the sophistication level of malware throughout the infection chain.</p><p>The more we understand the evasion tactics and techniques used in malware, the better we can defend against them.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8779c5272ec7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/csg-govtech/meet-the-final-boss-clearing-each-level-of-multi-stage-malware-8779c5272ec7">Meet The Final Boss— Clearing each level of Multi-Stage Malware</a> was originally published in <a href="https://medium.com/csg-govtech">CSG @ GovTech Singapore</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>