bigsnitch/mitmaddon/bigsnitch.py

288 lines
9.2 KiB
Python
Raw Normal View History

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-21 22:41:13 +00:00
from dataclasses import dataclass
from typing import List
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-21 22:41:13 +00:00
#
# bigsnitch communication types
#
2020-08-08 11:44:16 +00:00
2021-04-21 22:41:13 +00:00
@dataclass
class bHeader:
key: str
value: str
@dataclass
2021-04-04 14:24:46 +00:00
class bRequest:
2021-04-21 22:41:13 +00:00
server_ip_address: str
tls: str
content: str
scheme: str
method: str
host: str
port: int
http_version: str
path: str
timestamp_start: float
timestamp_end: float
2021-04-04 14:24:46 +00:00
# [("Header","Data")]
2021-04-21 22:41:13 +00:00
headers: List[bHeader]
error: str
def __init__(self, flow: dict):
self.server_ip_address = flow["server_ip_address"]
self.tls = flow["server_conn"]["tls_established"]
self.content = flow["content"]
self.scheme = flow["scheme"]
self.method = flow["method"]
self.host = flow["host"]
self.port = flow["port"]
self.http_version = flow["http_version"]
self.timestamp_start = flow["timestamp_start"]
self.timestamp_end = flow["timestamp_end"]
for k,v in flow["headers"]:
self.headers.append(bHeader(k,v))
@dataclass
2021-04-04 14:24:46 +00:00
class bResponse:
2021-04-21 22:41:13 +00:00
status_code: int
http_version: str
reason: str
content: str
timestamp_start: float
timestamp_end: float
2021-04-04 14:24:46 +00:00
# [("Header","Data")]
2021-04-21 22:41:13 +00:00
headers: List[bHeader]
def __init__(self, flow: dict):
self.status_code = flow["status_code"]
self.http_version = flow["http_version"]
self.reason = flow["reason"]
self.content = flow["content"]
self.timestamp_start = flow["timestamp_start"]
self.timestamp_end = flow["timestamp_end"]
for k,v in flow["headers"]:
self.headers.append(bHeader(k,v))
2021-04-04 14:24:46 +00:00
2021-04-21 22:41:13 +00:00
@dataclass
2021-04-04 14:24:46 +00:00
class bFlow:
2021-04-21 22:41:13 +00:00
uid: str;
request: bRequest
response: bResponse
def __init__(self, flow: dict):
self.uid = flow["id"]
self.request = bRequest(flow["request"])
self.response = bRequest(flow["response"])
2021-04-04 14:24:46 +00:00
class FlowState(Enum):
UNSENT_REQ = 0
SENT_REQ = 1
UNSENT_RES = 2
2021-04-21 22:41:13 +00:00
SENT_RES = 3
2021-04-04 14:24:46 +00:00
"""
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
2021-04-21 22:41:13 +00:00
# queue for communicating with the main mitmproxy thread
2020-08-12 13:11:29 +00:00
self.q = queue
2021-04-21 22:41:13 +00:00
# all current flows being handled by mitmproxy
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-21 22:41:13 +00:00
# timer for sending pings to check if the connection broke
2021-04-04 14:24:46 +00:00
self.timer = time.monotonic()
2021-04-21 22:41:13 +00:00
# retries left for reconnecting / resending a broken flow
2021-04-04 14:24:46 +00:00
self.retries = 5
2021-04-21 22:41:13 +00:00
# send a single message, no checks involved
def send(self, msg):
a = convert_to_strings(msg)
self.socket.send(str.encode(json.dumps(a)))
# add new flows from the queue
def get_new_flows(self):
2021-04-04 14:24:46 +00:00
while True:
try:
2021-04-21 22:41:13 +00:00
# get new flows that may occured
2021-04-04 14:24:46 +00:00
i, flow, typ = self.q.get(block=False)
2021-04-21 22:41:13 +00:00
if self.flows.get(i, None):
print(f"flow {i} doubled? ignoring...")
2021-04-04 14:24:46 +00:00
continue
2021-04-21 22:41:13 +00:00
# csave the new flows, if necessary
2021-04-04 14:24:46 +00:00
if typ == "request":
2021-04-21 22:41:13 +00:00
self.flows[i] = (FlowState.UNSENT_REQ, flow, time.monotonic(), 5)
2021-04-04 14:24:46 +00:00
elif typ == "response":
2021-04-21 22:41:13 +00:00
self.flows[i] = (FlowState.UNSENT_RES, flow, time.monotonic(), 5)
2021-04-04 14:24:46 +00:00
except Empty:
break
2021-04-21 22:41:13 +00:00
# update all current flows
def update_flows(self):
for k,v in self.flows.items():
2021-04-04 14:24:46 +00:00
state, flow, timer, retries = v
2021-04-21 22:41:13 +00:00
# state machine for flows
2021-04-04 14:24:46 +00:00
if state == FlowState.UNSENT_REQ:
2021-04-21 22:41:13 +00:00
msg = b""
# send the request
self.send(msg)
2021-04-04 14:24:46 +00:00
pass
elif state == FlowState.SENT_REQ:
# check timer, try resend
pass
elif state == FlowState.UNSENT_RES:
pass
elif state == FlowState.SENT_RES:
pass
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()
2021-04-21 22:41:13 +00:00
self.get_new_flows()
self.handle_packets()
self.update_flows()
2021-04-04 14:24:46 +00:00
if self.timer - time.monotonic() < -5:
2021-04-21 22:41:13 +00:00
pass
#self.send_msg_and_ack({"msg": "ping"})
2021-04-04 14:24:46 +00:00
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:
2021-04-21 22:41:13 +00:00
#print("m sending")
self.send(msg)
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
]