SFTP Port Forwarding: Enabling Suppressed Functionality
Introduction
The SSH protocol enables three major classes of remote server activities: a) command execution (including a login shell), b) network forwarding and manipulation, and c) file transfer.
The OpenSSH maintainers have determined that sftp
and scp
have no legitimate use for port forwarding (via the -L
and -R
options). A flag to explicitly disable these features is unconditionally passed to the child SSH executable during file transfers with these utilities.
There may be users with a legitimate need for these features. An obvious subset are penetration testers tasked to verify that this capability is explicitly disabled on public SFTP servers.
Below are two techniques to enable these suppressed features, by either modifying strings in the sftp binary itself, or by redirection through shells that are able to easily edit the command line. Depending upon the capabilities of the platform, either technique might be required to achieve this goal.
Suppression Details
To begin, it is important to locate running processes of interest. The shell function below will reveal PIDs that match a shell pattern (and note this is not a regex). This runs under Debian dash
(and most other common shells) and relies on BSD options to ps
:
pps () { local a= b= c= IFS=$'\r'; ps ax | while read -r a do [ "$b" ] || c=1; for b; do case "$a" in *"$b"*) c=1;; esac; done; [ "$c" ] && printf '%s\n' "$a" && c=; done; }
A conventional SFTP session is launched, in order to examine the processes associated with it:
$ id uid=1001(aturing) gid=1001(aturing) groups=1001(aturing)... $ sftp aturing@sftp.victimandum.com aturing@sftp.victimandum.com's password: Connected to sftp.victimandum.com. sftp>
We assume above that the local UNIX user has an account on the remote SFTP server of the same username.
Once the session is running, a local process search for the username reveals the child SSH process that is spawned by SFTP:
$ pps aturing PID TTY STAT TIME COMMAND 9666 pts/0 S+ 0:00 sftp aturing@sftp.victimandum.com 9667 pts/0 S+ 0:00 /usr/bin/ssh -oForwardX11 no -oPermitLocalCommand no -oClearAllForwardings yes -oForwardAgent no -l aturing -s -- sftp.victimandum.com sftp
The ClearAllForwardings yes
argument above will suppress any forwarding attempts without action to subvert it.
The -L
and -R
port forwarding flags are not otherwise present as valid options for the SFTP command line, but we can explicitly trigger them with the -S
option to specify a custom SSH handler, in this case to a mail server:
$ cat portssh #!/bin/sh exec ssh -L2525:smtp.victimandum.com:25 "$@"
Were the forwarding suppression not in place, this SFTP invocation would be sufficient to establish forwarding connectivity:
$ sftp -S ./portssh -oClearAllForwardings\ no aturing@sftp.victimandum.com aturing@sftp.victimandum.com's password: Connected to sftp.victimandum.com. sftp>
The forwarding attempt is now visible in the child SSH process:
$ pps aturing PID TTY STAT TIME COMMAND 9897 pts/0 S+ 0:00 sftp -S ./portssh -oClearAllForwardings no aturing@sftp.victimandum.com 9898 pts/0 S+ 0:00 ssh -L2525:smtp.victimandum.com:25 -oForwardX11 no -oPermitLocalCommand no -oClearAllForwardings yes -o ClearAllForwardings no -oForwardAgent no -l aturing -s -- sftp.victimandum.com sftp
However, attempts to contact the remote mail server via the local forwarded port are not successful, due to the explicit override:
$ nc localhost 2525 $
This unconditional suppression is visible in the source code:
$ sed -n /X11/,/Forwardings/p openssh-8.7p1/sftp.c addargs(&args, "-oForwardX11 no"); addargs(&args, "-oPermitLocalCommand no"); addargs(&args, "-oClearAllForwardings yes");
These static strings are also visible in the compiled binary:
$ strings /usr/bin/sftp | grep [-]o[CFP] -oForwardX11 no -oPermitLocalCommand no -oClearAllForwardings yes -oForwardAgent no -oPort %d
Finally, the documentation is clear that this suppression is intentional, and does give valid reasoning as to why:
$ man ssh_config | sed -n /ClearAllForwardings/,/default/p ClearAllForwardings Specifies that all local, remote, and dynamic port forwardings specified in the configuration files or on the command line be cleared. This option is primarily useful when used from the ssh(1) command line to clear port forwardings set in configura‐ tion files, and is automatically set by scp(1) and sftp(1). The argument must be yes or no (the default).
Altering Compiled Strings
For those who wish to disable the default ClearAllForwardings yes
configuration, one option is to directly edit the string within the SFTP binary with sed
(assuming that the platform's sed
is binary-safe):
$ sed 's/AllForwardings yes/AllForwardings no /' < /usr/bin/sftp > sftp.noclearforward
This direct modification is considerably less difficult than compiling a new binary.
We can confirm that the string has been successfully modified:
$ strings ./sftp.noclearforward | grep [-]o[CFP] -oForwardX11 no -oPermitLocalCommand no -oClearAllForwardings no -oForwardAgent no -oPort %d
While the content, and the checksums, of the modified SFTP will be different, any Linux BuildID sha1 that is present will remain the same (but please do not submit support tickets when using edited SFTP):
$ file /usr/bin/sftp ./sftp.noclearforward /usr/bin/sftp: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d7e77e24d5fac0fdc89e62a4c9c656091f2c4a33, for GNU/Linux 3.2.0, stripped ./sftp.noclearforward: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d7e77e24d5fac0fdc89e62a4c9c656091f2c4a33, for GNU/Linux 3.2.0, stripped $ sha1sum /usr/bin/sftp ./sftp.noclearforward d8bdaf0b4642b9c324f9c2e0aeee2d9578fbe383 /usr/bin/sftp b12dda8ecfd7bd2847919b5531aea7c03364c123 ./sftp.noclearforward $ sha256sum /usr/bin/sftp ./sftp.noclearforward 986eecdfc654c9b3ff3fd0dce59690d47cf56be96a4b98a04a3682aef95d3f52 /usr/bin/sftp c8f99ce33fc129250c11dc6dbb8a01112e01124e470a92d0acefb955fd17d670 ./sftp.noclearforward
The modified SFTP binary can be invoked to enable port forwarding:
$ chmod 755 sftp.noclearforward $ ./sftp.noclearforward -S ./portssh aturing@sftp.victimandum.com aturing@sftp.victimandum.com's password: Connected to sftp.victimandum.com. sftp>
The modified setting is now visible in the child process:
$ pps aturing PID TTY STAT TIME COMMAND 9991 pts/0 S+ 0:00 ./sftp.noclearforward -S ./portssh aturing@sftp.victimandum.com 9992 pts/0 S+ 0:00 ssh -L2525:smtp.victimandum.com:25 -oForwardX11 no -oPermitLocalCommand no -oClearAllForwardings no -oForwardAgent no -l aturing -s -- sftp.victimandum.com sftp
The functionality is enabled and operational on the remote server, and the connection can be verified in a separate shell:
$ nc localhost 2525 220 smtp.victimandum.com Microsoft ESMTP MAIL Service, Version: 1.2.3456.78901 ready at Sun, 1 Jan 2023 01:23:45 -0100 ^C
When forwarding functionality is disabled on the server, the client will receive notifications indicating this status upon connection attempts:
channel 3: open failed: administratively prohibited: open failed
SFTP administrators who allocate untrusted accounts should likely verify that the server configuration explicitly disables both forwarding and command execution.
Beyond POSIX Shell
While dash
and the POSIX standard offer set --
as a method to reset command line parameters, a more advanced feature is available in bash
and ksh93
:
$ cat ynargs #!/bin/bash echo "${@//yes/no}"
A quick test confirms a successful edit:
$ ./ynargs -oForwardX11 no -oPermitLocalCommand yes -oClearAllForwardings yes -oForwardAgent no -oForwardX11 no -oPermitLocalCommand no -oClearAllForwardings no -oForwardAgent no
Note that ${@//.../...}
above is not valid POSIX, and is not functional in dash
or any shell derived from pdksh
(mksh
, oksh
). There are many platforms that do not bundle shells with this functionality (examples include Android and OpenBSD, although there are methods to add them); the binary edit technique is likely more straightforward for restricted platforms, as opposed to installing alternative shells.
To exploit this feature with a capable shell, we create a directory, then create an SSH wrapper within it that clears the problem setting:
$ cat ~/switcharoo/ssh #!/bin/bash exec /usr/bin/ssh "${@//yes/no}"
Then set the directory ahead of the system SSH in the $PATH:
$ export PATH=~/switcharoo:$PATH $ which ssh ~/switcharoo/ssh
Then we call the system SFTP under this modified environment:
$ /usr/bin/sftp -S ./portssh aturing@sftp.victimandum.com aturing@sftp.victimandum.com's password: Connected to sftp.victimandum.com. sftp>
We observe that the shell has reset the problem parameter:
$ pps aturing PID TTY STAT TIME COMMAND 10058 pts/0 S+ 0:00 /usr/bin/sftp -S ./portssh aturing@sftp.victimandum.com 10059 pts/0 S+ 0:00 /usr/bin/ssh -L2525:smtp.victimandum.com:25 -oForwardX11 no -oPermitLocalCommand no -oClearAllForwardings no -oForwardAgent no -l aturing -s -- sftp.victimandum.com sftp
Local connectivity to the forwarded port is again confirmed:
$ nc localhost 2525 220 smtp.victimandum.com Microsoft ESMTP MAIL Service, Version: 1.2.3456.78901 ready at Sun, 1 Jan 2023 01:23:45 -0100 ^C
As a final demonstration, a full SMTP exchange can be conducted with the following script:
$ cat awkmail #!/bin/gawk -f BEGIN { smtp="/inet/tcp/0/localhost/2525"; ORS="\r\n"; r=ARGV[1]; s=ARGV[2]; sbj=ARGV[3]; # /bin/awkmail to from subj < in print "helo " ENVIRON["HOSTNAME"] |& smtp; smtp |& getline j; print j print "mail from:" s |& smtp; smtp |& getline j; print j if(match(r, ",")) { split(r, z, ",") for(y in z) { print "rcpt to:" z[y] |& smtp; smtp |& getline j; print j } } else { print "rcpt to:" r |& smtp; smtp |& getline j; print j } print "data" |& smtp; smtp |& getline j; print j print "From: " s |& smtp; ARGV[2] = "" # not a file print "To: " r |& smtp; ARGV[1] = "" # not a file if(length(sbj)) { print "Subject: " sbj |& smtp; ARGV[3] = "" } # not a file print "" |& smtp while(getline > 0) print |& smtp print "." |& smtp; smtp |& getline j; print j print "quit" |& smtp; smtp |& getline j; print j close(smtp) } # /inet/protocol/local-port/remote-host/remote-port
We can use the script to mail itself to a remote recipient that is accessible to the target SMTP server:
$ ./awkmail jatanasoff@victimandum.com aturning@localhost awkmail < awkmail 220 smtp.victimandum.com Microsoft ESMTP MAIL Service, Version: 1.2.3456.78901 ready at Sun, 1 Jan 2023 01:23:45 -0100 250 smtp.victimandum.com Hello [1.2.3.4] 250 2.1.0 aturing@localhost....Sender OK 250 2.1.5 jatanasoff@victimandum.com 354 Start mail input; end with . 250 2.6.0 <A1B2C3D4E5F6G@smtp.victimandum.com> Queued mail for delivery
In highly controlled environments, the presence of these capabilities is sub-optimal.
Server Restriction
It is understandable that SFTP administrators would not wish to allow their users to make arbitrary TCP connections with the assistance of the server, potentially placing sensitive networks at risk. Restricting this activity is a prudent security setting.
The common restrictive configuration is to add untrusted SFTP users into a group, then constrain the activities of this group in sshd_config
:
Match Group sftponly ChrootDirectory %h ForceCommand internal-sftp AllowTcpForwarding no
This recommended configuration is usually sufficient to block all attempts at forwarding.
It might be advisable to add DisableForwarding yes
:
$ man sshd_config | sed -n /DisableForwarding/,/configurations/p DisableForwarding Disables all forwarding features, including X11, ssh-agent(1), TCP and StreamLocal. This option overrides all other forwarding- related options and may simplify restricted configurations.
This is left as an exercise for the administrator.
Conclusion
Overly restrictive SFTP client settings potentially introduce some degree of server administration blindness. The SFTP client limitations are easily circumvented by multiple methods.
It is important for SFTP server administrators to understand what is limited, and where, and not to rely upon a client to keep the server safe from arbitrary TCP control. The client is under the control of the user, and TCP command of the server is far from difficult to achieve if misconfigured. Any testing should be conducted without extensive forwarding set in the user's ssh_config
, heeding the warnings in the documentation.
While there are conceivable legitimate uses of this functionality, they will be few and far between abuses.
These concerns are hardly new, as variations of site exec
have been available in cleartext FTP for decades. SFTP is not a simple replacement for cleartext file transfers, but itself carries many features that are easily exploited.
It is hoped that administrators will verify the security of their servers with these methods, so they are not caught unawares.