This post is a very informal writeup about multiple vulnerabilities in uftpd FTP server, some of which could lead to remote code execution.
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).
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
Then, I began looking for uses of functions that could be used insecurely that involve string manipulation (e.g.
strncpy, etc). Plenty of uses, but all were safe. No dice. I also carefully looked at uses of
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.
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
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
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
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.
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
The vulnerability is in the function it calls,
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
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
An unauthenticated stack-based buffer overflow vulnerability in
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.
There currently are no plans to develop an exploit. Please contact me if you are developing / have developed one.
Connect to the FTP server and send:
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
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.
Setup a TCP listener on port 1258, connect to the FTP server, and send:
PORT 127,0,0,1,1,1002 RETR ../../../etc/passwd
The project maintainer Joachim (troglobit) replied and fixed all of the vulnerabilities within 4 hours of my initial contact. Very impressive!
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