SSLproxy icon indicating copy to clipboard operation
SSLproxy copied to clipboard

Modify packet by nftables raw payload expression, without a listening program

Open Oniicyan opened this issue 10 months ago • 3 comments

I have a simple scenario that I need to modify port=12345 in the HTTP request to port=54321. The nftables rules like this work properly on non-encrypted HTTP packets:

chain BTTR_HTTP {
	@ih,768,80 0x706f72743d3132333435 @ih,800,48 set 0x3d3534333231 update @BTTR_HTTP { ip daddr . tcp dport } counter
}

I've been bothering how to modify HTTPS encrypted packets until I encounter SSLProxy. I have basic scripting skills and network knowledge, but I don't have the ability to write a listening program. Therefore, I intend to use nftables to emulate the work of the listening program.

The nftables rules like this:

chain encrypted {
	type nat hook output priority dstnat; policy accept;
	skuid != 58443 tcp dport 443 counter redirect to 58443
 }
chain decrypted {
	type filter hook prerouting priority filter; policy accept;
	ip daddr 127.0.0.1 tcp dport 50080 @ih,0,32 0x47455420 counter tcp sport set 50080 tcp dport set tcp sport
}
chain BTTR {
	type filter hook postrouting priority filter; policy accept;
	@ih,0,112 0x474554202f616e6e6f756e63653f add @BTTR_HTTP { ip daddr . tcp dport } goto BTTR_HTTP
}

Then run sslproxy -d -u sslproxy -k ca.key -c ca.crt https 0.0.0.0 58443 up:50080.

In fact, this attempt was almost successful. I returned the packets without a real listening program, nftables recognized the decrypted HTTP data and modified it. The problem now is that with netfilter, I can only return the packet to the source port of the current connection, not the dynamically assigned port specified in the SSLproxy line.

Image

I'll look into whether it's possible to use socat or other existing programs to identify the SSLproxy line and send back packets correctly.


Here are the feature requests.

Can other methods be used to determine the port that receives the modified packet? For example, the source/destination port of the current connection +1, or a fixed port specified at startup.

It can even be added to have a "reflection" feature that allows SSLproxy itself to act as a listening program and respond correctly so that netfilter and other framework can manage the decrypted HTTP data.

This can make SSLproxy more friendly!

Oniicyan avatar Feb 16 '25 06:02 Oniicyan

This works: sslproxy -d -k CA.key -c CA.crt ssl 0.0.0.0 58443 up:50080 socat TCP-LISTEN:50080,fork EXEC:socat.sh &

The socat.sh is:

#!/bin/bash
read LINE
PORT=$(echo $LINE | awk -F ':|,' '{print$3}')
exec socat - TCP:127.0.0.1:$PORT

It only works for the ssl proxyspec, where the SSLproxy line will appear on the first line of inner layer (before the HTTP request).


I improved the socat.sh. The port in the SSLproxy line can now be accurately identified and the full HTTP request is sent back after stripping. This script works on the https proxysepc, too.

#!/bin/bash
while read -r LINE; do
	CONTENT="$CONTENT"$'\n'$LINE
	[[ $LINE == $'\r' ]] && {
		CONTENT_LENGTH=$(echo "$CONTENT" | grep -i 'Content-Length' | awk '{print $2}' | tr -d '\r')
		[ $CONTENT_LENGTH ] && {
			read -r -d '' -n $CONTENT_LENGTH BODY
			CONTENT="$CONTENT"$'\n'"$BODY"
		}
		break
	}
done
PORT=$(echo "$CONTENT" | grep SSLproxy | awk -F ':|,' '{print$3}')
CONTENT=$(echo "$CONTENT" | sed -e '1{/^$/d}' -e '/SSLproxy/d')
exec socat -,ignoreeof TCP:127.0.0.1:$PORT <<<$CONTENT

Oniicyan avatar Feb 23 '25 18:02 Oniicyan

So do you now have a working solution, with your last socat.sh? Note that there is already a listening program in the sources, which lives under tests/testproxy/lp. Perhaps lp can help if you can modify it.

As for the feature requests,

The dynamically assigned port numbers are very important, because this is how we keep track of connections. If we did use a fixed port number, we would need to maintain a table of connections, and assign id numbers to each of the connections somehow, and so on, which is not easy (note that sslproxy runs multithreaded, to say the least). If we use port numbers, the operating system keeps track of those connections for us.

I am not sure I fully understand the reflection feature, but the purpose of SSLproxy is to decrypt and divert network traffic to listening programs. Btw, sslproxy supports the split mode of operation similar to SSLsplit, which does not require a listening program, but I guess that's not what you were hoping for.

What you probably mean by reflection is that if sslproxy could send packets to that dynamically assigned port number that sslproxy itself is listening on, instead of the port number of the listening program (no listening program, no divert port at all). What this basically does is that sslproxy decrypts the traffic, feeds it to the loopback interface, and captures them back itself, so that any program monitoring the packets on the loopback interface can inspect them. (Can those programs also modify those packets? Not sure, because there is no fixed port number on either side, src or dst, of connections.) This seems possible on first thought, but we should think about it in detail.

Sorry for the late reply.

sonertari avatar May 03 '25 21:05 sonertari

So do you now have a working solution, with your last socat.sh?

Yes.

Your understanding of the reflection feature I mentioned is completely correct.

In my scenario, I monitor and modify packets on the loopback interface using nftables. Even without a fixed port, nftables can filter packets from SSLProxy using the skuid. Alternatively, inspecting all packets on the loopback interface isn't really a problem either.

Oniicyan avatar May 08 '25 10:05 Oniicyan