1. 20

  2. 2

    Is there a video of the talk up anywhere?

    1. 1

      There will be no videos from EuroBSDcon this year, sadly.

    2. 2

      Nice work.

      However, I don’t like the bar graphs on page 70. I thought: “Nice, over half the ROP gadgets are gone! But wait… the code size doubled? That can’t be right…” Only then I realized that the y-axis doesn’t start at zero, so I can’t estimate the relative change by just visually comparing the bars.

      1. 1

        Author says a common class of gadgets uses such and such registers. Says avoid them in favor of other registers. Maybe the gadget type with those registers is common because the registers themselves are common from compiler choices. Switching registers might lead to gadgets just using those registers instead. Or are there x86-specific reasons that using different registers will do entirely different things you can’t gadget?

        Other than that confusion, slides look like great work. Especially on ARM.

        1. 15

          Author here. Thanks for having a look! It was fun to do this talk.

          Yes, there are X86 specific reasons that other registers don’t result in ROP gadgets. If you look at Table 2-2 in the Intel 64 and IA-32 Architectures Software Developer’s Manual you can see all of the ModR/M bytes for each register source / dest pair, and other places in that section describe how to encode the ModR/M bytes for various instructions using all of the possible registers. When I surveyed the gadgets in the kernel and identified which intended instructions resulted in C3 bytes that were used as returns in gadgets, there were a large number of gadgets that were terminating on the ModR/M byte encoding the BX series registers. You are correct that these gadgets are common because the compiler frequently chooses to use the BX series registers, and the essence of my change to clang is to encourage the compiler to choose something else. By shifting RBX down behind R14, R15, R12 and R13 the compiler will choose these registers before RBX, and therefore reduce the incidence of the use of RBX resulting in a C3 ModR/M byte. We can see that this works because just shifting the BX registers down the list results in fewer unique gadgets.

          To directly answer your inquiry, gadgets arising from using R14, R15, R12, R13 instead (now that they will be more common) are not a problem. The REX prefix is never C3, and we can look at the ModR/M bytes encoding operations using those registers, and none of them will encode to C3. When I look at gadgets that arise from instructions using these registers, they don’t get their C3 bytes from the instruction encoding - they get them from constants where the constant encodes to a C3, so the register used is irrelevant in these cases. So moving RBX down behind R14, R15, R12 and R13 doesn’t result in more gadgets using those registers.

          There are other register pairs that result in a C3 ModR/M byte. Operations between RAX and R11 can result in a C3 ModR/M byte, but these are less common when we survey gadgets in the kernel (~56 in the kernel I have here now). RAX and R11 were already ahead of RBX in the default list anyway, so moving RBX down the list does not result in more gadgets using R11. If you ask why we haven’t moved R11 down next to RBX, the answer is that gadgets using R11 this way are not that numerous, so it hasn’t risen to the top of the heap of most-common-sources-of-gadgets (and therefore has not got my attention). There are many other sources of gadgets that can be fixed and will have a larger impact on overall gadget counts and diversity.

          I hope this clarifies that part of the talk. :-)

          1. 3

            Thank eveyone for the answers. Thank you in particular for this very-detailed answer that clarifies how x86’s oddities are creating the attack vectors.

            The reason I wanted to know is that I planned to design around high-end ARM chips instead of x86 where possible because I believed we’d see less ISA-related attacks. Also, certain constructions for secure code might be easier to do on RISC with less performance hit. Your slides seem to support some of that.

            1. 2

              To be fair, x86 doesn’t create the attack vectors, but does make any bugs much easier to exploit.

              ARM doesn’t have nearly the same problem - you can always ROP into a jump to THUMB code on normal ARM instructions, but these entry points are usually more difficult to find than an 0xc3.

            2. 1

              I’m curious to learn more about ROP. I’d like to examine adding support for another target to ROPgadget.py. So what designates a gadget? Any sequence of instructions ending in a return? How do attackers compose functionality out of gadgets? By hand, or is there some kind of a ‘compiler’ for them?

              1. 3

                You might be interested in the ROP Emporium’s guide. Off the top of my head the only automatic tools I know of are ropper and angrop.

            3. 5

              Switching registers might lead to gadgets just using those registers instead. Or are there x86-specific reasons that using different registers will do entirely different things you can’t gadget?

              If I understand this correctly, it’s because the ebx register causes opcodes to be created that contain a return instruction, i.e., opcodes that are useful in ROP. So by avoiding ebx as much as possible, you also avoid creating collateral ROP gadgets with early returns. This issue only happens because x86/amd64 have variable-length opcodes.

              1. 4

                As far as I understand, the register allocation trick is indeed x86-specific. The point is to avoid C3 bytes because these will polymorph into the RET instruction when used in unaligned gadgets. See the “polymorphic gadget” and ‘register selection’ sections in the slide set.