scp: error: unexpected filename:

The culprit is CVE-2018-20685, whose description is:

In OpenSSH 7.9, scp.c in the scp client allows remote SSH servers to bypass intended access restrictions via the filename of . or an empty filename. The impact is modifying the permissions of the target directory on the client side.

This is part of a larger set of SCP vulnerabilities. Quoting from there:


SCP clients from multiple vendors are susceptible to a malicious scp server performing unauthorized changes to target directory and/or client output manipulation.


Many scp clients fail to verify if the objects returned by the scp server match those it asked for. This issue dates back to 1983 and rcp, on which scp is based. A separate flaw in the client allows the target directory attributes to be changed arbitrarily. Finally, two vulnerabilities in clients may allow server to spoof the client output.

The commit that patched this vulnerability in OpenBSD was made on Nov. 16, 2018

The other answer from @BlackBear explains why this no longer works.

But if like me you ended up on this question also looking for a solution, it seems the right way to do it is with rsync instead of scp. For example, one of my old scp commands would have looked like this:

# this no longer works due to the "."
scp -BCr output/html/. www:/var/www/site/html/

Now instead I use this:

rsync --recursive --times --compress --delete --progress output/html/ www:/var/www/site/html/

If you prefer the shorter flags, it would look like this:

rsync -rtz --del --progress output/html/ www:/var/www/site/html/

The trailing / on the source is important. It tells rsync you want the content of that directory without the directory name.

Also consider --dry-run and man rsync before messing things up.