April 7, 2020

GHSL-2020-009: UAF leads to RCE in ProFTPD

Antonio Morales

Summary

A use-after-free vulnerability exists in ProFTPD. Successful exploitation of this vulnerability could allow a remote attacker to execute arbitrary code on the affected system.

Product

ProFTPD

Tested Version

Development version - master branch (Jan 22, 2020)

Details

Use-after-free vulnerability in memory pool allocator (CVE-2020-9273)

It is possible to corrupt the ProFTPd memory pool by interrupting current data transfer (PoC Exploit Demo Video.webm). This can be done for example, by sending an interrupt order to the command channel while a transfer is active in the data channel.

In our PoC, the program crashes on the alloc_pool function (pool.c) when executing first_avail = blok->h.first_avail.

As you can see, the right side operand of the assignment in pool.c:569 is p->last (See Image 1). However, the problem is that p is a corrupted pool (See Image 2).

The source of the problem comes from the pcalloc call in netio.c:1066 (See Image 3). This function calls the alloc_pool function again which in turn calls new_block to obtain a new freed memory block (See Image 4). But the memory block returned by new_block is still referenced by the p pool.

The problem is that new_block function is not concurrently-secure, and under certain circumstances, the new_block function can return as a free block a block already present in the pool, causing the corruption of the pool list.

So, in short, p is a dangling pointer due to an use-after-free vulnerability.

It's important to note that our tests show that this vulnerability can also lead to other primitives such as OOB writes, which increases the severity of the vulnerability.

ProFTPD ASAN build instructions

CC="clang" CXX="clang++" CFLAGS="-fsanitize=address,undefined -g" CXXFLAGS="-fsanitize=address,undefined -g" LDFLAGS="-fsanitize=address,undefined" ./configure
LDFLAGS="-fsanitize=address,undefined" make -j4

Steps to reproduce:

  1. Create a new user fuzzing with password fuzzing and extract Compressed_Dir.tar.gz dir in their home folder.
  2. Compile ProFTPD using ASAN as mentioned above.
  3. Run ProFTPD as root with the basic configuration and and following options: ASAN_OPTIONS=verbosity=3,detect_leaks=0,abort_on_error=1,debug=true,check_initialization_order=true,detect_stack_use_after_return=true,strict_string_checks=true,detect_invalid_pointer_pairs=2 ./proftpd -n -c basic.conf -d 10 -X
  4. Run a netcat listener on port 1055 (PORT 127,0,0,1,4,31 in our PoC).
  5. Send the PoC trigger to the running ProFTPD server using telnet (assuming that it is running on port 21/TCP): telnet 127.0.0.1 21 < test.txt
  6. ProFTPD should crash showing the relevant ASAN trace

Impact

This issue may lead to Post-Auth RCE.

Remediation

The issue has been fixed here https://github.com/proftpd/proftpd/commit/929d6c5a107ad92705555a87c386abd8bdce5d0d

Coordinated Disclosure Timeline

This report is subject to our coordinated disclosure policy.

  • 01/22/2020: Report sent to Vendor
  • 01/25/2020: Vendor acknowledged report
  • 02/16/2020: Vendor proposed fixes
  • 02/17/2020: Fixes reviewed and verified
  • 02/18/2020: Report published to public

Supporting Resources

Credit

This issue was discovered and reported by GHSL team member @antonio-morales (Antonio Morales).

Contact

You can contact the GHSL team at securitylab@github.com, please include the GHSL-2020-009 in any communication regarding this issue.