35 comments

[ 3.3 ms ] story [ 84.0 ms ] thread
Is this a universal exploit? Which distros are unaffected?

If it's non-universal, why is it? E.g. if it only affects Ubuntu, then what is it about Ubuntu that allows this to work?

EDIT: This seems to be the answer: https://news.ycombinator.com/item?id=7154922 ... Any distro using the x32 ABI is vulnerable, and Ubuntu just recently enabled the x32 ABI.

I believe only Ubuntu is affected, of the major distros. The bug affects kernels compiled with CONFIG_X86_X32, which enables the new-ish x32 ABI, a way for processes to use x86-64 features like the extended register set, while retaining the 32-bit pointer size. The more conventional approach to 32/64-bit code mixing (e.g. in Debian) is that 32-bit processes use the old x86 ABI, while 64-bit processes use the new x86-64 ABI. The x32 ABI was borne out of people noticing that the x86-64 architecture's improvements weren't all about 64-bitness, and that some of them were relevant to 32-bit programs too. So it keeps 32-bit pointers, but otherwise uses x86-64 features, so even 32-bit processes can take advantage of running on x86-64 chips (everything except the 64-bit part). Nothing inherently wrong with the idea, but it introduces some new critical codepaths into the kernel that have to be thoroughly tested.
It only applies to one very new alternative ABI for 64-bit x86 machines called x32, I'm not sure how widely deployed this ABI is, but I'm pretty sure it's not the default on anything.

Edit: turns out that 13.10 is released with the CONFIG_X86_X32 option enabled, just the usual negligent excessive differentiation that Canonical loves to impose on its customers(see Mir and moving forward with Upstart for recent examples).

This exploit affects systems with CONFIG_x86_X32 enabled. As it was a point of confusion on the previous post, this refers to the x32 ABI on 64 bit systems.

Essentially, if this option is enabled, someone can use the linked code to exploit an x32 syscall and escalate privileges.

Ubuntu specifically is a target because they recently enabled this option; I'm not aware of any other distros that have done so.

You should clarify that it only applies to the x32 ABI.

Which is only used on the(already vastly irresponsibly-constructed) Ubuntu 13.10.

why is it "vastly irresponsibly-constructed" ? I mean major hosting providers like rackspace use it... so ??
rackspace eats vaseline on toast
(comment deleted)
Here's why the Fedora maintainers rejected request to enable it: https://bugzilla.redhat.com/show_bug.cgi?id=854317

* It's new and unanalyzed for security flaws.

* It's useless without the right userland tools, which don't exist.

Ubuntu switched it on either because they have no clues, or because they wanted a checkbox to say "look, we have features no-one else does!!!!".

The main problem with Fedora is that it usually doesn't work. At all.
Is this a joke?
I wish.

-Someone who just spent half of a perfectly good day wrestling with their buggy installer

I guess I'm imagining all these working installs then.
There was a new deb package hitting the repos a few hours after the CVE, so it doesn't seem that they are sleeping on the job...
What a ridiculous comment, a non-LTS release introducing new features are one of its core features. A security issue being introduced via upstream code is not something that should be an opportunity to berate Ubuntu. The way Ubuntu responded with a super quick turnaround should be praised.

Note, that Kees who raised it to linux-distro’s is a member of the Ubuntu Technical Board, Ubuntu provided a fixed kernel as part of the embargoed security window of a mere 2 days(!) and Kamal Mostafa (Canonical) provided a rebased patch back to upstream Linux 3.8.

Ubuntu was even praised on the disclosure thread [0], for this super-fast turnaround. This only cements my confidence in the Ubuntu product.. and has helped sow the seeds for an exciting Ubuntu 14.04 LTS release.

Seems the whole process worked as a well-oiled machine, making the whole Linux ecosystem safer. Note, these types of issues only tend to get discovered when they are enabled in the wild.

[0] http://seclists.org/oss-sec/2014/q1/201

Fedora and Archlinux are both bleeding-edge upstream releases, and neither of them were comfortable enabling X32.
Hmm...

  #define PAYLOADSIZE 0x2000
  code += PAYLOADSIZE - 1024;
  memcpy((void*)code, &kernel_payload, 1024);
Does anybody know if it's possible to find out the size of a function during run time? Could you like say, put a return at the end of the function then do a for-loop with memcpy() for each byte until you run into the OPCODE for RET? I guess I could do a test.
RET is 195 or C3 in hex which also corresponds to modrm operands "eax, ebx" or "ebx, eax" so there's a pretty good chance of say a MOV instruction e.g. 89 C3, "mov ebx, eax" or something else also having that byte inside it, not to mention constants and offsets. (If you work with x86 asm enough, you will start memorising opcodes...)
Hmm... so after looking at http://ref.x86asm.net/coder32.html apparently 0xC2 and 0xC3 are RET. On my 32bit Linux machine, the following code printed "Okay! c2 found @ 174" Is this code correct in any way??? I guess the fact that I'm assuming every byte is a whole instruction; not even accounting for the operands is already a problem. But am I even reading the bytes properly at all?

  #include <stdio.h>

  void rightmeow()
  {
      int q;
      for(q = 0; q < 10 ; q++)
          printf("It's new!\n");
      return;
  }

  int main(int argc, char** argv)
  {
    int i;
    unsigned char bytecode;
    for(i = 0; i < 9999 ; i++)
    {  
        bytecode = (unsigned char)(**(rightmeow+i));//--- does this really work?
        if (bytecode == 0xC2 || bytecode == 0xC3)
            break;
    }
    if( i == 9999)
        printf("No good.\n");
    else
        printf("Okay! %x found @ %d\n",bytecode,i);

    return 0;
  }
You probably mean

     *(((unsigned char *)rightmeow)+i)
but just change the "q < 10" line to "q < 195" and you should see the output change, since that immediate constant is going to be present at least once before the final ret. For me, changing that changes the output from c3 being found at 47 to it being found at 25.
Nice! For me it changed from being found at 38, to 34. I wonder why you get C3 while I get C2.
C2 is RET iw, i.e. has 16-bit immediate value following the instruction, the # of bytes of parameters to pop from the stack. That should be 0 in this case so it could use C3 and save 2 bytes, but compilers aren't always really intelligent, although this is the first time I've seen that.

Then again, it could be something else and not the actual return you found.

Of course, functions commonly have multiple rets, jump tables at the end, etc. I don't think theres a robust way sans symbols.
If you know the address of the function that comes immediately after it, then subtract the two. This will include padding nops at the end etc. so it might be slightly bigger than the real size but you won't be missing bytes from the tail, which is what really matters in this application.
Hence why I mention symbols, that would have been exactly the way I'd do it. But without symbols, you can't know what the next function is, thats entirely upon the compiler (linker even) to decide.
Usually they'll be in the same order as they were in the source code.
(comment deleted)
Maybe it is possible if you don't use any optimizations and always use the same compiler.

A function may have the RET in the middle of it. Or use a jmp instead of ret (tail optimization), etc.

As others said, you can't really do it without disassembling the function (bonus problem: the general case is undecidable). A solution is to have your linker insert a "end of function" symbol in the ELF object (using a linker script), and import it in your C code a pointer. Then the size of f is "f_end - (void\*) f".
Tested on ubuntu 13.10 with latest updates.

Doesn't work, but DID crash the kernel, so it's vulnerable.

tested on Ubuntu 13.10 with kernel 3.11.0-15-generic. CONFIG_X86_X32=y

the program did not get the root privilege. Kernel did not crash either. Strange.