> For the complete documentation index, see [llms.txt](https://0xsec.gitbook.io/0xsec/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://0xsec.gitbook.io/0xsec/malware-analysis/2026-01-01-agenttesla.md).

# AgentTesla Analysis

### Overview

It is a .NET based information stealer readily available to actors due to leaked builders. The malware is able to log keystrokes, can access the host's clipboard and crawls the disk for credentials or other valuable information. It has the capability to send information back to its C\&C via HTTP(S), SMTP, FTP, or towards a Telegram channel. It is also known as `AgenTesla, AgentTesla, Negasteal` and is utilized by threat actor group [SWEED](https://malpedia.caad.fkie.fraunhofer.de/actor/sweed).

SAMPLE - `sha256 458c119a3b7fd9a59a26a9f0af3f6490f131d5d5a6a01f293b728645df9f50cc` which is unpacked binary of packed binary `sha256 e1345fb134e131300974cc55448bdc0f547c13502a298496f0762e09fbe9be7a`.

### Static Analysis

As always I have downloaded the binary and done a basic search over [MalwareBazaar](https://bazaar.abuse.ch/) and [Virustotal](https://www.virustotal.com) and got info that it was marked by `19` vendors in malbazaar and `51` vendors by virustotal. So it is detectable by EDR's. It is using file name `tmpcih5l97u.exe`.

<figure><img src="/files/OTBBrq8r8Qgjx6G5q17S" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 1: Virustotal</em></p>

<figure><img src="/files/1MAf5WUoPPII2L48sxe8" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 2: Malware Bazaar</em></p>

Using DIE, I got to know that its written in C++ and compiled with [AutoIt](https://www.autoitscript.com/autoit3/docs/intro/compiler.htm) which means a `.au3, .a3x` script is compiled into `.exe`.

<figure><img src="/files/cYbnyW353iGK3zfQKzUX" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 3: Detect It Easy</em></p>

I have used a decompiler [myAutToExe](https://github.com/daovantrong/myAutToExe) which statically converts `.exe` to `.au3` there is dyamically way of doing same thing [ExeToAut](https://exe2aut.com/exe2aut-converter/). After running tool we have 2 files one is `.au3` script file and another is a bit intresting a file named `thixophobia`.

<figure><img src="/files/PJLFounFV4iCvMbSVOGh" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 4: Decompiled exe to au3</em></p>

I opened script file Notepad++ and started analysing, it was obfuscated but I will try to decode that. As we can see the first two custom functions, `Func QVWNSGO` converts string into numeric ASCII value with `Execute()` which hide function call to `Asc()` which is a common trick to bypass static scanners that look for specific function names. `Func CLMPQJQX` is primary decryption routine which takes 2 input likey a encrypted data and key , performing mathematical `MOD` operation which return original data.

<figure><img src="/files/VwNPUiX9xQx0S0IRvSv6" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 5: Custom Decryption function</em></p>

Next thing `FileInstall` which tells script to extract a file named `thixophobia` in the `temp` directory and the file is bundled inside the original `.exe` binary.

<figure><img src="/files/bwNKi4Mme2DkKRwcgWgs" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 6: Extraction of file to TEMP directory</em></p>

Then there is big encrypted blob `$ZRSHQDPEQ` which then calls `Func CLMPQJQX` with `$ZRSHQDPEQ` and key `Tc55s2WqM` as parameters which decryptes that encrypted data.

<figure><img src="/files/TW2gMy6F5qvKQD1lPuVP" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 7: Decryption of Encrypted data</em></p>

After decryption, it executes `DllCall` which decryptes Windows API at runtime using the same decryption function `CLMPQJQX` with the same key as above. And then uses [DllStructCreate](https://www.autoitscript.com/autoit3/docs/functions/DllStructCreate.htm) creates a space in memory that is the exact size of the decrypted data following with [DllStructSetData](https://www.autoitscript.com/autoit3/docs/functions/DllStructSetData.htm) which then puts decrypted data in created memory space and [DllCallAddress](https://www.autoitscript.com/autoit3/docs/functions/DllCallAddress.htm) jumps to specific memory location `$knjlyskn + 0x23b0` and starts executing the instructions there likely shellcode.

<figure><img src="/files/6sk1IuCo7b5lwhxjRnxJ" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 8: Execution of decrypted data</em></p>

Then there 2 for loops which are quite big and since shellcode was already executed so this is likey to be just junk codes to hamper analysis and evade antivirus detection as it includes size checks on random files, it forces the CPU to run thousands of useless loops, the malware hopes the antivirus will "give up" and mark the file as safe.

<figure><img src="/files/JyfgmRWDzA5K43Bw1UrX" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 9: Long For loops</em></p>

<figure><img src="/files/32zeiwo1rI2Wske9ZPeU" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 10: Size checks on random files and clicks on non-existing UI elements</em></p>

### Dynamic Analysis

On running the malware as intended it extracted the `thixophobia` inside `Temp` directory of the user.

<figure><img src="/files/B4AXGHXuM4rXbj0ksqUI" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 11: Extracts thixophobia in temp</em></p>

It then terminates the binary and starts another binary under name `RegSvcs.exe` in suspended state to evade detection under a legitimate name i.e. [Process Hollowing](https://www.portnox.com/cybersecurity-101/what-is-process-hollowing/). So then i used `pe-sieve` for that process and found `4` malicious intents of that binary although it has legitimate name.

<figure><img src="/files/gSth26uWw51b8oWSf3MZ" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 12: Executes RegSvcs and kills initial binary</em></p>

<figure><img src="/files/g0wDtT5ZOmZgjJYDnFLt" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 13: Scanning with pe-sieve</em></p>

It dumped out a `.exe` file and on analysing it in DIE and PeStudio.

<figure><img src="/files/xp6FYvhA0D4sRV9vchKk" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 14: Detect It Easy</em></p>

<figure><img src="/files/p4InzU8f83QeyvnqeI3O" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 15: PeStudio</em></p>

So I instantly gave to capa for analysis and returned some interesting results. Coming to the binary `RegSvcs.exe` I found that it is using modules like [vaultcli.dll](https://strontic.github.io/xcyclopedia/library/vaultcli.dll-0EFE7D82BC8B6B61E120B2B372764A79.html), [WbemComm.dll](https://learn.microsoft.com/en-us/windows/win32/wmisdk/about-wmi), `ws2_32.dll` a socket dll, this means that this binary is sending information's of the user to a remote host What I understand till now!.

<figure><img src="/files/X5fRbsW30vfSLpCumWPf" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 16: Obfuscated stack strings and many more..</em></p>

<figure><img src="/files/6fP3k0wewnqr8lHcrxjb" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 17: Interesting Modules</em></p>

```cpp
v3 = (const char *)sub_401650((int)&v36, v100); // Decrypts to "COR_ENABLE_PROFILING"
if ( getenv(v3) != (char *)&unk_41B2A0 )        // Compares result to "0"
```

Then I loaded it into IDA to analyze the binary further and first it checks some environment variable if it returns other than 0 i.e. [COR\_ENABLE\_PROFILING](https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/setting-up-a-profiling-environment) was set to 1 which means .NET [profiler](https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/profiling-overview) is being used.

```cpp
CurrentProcessId = GetCurrentProcessId();
Toolhelp32Snapshot = CreateToolhelp32Snapshot(8u, CurrentProcessId); //TH32CS_SNAPMODULE <==> 8u or 0x8
if ( Module32First(Toolhelp32Snapshot, &me) ) //checks first library
    {
      if ( !strcmp(me.szModule, (const char *)sub_401650((int)&v67, v98))
        || !strcmp(me.szModule, (const char *)sub_401650((int)&v36, v97)) )
      {
LABEL_4:
        CloseHandle(Toolhelp32Snapshot);
        return 0;
      }
      if ( Module32Next(Toolhelp32Snapshot, &me) )	//checks next library
      {
        while ( strcmp(me.szModule, (const char *)sub_401650((int)&v67, v98))
             && strcmp(me.szModule, (const char *)sub_401650((int)&v36, v97)) )
        {
          if ( !Module32Next(Toolhelp32Snapshot, &me) )
            goto LABEL_10;
        }
        goto LABEL_4;
      }
    }
LABEL_10:
    CloseHandle(Toolhelp32Snapshot);
```

Then it enters the module enumeration of itself to see list all of all modules that are being loaded into the memory of the loader prcess itself. It walks through each library loaded and checks some library that are not invited like `dbghelp.dll` it means a debugger is attached and it stops its execution.

```cpp
ModuleHandleA = GetModuleHandleA(0); //gets its own base address for loading payload resouce
v8 = (const CHAR *)sub_401650((int)&v36, v102);
ResourceA = FindResourceA(ModuleHandleA, v8, (LPCSTR)0xA); //Search for resource name inside file for RT_RCDATA
hResInfo = ResourceA;
Resource = LoadResource(ModuleHandleA, ResourceA); //loads resource into RAM
v59 = LockResource(Resource);
v11 = SizeofResource(ModuleHandleA, ResourceA);
v12 = (size_t *)malloc(v11); //create space of exact size of encrypted data
v13 = operator new(0x40022u); //allocates roughly 262kb
Src = v13; //address to junk table
```

Now when it confirms that no debugger/sandbox is attached, it then moves to its payload execution. It then decryptes the encrypted data from stack using `sub_401650` which performs a strings deobfuscation using hardcoded XOR keys, then search for that resource inside the file of constant `RT_RCDATA` which can hold raw binary data. It then loads the encrypted data into RAM and creates a empty space exactly size of encrypted data.

```cpp
sub_401300(v64){
  v16[7] = 122;	//construct a 20 byte array
  v16[8] = -35;
  v16[9] = -35;
  v16[11] = -35;
  v16[17] = 50;
  v16[4] = -32;
  v16[14] = -32;
  v16[19] = -32;
  qmemcpy(v16, "xa2z", 4);
  v16[6] = 1;
  v16[10] = 102;
  v16[13] = 51;
  v16[15] = 55;
  v16[16] = 116;
  v16[18] = 1;
  v16[12] = 53;
  v16[5] = 98
```

<figure><img src="/files/BCfffF6x24iXTeAZKj3L" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 18: Performs XOR and multiplication operations</em></p>

It calls a function `sub_401300` with address of junk table as parameter and this function construct a 20 byte array and first 4 byte with `xa2z` and rest with hardcoded value like `v16[7] = 122, v16[5] = 98` and performs some math operation and stops loop when exact 32 bytes of data is generated. Then it calls `sub_401050` which crunch down the new 32 byte of data into single value and stores it in `this[131104]` as it processes 4 bytes per turn and runs 8 times and performs `result = (byte_1 + byte_2 + byte_3 + byte_4 + result) % 256` for each loop.

```cpp
Size = SizeofResource(ModuleHandleA, hResInfo);
if ( (int)Size / 1024 > 0 )	//process in chunks
{
  Src = v59;
  rgsabound.cElements = (char *)v12 - (_BYTE *)v59;
  lpString = (LPCSTR)((int)Size / 1024);
  do
  {
    sub_401560(Src, 0x400u, (char *)Src + rgsabound.cElements);
    Src = (char *)Src + 1024;
    --lpString;
  }
  while ( lpString );
}
```

It then process the data in chunks of 1kb and decryptes them using `sub_401560` which is a **Two-Pass Substitution Cipher** now lets analyze this function.

```cpp
else
    {
      v5 = Size - 1;
      for ( i = 0; i < v5; ++i )
        //use them as row and col to lookup enc data and depends on next value(Forward lookup)
        a4[i] = this[256 * (unsigned __int8)a4[i] + 0x10000 + (unsigned __int8)a4[i + 1]]; 
      a4[Size - 1] = this[256 * (unsigned __int8)a4[Size - 1] + 0x10000 + (this[131104] ^ 0x55)]; //last byte
      v7 = Size - 1;
      if ( v5 >= 1 )
      {
        do
        {
          //backward lookup
          a4[v7] = this[256 * (unsigned __int8)a4[v7] + 0x10000 + (unsigned __int8)a4[v7 - 1]];
          --v7;
        }
        while ( v7 >= 1 );
      }
      *a4 = this[256 * (unsigned __int8)*a4 + 0x10000 + this[131104]]; //first byte
    }
```

At first, it copies the encrypted data in working buffer `a4` `memcpy(a4, Src, Size);` then enters a loop where it take `a4[i] and a4[i+1]` and use them as row and col and look for replacement value in encrypted data. Here, each byte's new value depends on next one and since the last byte doesn't have next byte so it XOR `this[131104]` with `0x55`. Now next it does opposite i.e. takes last byte and previous byte `a4[v7-1]` and perform similar lookup and first byte is processed with `this[131104]`, now its dependent to its neighbour from both side. This is more effective because changing any one byte of encrypted resource will lead in gibberish data.

```cpp
if ( (int)Size % 1024 > 0 )
      sub_401560((char *)v59 + Size - (int)Size % 1024, (int)Size % 1024, (char *)v12 + Size - (int)Size % 1024);
```

Then it handles the left over byte because mostly files aren't perfect multiple of `1024 bytes` and it calculate the bytes to handle by `Size - Remainder`. Once all data is decrypted it then clear out the memory area where encrypted data resides by `memset(v59, 0, Size);` because the decrypted is now stored in `v12` buffer and free up the resources.

```cpp
v14 = *v12;
lpString = (LPCSTR)malloc(v14);
v15 = SizeofResource(ModuleHandleA, hResInfo);
```

It then allocates memory space for newly decrypted payload and calls `sub_40AC60(lpString, &v94, v12 + 1, v15);`. Now lets anayze what this function performs, first it setup some variables

```cpp
v8 = a4;   // Total Resource Size
v7 = a3;   // Pointer to the Decrypted data
v9 = a1;   // Destination Buffer
v10 = *a2; // size of final payload
```

then calls `sub_407270(&v7, "1.2.3", 56);` which returns `sub_4071A0(a1, 15, a2, a3);` which is some what like a decompression function lets look into it.

```cpp
if ( !a3 || *a3 != 49 || a4 != 56 )
    return -6;
```

It checks if version number i.e `1.2.3` starts with `1` to make sure it uses same version on which it was made, it then sets up function like `malloc, free` not directly but by using own internal pointer to that functions and then allocates exactly `9520 bytes` in RAM. Then it performs some mathematical operation on value `15` and returns `sub_4070F0` which fills the created space with some values and return to `sub_40AC60`.

<figure><img src="/files/BlIzPmMnIMF0rGYGZGWw" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 19: Fills allocated space with some values</em></p>

Now it calls the decompression function and handles success and failure.

<figure><img src="/files/nfGFwapp6fU2ZF0Syjy1" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 20: Handles success and failure</em></p>

Now coming back to main function now it clear out the memory allocated for `v12` and skips first `14 bytes` so that it points exactly at payload `v17 = v16 + 14;`. Then it load some library since AgentTesla is a .NET based malware so something related to that might be `mscoree.dll` which is core library of .NET framework. And after it setup required things for .NET it then executes the decompressed malware in memory. The Agent Tesla now begins searching for Chrome passwords, Outlook emails, and FTP credentials.

<figure><img src="/files/ZpBFgNSB2FgJ5JYwYipT" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 21: Dump everything from running process</em></p>

Now lets see how can we extract the malware confiurations, first i ran the malware and let it execute for 30 seconds to complete all its needed things so that the final payload start executing keeping [SystemInformer](https://systeminformer.sourceforge.io/) opened. Then go to properties of the process and go in to memory tab and look for the `RWX` protection and there i saw a memory address mapped of `256kb` that caught my attention. Then i used [procdump](https://learn.microsoft.com/en-us/sysinternals/downloads/procdump) to dump out everything from running process. Now i ran strings on the dmp file and stored it in a new file.

<figure><img src="/files/h2RP4D8tUmRKqPna3I1F" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 22: Extracting important info</em></p>

<figure><img src="/files/oDgIEbAmENDvaccYpwAL" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 23: C2 domains and email with password</em></p>

Since its a infostealer I searched for things like Opera, Chrome and I got it, it was extracting each of these info including FTP, SFTP, logging keystrokes, system information, VNC passwords and so on. I also find the domain it is communicating to share these informations `mail.cottondreams.org` and email addresses `kc@cottondreams.org, admin@cottondreams.org` and a likely a password `PayDay2025`.

<figure><img src="/files/dTYLYLi5nolhxWeGaZwV" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 24: More Informations</em></p>

<figure><img src="/files/FLbnMKERns1lUX6KmauS" alt=""><figcaption></figcaption></figure>

<p align="center"><em>Figure 25: Exracting info from different browsers</em></p>

### IOCs

SMTP Server - `mail.cottondreams.org`&#x20;

Sender Mail - `kc@cottondreams.org`&#x20;

Receiver Mail - `admin@cottondreams.org`&#x20;

Port - `587`&#x20;

SMTP Auth Password - `PayDay2025`


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://0xsec.gitbook.io/0xsec/malware-analysis/2026-01-01-agenttesla.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
