sonertari / SSLproxy

Transparent SSL/TLS proxy for decrypting and diverting network traffic to other programs, such as UTM services, for deep SSL inspection
BSD 2-Clause "Simplified" License
385 stars 100 forks source link

Is this a fork and rebrand of SSLSplit? #1

Open TechnikEmpire opened 6 years ago

TechnikEmpire commented 6 years ago

Just curious.

sonertari commented 6 years ago

Good question, so I can explain the differences better.

SSLproxy is based on SSLsplit, a very nice work by Daniel.

With SSLsplit, you can decrypt and view encrypted packets. Great if that's all you need. But if you want to process (e.g. modify) those packets, SSLsplit does not provide a scripting language, nor can it divert them to another program. You need to modify the SSLsplit code in C if you need to process the packets (that's what I was doing before).

In contrast, SSLproxy decrypts the packets and diverts them to a program as defined in a specification in its configuration file (you can define multiple such specifications). The program can do whatever it wants to the packet and gives it back to the SSLproxy (the program can be written in any programming language). The details of this operation are described in the README file. However, this was easier to describe than to design and implement.

Above is the main difference, and my reason to start the SSLproxy project. But, there are a couple of further differences too:

The mode of operation supported by SSLproxy enables us to decrypt and divert the traffic to UTM services (this operation is so successful that the UTM service should be informed whether the original traffic was encrypted or not). In fact, my UTMFW project uses SSLproxy exactly for this purpose. You can download the installation iso file of UTMFW to try and see for yourself.

I have tried not to break any already available feature in SSLsplit, but I didn't test if certain features work, e.g. autossl. As obvious from my purpose, i.e. deep inspection of SSL/TLS traffic using UTM services, SSLproxy has been tested using UTMFW, hence on OpenBSD only. I'd like to hear if it runs on other systems as well.

TechnikEmpire commented 6 years ago

@sonertari Wow, thanks for the detailed explanation. Great work. I took notice of it because I'm working on porting a transparent filtering proxy from Windows to OSX/MacOS so anything "proxy" jumps out at me. Quite an impressive project.

One thing I'm curious about (forgive me if you said it already and I missed it, quite tired) is if the listening/modifying program can collect the packets from SSLProxy like a buffered stream, or is it a single packet context? To explain better, consider the use case where the listening application wants to collect a full HTML payload for inspection and possible modification before permitting any of the payload to finish passing down/upstream via SSLProxy. Would that be a supported use case?

sonertari commented 6 years ago

Everything between the SSLproxy and the listening program is exchanged in packets over networking sockets (SSLproxy/SSLsplit use bufferevents in libevent, which is responsible for all such lower level details as sockets and buffering). So, it is the responsibility of the listening program to reassemble any/all parts of an HTTP communication. In the case of UTMFW, this program is the combination of E2guardian web filter and Squid HTTP proxy working in cascade. They do exactly what you are describing. (Btw, the other listening programs in UTMFW are POP3 and SMTP proxies.)

sonertari commented 6 years ago

To explain my design decisions further, an important benefit of using packets over networking sockets between the SSLproxy and the listening program is that I use OpenBSD/pf to divert those packets to Snort IPS over divert sockets too. So, before E2guardian+Squid, POP3, and SMTP proxies receive those packets, the packet filter gives them first to Snort for intrusion detection and prevention. This way both plain and encrypted traffic can be inspected for intrusions, in both directions, from and to the SSLproxy. Note that if the communication between the SSLproxy and the listening program were not using packets over networking sockets, this inline inspection by Snort IPS would not be possible (or it would be quite difficult, if at all possible).

Sfinx commented 6 years ago

Greetings,

Can the SSLproxy be used for intercepting of some binary proprietary protocols that use the TLS transport ?

sonertari commented 6 years ago

Another good question to explain the details.

The critical part is the SSLproxy specific line it inserts into the first packet in the connection. Refer to the README file for the explanation of the information transferred by that line.

Here are the important points if you want to support a binary protocol in SSLproxy, if possible at all:

  1. SSLproxy code should be modified to insert the same line or a set of bytes containing the same information into the first packet in the connection.
  2. The program that the packets will be diverted to should be modified to extract that information and parse it, especially the dynamically assigned address, which the packets are expected to be returned to SSLproxy. And the program should return the first and subsequent packets to that address instead of sending them to their original destination.
  3. SSLproxy should remove those bytes in the first packet when it receives the packet back, and before sending it to its destination (currently that's the solution I am using with the supported protocols). Either that or the program should remove them from the packet before returning the packet back to SSLproxy.
  4. To do all that, you should know the details of the binary protocol you are planning to support in SSLproxy, so that you know if it is possible or not, and if possible where in the packet you can insert that info. If it is a proprietary protocol, this may not be possible, even with reverse engineering.
  5. Whether the binary protocol allows for such modifications in the packets or not may be important, depending on how the packets are handled in the packet handling software on the sending and receiving ends, which may not be just SSLproxy and the program that the packets will be diverted to (I mean, is there a closed sourced protocol stack on both ends, which processes the packets after receiving from SSLproxy and before handing over to the program?)
  6. The packets should be diverted to a port SSLproxy is listening, of course.
Sfinx commented 6 years ago

Well, I think that some unification in SSLproxy and its backends protocol should be made here. I intend to rewrite the SSLproxy backend protocol in the following way:

a) define backend packet types according to events (new connection, data flow, close) b) define predictable fixed data fileds (unique connection id, data len, close reason, etc.) c) SSLproxy & backend app will prefix each data flow with above packet type d) SSLproxy & backend app will remove this prefix header before processing the actual event

This way no need in complex custom high level protocol handling - the same backend protocol can be used for any high level protocols transparently including binary proprietary ones.

Sfinx commented 6 years ago

I've decided not to patch SSLproxy heavily and implemented bssl option - https://github.com/Sfinx/bmitm

the simple null c++ app included. I think that real patch for SSLproxy must be:

diff --git a/pxyconn.c b/pxyconn.c
index 77a1a78..1e38372 100644
--- a/pxyconn.c
+++ b/pxyconn.c
@@ -2223,7 +2223,7 @@ pxy_bev_readcb(struct bufferevent *bev, void *arg)

                        // @attention Cannot use string manipulation functions; we are dealing with binary arrays here, not NULL-terminated strings
                        if (!ctx->sent_header) {
-                               if (ctx->spec->mail) {
+                               if (ctx->spec->ssl || ctx->spec->mail) {
                                        memmove(packet + header_len + 2, packet, packet_size);
                                        memcpy(packet, ctx->header_str, header_len);
                                        memcpy(packet + header_len, "\r\n", 2);

So stock ssl option can be used with arbitrary [binary] protocols as it will just insert the header to the beginning of the first packet and this way header can be easily parsed by app. It is not clear why it is not done for all protocols (?!)

sonertari commented 6 years ago

The points 4 and 5 in my comment above explain possible issues with trying to support other protocols (binary, proprietary, or not). You may want to read those points again. But in brief, when you're talking about binary (proprietary) protocols, you should think beyond compressed http.

Btw, you could achieve the same result by using pop3(s) or smtp(s) proxy spec instead of modifying the code to add a new option.

Sfinx commented 6 years ago

The main idea of my patch is to make SSLproxy neutral to any binary protocol without any prior knowing it and this is already works for me.

The pop3/smtp is just confusing names here as they are not binary protocols. I thought that ssl must work here according to its description in docs, but it inserts header after first packet so there no way to parse arbitrary protocol without prior knowing it. I think this is the main bug of ssl option.

sonertari commented 6 years ago

OK, you are right about documentation. I have committed and pushed a new revision which enables SSLproxy header insertion similar to pop3 and smtp for all protocols other than http. So, in your case, you should use an ssl proxyspec now. Please try and report if it works for you.

Note to all users: My warnings above about possible issues with inserting SSLproxy specific header in the first packet still remain for protocols other than http, pop3, and smtp.

Sfinx commented 6 years ago

Thanks, it works now with stock ssl option. I've updated the C++ null app accordingly. Anybody that wish to use it with SSLproxy will have two simple callbacks:

bool app_tx_cb(uint cid, string &d)
{
 cerr << "conn N" << cid << " app_tx: sz: " << d.size() << endl;
 return true;
}

bool app_rx_cb(uint cid, string &d)
{
 cerr << "conn N" << cid << " app_rx: sz: " << d.size() << endl;
 return true;
}

string d contains app data that user can parse/change as he/she wish.