5 ms·
I also must be missing something. XORing the return address on the stack with the stack pointer is similar to other stack protection mechanisms. I forget the pr
by RolfRolles 9y ago
I also must be missing something. XORing the return address on the stack with the stack pointer is similar to other stack protection mechanisms. I forget the precise name of it, but I'm pretty sure one of the existing stack protection tools does exactly this? MSVC's /GS feature is similar but slightly different in that it XORs the return address with a random value initialized on module load.
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?
- sebcat 9y agoYou need to hit the first ret in the vulnerable function to enter the ROP chain, and before that ret the value at the top of stack will be (de-)mangled. 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.
- RolfRolles 9y agoYour comments only apply to exploitation of stack buffer overflows, which have largely been rendered extinct due to compiler-based strategies like this one. Exploiting, say, a use-after-free vulnerability still may require ROP but does not require corruption of a return address on the stack. Given that the proposed defense supposedly targets ROP in general and not exploitation of stack buffer overflows specifically, my points still stand.
- Mordak 9y agoOh, yes. There are other ways to kick off ROP chains that do not involve stack corruption. For these attacks, Retguard will only pollute the gadget space by inserting these return-address permuting instructions before some fraction of the c3 bytes in a program (a little under 50%, depending on the program). 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. :-)
- ycmbntrthrwaway 9y ago> 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. 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.
- RolfRolles 9y agoAs I said to the other user who replied to a similar comment, these observations apply only to exploitation of stack buffer overflows, and hence don't rebut what I've said about this not mitigating ROP as a general technique (which is also used in the exploitation of non-stack-based vulnerabilities like use-after-free).
- shaded-enmity 9y agoExactly, but also, ROP is not solely about RET instructions, but the general technique is applicable to other forms of control transfer like unconditional absolute/relative jumps as well. Some time ago I did an analysis on this topic [1] using radare2 - I was curious what number of ROP gadgets is present in a healthy instruction stream (I call those implicit, and those are mostly comprised of function epilogues) and what number of gadgets can be formed by jumping into the "middle" of some instruction (explicit gadgets). The idea was to get rid of dangerous ModRegRm/SIB/XOP prefixes at the compiler level, see last table at [2] for example ModRegRM bytes - if your compiler decides to move something between RAX and {RDX,RBX} it will unavoidably emit C2/C3 bytes as well. Another thing are immediate and constant values, which are literally embedded in the instruction stream so if I have a code like: something = 0xc351c131485958 For which the compiler can generate: movabs rdx,0xc351c131485958 Just by using unfortunate value for something at the code level I've actually introduced a new gadget into the program: pop rax // 0x58 pop rcx // 0x59 xor rcx, rax // 0x48 0x31 0xc1 push rcx // 0x51 ret // 0xc3 Not sure how it turned out, but I heard someone from GCC was trying to implement a mitigation strategy based on this idea. [1]: https://github.com/shaded-enmity/r2-ropstats https://github.com/shaded-enmity/r2-ropstats [2]: http://www.asmpedia.org/index.php?title=ModRegRM_byte_(32/64-bit_addressing) http://www.asmpedia.org/index.php?title=ModRegRM_byte_(32/64...