
35-year-old vulnerability discovered in scp - runn1ng
https://sintonen.fi/advisories/scp-client-multiple-vulnerabilities.txt
======
userbinator
It's interesting that PSCP has this option:

    
    
        -unsafe   allow server-side wildcards (DANGEROUS)
    

and as explained in the doc ([https://www.ssh.com/ssh/putty/putty-
manuals/0.68/Chapter5.ht...](https://www.ssh.com/ssh/putty/putty-
manuals/0.68/Chapter5.html)),

"This is due to a fundamental insecurity in the old-style SCP protocol: the
client sends the wildcard string (*.c) to the server, and the server sends
back a sequence of file names that match the wildcard pattern. However, there
is nothing to stop the server sending back a different pattern and writing
over one of your other files"

I haven't used the Linux version much so I assumed it had the same option.
PSCP has had this option for as long as I can remember. I guess no one
bothered to look at scp, or as one of the other comments here notes, scp is
overwhelmingly used with a server one already trusts.

~~~
walrus01
I strongly discourage anyone from using PuTTY, not for this reason, but for
its weird and nonstandard handling of SSH keys.

The last time I tried to help someone get it set up on a windows PC, totally
normal ssh2 rsa 2048 and 4096 bit public/private key pairs created with
openssh had to be converted into some other weird format before they could get
public/private key auth working.

Why the developers of putty felt they needed to deviate from standard ssh2-rsa
pub/privkey formats is a mystery to me.

~~~
cbhl
PuTTY's changelog from 2000 shows that PuTTYgen uses "the same RSA key file
format as SSH 1" (the proprietary one, that predates OpenSSH).

Reading and writing OpenSSH-style keys came later (2012).

[https://www.chiark.greenend.org.uk/~sgtatham/putty/changes.h...](https://www.chiark.greenend.org.uk/~sgtatham/putty/changes.html)

------
jedberg
I think I'm missing something. Step one is getting you to use a compromised
server. Which either means the server your home directory is on is compromised
(I’m assuming you have a login on the box, separate from your local home dir),
in which case scp is the least of your worries, or they get you to bounce
through a MITM server, in which case you have to accept an incorrect host key.

If you're accepting incorrect host keys, then you're completely breaking the
ssh security model anyway.

I guess I don't understand how this is actually any worse than how ssh already
worked?

Edit: To clarify, I understand why you’d want to fix this, for a good depth of
defense. I’m just saying I don’t feel any urgency in fixing this.

~~~
XCabbage
What you're missing is very simple: downloading files from a server need not
imply that you trust the server even slightly. That's true whether you're
downloading them via HTTPS (e.g. to view this web page) or via SCP.

Many of us have jobs in which we _typically_ only use SSH to connect to
servers that we trust and control. But that assumption is no more baked into
the security model of SSH than it is into the security model of HTTPS. The
argument that "you trusted this server enough to connect to it and download a
file, therefore you clearly should trust it enough to permit it to execute
arbitrary executables on your machine" is false in both cases.

~~~
nathan_long
> The argument that "you trusted this server enough to connect to it and
> download a file, therefore you clearly should trust it enough to permit it
> to execute arbitrary executables on your machine" is false in both cases.

Great point. Also, imagine that you control the server and you _know_ that it
was compromised. Surely you want to be able to download logs and other files
from it for inspection without having your own machine compromised.

~~~
toyg
_> Surely you want to be able to download logs_

Uhm, nope: you want to shut it down and perform forensics on the disk. Once
it's rooted, I wouldn't touch it with a barge pole.

~~~
XCabbage
I've been a web developer for over 5 years and never once worked somewhere
where I'd have any imaginable way of physically accessing the disk of a
server. Everything's been cloud-based. I don't know the exact ratios, but I'd
expect my experience not to be unusual.

~~~
organsnyder
You should still access that data in an offline manner, though—ideally:

1\. Shut down the instance

2\. Connect the storage as secondary storage on another (disconnected from the
network) instance

3\. Do forensics using your cloud provider's out-of-band management interface

4\. Throw away that instance as soon as you're done

------
JdeBP
The fact that OpenSSH's refresh_progress_meter() does not pass filename
strings through vis(3) is a bug in its own right, irrespective of whether it
can be abused for things like this.

The progress meter is meant to be a single line, and the formatting
calculations simplistically assume that every character in the buffer is a
(single-width) graphic or SPC. Anything in the filename that breaks that will
disrupt the progress meter.

------
mfontani
I read:

* [...] only directory traversal attacks are prevented

* [...] can overwrite arbitrary files in the scp client target directory

* [...] the server can manipulate subdirectories as well

... so nothing points to the ability of the server to "fiddle with" parent
directories.

Thus.. Is it an OK temporary workaround to _only_ perform scp from within a
freshly created directory in /tmp/?

~~~
korpiq
I read it as being MitM and manipulating target (final, originally intended
server) directories and files only, and in addition to spoof output to client
to hide the fact it's doing so. In that case it does not matter where you run
the client.

What makes MitMs possible is that checking fingerprint of new host is left to
user. Instead we should have processes to automatically 1\. acquire via other
means and add fingerprint of each new target host 2\. verify new host
fingerprints with a separate party (central server or a ring of trusted
buddies).

~~~
jakobegger
There is already a solution to this problem, which is host key certificates.

All you need to do is configure your SSH client to accept only host keys
signed by your CA.

However, setting that up is complicated. You need a lot of knowledge to set
that up securely. On the other hand, manual verification of host keys is
trivial -- anybody can compare a short string of characters.

~~~
h1d
Trivial but who does that when? I never even learnt what I should compare that
with and just accepted everything (and have been safe that way).

~~~
isodude
The best way to ensure that keys are correct, is to git a file like
.ssh/known_hosts2 and add known keys to that file before you connect to the
server.

How you get the public key is up to you, but they are located in /etc/ssh/ on
the server, or given to you when creating the server.

ssh-keyscan can scan a host and print the keys as well.

If you also add the servers to .ssh/config you also get tab completion.

It takes a bit more job to do, but it feels much safer afterwards, and it's a
good routine.

------
leni536
I quick test with strace and git shows that git doesn't use scp for handling
ssh remotes. I suspect that git is hardened against similar attacks (a
malicious server dropping files into .git/hooks would be worrisome).

~~~
fulafel
Git recently had a malicious-server-rce bug, though not related.

------
blattimwind
Honestly this seems a bit disappointing to me. Reading the issue description
scp sounds like the following pseudo-code:

    
    
        sock = connect(server)
        sock.do_crypto()
        sock.send_filelist(commandline.list)
        cd(commandline.targetdir)
        while not sock.eof:
            write_to_local_disk(sock.receive_file())
    

i.e. blindly create and change files the server tells the client about.
Complete lack of client-side verification.

~~~
jolmg
> sock.send_filelist(commandline.list)

Instead of a "filelist", it's got to be a remote shell command argument, since
it allows stuff like:

    
    
        scp server:'$(
          comm -23 \
            <(find dir1 -type f -printf "%P\n" | sort) \
            <(find dir2 -type f -printf "%P\n" | sort) \
          | sed "s:^:dir1/:"
        )' .
    

I'm not sure what you propose for client-side verification in this case.

------
jolmg
> 2\. CWE-20: scp client missing received object name validation
> [CVE-2019-6111]

> Due to the scp implementation being derived from 1983 rcp [1], the server
> chooses which files/directories are sent to the client. However, scp client
> only perform cursory validation of the object name returned (only directory
> traversal attacks are prevented). A malicious scp server can overwrite
> arbitrary files in the scp client target directory. If recursive operation
> (-r) is performed, the server can manipulate subdirectories as well (for
> example overwrite .ssh/authorized_keys).

I don't understand why this is considered a vulnerability. The user provides
scp server-side shell code to describe the files it wants. How's it supposed
to verify object names then? Am I the only one that likes to do things like
the following?[1]:

    
    
        scp server:'$(ls -t | head -1)' .
    

or

    
    
        scp server:'*.pdf' .
    

An argument could be made to have scp implement a glob pattern matcher, but
that wouldn't be shell agnostic (I doubt we'd get support for zsh-style
`*.pdf(oc[1,5])`) and it wouldn't include support for process substitution or
any other way the user might want to specify files.

scp already describes the files it's writing to stdout. I don't see what more
it can do without sacrificing usability.

The possibility of a compromised server writing things that have nothing to do
with what I asked just seems like an acceptable consequence for the power scp
provides.

[1] - Please, discussions about parsing ls output are besides the point, right
now. Heuristics are useful too, at times, for ad-hoc portable practices.

~~~
rtkwe
At the very least the client could perform a check that the files it's
receiving match the pattern it sent in the request by using a regular
expression replacing * with an non-whitespace+forbidden character. It doesn't
even check that if you request a single file like readme.txt that it receives
a single file!

~~~
jolmg
If you write:

    
    
        scp server:readme.txt .
    

"readme.txt" is still shell code. Most shells will evaluate it to the string
"readme.txt", but scp on the client should not make assumptions of the shell
used on the server.

If you write:

    
    
        scp server:'*.txt(oc[1,10])' .
    

A server that's setup with zsh with extended globs is going to return the
newest 10 .txt files. If scp is written with an expectation of basic globs, I
imagine it would try to match the files that have a character-by-character
literal extension of ".txt(oc[1,10])". That means no file is ever going to
match. You could say, "well, add recognition of zsh extended globs". Ignoring
how complicated that really is because there are glob options that allow you
to embed arbitrary zsh code, you're limiting the implementation of scp to work
with only particular shells. scp should not be a barrier to me implementing my
own shell, with it's own syntax and using it transparently. The current scp
doesn't care about what shells you use where. It makes little to no
assumptions of the tools you use, and that's cool.

~~~
blattimwind
Classic langsec failure.

~~~
jolmg
Can you expand on that? I can't tell if you're in favor or against a change.

~~~
blattimwind
Well the language scp uses for selecting files is turing complete _and_ relies
on state unavailable to the client _therefore_ it is impossible in the general
case for the client to check whether a given file sent by the server was
actually requested by itself. That's the langsec failure here. The "correct"
(in any case more sane) approach would be to use a stateless, easily
recognizable language for specifying files (e.g. something that reduces to
REs).

~~~
jolmg
And that's what I meant by saying that I couldn't see what else to do without
sacrificing usability. Whatever that language you propose would be, if it
doesn't allow use of state from the server, then it's pointless. You may as
well use the local shell in scp's invocation.

------
teacpde
> Due to accepting and displaying arbitrary stderr output from the scp server,
> a malicious server can manipulate the client output, for example to employ
> ANSI codes to hide additional files being transferred.

Can someone explain how “employ ANSI codes to hide” works?

~~~
freiheit
Your client asks for 1 file, the server comes back with 2 files (the 2nd one
what you actually asked for), but the name of the first (tiny) file's name
contains escape sequences that clear the line, move up a line, or otherwise
obscure that the first file was sent.

~~~
teacpde
Awesome, found
[https://en.wikipedia.org/wiki/ANSI_escape_code](https://en.wikipedia.org/wiki/ANSI_escape_code)
and played a bit in my terminal. It is fun.

    
    
        $ echo -e "hello\n\e[1A[2K\rworld"                                                                      
        $ world

------
cryptonector
This is just another in the series of rcp/scp considered harmful "bugs". This
isn't a bug -- rcp/scp are the bug. Use sftp to fetch files. rcp/scp are fine
for pushing files upstream, but not for fetching.

------
winkywooster
How is it a 35 year old vulnerability? ssh and friends have only been around
for 25.

~~~
cosmotron
I believe the title is derived from the description of vulnerability #2:

> Due to the scp implementation being derived from 1983 rcp [1] ...

> [1] [https://www.jeffgeerling.com/blog/brief-history-ssh-and-
> remo...](https://www.jeffgeerling.com/blog/brief-history-ssh-and-remote-
> access)

------
kakarot
Could anyone comment about whether any file-related Ansible modules use scp by
default instead of sftp?

~~~
evgen
I believe you need to set scp_if_ssh = True in ansible.cfg for scp to ever be
used, even if sftp is not available on the remote host.

~~~
jabl
Nowadays ansible uses a sort-of 'smart' method by default, where it first
tries to use sftp, and if that fails, falls back to scp. See
[https://docs.ansible.com/ansible/latest/plugins/connection/s...](https://docs.ansible.com/ansible/latest/plugins/connection/ssh.html)

~~~
zurn
So if the server is compromised, it can fail the sftp in a suitable way?

But its another matter if ansible's scp client is vulnerable to this.

------
mLuby
_SCP-035 has breached containment._

Oh whoops wrong context.

~~~
teilo
It was inevitable.

------
tzhenghao
uhh I think we killed it lol.

$ ping sintonen.fi

PING sintonen.fi (95.216.45.180): 56 data bytes

Request timeout for icmp_seq 0

Request timeout for icmp_seq 1

Request timeout for icmp_seq 2

Request timeout for icmp_seq 3

Request timeout for icmp_seq 4

...

------
aaaaaaaaaab
It’s easy to fix. Just rewrite scp in Rust.

~~~
dang
Please don't post unsubstantive comments here.

------
kazinator
> _Man-in-the-Middle attack does require the victim to accept the wrong host
> fingerprint._

This should be a WONTFIX.

The idea that it could be not only a man-in-the-middle attack but simply a
"malicious scp server" is completely ridiculous.

No secure login or transfer method can protect you from the actions of a
malicious server, which could be anything.

If I have the root privs to install malicious software on the server, why
would I muck around perpetrating a scp exploit on remote users.

~~~
dancek
Some people only check a couple of bytes in the beginning and end of the host
fingerprint. I've done that. It's not ridiculous to prevent attack vectors
that depend on common behavior, even if it's strictly speaking the user's
fault.

~~~
kakarot
You should not be checking the host fingerprint at all.

1\. Use `VisualHostKey=yes` in your ssh config. Learn the randomart image for
your server. Don't try to compare two long random strings directly, that's not
a job for humans.

2\. Use an offline SSH certificate authority to generate SSH certificates,
bypassing the need for a host fingerprint check altogether. If you can trust
your offline CA, you don't need to trust your server's host key at all. Either
they have a valid certificate or they don't. Your SSH client won't even ask
you if you trust the server.

It's one thing to access a compromised server, but you should _NEVER_ be
exposed to MITM attacks with proper SSH usage, except for your very first
connection on a brand-spanking new server when you drop your SSH host
certificates (ideally in an automated fashion which can be deployed
immediately after spinning up your box).

~~~
dancek
Sounds good in theory. Now, say I want to use github over ssh, how do I check
if the randomart image is correct? (Getting my configs from github tends to be
the first thing I do on a new machine.)

The CA approach is probably good if you control the servers you use. Right now
I connect to about 5-7 ssh servers on a regular basis, and I don't have (full)
control over any one of them.

~~~
kakarot
`ssh-keyscan -t rsa github.com | ssh-keygen -lf -` gives you the fingerprint
for github.com.

Save this key, and reuse it everywhere before connecting to github.

Now the issue of being MITM'd is once again only an issue with your very first
connection, which is done via ssh-keyscan.

As for your other servers, you should only check the host key once for any of
them, and then save it. And that's only if they are owned by a third-party,
like a shared server. If your provider routinely cycles host keys, get a new
provider who actually cares about security.

If these servers are owned by your employer, then your employer needs better
security practices and, failing a certificate authority, should provide you
with the necessary host fingerprints before you ever connect to a box.

~~~
dancek
I currently check fingerprints on first connection and then save them. They're
available for most servers, including github and other git providers. I think
I've only had to connect to one server without knowing the fingerprint in
advance in the last couple of years.

Looks to me like the fingerprint-checking process has to be manual if you
can't choose the server certificate.

