Strong opinions, loosely held
I'm presently able to do the entirety of my personal project work on the OS and I'm only a couple of tools from being able to do the same professionally.
Keep it up, y'all.
However, the claim that ROP is impacted seems a bit flimsy to me. After all, ROP only requires that the C3 (RET) or C2 xx yy (RETN YYXX) byte sequence be present at the end of it; these sequences do occur at the end of a function, but they also occur in other places (such as anywhere the byte C3 arises in compiled machine code). ROP tools are programmed to look for the C3/C2 XX YY sequences and do not know or care whether these sequences are at the end of a function. The post is claiming that by transforming the ends of functions, ROP will be affected; but given that it seems to makes no attempt to remove C3 and C2 bytes from elsewhere in the machine code, that ROP tools will in fact continue to work just fine.
Basically the whole thesis of this patch seems to be that "existing stack protection methods will change function epilogues and therefore break ROP". I don't think it will have much of an effect on existing ROP tools. What am I missing?
To use ROP you need not only the RET instruction, but the code before it. You want to execute some existing function and return only then, not just return.
Buffer overflow attacks rely on overwriting return address, which is stored on the stack, with address of some code that attacker wants to execute. But if before returning the function XORs the value attacker used with some value he does not know, it is impossible for attacker to start ROP chain.
Though like with ASLR, it is possible to defeat this with a leak. If attacker can defeat ASLR, he likely can defeat this as well.
something = 0xc351c131485958
pop rax // 0x58
pop rcx // 0x59
xor rcx, rax // 0x48 0x31 0xc1
push rcx // 0x51
ret // 0xc3
> To use ROP you need not only the RET instruction, but the code before it. You want to execute some existing function and return only then, not just return.
Ok, you found RET in some unexpected place, like an immediate value. But do you want to execute the code before it? Most likely it is just garbage.
Usually you want to return to mprotect() and then chain somewhere else from it. With this mitigation even if you manage to jump to mprotect() function, you will not be able to make it chain to the next function you want.
If the return address on the stack is overwritten by an attacker, it needs to be overwritten with a ROP gadget adjusted for the mangling.
Actually removing c2/c3 bytes and actively reducing the gadget space is a different endeavour. There has been a bunch of academic work in this regard, with varying levels of success. Some would say it is a fool's errand to try to remove all the ROP gadgets, but that's what fools are for. Stay tuned. :-)
A few remain but are being converted as they are discovered.
But you don't always have stack address leaks. Presently, in order to ROP you need (a) a leaked address to the space where your gadgets live and (b) the ability to write your ROP chain somewhere where the program will return into it. With this scheme, you now also need (c) the exact address where you are writing your ROP chain.
Not all info leak vulnerabilities leak arbitrary memory of the attacker's choosing. If they did, stack canaries would be pretty useless. So for those cases where a stack address leak is unavailable, this raises the bar against ROP.
LLVM also has an impressive set of CFI capabilities: https://clang.llvm.org/docs/ControlFlowIntegrity.html.
The proposal just sounds like higher entropy stack cookies.
Microsoft's CFG is cool, but works on the other end to this - by protecting CALL instructions instead of RET.
Stack cookies are similar, yes. This mechanism combines two ASLR-randomized values (the caller address and the stack address) to control program flow. Stack canaries use a constant random cookie per object file to detect stack smashing. Retguard raises the bar for successful ROP attacks, just as CFG raises the bar for function pointer corruption attacks.
Full disclosure, I am the author of the clang diff that kicked this off. The appeal of this mechanism is that it is cheap enough to apply in every function, pollutes the ROP gadget space with instructions that permute the next return address, and requires attackers to have more information in order to kick off ROP chains (the address where they are writing their ROP chain). I know about some of the other stuff people are working on (CPI seems promising), and look forward to turning those things on when possible. Meanwhile, this mitigation is cheap, localized inside every function, and doesn't require any program modification in the vast majority of situations (programs that inspect stack frames without consulting the unwind info may have problems), so is fairly easy to turn on everywhere.
Regarding cost, a few years back, I implemented a shadow stack system via static binary rewriting. The overhead was very low, 1-2%. SafeStack claims < 0.1%.
I like SafeStack, but was disappointed to learn about the limitations with shared libraries. Some SafeStack is better than no SafeStack though, and it can probably be turned on without too much effort.
Seriously.. these projects should talk more about each others stuff..
EDIT: Thanks mods? :-)
MARC, or marc.info, is the Mailinglist ARCive. You're reading email.