Headline
CVE-2020-20277: Aaron Esau (arinerron)
There are multiple unauthenticated directory traversal vulnerabilities in different FTP commands in uftpd FTP server versions 2.7 to 2.10 due to improper implementation of a chroot jail in common.c’s compose_abspath function that can be abused to read or write to arbitrary files on the filesystem, leak process memory, or potentially lead to remote code execution.
Aaron’s Blog
uftpd - Buffer Overflow and Directory Traversal Writeup
This post is a very informal writeup about multiple vulnerabilities in uftpd FTP server, some of which could lead to remote code execution.
Introduction
I’ve been wanting to find a binary exploitation-related vulnerability in something that isn’t a Capture The Flag challenge for a very long time. I know that I am not nearly as experienced as many people are, so in the past when I’ve searched, I would quickly begin to doubt that I would find anything and lose motivation.
On August 28, 2019, I sat down, picked an FTP server written in C to target, and told myself that I wouldn’t stop and I wouldn’t go to sleep until I found something (spoiler: I got to sleep that night).
- The section titled “Discovery Process” explains how I found the vulnerabilities and a bit about their root causes.
- If you want a tl;dr of the vulnerabilities I found, skip to the section titled "Summary of Vulnerabilities".
Discovery Process
In this section I will walk through my thought process as I was discovering these vulnerabilities. I am writing this so that in the future I can look back and read about my first binary exploitation related vulnerability that I found outside of CTF. I’m sure I’ll be glad I did it in a few years. :)
I chose the FTP server uftpd because I understand the FTP protocol very well, it is a plaintext protocol (mostly/usually/sometimes), and it involves a lot of string parsing. So, it seems prone to off-by-one (which could lead to a buffer overflow) or heap corruption-related vulnerabilities.
I wrote a fuzzer and ran it the entire time I was testing, but clearly I did not do a good job of writing it, because the fuzzer did not cause a single crash the entire time.
I started out by looking for uses of inherently vulnerable functions that should absolutely never be used. And wow. How stupid am I? It’s modern software compiled with a modern compiler. No gets anywhere.
Then, I began looking for uses of functions that could be used insecurely that involve string manipulation (e.g. strcat / strncat, strcpy / strncpy, etc). Plenty of uses, but all were safe. No dice. I also carefully looked at uses of malloc and free.
Keep in mind, at this point, I was only looking for obvious buffer overflows, use-after-frees, off-by-ones, and format string injection vulnerabilities. After a few dead ends I followed and several hours wasted, I really wished I hadn’t committed myself to finding something.
Buffer Overflow Vulnerability
For some reason I thought about what a friend had told me at Defcon about how Go is basically bulletproof as far as SQL injection goes. He told me about some awful code he had seen where the author had basically gone out of the way to make a program vulnerable by constructing the query with sprintf instead of the built-in parameterized library’s functions. That made me think to re-check all uses of ^[a-z]*printf$ in the code.
I eventually came upon the handle_PORT function in ftpcmd.c:
The buffer size is INET_ADDRSTRLEN? What’s that?
A quick search shows that it’s the value 16 (the maximum length of an IPv4 address in ###.###.###.### format).
Hold up. That sprintf line is using the %d format specifier which takes a signed integer. The string representation of a signed integer is not at most 4 bytes like an (octet + ‘.’ char) is, and it isn’t checking the value of each octet to ensure that they are each between 0 and 255 inclusive.
Sick! A buffer overflow vulnerability!
Crap, a stack canary. Right, this isn’t a CTF challenge.
I tried to find a way to leak memory, but other than leaking memory into logs (which maybe you could consider a vulnerability?), I couldn’t get anywhere. So an attacker would have to either find a way to leak the stack canary that I couldn’t or would have to brute force the canary.
Also, there’s another restriction: the address must be constructed out of the characters [0-9-] because of the %d specifier. Considering I can’t bypass the stack canary, I haven’t bothered looking into this.
Had the FTP server had written the address and port back to the user without updating it first anywhere in the code, I would’ve been able to leak memory by crafting a PORT command with not enough integers to satisfy the format.
And even if somehow I leaked memory and somehow I got lucky with ASLR and the addresses were all ASCII numeric, it wouldn’t be possible to exploit unless uftpd was compiled without stack canaries since I cannot write a null byte.
To reproduce this, try sending this in:
USER anonymous
PASS hi
PORT 13371337,13371337,13371337,13371337,1,1
Multiple Directory Traversal Vulnerabilities (Chroot Bypass)
Sigh. So that was fun finding that buffer overflow vuln. It is 12am by this point and I could go to sleep now, but why sleep when at peak performance?
I saw this function at the top of common.c.
The compose_abspath function that protects against directory traversal attacks was far too long for me to bother reading (that’s not its full source code in the above screenshot). So, just to confirm to myself that it wasn’t vulnerable, I pulled up my favorite FTP client, netcat:
Oh my… is it really…
Here is the source code of compose_abspath:
The vulnerability is in the function it calls, compose_path:
Then I tested most of the file I/O FTP commands and every command I tested was vulnerable (as they all use the same chroot jailing function compose_abspath).
That’s right. Arbitrary file write/read anywhere on the operating system that I have access to. It should be easy to get remote code execution from this if you write a backdoor to a webserver root with CGI enabled. I wrote a proof of concept to automate that.
To reproduce this, all you have to do is, after setting up a listener with nc -lvp 1258, send in:
USER anonymous
PASS hi
PORT 127,0,0,1,1,1002
RETR ../../../etc/passwd
But wait—it gets even better.
Think you need authentication to exploit any of these so far? Think again! You don’t even need to login as the anonymous user to exploit either of those.
This vulnerability is not exploitable if the FTP server is running as root; when running as root, it uses the “real” chroot instead of the custom (vulnerable) chroot implementation.
This bug was introduced in commit ce1c9234c78eb3b939f465bcb40c19cd5141eab7 and fixed in commit 455b47d3756aed162d2d0ef7f40b549f3b5b30fe.
Summary of Vulnerabilities****Unauthenticated Buffer Overflow Vulnerability in handle_PORT in ftpcmd.c
- Product: uftpd <=2.10
- CVE: CVE-2020-5204
An unauthenticated stack-based buffer overflow vulnerability in common.c’s handle_PORT in uftpd FTP server versions 2.10 and earlier can be abused to cause a crash and could potentially lead to remote code execution.
Exploits
There currently are no plans to develop an exploit. Please contact me if you are developing / have developed one.
Reproduction Steps
Connect to the FTP server and send:
PORT 13371337,13371337,13371337,13371337,1,1
Multiple Unauthenticated Directory Traversal Vulnerabilities
- Product: uftpd 2.6-2.10
- CVE: CVE-2020-5221
There are multiple unauthenticated directory traversal vulnerabilities in different FTP commands in uftpd FTP server versions 2.7 to 2.10 due to improper implementation of a chroot jail in common.c’s compose_abspath function that can be abused to read or write to arbitrary files on the filesystem, leak process memory, or potentially lead to remote code execution.
Exploits
- Metasploit: Download here. Module is in development and does not currently function.
- Python: Download here. Demonstration below.
Reproduction Steps
Setup a TCP listener on port 1258, connect to the FTP server, and send:
PORT 127,0,0,1,1,1002
RETR ../../../etc/passwd
Disclosure
The project maintainer Joachim (troglobit) replied and fixed all of the vulnerabilities within 4 hours of my initial contact. Very impressive!
Timeline
August 28, 2019: Discovered the vulnerabilities
August 29, 2019: Wrote this writeup
August 30, 2019: Created a proof of concept and wrote the report
August 31, 2019: Emailed a report to troglobit
August 31, 2019: Vulnerabilities fixed
References
- Commit that fixes the buffer overflow vulnerability: https://github.com/troglobit/uftpd/commit/0fb2c031ce0ace07cc19cd2cb2143c4b5a63c9dd
- Commit that fixes the directory traversal regression: https://github.com/troglobit/uftpd/commit/455b47d3756aed162d2d0ef7f40b549f3b5b30fe
Footnotes: Thank you to pwnpnw and Robert Chen for getting me interested in binary exploitation and for helping me learn.
Related news
uftpd versions 2.7 through 2.10 suffer from an authenticated directory traversal vulnerability.