Poison Ivy RAT Configuration & Communications
[email protected] [email protected] 10-21-2013
1
Poison Ivy RAT: Configuration & Communications
CONTENTS INTRODUCTION
2
I. RAT CONFIGURATION
3
X. Code injection
3
A. STAGE1
3
B. STAGE2/STAGE3
5
II. COMMUNICATIONS
7
A. Startup process
7
B. Standard communication
8
III. FINDING POISON IVY
18
A. On the machine
18
B. In the network
19
CONCLUSION
20
REFERENCES
21
APPENDIX
22
A. Structs / enums
22
B. Scanning running processes memory
25
C. Key-dependent snort rules generation script
32
2
Poison Ivy RAT: Configuration & Communications
INTRODUCTION Actually, there are many papers dealing with the Poison Ivy rat. We won’t describe the whole Poison Ivy stuff, FireEye labs released a great one [1] (really worth the reading). This paper is just a documentation we made after an analysis and we wanted to share. It is mainly focused on how it is possible to: Locate and parse the Poison Ivy configuration Decrypt and parse Poison Ivy communications We did not perform the whole reverse-engineering process of Poison Ivy, and we just put here what we found useful*: for instance, several commands and configuration values are not documented. However, it may be useful for some people**. *ok, this is just an excuse: I’m lazy. ** other lazy people :]
3
Poison Ivy RAT: Configuration & Communications
I. RAT CONFIGURATION X. Code injection Poison Ivy can perform various code injections in its process. Depending on the injection stage, the configuration can be stored in different formats. We will use the STAGEX descriptor to identify the injection step:
STAGE1: Poison Ivy is not started (binary file), or has just been launched (1st process)
STAGE2: Poison Ivy has injected machine code (persistence) into the explorer.exe process
STAGE3: Poison Ivy has injected machine code (main payload) into a running process (default browser, etc.)
These injections are performed using WriteProcessMemory/CreateRemoteThread technique. Depending on the rat configuration, Poison Ivy may also not perform any kind of injection and run in its startup process. The STAGE1 configuration differs with the STAGE2 and STAGE3 configurations. A. STAGE1 1. Location The configuration is directly hardcoded on the binary file itself: it is possible to extract it from this file or from its memory. If it is packed, this process can be paused after unpacking (i.e. placing a breakpoint on the CreateMutexA function, which is used at startup to assess the system infection status). The configuration can be found 0x1FB bytes after this pattern: 5E81C6FB0100008DBD84F0FFFF0FB7060FB74E0283C60403C751515650FF952DF1FFFF5903F1 66833E0075E183C6028975F866833E0074110FB7060FB74E0283
5e 81c6 fb010000 [...]
POP ESI ADD ESI, 0x1FB
; loads config addr
2. Parsing The configuration is a list of options which describe parameters. Any configuration option follows this structure: typedef struct _config_option{ WORD parameter_id; WORD parameter_length; BYTE parameter[]; } config_option;
//option unique identifier
4
Poison Ivy RAT: Configuration & Communications
Relevant configuration options are: ID 0x0f04
TYPE ANSI string
DESCRIPTION Active Setup value name (always « Stubpath »)
0x1804
ANSI string
Default browser path registry key
0x5604
ANSI string
Active Setup registry key
0xFA0A
ANSI string
RAT identifier
0xF90B
ANSI string
GROUP identifier
0x9001
c2_server_struct Connection information (proxy or C&C servers: if no proxy is set, the C&C servers will be indicated with this flag. If both are set, the 0xC502 id will be used for C&C information)
0xC502
c2_server_struct Connection information (C&C servers)
0x8C01
DWORD
C&C servers count -1
0xC102
DWORD
Proxy servers count -1
0x4501
ANSI string
Encryption key
0xFB03
ANSI string
Mutex name
0xF40A
BYTE
Hijack proxy flag
0xF50A
BYTE
Persistant proxy hijack flag
0x2D01
ANSI string
RAT filename
0xF703
BYTE
Installation folder identifier 0x01: %systemroot% 0x02: %systemroot%\system32
0xF903
BYTE
Persistance flag
0x4204
ANSI string
Custom injection process name
0x4104
BYTE
Custom injection process name flag
0x120E
ANSI string
HKLM/HKCU registry value name
0x090D
BYTE
HKLM/HKCU registry key persistance flag
0x120D
BYTE
ADS feature flag
0x080D
BYTE
Default browser injection flag
0xFA03
BYTE
Keylogger feature flag
0xF603
BYTE
ActiveX key flag
0x6501
ASCII STRING
ActiveX key name
0xF803
BYTE
Auto-remove dropper flag
0x0000
N/A
Config end
5
Poison Ivy RAT: Configuration & Communications
B. STAGE2/STAGE3 1. Location In the STAGE2 and STAGE3 states, the configuration is copied into a new memory region, along with a custom import address table. This pattern can be identified with the following information: Stage
Pattern
STAGE2
RWE memory zone starting with : • 33 null bytes • kernel32.dll:VirtualAlloc virtual address • kernel32.dll:VirtualFree virtual address • kernel32.dll:CreateThread virtual address
STAGE3
RWE memory zone starting with : • 1 null byte • ws2_32.dll:socket virtual address • ws2_32.dll:connect virtual address • ws2_32.dll:closesocket virtual address • ws2_32.dll:send virtual address • ws2_32.dll:recv virtual address
NB: thanks to Windows ASLR, these virtual addresses can be calculated in a separate process to generate a pattern to scan for. I think static values (such as the ActiveSetup registry key path) can also be used to locate the configuration, but they can be modified quite easily if they are not used.
6
Poison Ivy RAT: Configuration & Communications
2. Parsing The STAGE2/STAGE3 processes in-memory configuration is mapped into a 0x1000 RWE memory region and the information is located at fixed offsets from the start. Relevant configuration information is (offsets starting at the end of import table): OFFSET
TYPE
DESCRIPTION
0x012D 0x0144
→ char[]
RAT filename
0x0145 0x0164
→ char[]
Encryption key
0x0165 0x018A
→ char[]
Active Setup registry key name
0x0190 0x02BF
→ n* c2_server_struct
Connexion information (proxy servers)
0x02C5 0x03EF
→ n* c2_server_struct
Connexion information (C&C servers)
0x03FB 0x0403
→ char[]
Mutex name
0x0442 0x0455
→ char[]
Custom injection process name
0x04B3
Null-terminated ANSI string
Active Setup registry key complete path
0x05B2
Null-terminated ANSI string
RAT dropper file path (only if first time launched)
0x06B1
Null-terminated ANSI string
RAT file path
0x0AfA
Null-terminated ANSI string
RAT identifier
0x0BF9
Null-terminated ANSI string
GROUP identifier
0x0E12
Null-terminated ANSI string
HKLM/HKCU registry value name
There is other stuff in these memory regions; I just focused on most important configuration values (and easily recognizable ones ;]). The c2_server_struct structure is described in appendix A.
7
Poison Ivy RAT: Configuration & Communications
II. COMMUNICATIONS The communication process can be divided into 2 parts: startup process and standard communications. First of all, keep in mind that the cryptographic algorithm used is Camellia ECB. It is a symmetric encryption algorithm which works on 128 bits blobs (0x10 bytes) with a 256 bits key (0x00-padded). A. Startup process Basically, the handshake process follows this scheme: 1) 2) 3) 4)
Handshake Receive and execute PAYLOAD#1 Receive and execute PAYLOAD#2 in a new thread Communication start
1. Handshake The RAT generates 0x100 pseudo-random bytes and sends them to the C&C. Both of them encrypt the blob with their Camellia key, and the C&C sends back the encrypted blob to the RAT. The RAT verifies the response is valid and begins communication. 2. Startup payloads After the handshake step is validated, the C&C sends 2 payloads to the RAT. Both of them follow this structure: typedef struct _startup_payload { DWORD encrypted_payload_length ; BYTE encrypted_payload[] ; } startup_payload;
For instance, payload#1 length is always 0x15D4 bytes: it begins with the 0x000015D0 value. Once the payload is received, it is decrypted and stored into a RWE memory zone, and executed as follows: •
payload( config_address, payload_address )
Payload#2 is loaded by payload#1 into memory and executed into a new thread. Payload#1 resolves payload#2 dependencies before executing it. Finally, payload#1 is then dedicated to the communications handling: it receives and executes the C&C commands.
8
Poison Ivy RAT: Configuration & Communications
B. Standard communication 1. Basic structuration Any command received is encrypted and can be compressed. To allow the RAT interpreting the data, the commands comports an encrypted not compressed 0x20 bytes header. typedef struct _payload { BYTE header[0x20] ; BYTE data[] ; // can be compressed... or not }payload;
Depending on header information, data can be compressed. The compression is performed using the RtlCompressBuffer & RtlDecompressBuffer functions using the COMPRESSION_FORMAT_LZNT1 algorithm identifier. The rat uses the same formalism to send back responses to the C&C. Headers comports command/stream identifiers, which allows the C&C to link responses to past commands. 2. Header parsing The header struct is: typedef struct _header { DWORD command_id; DWORD stream_id; DWORD dwDataLen; DWORD dwRealDataLen; DWORD dwUncompressedDataLen; DWORD dwTotalStreamSize; DWORD padding1; DWORD padding2; }header;
// // // // // //
command identifier stream identifier (thanx FE) data length (to be received) data length (to be received) uncompressed data length total command stream length
If dwUncompressedDataLen >= dwRealDataLen, data is compressed and must be decompressed before execution. Remark: [2] reports a stack overflow on this implementation: the allocated memory for the data is a 0x8000 bytes buffer. If the data (and dwDataLen parameter) is bigger, the overflow is triggered. The source of this vulnerability can either be a mistake from the rat creator or a backdoor…
9
Poison Ivy RAT: Configuration & Communications
3. Command parsing Enumerating the different values of command_id allows guessing which command is performed in the data field. Once decrypted (and decompressed), the data can follow 2 schemes: • If the command has never been issued before, the C&C will send a shellcode + parameters. The shellcode will be executed in a dedicated thread. In this case, data 1st DWORD is not 0x00000000 (in fact, it is the shellcode length in most cases). • If the command has been issued at least one time before, the C&C will just send the parameters. In that case, data 1st DWORD value is 0x00000000. NB: in several cases, the Shellcode command is never issued as the command is natively supported by the RAT/payload#1. You will find in the following tables each command details (id, parameters, etc.). We just documented most important commands and responses (=> few :]) for DFIR purpose. If anyone wants to complete these tables without reverse-engineering the whole client, this can be done in at least 2 ways (we used both of them):
Placing an inline hook on the encryption (or decryption) routine, and log input (or output)
Writing a basic Poison Ivy parser, getting in a MITM position (2 VMs), giving it the traffic and displaying the data
10
Poison Ivy RAT: Configuration & Communications
4. Commands
ID (header) Hex value
Functionalities Description
Parameters(data) Offset
type
Shellcode + command data
Offset
type
Command data (without shellcode)
Offset
type
Response data
The following word indicates that other parameters immediately follow the previous one. Structs & enum are descripted in appendix A.
Filesystem features ID (header) 0x02
0x04
Functionalities Change directory + list directory
File download
Parameters(data) 0xFD3
char[]
path
0x0B
char[]
path
0x00
N * resp_0x2
Files information
0x12
char[]
Filename
0x00 0x30 0x34
char[] DWORD BYTE
Filename File length File content
0x03
Refresh
N/A
0x05
Search file
N/A
0x2A
Stop searching file
N/A
0x06
Upload file command
0x08
char[]
File path
File upload
0x00
BYTE[]
File content
(immediately following the upload file command)
0x07
Create directory
0x0A
char[]
Directory path / name
0x09
Rename file or directory
0x105 following
pi_string char[]
Filename New name
0x0A following
pi_string char[]
filename new name
0xC7
pi_string
filename
0x0A
pi_string
filename
char[]
path
0x0B
char[]
path
0x337
char[]
path
0x0A
0x0B 0x0C
Delete file (immediately followed by a refresh command)
Refresh current directory 0xFD3 Delete directory
11
Poison Ivy RAT: Configuration & Communications
0x3E 0x42
Wipe file Download directory (followed by 0x42 and 0x04 commands)
0x0A
char[]
path
0x328
pi_string
filename
0x0B
pi_string
filename
0x686
pi_string
dirname
0x0A
pi_string
dirname
Running processes features ID (header)
Functionalities
Parameters(data)
0x14
List running processes
N/A
0x15
Kill process (followed by a 0xC0 0x14 command) 0x0a
WORD
Process ID
WORD
Process ID
0x48
List running processes (without modules information)
N/A
0x54
Suspend/resume process 0x133 WORD
Process ID
0x0B
WORD
Process ID
Command prompt features ID (header)
Functionalities
0x16
Start command prompt
0x17
Send command
0x18
Close command prompt
Parameters(data) N/A 0x0A
char[]
Command (DOS) N/A
Taking screenshots ID (header)
Functionalities
0x19
Take screenshot
0x0F
Keystroke injection
Parameters(data) N/A 0x206 0x20A
DWORD BYTE[]
Window HANDLE Injected data (keystroke)
0x0A 0x0E
DWORD BYTE[]
Window HANDLE Injected data (keystroke)
12
Poison Ivy RAT: Configuration & Communications
Registry features: registry keys ID (header) 0x1E
0x1F
Functionalities List registry key subkeys
List registry key values
Parameters(data) 0x30D 0x311
registry_hive char[]
Registry hive Key path
0x0A 0x0E
registry_hive char[]
Registry hive Key path
0x30D 0x311
registry_hive char[]
Registry hive Key path
0x0A 0x0E
registry_hive char[]
Registry hive Key path
0x20
Delete registry key
0x0A 0x0E
registry_hive char[]
Registry hive Key path
0x21
Rename registry key
0xA9F 0xAA3 following following
registry_hive pi_string pi_string char[]
Registry hive Key path Key name New name
0x0A 0x0E following following
registry_hive pi_string pi_string char[]
Registry hive Key path Key name New name
0x0A 0x0E
registry_hive pi_string
Registry hive Key path
0x25
Create registry key
Registry features: registry values ID (header) 0x22
0x23
Functionalities Create registry value
Rename registry value
Parameters(data) 0x16C 0x170 0x174 following following
registry_hive registry_value_type pi_string pi_string BYTE[]
Registry hive Value type Key name Value name Value
0x0A 0x0E 0x12 following following
registry_hive registry_value_type pi_string pi_string BYTE[]
Registry hive Value type Key name Value name Value
0x1FD following following following
registry_hive pi_string pi_string pi_string
Registry hive Key name Value name New name
0x0A following following
registry_hive pi_string pi_string
Registry hive Key name Value name
13
Poison Ivy RAT: Configuration & Communications
following 0x24
Delete registry value
pi_string
New name
0x155 0x159 following following
registry_hive pi_string pi_string
Registry hive Key name Value name
0x0A 0x0E following
registry_hive pi_string pi_string
Registry hive Key name Value name
0x36
Search registry
N/A
0x37
Stop registry search
N/A
Services features ID (header)
Functionalities
Parameters(data)
0x2B
List installed services
0x2C
start / stop / uninstall 0x15F service 0x160
0x2E
0x30
install service
edit service
N/A service_action pi_string
Command service name
0x0A 0x0B
service_action pi_string
Command service name
0x2B3 following following following following following following following
pi_string pi_string pi_string pi_string pi_string service_type service_start service_start_now
service name display name file path run as description type start mode start now
0x0A following following following following following following following
pi_string pi_string pi_string pi_string pi_string service_type service_start service_start_now
service name display name file path run as description type start mode start now
0x248 following following following following following following following
pi_string pi_string pi_string pi_string pi_string service_type service_start service_start_now
service name display name file path run as description type start mode start now
0x0A
pi_string
service name
14
Poison Ivy RAT: Configuration & Communications
following following following following following following following
pi_string pi_string pi_string pi_string service_type service_start service_start_now
display name file path run as description type start mode start now
Network connections features ID (header)
Functionalities
Parameters(data)
0x38
List network connections
N/A
0x3D
Kill network connection
0x61 0x69
ip_struct ip_struct
Source IP + port Destination IP + port
0x0E 0x16
ip_struct ip_struct
Source IP + port Destination IP + port
Credentials dump features ID (header)
Functionalities
Parameters(data)
0x3C
Dump standard credentials
N/A
0x5A
Dump WiFi credentials
N/A
0x5B
Dump NT/NTLM hashes
0x00
N* hash_struct
Usernames + hashes
Installed software features ID (header) 0x58 0x59
Functionalities
Parameters(data)
List software Uninstall software or uninstall information retrieval (data length is shorter)
N/A 0x3D6
pi_string
Application name
0x0E
pi_string
Application name
Windows features ID (header)
Functionalities
0x0D
List windows
0x0E
Window screenshot
0x0F
Parameters(data) N/A 0x1CC
DWORD
Handle
0x0A
DWORD
Handle
DWORD BYTE[]
Handle Keystrokes
Single-window keystrokes 0x206 0x20A → end
15
Poison Ivy RAT: Configuration & Communications
0x10
0x11
Window operation
Close window
0x0A 0x0E → end
DWORD BYTE[]
Handle Keystrokes
0x67 0x68
window_action DWORD
Operation Handle
0x0A 0x0B
window_action DWORD
Operation Handle
0x54
DWORD
Handle
0x0A
DWORD
Handle
Miscellaneous features ID (header)
Functionalities
Parameters(data)
0x27
Keep-alive
N/A
0x4E
Payload #1
N/A
0x00
RAT information collection (startup)
N/A
0x29
Query system information
0x49
Download keylogger dump
N/A
0x53
Query system information (startup)
N/A
0x08
Execute file
0x55 0x61
Change RAT id Resources monitoring
0x00 following following following following following following following following following
pi_string pi_string pi_string c2_server_struct[5] pi_string pi_string pi_string pi_string pi_string pi_string
Machine name Domain CPU servers Peristance key Key value name Malware filename Malware running undefined Mutex name
0x14B pi_string following pi_string following BYTE
File path Command-line arguments Hidden (0x01 = Yes)
0x0A pi_string following pi_string following BYTE
File path Command-line arguments Hidden (0x01 = Yes)
0x18A
pi_string
New Identifier
0x0B
pi_string
New Identifier N/A
16
Poison Ivy RAT: Configuration & Communications
Proxies & gateways features ID (header) 0x43
Functionalities Create SOCKS4 proxy
Parameters(data) 0xE3D following following following following
WORD pi_string pi_string pi_string WORD
Listening port username Src IP Dst IP Dst port
0x0A following following following following
WORD pi_string pi_string pi_string WORD
Listening port username Src IP Dst IP Dst port
0x44
Get proxy list
0x45
Get GW information
0x00
DWORD
Identifier
0x46
Stop proxy
0xDF
DWORD
Identifier
0x0A
DWORD
Identifier
0xA29 following following following following following following
WORD BYTE pi_string pi_string pi_string pi_string WORD
Listening port Auth ? (1=Yes) username password (if auth) Src IP Dst IP port
0x0A following following following following following following
WORD BYTE pi_string pi_string pi_string pi_string WORD
Listening port Auth ? (1=Yes) username password (if auth) Src IP Dst IP port
0x47
Create SOCKS5 proxy
N/A
0x60
Get SOCKS4 proxy information
0x00
DWORD
Identifier
0x4C
Create gateway
0x377 following following following
WORD pi_string WORD pi_string
Listening port Dst IP Dst port Src filter
0x0A following following following
WORD pi_string WORD pi_string
Listening port Dst IP Dst port Src filter
17
Poison Ivy RAT: Configuration & Communications
Microphone & webcam features ID (header)
Functionalities
Parameters(data)
0x5C
Start recording
N/A
0x5D
Stop recording
N/A
0x34
Start webcam capture
N/A
18
Poison Ivy RAT: Configuration & Communications
III. FINDING POISON IVY With all this information, we tried to identify how generic Poison Ivy malwares can be found on a system or on the network.
A. On the machine 1. System There are various ways to find a Poison Ivy rat on a Windows system. As Poison Ivy may use Alternate Data Streams to hide itself, this behavior can be used against it, by searching: •
ADS attached to the %systemroot% or %systemroot%\system32 folders
• ADS in the startup keys (HKLM/HKU Run keys, Active Setup keys), easily identified by the “:” character •
ADS in the prefetch folder: the prefetcher will create SYSTEM32 or WINDOWS prefetch files.
Also, Poison Ivy stores its identifier in a separate file, in its own folder, which has the same name without extension. This can also be used to spot suspicious files. As Poison Ivy may inject itself in the default browser, a running browser process without any window may reveal an infection. Finally, one of these Poison Ivy default configuration values in a file may also reveal a nonpacked Poison Ivy binary: "\x18\x04\x28\x00SOFTWARE\\Classes\\http\\shell\\open\\command" "\x04\x35\x00\x53Software\\Microsoft\\Active Setup\\Installed Components\\" "\x0f\x04\x08\x00StubPath" 5E81C6FB0100008DBD84F0FFFF0FB7060FB74E0283C60403C751515650FF952DF1FFFF5903F1668 33E0075E183C6028975F866833E0074110FB7060FB74E0283
19
Poison Ivy RAT: Configuration & Communications
2. Memory As Poison Ivy injects various data in process memory, several markers can be used to identify this malware (performing a basic memory scan on RWE memory regions, for instance). Signature
Injection stage
"\x18\x04\x28\x00SOFTWARE\\Classes\\http\\shell\\open\\command"
STAGE1 (config.)
"\x04\x35\x00\x53Software\\Microsoft\\Active Setup\\Installed Components\\"
STAGE1 (config.)
"\x0f\x04\x08\x00StubPath"
STAGE1 (config.)
5E81C6FB0100008DBD84F0FFFF0FB7060FB74E0283C60403C751515650FF952D STAGE1 F1FFFF5903F166833E0075E183C6028975F866833E0074110FB7060FB74E0283 (config. loader
payload) 558BEC81C430FAFFFF8B75088D86FB030000506A006A00FF96850000008986C5 STAGE2/STAGE3 080000FF96890000003DB70000007504C9C20400568D866B090000508D864501 (shellcode)
If you ever plan to build your own memory scan tool to search processes memory for signatures, we put a very basic implementation in Appendix B. B. In the network Except using ET signatures (mostly encryption key-based [3], [4]), several behaviors can be used to detect the malware. 1. Handshake-based signatures To detect a successful handshake: •
$HOME_NET 0x100 bytes $EXTERNAL_NET (challenge)
•
$EXTERNAL_NET 0x100 bytes $HOME_NET (response)
•
$EXTERNAL_NET 0x15D4 bytes beginning with 0x000015D0 $HOME_NET (payload#1)
2. Keep-alive-based signatures The keep-alive is cyphered and its content depends to the encryption key. Thus this is not possible to create content-based signatures key-independent. However, the decrypted keep-alive content never changes and it is possible to detect send/receive couples of identical 0x30 binary data. In addition, as the keep-alive header never changes, encryption key can be bruteforced (just like malware.lu guys [5] did, without sending anything to the C&C server). Knowing the encryption key, it is also possible to create key-dependent signatures (cf. ET signatures). To generate those signatures, you must first find a constant plain text aligned with the camellia block size (0x10). In a spirit of cooperation, we chose the same block as the previous signature writers (i.e the second 0x10 block of the protocol frame header). See Appendix C.
20
Poison Ivy RAT: Configuration & Communications
CONCLUSION Uh… well, we hope this paper will be useful (if so, we love beer) to malware researchers / incident responders who face this malware and will upset Poison Ivy users (who said they will migrate to PlugX?). Thanks for the reading :]
21
Poison Ivy RAT: Configuration & Communications
REFERENCES [1] FireEye analysis (really complete): http://www.fireeye.com/resources/pdfs/fireeye-poison-ivy-report.pdf [2] PoisonIvy stack overflow vulnerability: http://badishi.com/own-and-you-shall-be-owned/ http://badishi.com/poison-ivy-exploit-metasploit-module/ http://badishi.com/decrypting-poison-ivys-communication-using-code-injection-and-dll-proxies/ http://badishi.com/initial-analysis-of-poison-ivy/ [3] PoisonIvy emerging threats rules update (key-based, linked to the FireEye analysis report): http://www.emergingthreats.net/2013/08/21/daily-ruleset-update-summary-08212013/ [4] Emerging threats key-based signature example (“MenuPass” key):
http://doc.emergingthreats.net/bin/view/Main/2013922 [5] Malware.lu PoisonIvy analysis, with the encryption key bruteforcing attack: http://www.malware.lu/Pro/RAP002_APT1_Technical_backstage.1.0.pdf Official website: http://www.poisonivy-rat.com/
22
Poison Ivy RAT: Configuration & Communications
APPENDIX A. Structs / enums typedef struct _pi_string { BYTE byStrLen; // str size, in bytes char str[]; // not null-terminated } pi_string; typedef struct _resp_0x2 { pi_string name[]; BYTE misc_info[0x18]; } resp_0x2; typedef enum _service_action { Uninstall = 0x00, Stop, Start } service_action;
// file/dir name // various information (mactime, type…)
// size : USHORT
typedef enum _service_type // size : ULONG { device_driver = 0x00000001, fs_driver = 0x00000002, std_driver = 0x00000010, shared_driver = 0x00000020 }service_type;
typedef enum _service_start { automatic = 0x00000002, manual = 0x00000003, disabled = 0x00000004 } service_start;
// size : ULONG
typedef enum _service_start_now // size : USHORT { no = 0x00, yes = 0x01 } service_start;
23
Poison Ivy RAT: Configuration & Communications
typedef enum _registry_hive { HKEY_CLASSES_ROOT = 0x00000000, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_WTF, HKEY_CURRENT_CONFIG } registry_hive;
// size : ULONG
typedef enum _registry_value_type { REG_SZ = 0x01, REG_EXPAND_SZ, REG_BINARY, REG_DWORD, REG_WTF1, REG_WTF2, REG_MULTI_SZ } registry_value_type;
// size : USHORT
typedef struct { BYTE BYTE BYTE BYTE WORD port; } ip_struct;
_ip_struct ip1; ip2; ip3; ip4;
typedef struct _hash_struct { BYTE hash_nt[0x10]; BYTE hash_lm[0x10]; DWORD username_length; // username size in bytes CHAR username[]; // may contain non-printable chars } hash_struct ; typedef enum _window_action { maximize = 0x03, show = 0x05, hide = 0x00, minimize = 0x06 } window_action; typedef struct _header { DWORD command_id; DWORD stream_id; DWORD dwDataLen; DWORD dwRealDataLen; DWORD dwUncompressedDataLen; DWORD dwTotalStreamSize; DWORD padding1; DWORD padding2; }
// size : USHORT
// // // // // //
command identifier stream identifier (thanx FE) data length (to be received) data length (to be received) uncompressed data length total command stream length
24
Poison Ivy RAT: Configuration & Communications typedef struct _payload_startup { DWORD dwDataLen; // data size, in bytes BYTE data[]; // encrypted payload } payload_startup; #define c_and_c_server #define socks4_proxy_server #define http_proxy_server
0x00 0x01 0x02
typedef struct _c2_server_struct { BYTE btFqdnLen; // fqdn size, in bytes CHAR fqdn[]; // C&C fqdn / IP BYTE server_type; // server type WORD port; } c2_server_struct; typedef struct _config_option { WORD parameter_id; WORD parameter_length; BYTE parameter[]; } config_option; typedef struct _payload { BYTE header[0x20] ; BYTE data[] ; // can be compressed... or not } payload;
25
Poison Ivy RAT: Configuration & Communications
B. Scanning running processes memory For anyone wanting to automate scanning running processes memory for patterns, this can be done following these steps: 1. Get the SE_DEBUG_NAME privilege 2. Loop into the running processes list (CreateToolhelp32Snapshot / Process32First /Process32Next) 3. Get memory regions information with VirtualQueryEx 4. For each memory region copy it in your process memory using the ReadProcessMemory function 5. Search for your pattern Poison Ivy p0c: #ifdef UNICODE #undef UNICODE #endif #include #include #include // memory region typedef struct _memoryRegion { ULONG baseAddress; ULONG size; ULONG type; ULONG protect; ULONG state; PVOID blink; }memoryRegion, *pmemoryRegion; // memory pattern typedef struct _mempattern { ULONG protect; WORD patternId; ULONG patternlen; PVOID pattern; PVOID flink; }mempattern, *pmempattern;
// // // // // //
base address size memory type protection (mask!) memory state next region
// memory protection // pattern unique identifier // pattern length // BYTE[pattern length] pattern
DWORD elevate_access_rights(); DWORD __fastcall memscan(_In_ pmempattern mempatterns); DWORD __fastcall processMemScan(_In_ ULONG pid, _In_ pmempattern mempatterns); PVOID __stdcall searchMem(_In_ PVOID needle,_In_ ULONG needleLen,_In_ PVOID memory,_In_ ULONG memoryLen); // main => scan for PIVY :] int main(int argc, char** argv) { pmempattern list, current; BYTE pattern1[]={
26
Poison Ivy RAT: Configuration & Communications 0x5E,0x81,0xC6,0xFB,0x01,0x00,0x00,0x8D, 0xBD,0x84,0xF0,0xFF,0xFF,0x0F,0xB7,0x06, 0x0F,0xB7,0x4E,0x02,0x83,0xC6,0x04,0x03, 0xC7,0x51,0x51,0x56,0x50,0xFF,0x95,0x2D, 0xF1,0xFF,0xFF,0x59,0x03,0xF1,0x66,0x83, 0x3E,0x00,0x75,0xE1,0x83,0xC6,0x02,0x89, 0x75,0xF8,0x66,0x83,0x3E,0x00,0x74,0x11, 0x0F,0xB7,0x06,0x0F,0xB7,0x4E,0x02,0x83 }; BYTE pattern2[]={ 0x55,0x8B,0xEC,0x81,0xC4,0x30,0xFA,0xFF, 0xFF,0x8B,0x75,0x08,0x8D,0x86,0xFB,0x03, 0x00,0x00,0x50,0x6A,0x00,0x6A,0x00,0xFF, 0x96,0x85,0x00,0x00,0x00,0x89,0x86,0xC5, 0x08,0x00,0x00,0xFF,0x96,0x89,0x00,0x00, 0x00,0x3D,0xB7,0x00,0x00,0x00,0x75,0x04, 0xC9,0xC2,0x04,0x00,0x56,0x8D,0x86,0x6B, 0x09,0x00,0x00,0x50,0x8D,0x86,0x45,0x01 }; char pattern3[]="\x18\x04\x28\x00SOFTWARE\\Classes\\http\\shell\\open\\command"; char pattern4[]="\x04\x35\x00\x53Software\\Microsoft\\Active Setup\\Installed Components\\"; char pattern5[]="\x0f\x04\x08\x00StubPath"; current = NULL; list = (pmempattern)malloc(sizeof(mempattern)); list->patternId = 1; list->pattern = pattern1; list->patternlen = sizeof(pattern1); list->protect = PAGE_EXECUTE_READWRITE; list->flink = current; current = list; list = (pmempattern)malloc(sizeof(mempattern)); list->patternId = 2; list->pattern = pattern2; list->patternlen = sizeof(pattern2); list->protect = PAGE_EXECUTE_READWRITE; list->flink = current; current = list; list = (pmempattern)malloc(sizeof(mempattern)); list->patternId = 3; list->pattern = pattern3; list->patternlen = sizeof(pattern3)-1; list->protect = PAGE_EXECUTE_READWRITE; list->flink = current; current = list; list = (pmempattern)malloc(sizeof(mempattern)); list->patternId = 4; list->pattern = pattern4; list->patternlen = sizeof(pattern4)-1; list->protect = PAGE_EXECUTE_READWRITE; list->flink = current; current = list; list = (pmempattern)malloc(sizeof(mempattern)); list->patternId = 5; list->pattern = pattern5; list->patternlen = sizeof(pattern5)-1; list->protect = PAGE_EXECUTE_READWRITE; list->flink = current;
27
Poison Ivy RAT: Configuration & Communications
printf("Find PIVY p0c\n"); elevate_access_rights(); if(memscan(list)==ERROR_VIRUS_INFECTED) printf("PIVY found.\n"); else printf("PIVY not found.\n"); current = list; while(current!=NULL) { list = current; current = (pmempattern)(current->flink); free(list); } return 0; } /******************************** Get SE_DEBUG_NAME. ********************************/ DWORD elevate_access_rights() { TOKEN_PRIVILEGES priv; HANDLE n1; LUID luid; if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &n1)) { return ERROR_ACCESS_DENIED; } if(!LookupPrivilegeValueA(NULL, "SeDebugPrivilege", &luid)) { return ERROR_ACCESS_DENIED; } priv.PrivilegeCount=1; priv.Privileges[0].Luid=luid; priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if(!AdjustTokenPrivileges(n1, FALSE, &priv, sizeof(priv), NULL, NULL)) { return ERROR_ACCESS_DENIED; } CloseHandle(n1); return ERROR_SUCCESS; } /******************************** List running processes and scan 'em all ********************************/ DWORD __fastcall memscan( _In_ pmempattern mempatterns) { HANDLE hThlp; PROCESSENTRY32 pe; DWORD found; DWORD retval; if(mempatterns == NULL) return ERROR_INVALID_PARAMETER;
28
Poison Ivy RAT: Configuration & Communications
found = ERROR_SUCCESS; pe.dwSize=sizeof(PROCESSENTRY32); if(elevate_access_rights() != ERROR_SUCCESS) { printf("[!] Insufficient privileges.\n"); return ERROR_ACCESS_DENIED; } // Enumerates the running process list hThlp=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); if(hThlp==INVALID_HANDLE_VALUE) { printf("[!] Process enumeration failed.\n"); return ERROR_OPEN_FAILED; } if(!Process32First(hThlp,&pe)) { printf("[!] Process enumeration failed\n"); CloseHandle(hThlp); return ERROR_OPEN_FAILED; } do { if(pe.th32ProcessID!=0 && pe.th32ProcessID!=GetCurrentProcessId()) { printf("\t[-] Scan %s...",pe.szExeFile); retval = processMemScan(pe.th32ProcessID,mempatterns); if(retval == ERROR_VIRUS_INFECTED) { found = ERROR_VIRUS_INFECTED; } else if(retval == ERROR_SUCCESS) { printf(" CLEAN.\n"); } } }while(Process32Next(hThlp,&pe)); CloseHandle(hThlp); return found; } /******************************** Scans a running process for patterns ********************************/ DWORD __fastcall processMemScan( _In_ ULONG pid, _In_ pmempattern mempatterns) { HANDLE hProcess; pmempattern currentPattern = mempatterns; BOOL continueScan; DWORD found = ERROR_SUCCESS; PVOID regionMemory; PVOID match; ULONG i = 0; MEMORY_BASIC_INFORMATION mbi;
29
Poison Ivy RAT: Configuration & Communications pmemoryRegion last, current; DWORD dwRead; last=NULL; if(pid == 0 || mempatterns == NULL) { return ERROR_INVALID_PARAMETER; } // opens the process with minimal required access rights. hProcess = OpenProcess(PROCESS_VM_READ|PROCESS_QUERY_INFORMATION,FALSE,pid); if(hProcess==NULL) { printf("\n\t\t[!] Cannot open process.\n "); #ifdef DEBUG printf("\n\t\t[!] processMemScan :: OpenProcess() failed.\n",pid); #endif return ERROR_ACCESS_DENIED; } // gather the virtual memory regions of the process for(i = 1; VirtualQueryEx(hProcess,(PVOID)i,&mbi,sizeof(MEMORY_BASIC_INFORMATION))==sizeof( mbi) ; i += mbi.RegionSize) { current = (pmemoryRegion)malloc(sizeof(memoryRegion)); current->baseAddress=(ULONG)mbi.BaseAddress; current->size=mbi.RegionSize; current->protect=mbi.Protect; current->state=mbi.State; current->type=mbi.Type; current->blink=last; last=current; } // For each memory region while(current!=NULL) { // Search the pattern currentPattern=mempatterns; while(currentPattern != NULL) { continueScan = TRUE; // Memory access test if(currentPattern->protect!=0) { // tests the memory access rights if((currentPattern->protect & current->protect) == 0) { continueScan=FALSE; } } if(continueScan) { regionMemory = malloc(current->size); // copy the whole memory region if(ReadProcessMemory(hProcess,(PVOID)current>baseAddress,regionMemory,current->size,&dwRead)!=0) { // search match = searchMem(currentPattern-
30
Poison Ivy RAT: Configuration & Communications >pattern,currentPattern->patternlen,regionMemory, current->size); while(match != NULL) { found = ERROR_VIRUS_INFECTED; // match printf("\n\t\t[-] Pattern %d matched at 0x%.8x\n",currentPattern->patternId, ((ULONG)match-(ULONG)regionMemory+current>baseAddress)); // => dump data at "match" if needed match = (PVOID)((ULONG)match+currentPattern>patternlen); match = searchMem(currentPattern>pattern,currentPattern->patternlen,match, ((ULONG)regionMemory + current->size - (ULONG)match)); } } free(regionMemory); } // next currentPattern = (pmempattern)currentPattern->flink; } // next memory region current=(pmemoryRegion)current->blink; } current = last; while(current != NULL) { last = (pmemoryRegion)current->blink; free(current); current = last; } return found; } /******************************** Searchs "needle" in "memory" space. The respective lengths are needleLen and memoryLen. /!\ NO INPUT CHECKS PERFORMED /!\ ********************************/ __declspec(naked) PVOID __stdcall searchMem( _In_ PVOID needle, _In_ ULONG needleLen, _In_ PVOID memory, _In_ ULONG memoryLen) { __asm { push ebp mov ebp, esp push esi push edi push ebx mov mov mov mov
eax, edx, edi, esi,
memory needle needleLen memoryLen
// memory regions pointers // memory regions lengths
31
Poison Ivy RAT: Configuration & Communications loop1: // first loop: reads the memory region add esi, eax xor ecx, ecx loop2:
// second loop: searchs the current memory
pointer for the pattern mov cmp jnz add cmp jnz
bl, byte ptr ds:[edx+ecx] bl, byte ptr ds:[eax+ecx] breakloop2 ecx,1 ecx, edi loop2
jmp epilogFound breakloop2: add eax, 1 sub esi, eax jz epilogNotFound cmp esi, edi jnz loop1 epilogNotFound: xor eax,eax epilogFound: pop ebx pop edi pop esi mov esp, ebp pop ebp ret 0x10 } }
// return NULL // restore registers
32
Poison Ivy RAT: Configuration & Communications
C. Key-dependent snort rules generation script
#!/usr/bin/ruby begin require 'camellia' rescue LoadError puts ':( It seems some dependencies are missing' puts 'gem install camellia-rb' end class CamelliaECB def initialize(pass) #null pad the password to the right length pass = pass+"\x00"*(32-pass.length) unless pass.length >= 32 pass = pass[0...32] #or truncate it, whatever @cypher = Camellia.new(pass) End def encrypt(string) enc='' while string.length > 0 do blob = string[0...16] string = string[16..-1] enc