yolo
This commit is contained in:
		
							parent
							
								
									f91da7a47c
								
							
						
					
					
						commit
						d8b52dcb44
					
				
					 12 changed files with 278 additions and 119 deletions
				
			
		|  | @ -34,11 +34,13 @@ add_executable(littlesnitch | |||
|     mainwindow.cpp | ||||
|     networkthread.cpp | ||||
|     session.cpp | ||||
|     mainwindow.ui | ||||
|     httpflow.cpp | ||||
|     mainwindow.h | ||||
|     httpflow.h | ||||
|     networkthread.h | ||||
|     session.h | ||||
|     includes.h | ||||
|     mainwindow.ui | ||||
| ) | ||||
| 
 | ||||
| target_link_libraries(littlesnitch PRIVATE Qt5::Widgets cppzmq sqlite3) | ||||
|  |  | |||
							
								
								
									
										2
									
								
								httpflow.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								httpflow.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| #include "httpflow.h" | ||||
| 
 | ||||
							
								
								
									
										153
									
								
								httpflow.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								httpflow.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,153 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <includes.h> | ||||
| 
 | ||||
| /*
 | ||||
| 
 | ||||
| { | ||||
|     "flow": { | ||||
|     "client_conn": { | ||||
|         "address": [ | ||||
|         "::1", | ||||
|         37570, | ||||
|         0, | ||||
|         0 | ||||
|         ], | ||||
|         "alpn_proto_negotiated": "http/1.1", | ||||
|         "cipher_name": "TLS_AES_256_GCM_SHA384", | ||||
|         "clientcert": null, | ||||
|         "id": "a1e82917-2d58-4b99-be9e-2b962bc499b2", | ||||
|         "mitmcert": "mitmcertstring", | ||||
|         "sni": "yolo.jetzt", | ||||
|         "timestamp_end": null, | ||||
|         "timestamp_start": 1597284668.6260498, | ||||
|         "timestamp_tls_setup": 1597284669.8449724, | ||||
|         "tls_established": true, | ||||
|         "tls_extensions": [...], | ||||
|         "tls_version": "TLSv1.3" | ||||
|     }, | ||||
|     "error": null, | ||||
|     "id": "a6aa4e6e-ca31-4f58-bf47-2da7bfcf0000", | ||||
|     "intercepted": false, | ||||
|     "marked": false, | ||||
|     "metadata": {}, | ||||
|     "mode": "transparent", | ||||
|     "request": { | ||||
|         "content": "", | ||||
|         "first_line_format": "relative", | ||||
|         "headers": [ | ||||
|         [ | ||||
|             "Host", | ||||
|             "yolo.jetzt" | ||||
|         ], | ||||
|         [ | ||||
|             "User-Agent", | ||||
|             "curl/7.68.0" | ||||
|         ], | ||||
|         [ | ||||
|             "Accept", | ||||
|         ] | ||||
|         ], | ||||
|         "host": "yolo.jetzt", | ||||
|         "http_version": "HTTP/1.1", | ||||
|         "is_replay": false, | ||||
|         "method": "GET", | ||||
|         "path": "/", | ||||
|         "port": 443, | ||||
|         "scheme": "https", | ||||
|         "timestamp_end": 1597284669.92817, | ||||
|         "timestamp_start": 1597284669.8761458 | ||||
|     }, | ||||
|     "response": null, | ||||
|     "server_conn": { | ||||
|         "address": [ | ||||
|         "yolo.jetzt", | ||||
|         443 | ||||
|         ], | ||||
|         "alpn_proto_negotiated": "http/1.1", | ||||
|         "cert": "certstring", | ||||
|         "id": "50a3b79d-2912-45f3-991b-c03406a1018f", | ||||
|         "ip_address": [ | ||||
|         "95.156.226.69", | ||||
|         443 | ||||
|         ], | ||||
|         "sni": "yolo.jetzt", | ||||
|         "source_address": [ | ||||
|         "192.168.42.102", | ||||
|         44949 | ||||
|         ], | ||||
|         "timestamp_end": null, | ||||
|         "timestamp_start": 1597284669.2133315, | ||||
|         "timestamp_tcp_setup": 1597284669.2892282, | ||||
|         "timestamp_tls_setup": 1597284669.584602, | ||||
|         "tls_established": true, | ||||
|         "tls_version": "TLSv1.2", | ||||
|         "via": null | ||||
|     }, | ||||
|     "type": "http", | ||||
|     "version": 7 | ||||
|     }, | ||||
|     "msg": "request" | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| namespace http { | ||||
| 
 | ||||
| struct Request | ||||
| { | ||||
|     std::string server_conn_id; | ||||
|     std::string server_ip_address; | ||||
| 
 | ||||
|     bool tls; | ||||
|     std::string content; | ||||
|     std::string scheme; | ||||
|     std::string method; | ||||
|     std::string host; | ||||
|     std::string address; | ||||
|     unsigned short port; | ||||
|     std::string http_version; | ||||
|     std::string path; | ||||
|     double timestamp_start; | ||||
|     double timestamp_end; | ||||
|     std::vector<std::tuple<std::string,std::string>> headers; | ||||
| 
 | ||||
|     std::string error; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| struct Response {}; | ||||
| 
 | ||||
| struct Flow { | ||||
|     Request request; | ||||
|     Response response; | ||||
| }; | ||||
| 
 | ||||
| inline void to_json(json& j, const Flow& flow) {} | ||||
| 
 | ||||
| inline void from_json(const json& j, Flow& flow) { | ||||
|     std::cout << std::setw(4) << j << "\n\n"; | ||||
|     if(!j.contains("flow")) { | ||||
|         return; | ||||
|     } | ||||
|     if(j.at("flow").contains("server_conn")) { | ||||
|         j.at("flow").at("server_conn").at("id").get_to(flow.request.server_conn_id); | ||||
|         j.at("flow").at("server_conn").at("tls_established").get_to(flow.request.tls); | ||||
|     } | ||||
|     if(j.at("flow").contains("request")) { | ||||
|         j.at("flow").at("request").at("port").get_to(flow.request.port); | ||||
|         j.at("flow").at("request").at("host").get_to(flow.request.host); | ||||
|         j.at("flow").at("request").at("scheme").get_to(flow.request.scheme); | ||||
|         j.at("flow").at("request").at("path").get_to(flow.request.path); | ||||
|         j.at("flow").at("request").at("content").get_to(flow.request.content); | ||||
|         j.at("flow").at("request").at("method").get_to(flow.request.method); | ||||
|         j.at("flow").at("request").at("http_version").get_to(flow.request.http_version); | ||||
| 
 | ||||
|     //j.at("server_conn").at("address").get_to(flow.server_conn_address);
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| Q_DECLARE_METATYPE(http::Flow) | ||||
|  | @ -5,5 +5,6 @@ | |||
| #include <sqlite3.h> | ||||
| #include <filesystem> | ||||
| #include <QDebug> | ||||
| #include <iostream> | ||||
| 
 | ||||
| using json = nlohmann::json; | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ MainWindow::MainWindow(QWidget *parent) | |||
|     : QMainWindow(parent), | ||||
|       ui(new Ui::MainWindow) | ||||
| { | ||||
|     int metatype_id = qRegisterMetaType<http::Flow>("httpflow"); | ||||
|     current_session = new Session(); | ||||
|     current_session->load("/tmp/littlesnitch.session"); | ||||
| 
 | ||||
|  | @ -16,8 +17,8 @@ MainWindow::MainWindow(QWidget *parent) | |||
|     worker->moveToThread(thread); | ||||
|     connect(thread, SIGNAL (started()), worker, SLOT (process())); | ||||
|     //connect(worker, SIGNAL (error(QString)), this, SLOT (errorString(QString)));
 | ||||
|     connect(worker, SIGNAL (httpMessage(json)), current_session, SLOT (saveRequest(json))); | ||||
|     connect(worker, SIGNAL (httpMessage(json)), this, SLOT (updateHistory(json))); | ||||
|     connect(worker, SIGNAL (httpMessage(http::Flow)), current_session, SLOT (saveRequest(http::Flow)), Qt::QueuedConnection); | ||||
|     connect(worker, SIGNAL (httpMessage(http::Flow)), this, SLOT (updateHistory(http::Flow)), Qt::QueuedConnection); | ||||
|     thread->start(); | ||||
| 
 | ||||
|     ui->setupUi(this); | ||||
|  | @ -40,10 +41,10 @@ MainWindow::~MainWindow() | |||
|     delete ui; | ||||
| } | ||||
| 
 | ||||
| void MainWindow::updateHistory(json data) { | ||||
|     ui->historyHTTPTable->setItem(0,0,new QTableWidgetItem(QString::fromStdString(data["id"]))); | ||||
| //    ui->historyHTTPTable->setItem(0,1,new QTableWidgetItem(QString::fromStdString(data.method)));
 | ||||
| //    ui->historyHTTPTable->setItem(0,2,new QTableWidgetItem(QString::fromStdString(data.url)));
 | ||||
| void MainWindow::updateHistory(http::Flow flow) { | ||||
|     ui->historyHTTPTable->setItem(0,0,new QTableWidgetItem(QString::fromStdString(flow.request.server_conn_id))); | ||||
|     ui->historyHTTPTable->setItem(0,1,new QTableWidgetItem(QString::fromStdString(flow.request.method))); | ||||
|     ui->historyHTTPTable->setItem(0,2,new QTableWidgetItem(QString::fromStdString(flow.request.host))); | ||||
|     //ui->historyHTTPTable->setItem(0,3,new QTableWidgetItem(QString::number(data.ttl)));
 | ||||
|     ui->historyHTTPTable->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); | ||||
| } | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #include <string> | ||||
| #include <session.h> | ||||
| #include <includes.h> | ||||
| #include <httpflow.h> | ||||
| 
 | ||||
| QT_BEGIN_NAMESPACE | ||||
| namespace Ui { class MainWindow; } | ||||
|  | @ -21,5 +22,5 @@ public: | |||
|     MainWindow(QWidget *parent = nullptr); | ||||
|     ~MainWindow(); | ||||
| public slots: | ||||
|     void updateHistory(json data); | ||||
|     void updateHistory(http::Flow flow); | ||||
| }; | ||||
|  |  | |||
|  | @ -8,18 +8,6 @@ import zmq | |||
| import json | ||||
| from enum import Enum | ||||
|          | ||||
| NO_MSG = {"msg": None} | ||||
| INIT_MSG = {"msg": "init"} | ||||
| ACK_MSG = {"msg": "ack"} | ||||
| PING_MSG = {"msg": "ping"} | ||||
| PONG_MSG = {"msg": "pong"} | ||||
| 
 | ||||
| class NetworkState(Enum): | ||||
|         DISCONNECTED = auto() | ||||
|         CONNECTED = auto() | ||||
|         PING = auto() | ||||
|         SENDING = auto() | ||||
|          | ||||
| def convert_to_strings(obj): | ||||
|         if isinstance(obj, dict): | ||||
|             return {convert_to_strings(key): convert_to_strings(value) | ||||
|  | @ -38,87 +26,57 @@ class NetworkThread(threading.Thread): | |||
|                 self.context = zmq.Context() | ||||
| 
 | ||||
|         def run(self): | ||||
|                 print("thread started") | ||||
|                 self.connect() | ||||
|                 msg = self.send_msg_and_expect() | ||||
|                 while True: | ||||
|                         timer = time.monotonic() | ||||
|                         a = None | ||||
|                         if not a: | ||||
|                                 try: | ||||
|                                         a = self.q.get(block=False) | ||||
|                                 except Empty: | ||||
|                                         pass | ||||
|                         if a: | ||||
|                                 self.send_msg_and_ack(a) | ||||
|                                 timer = time.monotonic() | ||||
|                         if timer - time.monotonic() < -5: | ||||
|                                 self.send_msg_and_ack({"msg": "ping"}) | ||||
|                                          | ||||
|         def disconnect(self): | ||||
|                 self.socket.setsockopt(zmq.LINGER,0) | ||||
|                 self.socket.close() | ||||
|                 print("disconnected") | ||||
|                  | ||||
|         def reconnect(self): | ||||
|                 self.disconnect() | ||||
|                 time.sleep(1) | ||||
|                 self.connect() | ||||
|          | ||||
|         def connect(self): | ||||
|                 self.socket = self.context.socket(zmq.PAIR) | ||||
|                 self.socket.connect("tcp://127.0.0.1:12345") | ||||
|                 self.send_msg_and_ack({"msg": "ping"}) | ||||
|                 print("successfully connected") | ||||
|          | ||||
|         def send_msg_and_expect(this, msg, expect, timeout=5, retries=3): | ||||
|                 while retries: | ||||
|         def send_msg_and_ack(self, msg): | ||||
|                 while True: | ||||
|                         a = convert_to_strings(msg) | ||||
|                         self.socket.send(str.encode(json.dumps(a))) | ||||
|                         if (client.poll(REQUEST_TIMEOUT) & zmq.POLLIN) != 0: | ||||
|                         if (self.socket.poll(5) & zmq.POLLIN) != 0: | ||||
|                                 msg = self.socket.recv() | ||||
|                                 try: | ||||
|                                         if msg: | ||||
|                                                 result = json.loads(msg) | ||||
|                                                 if result["msg"] in expect: | ||||
|                                                 if result["msg"] == "ack": | ||||
|                                                         return result | ||||
|                                                 else: | ||||
|                                                         print("got unexpected message {result}") | ||||
|                                          | ||||
|                                 except json.JSONDecodeError: | ||||
|                                         print(f"malformed message received {msg}") | ||||
|                         retries -= 1 | ||||
|                         print("no ack received, reconnecting...") | ||||
|                         self.reconnect() | ||||
|                 return NO_MSG | ||||
| 
 | ||||
|         """ | ||||
| def networking(q): | ||||
|         print("starting thread") | ||||
| 
 | ||||
|         state = NetworkState.DISCONNECTED | ||||
|         a = None | ||||
|         while state == NetworkState.DISCONNECTED: | ||||
|                 socket = context.socket(zmq.PAIR) | ||||
|                 socket.connect("tcp://127.0.0.1:12345") | ||||
|                 msg = get_msg(socket) | ||||
|                 if msg["msg"] == "init": | ||||
|                         send_msg(ACK_MSG, socket) | ||||
|                         state = NetworkState.CONNECTED | ||||
|                          | ||||
| 
 | ||||
| 
 | ||||
|                          | ||||
|                 timer = time.monotonic() | ||||
|                 while state != NetworkState.DISCONNECTED: | ||||
|                         if state == NetworkState.CONNECTED and timer - time.monotonic() >= 5: | ||||
|                                 timer = time.monotonic() | ||||
|                                 send_msg(PING_MSG,socket) | ||||
|                                 msg = get_msg(socket) | ||||
|                                 if msg["msg"] != "pong": | ||||
|                                         state = NetworkState. | ||||
|                          | ||||
|                         msg = get_msg(socket) | ||||
|                         if msg['msg'] == "ping": | ||||
|                                 send_msg(PONG_MSG, socket) | ||||
|                                 timer = time.monotonic() | ||||
| 
 | ||||
|                         if not a: | ||||
|                                 try: | ||||
|                                         a = q.get(block=False) | ||||
|                                 except Empty: | ||||
|                                         pass | ||||
|                         if a: | ||||
|                                 send_msg(a, socket) | ||||
|                                 msg = get_msg(socket) | ||||
|                                 if msg["msg"] == "ack": | ||||
|                                         timer = time.monotonic() | ||||
|                                         a = None | ||||
|                                         self.q.task_done() | ||||
|                                 else: | ||||
|                                         connected = False | ||||
| """ | ||||
| class LittleSnitchBridge: | ||||
|     def __init__(self): | ||||
|         self.q = Queue() | ||||
|  | @ -127,11 +85,9 @@ class LittleSnitchBridge: | |||
| 
 | ||||
|     def request(self, flow): | ||||
|         self.q.put({'msg': 'request', 'flow': flow.get_state()}) | ||||
|         self.q.join() | ||||
| 
 | ||||
|     def response(self, flow): | ||||
|         self.q.put({'msg': 'response', 'flow': flow.get_state()}) | ||||
|         self.q.join() | ||||
| 
 | ||||
| addons = [ | ||||
|         LittleSnitchBridge() | ||||
|  |  | |||
|  | @ -1,40 +1,70 @@ | |||
| #include "networkthread.h" | ||||
| #include <iostream> | ||||
| 
 | ||||
| NetworkThread::NetworkThread(QObject *parent) : QObject(parent) | ||||
| NetworkThread::NetworkThread(QObject *parent) : | ||||
|         QObject(parent), | ||||
|         context(1) | ||||
| { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void NetworkThread::process() { | ||||
| 	zmq::context_t ctx(1); | ||||
| 	zmq::socket_t sock(ctx, zmq::socket_type::pair); | ||||
| 
 | ||||
| void NetworkThread::connect() { | ||||
|     socket = new zmq::socket_t(context, zmq::socket_type::pair); | ||||
|     int linger = 0; | ||||
|     socket->setsockopt(ZMQ_LINGER, &linger, sizeof (linger)); | ||||
|     try { | ||||
| 		sock.bind("tcp://127.0.0.1:12345"); | ||||
|         socket->bind("tcp://127.0.0.1:12345"); | ||||
|     } catch (zmq::error_t err) { | ||||
|         qDebug() << "failed binding socket" << err.what(); | ||||
|         emit error(err.what()); | ||||
| 		return; | ||||
|         throw; | ||||
|     }; | ||||
|     qDebug() << "connected"; | ||||
| } | ||||
| 
 | ||||
| void NetworkThread::disconnect() { | ||||
|     delete socket; | ||||
|     qDebug() << "disconnected"; | ||||
| } | ||||
| 
 | ||||
| void NetworkThread::reconnect() { | ||||
|     this->disconnect(); | ||||
|     this->connect(); | ||||
| } | ||||
| 
 | ||||
| void NetworkThread::process() { | ||||
|     connect(); | ||||
|     while(true) { | ||||
| 		bool connected = false; | ||||
|         while(!connected) { | ||||
|             sock.send(zmq::buffer(INIT_MSG_STR.c_str(), INIT_MSG_STR.length()), zmq::send_flags::dontwait); | ||||
| 			zmq::message_t msg; | ||||
| 			const auto ret = sock.recv(msg, zmq::recv_flags::dontwait); | ||||
|         zmq::message_t response; | ||||
|         const auto ret = socket->recv(response, zmq::recv_flags::dontwait); | ||||
|         if(ret) { | ||||
|                 if(msg.to_string() == ACK_MSG_STR) { | ||||
| 					connected = true; | ||||
|             auto j = json::parse(response.to_string()); | ||||
|             //std::cout << std::setw(4) << j << "\n\n";
 | ||||
|             if(j.contains("msg") && j["msg"].is_string()) { | ||||
|                 std::string msg_type; | ||||
|                 try { | ||||
|                     j["msg"].get_to(msg_type); | ||||
|                 } catch (nlohmann::detail::type_error& err) { | ||||
|                     qDebug() << "json type error, message type not a string: " << response.to_string().c_str(); | ||||
|                     emit error("no message type"); | ||||
|                 } | ||||
|                 if(msg_type == "request") { | ||||
|                 } else if (msg_type == "response") { | ||||
|                     try { | ||||
|                         emit httpMessage(j); | ||||
|                     } catch (nlohmann::detail::type_error& err) { | ||||
|                         qDebug() << "error reading HTTP Flow"; | ||||
|                         emit error("error converting to flow"); | ||||
|                     } | ||||
|                 } else if(msg_type == "ping") { | ||||
|                 } else { | ||||
|                     qDebug() << "unknown or broken message type received: " << msg_type.c_str(); | ||||
|                     emit error("unknown message"); | ||||
|                 } | ||||
| 		while(connected) { | ||||
|             zmq::message_t msg; | ||||
|             const auto ret = sock.recv(msg, zmq::recv_flags::dontwait); | ||||
| 			if(ret) { | ||||
|                 qDebug() << msg.to_string().c_str(); | ||||
|                 emit httpMessage(json(msg.to_string())); | ||||
|                 std::string m = "{\"msg\": \"ack\"}"; | ||||
|                 socket->send(zmq::buffer(m.c_str(), m.length()), zmq::send_flags::dontwait); | ||||
|             } else { | ||||
|                 qDebug() << "broken message, but correct json: " << response.to_string().c_str(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -2,15 +2,17 @@ | |||
| 
 | ||||
| #include <QObject> | ||||
| #include <includes.h> | ||||
| 
 | ||||
| const std::string INIT_MSG_STR = R"({"msg": "init"})"; | ||||
| const std::string ACK_MSG_STR = R"({"msg": "ack"})"; | ||||
| #include <httpflow.h> | ||||
| 
 | ||||
| class NetworkThread : public QObject | ||||
| { | ||||
|     Q_OBJECT | ||||
| private: | ||||
|     // add your variables here
 | ||||
|     zmq::context_t context; | ||||
|     zmq::socket_t *socket; | ||||
|     void connect(); | ||||
|     void disconnect(); | ||||
|     void reconnect(); | ||||
| public: | ||||
|     explicit NetworkThread(QObject *parent = nullptr); | ||||
| public slots: | ||||
|  | @ -18,5 +20,5 @@ public slots: | |||
| signals: | ||||
|     void finished(); | ||||
|     void error(QString err); | ||||
|     void httpMessage(json data); | ||||
|     void httpMessage(http::Flow flow); | ||||
| }; | ||||
|  |  | |||
|  | @ -32,6 +32,6 @@ void Session::unload() { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void Session::saveRequest(json data) { | ||||
| void Session::saveRequest(http::Flow flow) { | ||||
|     // todo
 | ||||
| } | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| 
 | ||||
| #include <QObject> | ||||
| #include <includes.h> | ||||
| #include <httpflow.h> | ||||
| 
 | ||||
| //  static int callback(void *NotUsed, int argc, char **argv, char **azColName){
 | ||||
| //    int i;
 | ||||
|  | @ -51,7 +52,7 @@ public: | |||
|     void unload(); | ||||
|     bool isLoaded(); | ||||
| public slots: | ||||
|     void saveRequest(json data); | ||||
|     void saveRequest(http::Flow flow); | ||||
| signals: | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
							
								
								
									
										12
									
								
								test.sh
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								test.sh
									
										
									
									
									
								
							|  | @ -4,5 +4,15 @@ mitmdump -k -p 8080 -s mitmaddon/littlesnitch.py & | |||
| ./build/littlesnitch & | ||||
| 
 | ||||
| sleep 1 | ||||
| curl -x http://localhost:8080 -k https://yolo.jetzt | ||||
| curl -x http://localhost:8080 -k https://get.yolo.jetzt | ||||
| sleep 1 | ||||
| curl -x http://localhost:8080 -k https://get.yolo.jetzt | ||||
| 
 | ||||
| killall mitmdump | ||||
| sleep 1 | ||||
| killall mitmdump | ||||
| sleep 2 | ||||
| killall mitmdump | ||||
| sleep 3 | ||||
| killall mitmdump | ||||
| killall mitmdump | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue