Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions CTConsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ def do_open(self, line):
ans = raw_input()
if ans.lower() == "y" or ans == "":
bOpen = True
CTCore.web_server = server()
CTCore.web_server.start()
time.sleep(0.1) # Fixes graphic issues
CTCore.web_server_turned_on = True
else:
bOpen = True

Expand Down Expand Up @@ -452,7 +456,8 @@ def do_server(self,line):
s_cmd = l[0]
if s_cmd.lower() == "on":
if CTCore.web_server_turned_on:
print " Web Server already on: http://" + CTCore.HOST + ":" + CTCore.PORT
print " Web Server already on: http://" + CTCore.HOST + ":" +\
str(CTCore.PORT)
else:
CTCore.web_server = server()
CTCore.web_server.start()
Expand Down Expand Up @@ -720,12 +725,15 @@ def do_strings(self, line):
if (l[0] == ""):
self.help_strings()
else:
string_minlen = 5
if len(l) == 2:
string_minlen = int(l[1])
id, size = get_id_size(line)
response, size = CTCore.get_response_and_size(id, "all")
name = CTCore.get_name(id)

print "Strings found in object {} ({}) [{} bytes]:".format(id, name, size)
strings = CTCore.get_strings(response)
strings = CTCore.get_strings(response, string_minlen)
print (newLine.join(str for str in strings))
except Exception,e:
print str(e)
Expand Down Expand Up @@ -782,6 +790,7 @@ def help_plugin(self):
print " plugin find_scripts"
print " plugin 1"
print " p find_scripts"
print " p check_yara all"


def do_output(self, line):
Expand Down
16 changes: 9 additions & 7 deletions CTCore.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
plugins_folder = "plugins/"
pcap_file = ""
VERSION = "0.3"
BUILD = "13"
BUILD = "14"
ABOUT = "CapTipper v" + VERSION + " b" + BUILD + " - Malicious HTTP traffic explorer tool" + newLine + \
"Copyright 2015 Omri Herscovici <omriher@gmail.com>" + newLine

Expand Down Expand Up @@ -388,8 +388,8 @@ def print_objects(self):

def update_captipper():
currentVersion = "v{} b{}".format(VERSION,BUILD)
rawURL = "https://raw.githubusercontent.com/omriher/CapTipper/master/"
archiveURL = "https://github.com/omriher/CapTipper/archive/"
rawURL = "https://raw.githubusercontent.com/kisec/CapTipper/master/"
archiveURL = "https://github.com/kisec/CapTipper/archive/"
Comment on lines +391 to +392
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The update link changed to forked rep

CoreFile = "CTCore.py"
CTArchive = "master.zip"

Expand All @@ -406,10 +406,12 @@ def update_captipper():
if repoVer:
newVersion = "v{} b{}".format(repoVer[0][0],repoVer[0][1])
else:
alert_message("Error getting repository version", msg_type.ERROR)
sys.exit('[-] Error getting repository version')

if newVersion == currentVersion:
sys.exit("[+] You have the newest version!")
alert_message("You have the newest version!", msg_type.INFO)
return
else:
print "[+] Updating CapTipper to {}".format(newVersion)
bPackSize = False
Expand Down Expand Up @@ -498,9 +500,9 @@ def send_to_vt(md5, key_vt):
return (-1, 'Error during VirusTotal response parsing')
return (0, json_dict)

def get_strings(content):
strings = re.findall("[\x1f-\x7e]{5,}", content)
strings += [str(ws.decode("utf-16le")) for ws in re.findall("(?:[\x1f-\x7e][\x00]){5,}", content)]
def get_strings(content, minlen=5):
strings = re.findall("[\x1f-\x7e]{"+str(minlen)+",}", content)
strings += [str(ws.decode("utf-16le")) for ws in re.findall("(?:[\x1f-\x7e][\x00]){"+str(minlen)+",}", content)]
return strings

def get_request_size(id, size, full_request=False):
Expand Down
2 changes: 1 addition & 1 deletion CTServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def log(self, uri):

def handle(self):
try:
self.data = self.request.recv(1024).strip()
self.data = self.request.recv(4096).strip()
request = HTTPRequest(self.data)

if self.data != "":
Expand Down
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,53 @@ CT> vt 13
AVG Exploit_c.ABKR 15.0.0.4235 20141211
Baidu-International Trojan.Win32.CVE-2013-0074.bBZ 3.5.1.41473 20141211
```
YARA Pattern Detection plugin(check\_yara)
```sh
CT> plugin -l
Loaded Plugins (4):
0 : find_scripts - Finds external scripts included in the object body
1 : check_yara - Checks Yara Patterns : Usage>> plugin check_yara [all | conversation ID]
2 : check_host - Checks if a given id's host is alive
3 : print_body - Prints the body of a conversation and ungzip if needed

```

Sample YARA Pattern(./plugins/rules/redirection_302.yar)
```
rule rediction_302
{
meta:
description = "302 Redirection detection rule"
author = "madwind@kisec.com"

strings:
$a = "<title>302 Found</title>"
$b = "<p>The document has moved"

condition:
all of them
}
```

302 Redirection Detection

```sh
CT> plugin check_yara all
[!] Check Yara Patterns in objects
[+] Detection: [2] seedadmin17.html :[rediction_302]

CT> body 2 200
Displaying body of object 2 (seedadmin17.html) [200 bytes]:

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="http://grannityrektonaver.co.vu/15c0b14drr9f_1_

```

We notice that most of the Anti-Viruses detected this file as malicious, while some even provided the exploit CVE (2013-0074).
* If you don't have a VirusTotal public API key, you can use the command 'hashes', and manually send the hash to VirusTotal.

Expand Down
93 changes: 93 additions & 0 deletions plugins/check_yara.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""Yara Pattern Detection plugin"""
import glob
import os
import sys
from CTCore import (
colors,
msg_type,
alert_message
)
from CTPlugin import ConsolePlugin
try:
import yara
except ImportError as err:
alert_message("{} : pip install yara-python==3.5.0".format(err), msg_type.ERROR)
sys.exit(-1)

class check_yara(ConsolePlugin):

rules = None
help_string = "Usage>> plugin check_yara [all | conversation ID]"
description = "Checks Yara Patterns : {}".format(help_string)
author = "madwind@kisec.com"

def run(self, args):
if not self.yara_rule_load():
alert_message("Yara rules load Error..", msg_type.ERROR)
return ""

if len(args) > 0:
command = args[0]
if command == "all":
alert_message("Check Yara Patterns in objects", msg_type.INFO)
for id in range(len(self.objects)):
# Check if id number is a valid conversation
if self.is_valid_id(id):
name = self.get_name_by_id(id)
# Get response body as text even in case it was Gzipped
response_body = self.get_plaintext_body_by_id(id)
matches = self.rules.match(data=response_body)
if matches:
alert_message("Detection: [{}] {} :[{}]".format(
id, name, (", ".join([str(rule) for rule in matches]))),
msg_type.GOOD)
else:
alert_message("Invalid conversation ID {}".format(str(id)), msg_type.ERROR)
elif command.isdigit():
# Gets the conversation ID
convid = int(command)
# Check if id number is a valid conversation
if self.is_valid_id(convid):
name = self.get_name_by_id(convid)
alert_message("Check Yara Patterns in object {} ({})...".format(
str(convid), name),
msg_type.INFO)
# Get response body as text even in case it was Gzipped
response_body = self.get_plaintext_body_by_id(convid)
matches = self.rules.match(data=response_body)
if matches:
alert_message("Detection: [{}] {} :{}".format(
convid, name, (", ".join([str(rule) for rule in matches]))),
msg_type.GOOD)
else:
alert_message("Invalid conversation ID {}".format(str(convid)), msg_type.ERROR)
else:
alert_message("Invalid command {} : {}".format(
command, self.help_string), msg_type.ERROR)
else:
alert_message("No arguments given : {}".format(self.help_string), msg_type.ERROR)

def yara_rule_load(self):
"""Yara rule load"""
plugin_path = os.path.dirname(os.path.realpath(__file__))
p_files = glob.glob(plugin_path + "/rules/*.yar")
total_yara_rule = ""
for p in p_files:
yara_rule = ""
with open(p, "r") as rule_file:
yara_rule = rule_file.read()
try:
yara.compile(source=yara_rule)
except yara.SyntaxError as error:
alert_message("{} : SyntaxError : {}".format(p, error), msg_type.ERROR)
return False
total_yara_rule += yara_rule+"\n"
try:
self.rules = yara.compile(source=total_yara_rule)
except yara.SyntaxError as error:
alert_message("{} : SyntaxError : {}".format(p, error), msg_type.ERROR)
return False
if self.rules is None:
alert_message("Empty Yara rules", msg_type.ERROR)
return False
return True
3 changes: 2 additions & 1 deletion plugins/print_body.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ def run(self, args):
else:
data = self.get_body_by_id(id)
else:
print "invalid id"
print "invalid id"
return data
13 changes: 13 additions & 0 deletions plugins/rules/redirection_302.yar
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
rule rediction_302
{
meta:
description = "302 Redirection detection rule"
author = "madwind@kisec.com"

strings:
$a = "<title>302 Found</title>"
$b = "<p>The document has moved"

condition:
all of them
}