2020-08-05 00:08:41 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
import pdb
|
|
|
|
from mitmproxy import ctx
|
|
|
|
import threading
|
2020-08-09 12:38:41 +00:00
|
|
|
from queue import Queue, Empty
|
2020-08-05 00:08:41 +00:00
|
|
|
import time
|
|
|
|
import zmq
|
2020-08-08 11:44:16 +00:00
|
|
|
import json
|
2020-08-10 22:24:08 +00:00
|
|
|
from enum import Enum
|
2021-04-04 14:24:46 +00:00
|
|
|
|
|
|
|
# this method is used to convert flow states (generated with get_state()) to json
|
2020-08-08 11:44:16 +00:00
|
|
|
def convert_to_strings(obj):
|
|
|
|
if isinstance(obj, dict):
|
|
|
|
return {convert_to_strings(key): convert_to_strings(value)
|
|
|
|
for key, value in obj.items()}
|
|
|
|
elif isinstance(obj, list) or isinstance(obj, tuple):
|
|
|
|
return [convert_to_strings(element) for element in obj]
|
|
|
|
elif isinstance(obj, bytes):
|
2020-09-03 08:54:25 +00:00
|
|
|
data = ""
|
|
|
|
try:
|
|
|
|
data = obj.decode('unicode-escape').encode('latin1').decode('utf-8')
|
|
|
|
except:
|
2021-04-04 14:24:46 +00:00
|
|
|
print(obj)
|
2020-09-03 08:54:25 +00:00
|
|
|
data = str(obj)[2:-1]
|
|
|
|
return data
|
|
|
|
|
2020-08-08 11:44:16 +00:00
|
|
|
return obj
|
|
|
|
|
2021-04-04 14:24:46 +00:00
|
|
|
# bigsnitch Request type
|
|
|
|
class bRequest:
|
|
|
|
server_ip_address = ""
|
|
|
|
|
|
|
|
tls = ""
|
|
|
|
content = ""
|
|
|
|
scheme = ""
|
|
|
|
method = ""
|
|
|
|
host = ""
|
|
|
|
port = 0
|
|
|
|
http_version = ""
|
|
|
|
path = ""
|
|
|
|
timestamp_start = 0.0
|
|
|
|
timestamp_end = 0.0
|
|
|
|
# [("Header","Data")]
|
|
|
|
headers = []
|
|
|
|
|
|
|
|
error = ""
|
|
|
|
|
|
|
|
class bResponse:
|
|
|
|
status_code = 0
|
|
|
|
http_version = ""
|
|
|
|
reason = ""
|
|
|
|
content = ""
|
|
|
|
timestamp_start = 0.0
|
|
|
|
timestamp_end = 0.0
|
|
|
|
# [("Header","Data")]
|
|
|
|
headers = []
|
|
|
|
|
|
|
|
class bFlow:
|
|
|
|
uid = "";
|
|
|
|
request = None
|
|
|
|
response = None
|
|
|
|
|
|
|
|
class FlowState(Enum):
|
|
|
|
UNSENT_REQ = 0
|
|
|
|
SENT_REQ = 1
|
|
|
|
UNSENT_RES = 2
|
|
|
|
SENT_REQ = 3
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
The network thread communicates with the bigsnitch plugin using zeromq.
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2020-08-12 13:11:29 +00:00
|
|
|
class NetworkThread(threading.Thread):
|
|
|
|
def __init__(self, name, queue):
|
|
|
|
threading.Thread.__init__(self)
|
|
|
|
self.name = name
|
|
|
|
self.q = queue
|
2021-04-04 14:24:46 +00:00
|
|
|
# id : (state, flow, timer, retries left)
|
|
|
|
self.flows = {}
|
2020-08-12 13:11:29 +00:00
|
|
|
self.context = zmq.Context()
|
2021-04-04 14:24:46 +00:00
|
|
|
# timer for sending pings
|
|
|
|
self.timer = time.monotonic()
|
|
|
|
# retries left for reconnecting
|
|
|
|
self.retries = 5
|
|
|
|
|
|
|
|
# get new flows that may occured
|
|
|
|
def update_flows(self):
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
i, flow, typ = self.q.get(block=False)
|
|
|
|
if flows.get(i, None):
|
|
|
|
print("flow {} doubled? ignoring...")
|
|
|
|
continue
|
|
|
|
|
|
|
|
if typ == "request":
|
|
|
|
flows[i] = (FlowState.UNSENT_REQ, flow, self.timer, 5)
|
|
|
|
elif typ == "response":
|
|
|
|
flows[i] = (FlowState.UNSENT_RES, flow, self.timer, 5)
|
|
|
|
except Empty:
|
|
|
|
break
|
|
|
|
|
|
|
|
# state machine for flows
|
|
|
|
def handle_flow(self, state, flow):
|
|
|
|
for i, v in flows.items:
|
|
|
|
state, flow, timer, retries = v
|
|
|
|
|
|
|
|
if state == FlowState.UNSENT_REQ:
|
|
|
|
# send it
|
|
|
|
a = convert_to_strings(msg)
|
|
|
|
self.socket.send(str.encode(json.dumps(a)))
|
|
|
|
pass
|
|
|
|
elif state == FlowState.SENT_REQ:
|
|
|
|
# check timer, try resend
|
|
|
|
pass
|
|
|
|
elif state == FlowState.UNSENT_RES:
|
|
|
|
pass
|
|
|
|
elif state == FlowState.SENT_RES:
|
|
|
|
pass
|
|
|
|
|
|
|
|
self.send_msg_and_ack(a)
|
|
|
|
|
|
|
|
def handle_packets(self):
|
|
|
|
while((self.socket.poll(50) & zmq.POLLIN) != 0):
|
|
|
|
msg = self.socket.recv()
|
|
|
|
try:
|
|
|
|
if msg:
|
|
|
|
result = json.loads(msg)
|
|
|
|
# packet statemachine
|
|
|
|
if result["msg"] == "ack":
|
|
|
|
print("m ack received")
|
|
|
|
return result
|
|
|
|
else:
|
|
|
|
print("got unexpected message {result}")
|
|
|
|
except json.JSONDecodeError:
|
|
|
|
print(f"malformed message received {msg}")
|
2020-08-08 11:44:16 +00:00
|
|
|
|
2020-08-12 13:11:29 +00:00
|
|
|
def run(self):
|
2020-08-13 03:35:22 +00:00
|
|
|
print("thread started")
|
2020-08-12 13:11:29 +00:00
|
|
|
self.connect()
|
2020-08-13 03:35:22 +00:00
|
|
|
while True:
|
2021-04-04 14:24:46 +00:00
|
|
|
self.timer = time.monotonic()
|
|
|
|
update_flows()
|
|
|
|
|
|
|
|
if self.timer - time.monotonic() < -5:
|
|
|
|
self.send_msg_and_ack({"msg": "ping"})
|
|
|
|
|
|
|
|
def send(self, msg):
|
|
|
|
a = convert_to_strings(msg)
|
|
|
|
self.socket.send(str.encode(json.dumps(a)))
|
|
|
|
|
2020-08-12 13:11:29 +00:00
|
|
|
def disconnect(self):
|
|
|
|
self.socket.setsockopt(zmq.LINGER,0)
|
|
|
|
self.socket.close()
|
2020-08-13 03:35:22 +00:00
|
|
|
print("disconnected")
|
2020-08-12 13:11:29 +00:00
|
|
|
|
|
|
|
def reconnect(self):
|
|
|
|
self.disconnect()
|
2020-08-13 03:35:22 +00:00
|
|
|
time.sleep(1)
|
2020-08-12 13:11:29 +00:00
|
|
|
self.connect()
|
2020-08-08 11:44:16 +00:00
|
|
|
|
2020-08-12 13:11:29 +00:00
|
|
|
def connect(self):
|
|
|
|
self.socket = self.context.socket(zmq.PAIR)
|
|
|
|
self.socket.connect("tcp://127.0.0.1:12345")
|
2020-08-13 03:35:22 +00:00
|
|
|
self.send_msg_and_ack({"msg": "ping"})
|
|
|
|
print("successfully connected")
|
2021-04-04 14:24:46 +00:00
|
|
|
|
2020-08-13 03:35:22 +00:00
|
|
|
def send_msg_and_ack(self, msg):
|
2021-04-04 14:24:46 +00:00
|
|
|
self.timer = time.monotonic()
|
2020-08-13 03:35:22 +00:00
|
|
|
while True:
|
2020-08-21 18:40:37 +00:00
|
|
|
print("m sending")
|
2020-08-12 13:11:29 +00:00
|
|
|
a = convert_to_strings(msg)
|
|
|
|
self.socket.send(str.encode(json.dumps(a)))
|
2020-08-21 18:40:37 +00:00
|
|
|
if (self.socket.poll(50) & zmq.POLLIN) != 0:
|
2020-08-12 13:11:29 +00:00
|
|
|
msg = self.socket.recv()
|
|
|
|
try:
|
|
|
|
if msg:
|
|
|
|
result = json.loads(msg)
|
2020-08-13 03:35:22 +00:00
|
|
|
if result["msg"] == "ack":
|
2020-08-21 18:40:37 +00:00
|
|
|
print("m ack received")
|
2020-08-12 13:11:29 +00:00
|
|
|
return result
|
|
|
|
else:
|
|
|
|
print("got unexpected message {result}")
|
|
|
|
|
|
|
|
except json.JSONDecodeError:
|
|
|
|
print(f"malformed message received {msg}")
|
2020-08-13 03:35:22 +00:00
|
|
|
print("no ack received, reconnecting...")
|
2020-08-12 13:11:29 +00:00
|
|
|
self.reconnect()
|
2020-08-19 00:17:42 +00:00
|
|
|
return NO_MSG
|
2020-08-05 00:08:41 +00:00
|
|
|
|
2021-01-01 23:19:30 +00:00
|
|
|
class BigSnitchBridge:
|
2020-08-05 00:08:41 +00:00
|
|
|
def __init__(self):
|
|
|
|
self.q = Queue()
|
2020-08-12 13:11:29 +00:00
|
|
|
self.thread = NetworkThread("network", self.q)
|
2020-08-05 00:08:41 +00:00
|
|
|
self.thread.start()
|
2021-04-04 14:24:46 +00:00
|
|
|
|
2020-08-05 00:08:41 +00:00
|
|
|
def request(self, flow):
|
2021-04-04 14:24:46 +00:00
|
|
|
self.q.put_nowait((flow.id, flow, "request"))
|
|
|
|
# intercept until ACK received
|
|
|
|
flow.intercept()
|
|
|
|
|
|
|
|
def response(self, flow):
|
|
|
|
self.q.put_nowait((flow.id, flow, "response"))
|
2021-03-21 01:35:40 +00:00
|
|
|
# intercept until ACK received
|
|
|
|
flow.intercept()
|
2020-08-05 00:08:41 +00:00
|
|
|
|
2021-04-04 14:24:46 +00:00
|
|
|
def error(self, flow):
|
|
|
|
self.q.put_nowait((flow.id, flow, "error"))
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
2020-09-02 22:23:37 +00:00
|
|
|
def requestheaders(self, flow):
|
|
|
|
self.q.put({'msg': 'requestheaders', 'flow': flow.get_state()})
|
|
|
|
|
|
|
|
def responseheaders(self, flow):
|
|
|
|
self.q.put({'msg': 'responseheaders', 'flow': flow.get_state()})
|
|
|
|
|
|
|
|
def websocket_handshake(self):
|
|
|
|
self.q.put({'msg': 'websocket_handshake', 'flow': flow.get_state()})
|
|
|
|
|
|
|
|
def websocket_start(self, flow):
|
|
|
|
self.q.put({'msg': 'websocket_start', 'flow': flow.get_state()})
|
|
|
|
|
|
|
|
def websocket_message(self, flow):
|
|
|
|
self.q.put({'msg': 'websocket_message', 'flow': flow.get_state()})
|
|
|
|
|
|
|
|
def websocket_error(self, flow):
|
|
|
|
self.q.put({'msg': 'websocket_error', 'flow': flow.get_state()})
|
|
|
|
|
|
|
|
def websocket_end(self, flow):
|
|
|
|
self.q.put({'msg': 'websocket_end', 'flow': flow.get_state()})
|
2021-04-04 14:24:46 +00:00
|
|
|
"""
|
|
|
|
|
2020-08-05 00:08:41 +00:00
|
|
|
addons = [
|
2021-01-01 23:19:30 +00:00
|
|
|
BigSnitchBridge()
|
2020-08-05 00:08:41 +00:00
|
|
|
]
|