SRI International

Conficker C Peer-to-peer Detector

snort plugin

Computer Science Laboratory
SRI International
333 Ravenswood Avenue
Menlo Park CA 94025 USA


1.  Version 0.1B:   Conficker_C_P2P_Detector.patch 

2.  SourceFire has converted and posted this plugin as an SO Rule:   ( SO Package / Blog )

author: Vinod Yegneswaran
tested on -  Linux, Snort version, little endian only

Acknowledgements:  thanks to Fabien Perigaud - CERT Lexsi, France


Conficker_C_P2P_Detector is a Snort  preprocessor module that detects the presence of machines within the HOME_NET boundary that are infected with Conficker C.   

Detection Synopsis:

All Conficker C hosts perform outbound P2P scanning in search of other C infected peers.   Each C-infected host opens four network ports  in listen mode:  2 TCP ports and 2 UDP.  These four listen ports are derived from a function of the host's own IP address and the current  epoch week. To illustrate the algorithm used to compute C's P2P client listen ports, we include a source code example C implementation, which we reverse-engineered from a Conficker C binary captured on 5 March 2009.

For each outbound TCP or UDP packet targeting a destination port (dport) greater than 2000, this plugin evaluates whether the dport matches the Conficker C dport value given the destination IP and the current epoch week.  We refer to this as a P2P-scan match.   An alarm is declared when an internal machine is found to have sent P2P-scan matches to 10 or more external IP addresses.   After the first alert, additional alerts are fired for that host after it performs 100 P2P-scan matches, 1,000 P2P-scan matches, and 10,000 P2P-scan matches.  Alerts then repeat every 10,000 packets.  In a typical 4 hours Conficker C run (which performs continual P2P scanning) we observe an approximate total of 6-8 alarms.   Note: this plugin is not designed for inbound scan detection.

An Example Alert Set:

The following alert log was produced from a 4-hour batch run of Conficker C.  During this run, Conficker C produces a continual stream of P2P scans for the entire period.  The scanner produced 6 alerts:

03/31/09-11:38:29.269720  [**] [231:1:1] Conficker P2P Communication (> 10 hosts contacted) [**]
         {TCP} ->
03/31/09-11:38:46.661539  [**] [231:1:1] Conficker P2P Communication (> 10 hosts contacted and > 100 packets sent) [**]
         {UDP} ->
03/31/09-11:40:09.992854  [**] [231:1:1] Conficker P2P Communication (> 10 hosts contacted and > 1,000 packets sent) [**]
         {UDP} ->
03/31/09-12:24:44.076220  [**] [231:1:1] Conficker P2P Communication (> 10 hosts contacted and > 10,000 packets sent) [**]
         {UDP} ->
03/31/09-13:03:57.944053  [**] [231:1:1] Conficker P2P Communication (> 10 hosts contacted and > 10,000 packets sent) [**]
         {UDP} ->
03/31/09-14:16:53.152213  [**] [231:1:1] Conficker P2P Communication (> 10 hosts contacted and > 10,000 packets sent) [**]
         {TCP} ->

Installation Procedure:

This installation procedure was tested on Linux, running Snort

Step 1:   Apply the Conficker_C_P2P_Detector.patch

      % cd  ./snort-
      % patch  -u -p1  <  Conficker_C_P2P_Detector.patch

Step 2:    Build Snort.  (You may apply your appropriate configure and build arguments as usual)

      % ./configure
      % make

Step 3:   Enable this snort plugin:

       %  cd ./etc
       -  edit file snort.conf
           add the following text within the snort.conf file

   ## SRI Conficker C Peer-to-peer detector
   preprocessor cfkr


Good Luck.
SRI International

Example Conficker C P2P Port Generation Algorithm

/*  Copyright: SRI International
    authors:   Hassen Saidi, Vinod Yegneswaran, and
Drew Dean, 31 March 2009.
    Acknowledgments: thanks to David Fifield from Bam Software for identifying
    the purpose of portBlacklist.

#include <stdio.h>

#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

typedef union {
  int16_t s16[8];
  uint16_t u16[8];
  int32_t s32[4];
} result_t;

typedef union {
  int64_t s64;
  int32_t s32[2];
  uint32_t u32[2];
  int16_t s16[4];
} var_t;

uint32_t portBlacklist[64] =
    0xffffffff, 0xffffffff,     0xf0f6bfbb,     0xbb5a5ff3,
    0xf3977011, 0xeb67bfbf,     0x5f9bfac8,     0x34d88091,
    0x1e2282df, 0x573402c4,     0xc0000084,     0x03000209,
    0x01600002, 0x00005000,     0x801000c0,     0x00500040,
    0x000000a1, 0x01000000,     0x01000000,     0x00022a20,
    0x00000080, 0x04000000,     0x40020000,     0x88000000,
    0x00000180, 0x00081000,     0x08801900,     0x00800b81,
    0x00000280, 0x080002c0,     0x00a80000,     0x00008000,
    0x00100040, 0x00100000,     0x00000000,     0x00000000,
    0x10000008, 0x00000000,     0x00000000,     0x00000004,
    0x00000002, 0x00000000,     0x00040000,     0x00000000,
    0x00000000, 0x00000000,     0x00410000,     0x82000000,
    0x00000000, 0x00000000,     0x00000001,     0x00000000,
    0x00000000, 0x00000000,     0x00000000,     0x00000000,
    0x00000000, 0x00000000,     0x00000000,     0x00000000,
    0x00000000, 0x00000000,     0x00000008,     0x80000000

#define IS_PORT_BLACKLISTED(x) (1 << ((x >> 5) & 0x1F)) & portBlacklist[x >> 10]

/* NB: the portgen() function below is endian-dependent, and reverse-engineered
   for a little-endian (e.g. x86, VAX, Alpha, etc.) machine.  Changes will be
for the array offsets in the unions to run on a big-endian (e.g. most
   SPARC, Power(PC), MIPS, etc.)
machine.  Other than the endian issue, this
   implementaiton should be portable. */

int portgen(int ip, result_t *res, int week)
  var_t v;      
  int64_t KONST = 0x15A4E35;
  int i;
  memset(res, 0, sizeof(result_t));
  v.s32[0] = ~ip;

   /* Generate fixed ports */
  do {
    do {
      v.s64 = KONST * v.u32[0] + 1;
      res->s16[0] ^= v.s16[2];
      for (i=1; i < 10; i++) {
        v.s64 = KONST * v.u32[0] + 1;
        res->s16[(i%2)*2] ^= v.s32[1] >> i;

    } while (IS_PORT_BLACKLISTED(res->s32[0]));
  } while (IS_PORT_BLACKLISTED(res->s32[1]) || res->s32[0] == res->s32[1]);

  v.s32[0] = week ^ v.s64;

   /* Generate week-dependent ports  */
  do {
    do {
      v.s64 = KONST * v.u32[0] + 1;
      res->s16[4] ^= v.s16[2];
      for (i=1; i < 10; i++) {
        v.s64 = KONST * v.u32[0] + 1;
        res->s16[(i%2)*2 + 4] ^= v.s32[1] >> i;
    } while (IS_PORT_BLACKLISTED(res->s32[2]));
  } while (IS_PORT_BLACKLISTED(res->s32[3]) || res->s32[2] == res->s32[3] ||
           res->s32[0] == res->s32[2] || res->s32[1] == res->s32[2] ||
           res->s32[0] == res->s32[3] || res->s32[1] == res->s32[3]);
  return v.s64;

int main(int argc, char** argv) {
  int a1,a3;
  result_t res; 
  int i, rc;
  if (argc != 3) {
    printf("usage: conficker_ports <ip addr> <epoch week>\n");
  a1 = inet_addr(argv[1]);
  a3 = atoi(argv[2]);
  rc=portgen(a1, &res, a3);
  printf("ports are TCP (fixed), UDP (fixed), TCP (week-dependent), UDP

  for (i=0;i<8;i++) {
    if (res.u16[i])
      printf("%d\t",  res.u16[i]);
  return 0;


We gratefully thank the  National Science Foundation (NSF) and the Army Research Office (ARO) for their sponsorship of this research activity.