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).


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:

handle_PORT

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!

pwned

Crap, a stack canary. Right, this isn't a CTF challenge.

canary

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.

chroot

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:

verycool

Oh my... is it really...

handle_MKD

Here is the source code of compose_abspath:

chroot

The vulnerability is in the function it calls, compose_path:

abspath2

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.

passwd

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

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

bof gif

Multiple Unauthenticated Directory Traversal Vulnerabilities

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

uftpd_dirtrav.py demonstration

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

dir trav gif

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

Footnotes: Thank you to pwnpnw and Robert Chen for getting me interested in binary exploitation and for helping me learn.

Loading...