From f698f6c0923b1ffe390b32d22c36ee0c9e2c8b93 Mon Sep 17 00:00:00 2001 From: Peace-Maker Date: Thu, 30 Jan 2025 11:40:12 +0100 Subject: [PATCH 1/3] Add `ssh -L` / `ssh.connect_remote()` workaround when `AllowTcpForwarding` is disabled Use a netcat process on the remote to connect to the specified host:port and tunnel the traffic using normal `ssh.process` I/O. This was inspired by the "Circumventing Disabled SSH Port-Forwarding with a Multiplexer" article by @guysv in the Paged Out! zine no. 5. It allows to use `gdb.debug(arg, ssh=ssh)` to debug processes on pwn.college. --- pwnlib/tubes/ssh.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pwnlib/tubes/ssh.py b/pwnlib/tubes/ssh.py index 6c76746b7..b39026f0f 100644 --- a/pwnlib/tubes/ssh.py +++ b/pwnlib/tubes/ssh.py @@ -441,14 +441,34 @@ def __init__(self, parent, host, port, *a, **kw): # keep the parent from being garbage collected in some cases self.parent = parent + # keep reference to tunnel process to avoid garbage collection + self.tunnel = None + self.host = parent.host self.rhost = host self.rport = port + import paramiko.ssh_exception msg = 'Connecting to %s:%d via SSH to %s' % (self.rhost, self.rport, self.host) with self.waitfor(msg) as h: try: self.sock = parent.transport.open_channel('direct-tcpip', (host, port), ('127.0.0.1', 0)) + except paramiko.ssh_exception.ChannelException as e: + # Workaround AllowTcpForwarding no in sshd_config + if e.args != (1, 'Administratively prohibited'): + self.exception(str(e)) + raise e + + self.debug('Failed to open channel, trying to connect to remote port manually using netcat.') + if parent.which('nc'): + ncat = 'nc' + elif parent.which('ncat'): + ncat = 'ncat' + else: + self.exception('Could not find ncat or nc on remote. Cannot connect to remote port.') + raise + self.tunnel = parent.process([ncat, host, str(port)]) + self.sock = self.tunnel.sock except Exception as e: self.exception(str(e)) raise @@ -949,6 +969,7 @@ def process(self, argv=None, executable=None, tty=True, cwd=None, env=None, igno self.upload_data(script, tmpfile) return tmpfile + executable = executable or argv[0] if self.isEnabledFor(logging.DEBUG): execve_repr = "execve(%r, %s, %s)" % (executable, argv, From 306a859c8b60c1e5ebe52699e2e134914c2dee50 Mon Sep 17 00:00:00 2001 From: Peace-Maker Date: Thu, 30 Jan 2025 11:51:59 +0100 Subject: [PATCH 2/3] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f63c1ddc..2ed173e82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,6 +83,7 @@ The table below shows which release corresponds to each branch, and what date th - [#2527][2527] Allow setting debugger path via `context.gdb_binary` - [#2530][2530] Do NOT error when passing directory arguments in `checksec` commandline tool. - [#2529][2529] Add LoongArch64 support +- [#2538][2538] Add `ssh -L` / `ssh.connect_remote()` workaround when `AllowTcpForwarding` is disabled [2519]: https://github.com/Gallopsled/pwntools/pull/2519 [2507]: https://github.com/Gallopsled/pwntools/pull/2507 @@ -93,6 +94,7 @@ The table below shows which release corresponds to each branch, and what date th [2527]: https://github.com/Gallopsled/pwntools/pull/2527 [2530]: https://github.com/Gallopsled/pwntools/pull/2530 [2529]: https://github.com/Gallopsled/pwntools/pull/2529 +[2538]: https://github.com/Gallopsled/pwntools/pull/2538 ## 4.15.0 (`beta`) From 6f25755d791133c3ffd292304d49d4c4ebb8e088 Mon Sep 17 00:00:00 2001 From: peace-maker Date: Sat, 1 Feb 2025 02:02:57 +0100 Subject: [PATCH 3/3] Lookup `netcat` command too Co-authored-by: Xeonacid --- pwnlib/tubes/ssh.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pwnlib/tubes/ssh.py b/pwnlib/tubes/ssh.py index b39026f0f..572b6a0f0 100644 --- a/pwnlib/tubes/ssh.py +++ b/pwnlib/tubes/ssh.py @@ -464,8 +464,10 @@ def __init__(self, parent, host, port, *a, **kw): ncat = 'nc' elif parent.which('ncat'): ncat = 'ncat' + elif parent.which('netcat'): + ncat = 'netcat' else: - self.exception('Could not find ncat or nc on remote. Cannot connect to remote port.') + self.exception('Could not find ncat, nc or netcat on remote. Cannot connect to remote port.') raise self.tunnel = parent.process([ncat, host, str(port)]) self.sock = self.tunnel.sock