Professional Documents
Culture Documents
TE C H N I C A L R E P O R T
A D D EN D
M O C O L A N D I M P LEM EN A IO N
C O N F I C K ER C P 2 P P R O
P HILLIP P ORRAS , HAS S E N S AIDI, AND V INOD HT P://MTC.SRI. T COM/CONFICKER/P2P/ R E L E A S E D A TE : 2 1 S E P TE M B E R 2 0 0 9 L A S T U P D A TE : 2 1 S E P TE M B E R 2 0 0 9 C OMP UT ER S C IENC E LAB OR AT OR S R I INT ER NAT IONAL 333 R AVENS W OOD AVENUE MENLO P AR K C A 94025 US A
E G NE S WARAN
Ackno ledements Thi ma e ial i ba ed pon o k ppo ed h o gh he U.S. A m Re ea ch Office nde he C be -TA Re ea ch G an No. W911NF-06-10316, b he Na ional Science Fo nda ion, G an No. C NS-07-16 612, and b he Office of Na al Re ea ch, G an No. N00014-09-10683. The ie e p e ed in hi doc men a e ho e of he a ho and do no nece a il ep e en he official po i ion of he pon o .
ABSTRACT This report presents a reverse engineering of the obfuscated binary code image of the Conficker C peer-to-peer (P2P) service, captured on 5 March 2009 (UTC). The P2P service implements the functions necessary to bootstrap an infected host into the Conficker P2P network through scan-based peer discovery, and allows peers to share and spawn new binary logic directly into the currently running Conficker C process. Conficker's P2P logic and implementation are dissected and presented in source code form. The report documents its thread architecture, presents the P2P message structure and exchange protocol, and describes the major functional elements of this module.
1. Introduction
While much has been written of the inner workings of Conficker C, and its predecessors, details have remained scant regarding the structure and logic of C's most significant new feature, its peer-to-peer (P2P) module. One likely reason for this is that decompilation of the P2P module is hindered by various binary transformations that obfuscate its logic, making disassembly and interpretation more involved. In fact, the P2P module of Conficker C stands uniquely apart as the only binary code segment so far in the Conficker family that has undergone substantial structural obfuscation to thwart reverse engineering. The introduction of the P2P protocol in C is a rather important addition to what is already a large-scale menace to the Internet. Conficker's new peer network frees its drones from their reliance on the domain generation algorithm (DGA) for locating and pulling new signed binaries. This new peer network was critically needed, as whitehats have been blocking Conficker's DGA-generated domains from public registration since February 2009. While Conficker's authors were able to overcome these registration blocking efforts for a few Conficker B DGA sites, allowing many B hosts to upgrade to Conficker C, to our knowledge Conficker A machines have thus far been blocked from upgrading. Nevertheless, even with its incomplete upgrade to Conficker C, this new peer network has already proven useful. It was recently used to distribute malicious binaries to Conficker C hosts [1] . Unfortunately, unlike the binary delivery disruptions over the DGA rendezvous points that were achieved by the Conficker Working Group [2], whitehats currently employ no equivalent capability to hinder binary distributions through Conficker's peer network. One unusual aspect of the P2P scanning algorithm is that it searches for new peers by scanning the entire Internet address space. This choice is unusual, as C infected hosts are currently overlays of Conficker B hosts. Conficker A and B both share a common flaw in their scanning logic, which prevents them from scanning all but one quarter of the Internet address space [3]. Why then does Conficker's P2P module probe the entire Internet when the A and B scanning flaw significantly reduces the probability that C-infected hosts will reside in all but one quarter of those P2P probes? One explanation could be that the authors were simply unaware of the scanning flaw. Another could be that it is also looking for hosts that spread through local scanning or USB propagations. Despite this mismatch in probing schemes, we believe the P2P module is a customized library developed specifically for Conficker. We further speculate that this module is most likely not coded by the same developers who coded the other major components of Conficker C. The narrowness of function does not suggest that it has been adapted from a broader P2P sharing algorithms (such as from a file sharing algorithm or other rich P2P application). Conficker's P2P module stands self-contained as a C++ library, whereas the rest of the Conficker C codebase is written in C. The coding style is less sophisticated and less rich with functionality relative to that of other Conficker modules. The P2P module provides a limited peer command set, keeping complexity to a minimum - perhaps due to scheduling pressures and quality control concerns in deploying new functionality across millions of geographically dispersed victim machines.
Indeed, the simplicity of the P2P protocol does appear to limit the opportunity for exploitable mistakes. Overall, the P2P protocol operates as a simple binary exchange protocol, where peers connect with each other for the purpose of synchronizing to the binary payload signed with the highest version number. In this sense, the Conficker peer network incorporates the notion of a global highest binary payload version. The security for this protocol relies entirely on the use of an encryption and hash scheme that has already been vetted and deployed by the DGA protocol of Conficker. Both the header (through replication) and the binary payload are digitally signed, and are then cryptographically verified by the receiver. Although the protocol does not support complex data exchanges or network-wide synchronizations, such tasks remain easily achievable by simply encoding such logic into signed Windows binary logic and seeding such binaries into the peer network. In another design decision, the Conficker authors selected to spawn the binary payload as a thread to the Conficker process, rather than fork binary payloads as independent processes. One implication of this decision is that, as a thread, the executable payload is granted direct access to the currently running Conficker C threads, data structures, memory buffers, and registries. Thus, a downloaded binary can hot patch critical data within a live C process, such as peer lists, the DGA's top level domains (TLD) arrays, and other variables or seed data. It also appears that the P2P service is intended to supersede Conficker's DGA service as the preferred method for distributing binaries to C drones. In Conficker A and B, the DGA algorithm is executed on each drone unconditionally. In Conficker C, the DGA executes only under the following condition: the P2P service spawning thread P P M i reports that it was unable to spawn the appropriate P2P threads, or there does not exists a 2_an binary payload within the P2P server directory that is available for sharing. In C, the DGA can clearly supplement the P2P service in locating and retrieving binary payloads. However, when a binary payload is retrieved and made available for propagation through the P2P service, C's DGA function will cease to operate. This suppression of the DGA will continue as long as the binary payload has not expired and been removed by the P2P server. In this report, we examine details of the underlying Conficker C P2P logic and its software structure, and review its P2P protocol. Significant work in understanding the P2P protocol has already been undertaken by members of the Conficker Working Group [4, 5]. In this report, we present the full reversal of the obfuscated binary, back to its human-readable C source code. We begin in Section 2, where we explain which obfuscation strategies were used by the Conficker developers to thwart reverse engineering attempts, such as that documented in this report. We outline the techniques that were applied to undo these obfuscations. Section 3 presents an overview of the P2P module's thread architecture, its calling structure, and its initialization steps. Section 4 presents an overview of the protocol message exchange rules. Sections 5, Section 6, and Section 7 offer a description of the implementation of simple dialog exchanges that occur between peers, and Section 8 reveals the details of binary payload management. Our analysis concludes with an appendix that provides the decompiled C-like implementation of the full Conficker P2P code base.
following disassembly of the Conficker function that tests whether a candidate IP address (for use by the P2P scan routine) corresponds to a private subnet: .e .e .e .e .e .e .e :0B2C 0937 :0B2COFSAE_ESO_Fi_rvt_untpo na 0937 BUCTDVRINO_spiaesbe rc er :0B2C 0937 m o e ,e c a :0B2E 0937 ad n e ,0FF c FFh :0B24 0938 cp m e ,080 c ACh :0B2A 0938 j lc946 o_B24 :0B20 0939 jp m of9AA f_BA5 / .e :0BA5of9AA * 09AA f_BA5 d ofe lc9B0 * d f o_A1C / .e :0B20OFSAE_ESO_Fi_rvt_unted 0939 BUCTDVRINO_spiaesbe np . .e .e .e .e
. .e :0A1C 09B0 cp m a,0h l A .e :0A1E 09B0 j lc946 o_B24 .e :0A14 09B1 jp m of9A3 f_B17 / .e :0B17of9A3 * 09A3 f_B17 . .e :0BC4 092E ad n e ,00F a FFh .e :0BC9 092E cp m e ,1Ah a 0C .e :0BCE 092E j lc946 o_B24 .e :0BC4 092F jp m of9AC f_B3C / .e :0B3Cof9AC * 09AC f_B3C . .e :0AA4 09B2 o e ,e a a .e :0AA6 09B2 en
This function has been split into five different instruction blocks. The blocks were moved from a contiguous memory space to different memory locations within the image of the Conficker code. For each unconditional jump instruction, we provide the memory location where the jump target is stored. Such obfuscations can pose a significant barrier for reverse engineering and decompilation tools. To solve this problem, we have developed a dechunking algorithm that systematically rewrites all such functions back to their original form. The i _ i a e p _ b e function code after deobfuscation is n .e .e .e .e .e .e .e .e .e .e .e .e .e .e .e .e .e .e .e :0A31i_rvt_untpo na 0911 spiaesbe rc er ;CD XE:sb9BA+3 OE RF u_A101A :0A31 0911 m o e ,e c a :0A33 0911 ad n e ,0FF c FFh :0A39 0911 cp m e ,080 c ACh :0A3F 0911 j lc914 o_A30 :0A35 0912 cp m a,0h l A :0A37 0912 j lc914 o_A30 :0A3D 0912 ad n e ,00F a FFh :0A32 0913 cp m e ,1Ah a 0C :0A37 0913 j lc914 o_A30 :0A3D 0913 o e ,e a a :0A3F 0913 en :0A30;---------------------------0914 --------------------------:0A30 0914 :0A30lc914: 0914 o_A30 ;CD XE: OE RF b911+ _A31E :0A30 0914 ; b911+6.. _A311 . :0A30 0914 m o e ,1 a :0A35 0914 en :0A35i_rvt_unt ed 0914 spiaesbe np
its decompilation (to C-like syntax) is bo _uecl i_rvt_untex(nind_i 1 a< > { ol _sral spiaesbe<a> ge _n 6 1a ) e na = 420 1 = 30 (BT)1= 1 _YEa = 0 (nind_i 1)a &0FF)= 46; ge _n 6(1 0F = 28
2.2.4 A u to m ated Stan dar dizatio n o f C allin g C o n v en tio n s Compilers often depend on the recognition of standard calling conventions in order to interpret the parameters and return values of functions. Such calling conventions typically involve the use of the stack or special registers to pass arguments to functions. Decompilers and disassemblers can recognize the patterns of x86 machine instructions that correspond to each calling convention, and use this insight to determine the number of arguments for each function. This represents a powerful technique for enabling dataflow analysis across functions, and is critical for reversing program semantics from binaries. In the Conficker analysis, three calling conventions are of particular interest:
CDECL is the standard calling convention for Windows APIs. This convention is used by many C systems for the x86 architecture. In the CDECL convention, function parameters are pushed onto the stack in a right-toleft order. Function return values are returned in the EAX register. Registers EAX, ECX, and EDX are available for use in the function. Fast indicates that the arguments should be placed in registers, rather than on the stack, whenever possible. This reduces the cost of a function call, because operations with registers are faster than with the stack. The first two function arguments that require 32 bits or less are placed into registers ECX and EDX. The rest of them are pushed on the stack from right to left. Arguments are popped from the stack by the called function. is the default convention for calling member functions of C++ classes. Arguments are passed from right to left and placed on the stack, and this is placed in ECX. Stack cleanup is performed by the called function.
This
Conficker's P2P code contains an additional layer of obfuscation, in which each function has been rewritten to a user-defined calling convention, and where some of the arguments are pushed onto the stack and others are written to registers that are not associated with standard calling conventions. This obfuscation is significant, in that decompilation will fail to recognize the actual number of arguments of a function. The resulting source code derived from the decompiler incorporates fundamental misinterpretations that hinder semantic analyses. In Source Listing 1 we first provide an example of such a mangled subroutine followed by a version of the subroutine that is restored by our system.
Figure 1 presents a call graph overview of the thread architecture and the major subroutines used by the Conficker P2P module. The figure is composed of rectangles, which represent the threads of the P2P module, and ovals to represent the major subroutines called by each thread. The figure is a simplification of the software call graph. All tasks are represented, but some subroutines are not shown for readability. The dashed arrows indicate the thread spawning graph, while solid arrows represent the subroutine invocation graph. Figure 1 also indicates which sections in this document present the description of which threads. Finally, the dotted label in some threads and subroutines indicates which module's source code is presented in the associated section. Initialization and establishment of the P2P service occurs in three separate locations within the Conficker binary.
This thread performs P2P registry initialization, used for peer list management. P P S 2 _ e p creates a registry entry that corresponds to C's scratch directory: HL\o KMSf aeMcoo \id \i f Wno \ C e V n e inEpoe\%8-0X%4-0X%8%4 o\ l 0X%4-0X%4-0X0X
PPS 2 _ e p incorporates an anti-debugging code segment that will cause debuggers to exit this process with exceptions. Finally, once all P2P service state initializations are performed, this threads spawns P P M i , which 2_an activates the P2P service.
- spawns c _ e e _ i e (spawns 1 thread per network interface) p l n - spawns d _ e e p (spawns 1 thread per network interface) I e n _ i e (Sec ion 3): synchronizes the peer to the common epoch week used for port selection n e m during peer discovery. c _ l e _ c n(Sec ion 5} - Peer Discovery): conducts local and Internet-wide P2P scanning. pcin a - spawns c _ l e _ o n c e (spawns 1 thread per connected peer) pcin cne d c _ e e _ i e (Sec ion 6): monitors local network interfaces on fixed and dynamically computed P2P p l n ports. It spawns the tcp server connection thread manager when TCP packets are sent to its P2P ports. - spawns c _ e e _ o n c e (spawns 1 thread per connected peer) p cne d d _ e e (Sec ion 6): monitors its network interface on fixed and dynamically computed P2P ports. This p thread manages a UDP-based stateless server and is responsible for tracking each incoming P2P message over its assigned network interface. d _ l e (Sec ion 6 and Sec ion 7): conducts local and Internet-wide P2P scanning and implements pcin Conficker's client-side P2P protocol. c _ l e _ o n c e (Sec ion 7): pcin cne d client-side P2P protocol. called from c _ l e _ c n this thread implements Conficker's pcin a,
The network representation of a P2P p a e m g structure is illustrated in Figure 2. In this figure, the field _e lengths shown in brackets are in bytes. Dotted fields are present only in TCP packets and fields in blue are encrypted using the keys. In this network representation, the header begins with an optional 2-bytelength field which is present only in TCP packets. The remainder of the header is identical for both TCP and UDP. An 8-byte key data field is used to encrypt the remainder of the message. Finally, a random-length noise segment is appended to the end of the message, presumably to thwart length-based packet signatures. The first byte of the p l a _ a afield indicates whether this packet is the last data packet. The last two bytes a odd indicate the length of the following payload data chunk. Similarly, the peers data table is a complex field with a 2byte header that indicates the number of entries. Each entry is 12 bytes in length, with a 4-byte IP address and 8byte bot ID field. Included in the payload data header is a 2-byte packet control ord. The bit fields of the decrypted control word describe which fields are present in the P2P data section. Table 1 summarizes of the data fields corresponding to each bit in the control word and their corresponding lengths. The length of the message can thus be inferred by parsing the control word except for the variable length fields of pa load data and peers data.
Bit 0 1 2 3 4 5 6 7 8
Nam e Role Local Proto Location Payload Version Payload Offset Payload Data Data_X0 Peer Data
Des c r iptio n Client=1/Server=0 Is Peer in the same subnet? TCP=1/UDP=0 External IP address and Port Version of running payload Offset of current payload chunk Payload chunk data & variable System summary information? Presence of a table of peer data & variable
In our example, the remote P2P server has a more recent binary (higher version ID) than the local P2P client (the connection initiator). The client begins this scenario by sending a hello message with its current binary payload version ID. The other payload fields (p l a _ f e , p l a _ e g h l a odof a odln , a _ h n ) are all set to 0 in c k this hello message. Next, the server determines that its payload version is greater than the client's version, and in response immediately begins transfer of its current high-version binary. The server responds with its first encrypted payload data segment. In this protocol, messages may be sent over multiple packets, with the l a _ h n field c k set to 0 to indicate that the current packet does not contain the end of the message. The client acknowledges receipt of the server's packet by replying with an ack message. This ack message triggers the release of the next payload segment from the server. This exchange continues until the last data segment (a packet with the p l a _ a _ a a field set to 1) is sent from the server. The binary is then a odl d cryptographically validated and executed by the client, and if successful its binary version ID is updated. In the second case, where the client's version is more recent, the server replies to the client's hello with an ack packet that is devoid of payload data. The client checks the version number on the ack packet. Finding that its version number is greater than the server, the client immediately initiates the uploading of its more recent binary version to the server. The server replies to these packets with acknowledgments until the last data packet is transmitted. The payload is then validated and executed at the server.
5. Pe e r Discove ry Protocol
Conficker's P2P module does not incorporate a seed list of peers to help each drone establish itself in the peer network, as has been a well established practice with other malware ([8, 11, 12] provide several examples). Rather, Conficker hosts must bootstrap themselves into the network through Internet-wide address scanning, and continue this scanning throughout the life of the process. Once a C drone locates or is located by other Conficker peers, the P2P module allows it to act as both client or server, respectively. Peer discovery occurs over both the TCP and UDP protocols. The thread responsible for TCP peer scanning is c _ l e _ c n sho n in Source Listing 7 , which is spawned from P P M i . Two copies of this thread are pcin a, 2_an created: one for local scanning and one for non-local (i.e., Internet) scanning. The local c_le _cn pcin a thread defines local target IP addresses as those within the range of the current network interface's subnet netmask. This pair of local and global c _ l e _ c nthreads service all valid network interfaces (Section 3) pcin a within the host. UDP scans are performed directly by the d _ l e thread. Details of the Conficker P2P Client logic are pcin presented in Section 7. Similar to the TCP thread logic, two d _ l e threads are spawned to handle both pcin local and global network scanning. However, a local and global instance of this thread is spawned per valid network interface (i.e., if an infected host has both a valid wired and wireless network interface, then two pairs of the local/global d _ l e p c i n thread are created). Peer discovery involves three phases: target IP generation, network scanning, and (in the case of TCP) spawning the c _ l e _ o n c i nthread when a network peer acknowledges the scan request. Peer list creation pcin cne o and management is handled within the tcp and upd client logic, and is therefore discussed in Section 7. Target IP generation for both TCP and UDP is handled by the subroutine b i d c n a g _ p l l_ a_ e i _ i , Source Listing 8. This routine relies on the arrays of random numbers produced during the initialization phase of the P2P service, Section 3. A global counter ensures that a different list is produced each time the function is invoked. Each time b i d c n a g _ p l l_ a_ e i _ i is called, it computes an array of 100 random IP addresses. When called by the local scan thread, it produces 100 IP addresses within the range of the network interface subnet mask. When called by the global scan thread, it produces 100 IP addresses based on the following criteria. If the number of peers in its peerlist is non-zero, then with a small probability, i.e., ( a d m ) % ( 0 0 - 9 0 * no( 10 5 nmpe _e / 2 4 ) = 0 , it adds one of the existing peers to the target list. Otherwise, it pseudo-randomly 08 = ) produces an IP that passes the following constraints: - The IP address is legitimate (not a broadcast or DHCP failure address). - The IP address is not a private subnet address (10.0.0.0/8, 192.168.0.0/16, 172.16/12). - The IP address is not found within the Conficker C's filtered address range (c e k I _ _ n a g ). hc_Pi i_ ne Even with a full peerlist of 2048 peers, the probability of adding an existing peer is just 1/50 per location. We would expect two of the existing peers to be in the list of 100 IPs. When the peerlist is just 1, then the probability of picking that peer is 1/1000 per location or approximately 1/10 per list. The exact value can be expressed as the following binomial: Pr(peer is chosen) = 1 - Pr(none of the 100 picks is the peer) = 1 - (999/1000) (100) = 0.0952.
Once the list of 100 IP targets is computed, the TCP and UDP scanners enter the scan phase. For each target IP address, the scanner must first compute an IP-dependent fixed port, and a variable port that is dependent on both the target IP and the current epoch week. Implementation details of the Conficker C port generation algorithm are available at [9]. Each fixed and variable port computed is vetted against a port blacklist. An
analysis of the port blacklist check is available at [10]. The lists of targeted IPs computed by TCP and UDP are independent and not meant to overlap. Each IP targeted by the TCP client scan thread or UDP client scan thread receives a probe either on the fixed or variable port for that destination IP and protocol. The choice between fixed or variable port is random. The TCP and UDP scanning logic for both local and global addresses ranges continuously loops from the target IP generation phase to build the list of 100 candidates, to the scan phase, which probes each targeted IP address over the computed fixed and variable ports. Over each iteration, the d _ l e scanner, pauses for 5000 ms, and then recomputes the next pcin set of 100 candidate IPs. The c _ l e _ c n shown in Source Listing 7, includes no such pause logic. When pcin a, a peer is discovered, the c _ l e _ c nthread spawns the c _ l e _ o n c thread to establish a peer pcin a pcin cne session, while the d _ l e p c i n thread handles client communications in the same thread.
Regardless of which decision the server makes, the client prepares for this peer exchange by entering a loop in which it will alternate from sending to receiving P2P messages, until one of the following conditions is reached: -cin_ade e le h n l _ c returns an empty buffer ( e l _ o - WaitForSingleObject (a2) returns true. - c l _ e returns an error or empty message. al c - c l _ e e _ n _ e dreturns an error. al lc ad n - The number of messages sent exceeds 2000. Each message received by the client is processed via the c i n _ a d e e function. This function waits for le hnl_ c an incoming message on the socket, and then validates whether the received message conforms to the Conficker TCP protocol (i.e., the value of the first two bytes corresponds to the remaining length of the message). This message is then passed to the c i n _ a d e e function that parses the message and composes an le hnl_ c appropriate response. In the case of UDP, the client scan routine initiates communication by sending the first hello packet and receiving the response. It then calls the d _ h c I _ e d e function shown in Source Listing 14. The arguments to pcekP n_ c this function include the socket, a complex type and flag indicating whether the communicating peer is local or remote. The 40-byte second argument contains two sockaddrs, a pointer to a buffer, and the buffer length. It begins by checking if the peer address is a broadcast address or in one of the filtered addresses. If the conditions are false, then it sends the buffer to the peer (retrying as many as three times if necessary). It then enters a finite loop of as many as 2000 messages where it receives messages, parses them by calling c i n _ a d e e le hnl_ c and sends replies using the c l _ e e _ n _ e d osubroutine. If the send fails, it retries three times. al lc ad n The c i n _ a d e e subroutine is shown in Source Listing 15. In many ways, this is similar to the le hnl_ c e e _ a d e e function. It also begins by parsing the received message using the p hnl_ c a e e d _ c _ p c e function, which validates the message contents and populates the p ak a e _ e a e(i.e., structure p in dm g m the source listing). Like the server, the client is responsible for handling the three possible outcomes that may arise when the binary payload version comparison is performed with the server: s er v er pay lo ad v er s io n = = c lien t pay lo ad v er s io n : the server's IP address is added to the client's local peerlist table, and the function exits. Peerlist updating happens only from the client handler. Peers do not update their peerlists when operating in server mode. s er v er pay lo ad v er s io n < c lien t pay lo ad v er s io n : the client begins sending chunks of its local binary payload to the server. The chunk size is 1024 to 4096 or 512 to 1024, depending on whether the protocol is TCP or UDP, respectively. The function g n a e n _ e a eis utilized to pack each new message to ee _e m g the server. s er v er pay lo ad v er s io n > c lien t pay lo ad v er s io n : the client recognizes that the server has immediately initiated a binary payload upload. The client enters a receive loop and reconstructs the server's payload message. Once downloaded, the client cryptographically validates that the binary payload is valid, and proceeds to spawn this binary code segment, store the payload, and update its local version information. Section 8 explains how received binaries are stored and managed. = N L ). UL
The key subroutine to examine for understanding how files are locally handled and then shared across the P2P network is a aiain c_e _ ld o _ 4 d c p i n This subroutine checks the signature of the digitally signed o. payload, and takes the following arguments: the public exponent, the modulus, the downloaded payload, its size and the appended signature. The call graph in Figure 4 describes how this function gets called from the set of P2P threads.
Fig u r e 4: Bin ar y pay lo ad v alidatio n c all g r aph Two primary paths are possible from the client and server threads that process incoming messages from peers. In one case, a single message is interpreted as an encrypted code segment, which is spawned directly as a thread within the Conficker process image. In the second case, the payload is stored in a file and checked whether it is has been encrypted multiple times. The payload is further checked to determine whether it is code that needs to be spawned as a thread, or data that needs to be stored in its encrypted form in the registry, and further distributed within the peer network. To understand how files are downloaded and validated by the various threads of the P2P service, let us first describe several key data structures that the P2P file management subroutines manipulate: - A registry entry provides a persistent store for capturing the contents of a downloaded payload - A separate registry entry stores the list of peers found to possess the same binary version as the client - A global array of 64 elements allows Conficker's P2P service to simultaneously downloaded as many as files - An array of IP addresses is used to identify scan targets The non-persistent stores in the form of arrays of multiple file downloads and peers IPs are used as temporary data structures to keep track of the simultaneous download of multiple files, depending on how many peers have a binary ready to share. The two persistent stores are used to store a list of peers and a digitally signed file. The following structure is associated with each downloaded payload from a remote peer. Conficker maintains an array of 64 elements that support the potential of as many as 64 simultaneous payload downloads. Once a download is achieved, the information stored in this structure is passed to a subroutine that decrypts the payload and eventually stores it in the registry, and then spawns the decrypted payload code as a thread inside the Conficker running process:
tpdfsrc ppfl_trg { yee tut 2_iesoae i 3 i_o l e; n 2 pp a d i 3 c n ; n 2 h k i 3 lclp n 2 oa_o ; i 3 pe_P n 2 e I; i 3 pe_o ; n 2 e p i 3 lclI; n 2 oa_P i 3 n 2 e in o; i 3 pooo; n 2 cl i 3 n 2 iece in m_ a o; i 3 l n 2 a _ie m_ i e; n i 3 FlHnl; n 2 ieade i 2n_ i eb e; n b n
Below, we describe how both the client and server threads manipulate the payload data received as a result of the message exchange described in Section 4, beginning with how the function a aiain c_e _ ld o_ 4dc pin o is invoked.
The data structure size is 76 bytes. The first four bytes contain the size of the received payload. The last 8 bytes contain pointers to two newly allocated heap spaces, where both the received encrypted and digitally signed payload and its corresponding encrypted payload are stored. Upon a successful digital signature check and decryption, the first 64 bytes of the decrypted payload content are copied to the header field of the p l a _ h c structure. Upon examining the extracted header from the decrypted payload, further actions a odcek are undertaken. Payload validation is wrapped within a function that also extracts the P2P message header, called cekp la_ hc_a ode a _ e d . The source listing of this subroutine is shown in Source Listing 16. c hae
8.5.1 Ex ec u tio n with No Sto r ag e The P2P server can spawn a binary payload segment directly from a single message, without the need to store this code in the registry first. This payload is spawned directly as a thread. The thread is created with a list of parameters that include the IP of the peer that sent the payload, the port from which it was sent a pointer to the list of obfuscated APIs, and a pointer to G P o A d e so that the thread can load more APIs, if needed. e cd In this way, threads can be delivered to a resident Conficker process directly from the P2P channel, where the only sources of the downloaded code logic reside in the resident memory of the Conficker process or the encrypted P2P message. This method of message-to-thread spawning is implemented via subroutine p a e e d _ c _ p c e ,and is shown in Source Listing 5. ak
8.5.2 Ex ec u tio n with Sto r ag e The P2P server can also exchange complex multi-encrypted payload structures, and then iteratively decrypt each chunk. Upon decryption, the file expiration date on the payload header is validated and version number is updated. The decrypted payload could be code or data. If code, it is spawned as a thread but with no parameters. This capability is provided within the subroutine i e a i e p l a _ e _ a o d d c p , and is shown in Source Listing 19. The following subroutine decrypts the payload that is received as multiple chunks and then checks for the presence of layered encryption and signatures. It also includes the checks for whether the payload is data or code that needs to be spawned as a thread. In both cases, the extracted payload is stored in the registry.
Conclusion
We have presented a reverse engineering analysis of the Conficker C P2P service, described its software architecture in detail, and provided a source-level description of much of its key functionality. The Conficker C binary used to conduct this analysis was harvested on 5 March 2009 (UTC), and has an MD5 checksum hash of 5e279ef7fcb58f841199e0ff55cdea8b. Perhaps the best way to view the Conficker P2P server is as a mechanism for propagating new executable thread segments into the resident Conficker C processes of all infected drones. Such threads not only allow the Conficker authors to implement complex coordination logic via these threads, they may use these threads to directly hot patch the resident Conficker C process itself, and fundamentally change its behavior without requiring new portable executable (PE) distributions and installations. In fact, thread segments can be shipped throughout the peer network in a mode in which they are executed but not stored on the local hard drive. Thus, the only evidence of such code-level updates will reside in the encrypted packet stream. These capabilities are provided while requiring reliance on the correct implementation of an extremely minimal P2P protocol. The protocol appears specially crafted for meeting the well known functional requirements of the Conficker C network. Most of the significant functional complexity and cryptographic protections that exist within the P2P service are located in the binary download management logic. The effect of these design decisions appears to be a minimal and robust network protocol, which requires one to break the encryption protocols to succeed in compromising the security of the P2P network. As with our previous efforts to understand Conficker, this report represents a snapshot of our understanding of a few key aspects of Conficker's functionality. We remain in direct communication with numerous groups that continue to monitor this botnet and analyze its internal logic, along with many other Internet malware threats. We strongly encourage and appreciate your feedback, questions, corrections, and additions. This report is a living online document, and we will continue to update its content as warranted and as new developments arise.
Acknowledgments
We thank David Dittrich from the University of Washington, Aaron Adams from Symantec Corporation, Andre Ludwig from Packet Spy, and Rhiannon Weaver from the Software Engineering Institute at Carnegie Mellon, for their thoughtful feedback. Also thanks to Vitaly Kamlyuk from Kaspersky Lab for his help in verifying our mistakes. Most importantly, thanks to Cliff Wang from the Army Research Office, Karl Levitt from the National Science Foundation, Doug Maughan from the Department of Homeland Security, and Ralph Wachter from the Office of Naval Research, for their continued efforts to fund open INFOSEC research, when so few other U.S. Government agencies are wiling or able.
References
[1] Computer Associates. Win32/Conficker teams up with Win32/Waladec. http://community.ca.com/blogs/securityadvisor/archive/2009/04/15/win32-conficker-teams-up-with -win32-waledac.aspx, 2009. Conficker Working Group. Home Page. http://www.confickerworkinggroup.org, 2009. CAIDA. Conficker/Conflicker/Downadup as seen from the UCSD Network Telescope. http://www.caida.org/research/security/ms08-067/conficker.xml, 2009. Vitaly Kamlyuk (Kaspersky Laboratory). Conficker C/E P2P Protocol. private document, 2009. Aaron Adams, Anthony Roe, and Raymond Ball (Symantec). W32.Downadup.C. DeepSight Threat Manage ment System, Symantec Corporation. Phillip Porras, Hassen Saidi, and Vinod Yegneswaran. An Analysis of Conficker s Logic and Rendezvous. http://mtc.sri.com/Conficker/, 2009. Phillip Porras, Hassen Saidi, and Vinod Yegneswaran. Conficker C Analysis. http://mtc.sri.com/Conficker/addendumC/, 2009.
[ 8] J. B. Grizzard, V. Sharma, C. Nunnery, B. B. Kang, and D. Dagon. Peer-to-peer botnets: overview and case study. In HotBots '07: Proceedings of the first conference on First Workshop on Hot Topics in Understand ing Botnets, Berkeley, CA, USA, 2007. USENIX Association. [9] SRI International. Conficker C Actived P2P Scanner. http://mtc.sri.com/Conficker/contrib/scanner.html, 2009.
[10] David Fifield. An Analysis of Magic Table. http://www.bamsoftware.com/wiki/Nmap/PortSetGraphics#conficker, 2009. [11] D. Dittrich and S. Dietrich. P2P as botnet command and control: a deeper insight. In Proceedings of the 3rd International Conference On Malicious and Unwanted Software (Malware 2008). IEEE Computer Society, October 2008. http://staff.washington.edu/dittrich/misc/malware08-dd-final.pdf [12] D. Dittrich and S. Dietrich. New directions in P2P malware. In Proceedings of the 2008 IEEE Sarnoff Symposium, Princeton, New Jersey, USA, April 2008. http://staff.washington.edu/dittrich/misc/sarnoff08-dd.pdf
Appendices
Appendix 1
SOURCE LISTING 1: SOURCE LISTING 2: SOURCE LISTING 3: SOURCE LISTING 4: SOURCE LISTING 5: SOURCE LISTING 6: SOURCE LISTING 7: SOURCE LISTING 8: SOURCE LISTING 9:
SOURCE LISTING 10: TC P-Se SOURCE LISTING 11: TC P-Se SOURCE LISTING 12: Se
e -Handle-Recei e
SOURCE LISTING 13: TC P-C lien -C onnec ed SOURCE LISTING 14: UDP-Send-Rec SOURCE LISTING 15: C lien -Handle-Rec SOURCE LISTING 16: C heck-Pa load-E ac -Heade
SOURCE LISTING 18: Spa n-Pa load-Th ead SOURCE LISTING 19: I e a i e-Dec