Severe SwiftNIO Security Vulnerability (affecting SwiftNIO < 1.8.0)

tl;dr

swift package update

Slightly longer version

After an initial report from @Pushkar_N_Kulkarni (thank you!) we recently identified a severe security vulnerability in SwiftNIO's ByteBuffer code which was fixed in the following releases: 1.0.1, 1.1.1, 1.2.2, 1.3.2, 1.4.3, 1.5.2, 1.6.2, 1.7.3, 1.8.0, all other SwiftNIO versions are affected.

What should I do?

  • please run swift package update in any project that uses SwiftNIO
  • make sure that after running this command, you're using one of the unaffected SwiftNIO versions (to see what version you're currently using, run cat Package.resolved | grep -A7 '"swift-nio"' | grep version)

Optional further steps:

  • to make sure your application can not ever be run with an affected SwiftNIO version, change the SwiftNIO dependency to .package(url: "https://github.com/apple/swift-nio.git", from: "1.8.0")

Details

In all prior NIO releases (that means all except for 1.0.1, 1.1.1, 1.2.2, 1.3.2, 1.4.3, 1.5.2, 1.6.2, 1.7.3, 1.8.0) ByteBuffer had a very bad (exploitable!) security vulnerability if the following conditions are all true:

  • user writes to a ByteBuffer which is a slice (slice.lowerBound != 0)
  • the slice is uniquely referenced (ie. the buffer that it was sliced
    from is gone)
  • the write triggers a re-allocation

Then the slice is actually larger than the overall available capacity so another write to said ByteBuffer could end up out of bounds.

4 Likes

Would somebody mind elaborating on how exactly is this exploitable? I don't know a lot about security but I am very interested. To my untrained and naive mind, this looks like it would crash at worst case.

Writing outside the bounds of an array will overwrite whatever content was in that memory. If you can guess what variables are stored in that memory, then you can carefully craft a payload to write which sets those variables values to anything you like.

Most critically, if one of these variables is a function pointer that's jumped to, you could overwrite it with the address of some payload, which contains executable code that the process will jump to.

I see.. I thought kinds of out-of-bounds writes are guarded against in swift. Are those striped away in release builds or are they somehow bypassed by slices?

ByteBuffer drops down to use unsafe pointers to manage its memory, and those do not incur bounds checks.

Gotcha, thank you guys!

I agree with everything said above. It's probably not super easy to exploit but it's for sure possible as there might be something interesting (could be anything, data, function pointers, etc) next to the ByteBuffer's storage that we allocated. Especially because NIO is a framework and not an application by itself we can't really reason about what an application stores in memory so we need to assume the worst.

Also: Pretty much every bug where you can write to memory you're not supposed to is actually exploitable or at the very least one shouldn't claim it's not exploitable. This is a cool example where a 1 byte overflow lead to an exploitable vulnerability in Chrome.

Moreover, crashing the software is a DOS and is also a severe attack. Especially if it is easy to craft a package that crash the app. You can send it repeatedly and prevent the service to be restarted.