Friday, January 2, 2015

Finding and exploiting ntpd vulnerabilities

Posted by Stephen Röttger, Time Lord


[Foreword by Chris Evans: this post by Stephen represents cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first Project Zero guest blog post. From time to time, we’ll be featuring guest blog posts for top-tier security research. In this instance, we’ve been impressed by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remotely exploitable nature of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se vulnerabilities, as well as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clever chain of bugs and quirks that eventually leads to remote code execution. You’ve probably seen cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 recent ntpd vulnerability disclosures and this blog post tells cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 story from one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 researchers who discovered cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issues. Over to Stephen…]

A few months ago I decided to get started on fuzzing. I chose cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference implementation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Network Time Protocol (NTP), ntpd, as my first target, since I have some background with NTP and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protocol seemed simple enough to be a good learning experience. Also, ntpd is available for many platforms and widely in use, including being part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 default OS X installation.
While looking at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source to get a better understanding of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protocol I noticed that its processing is far more complex than I expected. Besides cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time synchronization packets, ntpd supports symmetric and asymmetric (Autokey) aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication and so called private and control mode packets that let you query cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 daemon for stats or perform configuration changes (if I’m not mistaken, this is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protocol spoken by ntpdc and ntpq respectively). I quickly stumbled over a bug in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code processing Autokey protocol messages and decided to dig deeper and perform a manual code review of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r parts as well. This resulted in finding CVE-2014-9295 and writing my first ever OS X exploit for which I will present a write up today.

tl;dr: a global buffer overflow can be triggered on common configurations by an attacker on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local network through an IPv6 packet with a spoofed ::1 source. If your ntpd is not patched yet, add nomodify or noquery to every restrict line in your config, even cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ones for localhost.

But enough of that, let's jump into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 details.

The Bug
The most severe bug that turned out to be exploitable on OS X Mavericks is a buffer overflow in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code which handles control packets. Control mode responses are fragmented if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y exceed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer used to store cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m, as implemented in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following function:

static void
ctl_putdata(
const char *dp,
unsigned int dlen,
int bin /* set to 1 when data is binary */
)
{
//[...]
 
/*
* Save room for trailing junk
*/
if (dlen + overhead + datapt > dataend) {
/*
* Not enough room in this one, flush it out.
*/
ctl_flushpkt(CTL_MORE);
}
memmove((char *)datapt, dp, (unsigned)dlen);
datapt += dlen;
datalinelen += dlen;
}

As you can see, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 data to be written doesn't fit into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remaining buffer space is called, which will send out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current packet and reset cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 datapt to point to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 beginning of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer. However, memmove will be called in any case and if dlen is bigger than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 total buffer size it will overflow cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer. Note that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overflow happens in a global buffer and thus stack cookies won’t help in this case. So let's see if we can find a code path that will trigger this.

In most invocations, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 data to be written comes from a fixed size buffer that is smaller cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 output buffer and thus won't overflow.
The function which handles ntp.conf style remote configurations sent by a privileged client will send any error messages back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client using . By sending a configuration with enough errors, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 error message string will exceed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer size.
However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fact that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 written data is restricted to a fixed set of error messages makes exploitation difficult.

A more powerful overwrite can be found in . The NTP daemon keeps a list of name=value variables that can be set through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 configuration and read back with a control mode packet. If a variable bigger than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 output buffer is read back, it will overflow and corrupt whatever is stored behind cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer.

Setting Variables
So how can we set variables? As mentioned before, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is a control mode packet through which we can send configuration commands to ntpd and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365reby set any variable we want. But this is obviously a privileged operation and protected by two mechanisms:

  1. Access to private and control mode queries can be restricted in ntp.conf based on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source IP. Default installations usually prohibit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se queries for every source IP except for 127.0.0.1 and ::1. This is what e.g. Ubuntu, Debian and OS X do.
  2. The packet needs to be aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated with a MAC for which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shared key has to be specified in ntp.conf, which again shouldn't be set on default installations.

Bypassing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first one is actually not that hard if you’re on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same network. As we all know IP addresses can be spoofed. But can we spoof cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of localhost? It turns out OS X and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Linux Kernel behave similarly in this case. Any IP packet arriving on an external interface and with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source IP 127.0.0.1 will be dropped immediately. But if we use IPv6 instead we can actually spoof ::1 and send control mode packets to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 daemon (some Linux distributions have firewall rules in place that protect against this, e.g. Red Hat). Thus, if we are on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same local network, we can send spoofed packets to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 link-local address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target and bypass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IP restrictions. But what about requirement number 2? This one sounds tough: how can you have a valid MAC if no key is specified?

Quest for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Key
Let’s back up and discuss a little bit of background first. Through ntp.conf you can specify multiple keys and assign key ids to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. These key ids can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n be assigned to different roles, i.e., a requestkey can be used to aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticate private mode packets and a controlkey is used for control mode packets. We need a controlkey to send our configuration requests but a requestkey would actually suffice since a private mode packet exists that will set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 controlkey id to a specified value.

And that’s where anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r bug comes into play that was discovered by Neel Mehta. Let’s take a look what ntpd does if no requestkey was specified in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 config:

/* if doesn't exist, make up one at random */
if (authhavekey(req_keyid)) {
//[...]
} else {
unsigned int rankey;
 
rankey = ntp_random();
req_keytype = NID_md5;
req_hashlen = 16;
MD5auth_setkey(req_keyid, req_keytype,
  (u_char *)&rankey, sizeof(rankey));
authtrust(req_keyid, 1);
}

That’s right, if no key was specified, a random 31 bit key will be generated, which means we can brute force it by sending 2^31 packets to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerable daemon with a 68 byte payload each. But wait, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s more! The random key is created by a custom random number generator implementation that is seeded with a 32 bit value and we can get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 output of this generator through standard time synchronization requests. Part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 receive timestamp that we get by querying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 daemon is a random value from this generator and each query allows us to recover around 12 bits of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 output which we can use to brute force cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 seed offline. However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 feasibility of a naive brute force approach highly depends on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 uptime of ntpd since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of random values that have been created will increase cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 search space. To give an idea of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time complexity, my single core implementation takes a few hours on my laptop even if I limit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 search space to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first 1024 random values, but you can throw more cores at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 problem or precompute as much as possible and build a lookup table.
At this point, we have an overflow in a global buffer that can be triggered remotely on standard configurations. Neat!

The Overflow
Now that we have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 key, we can send configuration commands and write arbitrary variables. When reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m back from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 daemon, you can optionally specify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 variables that you’re interested in. ntpd will iterate through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m, write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m (separated by a comma) to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 global buffer through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function and finally flush cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m out with . There are still some restrictions on this overflow that make exploitation notably harder.

  1. We can’t write 0x00, 0x22 (“) and 0xff.
  2. Some data will be appended after our overwrite. That is, “, “ between two variable writes and “\r\n” on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 final flush.

How to proceed from here depends on which OS/distribution/architecture you target since protection mechanisms and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory layout of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 global data structures will differ. A few examples:

  • On x64, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 inability to write null bytes prevents us from completely overwriting pointers since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most significant bytes are null bytes. This poses a problem since “\r\n” is appended to our data, which will limit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 control over partial pointer overwrites. On x86 however, this shouldn’t be an issue.
  • At least on Debian, some compile time protections are not enabled for ntpd. I.e. cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 executable is not position independent and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 global offset table (GOT) is writable during runtime.
  • On OS X Mavericks, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 datapt variable which points to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current position in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer is located after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer itself while on Debian and Ubuntu cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer is in front of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer and can’t be overwritten.

I chose to try my luck on a 64 bit OS X Mavericks. Since I have no prior experience with OS X, please bear with me if I missed something obvious or use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 wrong nomenclature :).
The environment looks like this:

  • The binary, stack, heap and shared libraries are individually randomized with 16 bit entropy.
  • The address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shared libraries is randomized at boot time.
  • On a crash, ntpd is restarted automatically with approximately 10 seconds delay.
  • ntpd is compiled with stack cookies (which doesn’t matter in our case since we overflow a global buffer).
  • The global offset table (GOT) is writable during runtime.

For a reliable exploit we will have to bypass ASLR somehow, so let’s leak some pointers. This one is actually quite easy since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 datapt variable, which as you might remember points to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current write location, is located after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer itself:

We just have to overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 two least significant bytes of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 datapt variable and as a consequence, ntpd will miscalculate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length and send you data after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer which leaks a pointer into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ntpd binary as well as a heap pointer. After that, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 datapt variable is conveniently reset to point to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 beginning of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer again.
Note that usually “\r\n” would get appended to our data and corrupt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 partial pointer overwrite. But since we overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 write pointer itself, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 newline sequence will be written to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new destination instead.

With cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same trick, we can turn cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug into a slightly restricted write-what-where primitive: partially overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 datapt variable to point to where you want to write (minus a few bytes to make room for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 separator) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n write arbitrary data with a second ntpd variable. Again, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fact that garbage is appended to our data is no issue for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first write since it will be written to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new location instead and won’t corrupt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer. Note that we can only write arbitrary data in front of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer since a higher address will trigger a flush and reset cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 datapt (after writing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 separator, so this might still be used to corrupt a length field).

Unfortunately, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 appended bytes still pose a problem. If we try to do a partial pointer overwrite through this, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “\r\n” sequence will always corrupt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer before it is used. Well, almost always. The GOT, and this took me way too long to figure out, is actually writable and used twice before our overwrite gets corrupted by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 addition of “\r\n”. Between writing a variable and flushing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 packet, and are called. That means, if we partially overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GOT entry of eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r of those functions, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer will be used before it gets corrupted and we control rip.

Info leak, again
Since we know cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 base address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary and can overwrite GOT entries we can just find a nice gadget in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary and jump to it, right? Unfortunately, that doesn’t work. To see why, let’s take a look at a couple of example addresses from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary and libsystem_c:

0x000000010641c000 /usr/sbin/ntpd
0x00007fff88791000 /usr/lib/system/libsystem_c.dylib

The addresses of system libraries have two null bytes as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir most significant bytes while cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary address starts with three null bytes. Thus if we overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GOT entry of with an address from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re will still be 0x7f byte left from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 library address (remember: we can’t write nul bytes).
To obtain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of a system library we could try to turn our overwrite into a better leak, e.g. by overwriting some length field. But cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is a lazier approach due to a weakness of ASLR on OS X Mavericks.
The most common libraries are loaded in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 split library region (as “man vmmap” calls it) which is shared by all processes in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system. The load address of this region is randomized during boot. This means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 addresses stay cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same if a program is restarted and that even libraries which are not used by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 program are loaded in its address space and can be used for ROP gadgets. This and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fact that ntpd is restarted automatically when it crashes makes it possible to brute force cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 library addresses for (libsystem_c) or (libsystem_malloc) bytewise.
If you reboot your system a few times, you can observe that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 load address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 split library region is always of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 form 0x00007fff8XXXX000, providing 16 bit of entropy or 17 bit in our case since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 region can extend to 0x00007fff9XXXX000. Let’s use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 libsystem_c address from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 example before: 0x00007fff88791000. We know that is located at offset 0x1720 and thus 0x00007fff88792720 is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address we’re trying to brute force.
We start by brute forcing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 upper 4 bits of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second least significant byte. We overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GOT entry of with 0x0720, resulting in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new entry 0x00007fff88790720. Since we didn’t hit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 correct address ntpd will crash and won’t send us any replies anymore. In that case, we increment cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address to 0x1720 and try it again. If ntpd does send us a reply, which will happen at 0x2720, we know that we found cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 correct byte and continue with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next one (0x012720).
This way, we can recover cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 libsystem_c address in 304 tries (4 bit + 8 bit + 5 bit) in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 worst case. OS X will restart ntpd approximately every 10 seconds but you will need to brute force cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 key anew for every try, so bring your supercomputer. Also, if you’re unlucky you will run into an endless loop and ntpd has to be killed manually.

Arbitrary Code Execution
If it wasn’t for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fact that ntpd runs in a sandbox we would be finished now. Just overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GOT entry of with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of and execute arbitrary commands since it will get called with a user controlled string. But all you get out of this is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following line in /var/log/system.log:

sandboxd[405] ([41]): ntpd(41) deny process-exec /bin/sh

Instead, we need to find a nice gadget to control cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack pointer and make it point to a ROP chain. The usual way to do this would be a stack pivot but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 data we control on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack is limited.

On cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack, we control data in 3 locations which we can fill with arbitrary pointers, this time without any restrictions. Besides that, we completely control cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of a global buffer at a known address in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary and if we can get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack pointer (rsp) to point to this buffer we can execute an arbitrary ROP chain.
Since our exploit overwrites cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GOT, we only control cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 instruction pointer once, i.e. we can’t chain multiple calls. Thus, our first gadget needs to increment cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack pointer by eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r 0x80, 0x90 or 0xb8 so that it will use one of our addresses on return and do something useful at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same time. Fortunately, I found cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following gadget in libsystem_c.dylib:

add rsp, 0x88
pop rbx
pop r12
pop r13
pop r14
pop r15
pop rbp
ret

This gadget returns to our address at rsp+0xb8 and at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same time loads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value from rsp+0x90 into r12. Since we now control a register, we can chain gadgets that end in a call qword [reg+n] where reg points to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 global buffer that we control. For example, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second gadget looks like this:

mov rdi, r12
mov rsi, r14
mov rdx, r13
call qword [r12+0x10]

With a few gadgets of this kind, we control rsi and can load it into rsp:

push rsi
pop rsp
xor eax, eax
pop rbp
ret

And with that, we’re done. This will crash on a ret instruction with rsp pointing to user-controlled and thus arbitrary code execution is straightforward. Since we control cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack, we can build a ROP chain that loads and executes shellcode and from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re try to break out of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandbox by attacking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel or IPC channels. But that is left as an exercise for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reader :).

Exploit Summary
  1. Send a bunch of regular time synchronization requests to leak random values.
  2. Brute force cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 seed and calculate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 requestkey (which has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 keyid 65535).
  3. Send a private mode packet signed with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 requestkey and with a spoofed source IP of ::1 to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server to set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 controlkey id to 65535.
  4. Send a configuration change to lift all restrictions for our IP address.
  5. Add our IP to get async notifications (we have to do this, since we overwrite a flag later that triggers if responses are sent directly or asynchronously).
  6. Trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overflow by setting a long variable and reading it back and leak cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary base address.
  7. Use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overflow again as a write-what-where primitive to brute force cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of bytewise.
  8. Prepare cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 data on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack and in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 global buffer.
  9. Call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 gadgets to control rsp and execute a ROP chain.

Mitigation
In case your ntpd is not patched yet, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se bugs can be effectively protected against through changes in your ntp.conf. The vulnerable function is used by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 processing of control mode packets and this can be blocked completely by adding “noquery” to every restrict line in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 configuration. As explained before, it is important to also add “noquery” to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 restrict lines for localhost, since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IP based access restrictions can often be bypassed through spoofing. But note that this will prevent ntpq from working and you won’t be able to query for peer information and ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r stats anymore.

For example, if your configurations includes multiple “restrict” lines:

restrict default kod nomodify notrap nopeer noquery
restrict -6 default kod nomodify notrap nopeer noquery
restrict 127.0.0.1
restrict -6 ::1

make sure that “noquery” is included in all of those:

restrict default kod nomodify notrap nopeer noquery
restrict -6 default kod nomodify notrap nopeer noquery
restrict 127.0.0.1 noquery
restrict -6 ::1 noquery

12 comments:

  1. Is this attack on ntp server or ntp client? Please let me know.

    ReplyDelete
  2. The ntpd daemon runs as a peer (both client and server, with query types allowed defined by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 configuration).

    ReplyDelete
    Replies
    1. I am a bit new to ntp concept and I have done several readings, but I am not able to understand how this is going to affect a ntp client. Please let me know. Thanks in advance.

      Delete
  3. Hello,

    Really nice work.

    I would just like to focus on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ntp_random() part and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fact that you can bruteforce cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 seed value by using future sample values. I have successfully bruteforced a 32bit state space, in a similar vulnerability, in about 7-10 minutes using CUDA on a 4-5 years old NVidia GPU. I guess CUDA speeds have greatly improved nowadays, so, you can use it to avoid cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "few hours" bottleneck.

    ~hk

    ReplyDelete
  4. This comment has been removed by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 author.

    ReplyDelete
  5. This comment has been removed by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 author.

    ReplyDelete
    Replies
    1. This comment has been removed by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 author.

      Delete
  6. I checked cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NTPd code and found out that it doesnt support fragmented Control Messages:

    ntpd/ntp_control.c

    Line 697:

    /*
    * If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length is less than required for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 header, or
    * it is a response or a fragment, ignore this.
    */
    if (rbufp->recv_length < CTL_HEADER_LEN
    || pkt->r_m_e_op & (CTL_RESPONSE|CTL_MORE|CTL_ERROR)
    || pkt->offset != 0) {
    DPRINTF(1, ("invalid format in control packet\n"));
    if (rbufp->recv_length < CTL_HEADER_LEN)
    numctltooshort++;
    if (pkt->r_m_e_op & CTL_RESPONSE)
    numctlinputresp++;
    if (pkt->r_m_e_op & CTL_MORE)
    numctlinputfrag++;
    if (pkt->r_m_e_op & CTL_ERROR)
    numctlinputerr++;
    if (pkt->offset != 0)
    numctlbadoffset++;
    return;
    }


    My question is, how did you manage to set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 variable to be big engough? I guess more than one packet, since I tried one with max value that one packet can fit and it isn't enough to overflow cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer.

    ReplyDelete
  7. I think I know how, via readvar sys_var_list while before setting long variables names or with long values. That seems to do cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 job

    ReplyDelete
  8. Actually, it didn't work. The biggest dlen in ctl_pudata() I was able to get was 466, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 global buffersize as I understand is 504 bytes.

    I tried to set a long variable name in order to trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overflow.

    :config setvar bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = 1

    Than read it via

    :rv 0 sys_var_list

    It displays blank, but that's how stack looks like:

    (gdb) x/400x 0x684b0c
    0x684b0c : 0x5f737973 0x5f726176 0x7473696c 0x656c223d
    0x684b1c : 0x732c7061 0x74617274 0x702c6d75 0x69636572
    0x684b2c : 0x6e6f6973 0x6f6f722c 0x6c656474 0x722c7961
    0x684b3c : 0x64746f6f 0x2c707369 0x69666572 0x65722c64
    0x684b4c : 0x6d697466 0x63742c65 0x6565702c 0x666f2c72
    0x684b5c : 0x74657366 0x6572662c 0x6e657571 0x732c7963
    0x684b6c : 0x6a5f7379 0x65747469 0x6c632c72 0x696a5f6b
    0x684b7c : 0x72657474 0x6f6c632c 0x702c6b63 0x65636f72
    0x684b8c : 0x726f7373 0x7379732c 0x2c6d6574 0x73726576
    0x684b9c : 0x2c6e6f69 0x5f6b6c63 0x646e6177 0x732c7265
    0x684bac : 0x765f7379 0x6c5f7261 0x2c747369 0x2c696174
    0x684bbc : 0x7061656c 0x2c636573 0x69707865 0x6d2c65---Type to continue, or q to quit---
    72
    0x684bcc : 0x63746e69 0x6561642c 0x5f6e6f6d 0x73726576
    0x684bdc : 0x2c6e6f69 0x74746573 0x6f656d69 0x79616466
    0x684bec : 0x6363612c 0x5f737365 0x696c6f70 0x612c7963
    0x684bfc : 0x6262622c 0x62626262 0x62626262 0x62626262
    0x684c0c : 0x62626262 0x62626262 0x62626262 0x62626262
    0x684c1c : 0x62626262 0x62626262 0x62626262 0x62626262
    0x684c2c : 0x62626262 0x62626262 0x62626262 0x62626262
    0x684c3c : 0x62626262 0x62626262 0x62626262 0x62626262
    0x684c4c : 0x62626262 0x62626262 0x62626262 0x62626262
    0x684c5c : 0x62626262 0x62626262 0x62626262 0x62626262
    0x684c6c : 0x62626262 0x62626262 0x62626262 0x62626262
    ---Type to continue, or q to quit---
    0x684c7c : 0x62626262 0x62626262 0x62626262 0x62626262
    0x684c8c : 0x62626262 0x62626262 0x62626262 0x62626262
    0x684c9c : 0x62626262 0x62626262 0x62626262 0x62626262
    0x684cac : 0x62626262 0x62626262 0x62626262 0x62626262
    0x684cbc : 0x62626262 0x62626262 0x62626262 0x62626262
    0x684ccc : 0x62626262 0x62626262 0x62626262 0x62626262
    0x684cdc : 0x0a0d2262 0x00000000 0x00000000 0x00000000
    0x684cec : 0x00000000 0x00000000 0x00000000 0x00000000
    0x684cfc : 0x00000000 0x00000000 0x00000401 0x00000000
    0x684d0c: 0x00000000 0x00000000 0x00000000 0x00000000
    0x684d1c: 0x00000000 0x756e694c 0x00000078 0x00000000


    How did you overflow it than?

    ReplyDelete
  9. OK, figured it out :) Created cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 packet from hand, instead of using ntpq client

    ReplyDelete
  10. It is mentioned that data is controlled on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 origin of this data is not explained. What is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 data that is controllable on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack and where does it come from?

    ReplyDelete