[HN Gopher] Fun with Gentoo: Why don't we just shuffle those ROP... ___________________________________________________________________ Fun with Gentoo: Why don't we just shuffle those ROP gadgets away? Author : crtxcr Score : 104 points Date : 2023-01-26 14:57 UTC (8 hours ago) (HTM) web link (quitesimple.org) (TXT) w3m dump (quitesimple.org) | jiripospisil wrote: | I'm guessing "dev-libs/openssl shuffleld" should go into | "/etc/portage/package.env" instead (in the appendix). | crtxcr wrote: | Good catch, thx! | hermitdev wrote: | One gap to this approach: gcc can use argument files (pass a file | that contains the actual arguments). I've only really seen this | with build systems that expect to work on large numbers of | arguments that will not fit on the command line. Still, something | to be aware of. | crtxcr wrote: | I'll keep an eye on that, thx! | lucideer wrote: | > _The potential issue comes from the assumption that all .o | files will be given continuously in the command line. The | assumption appear to hold, but could blow up down the road. But | well, it 's hack._ | | Other than this issue (which may well be a large / unsolvable | one), I wonder what other disadvantages to this approach there | might be. Does this hack have any potential for a Gentoo profile | or mainlining? | jchw wrote: | I like this idea. I have an idea for something that would be | cool, if impractical: Imagine a GCC wrapper that _doesn 't_ | actually link, but produces a bundle that performs the linking in | randomized order in realtime and then runs. | | I think that you could do this quite well on NixOS, and I'm now | intrigued to try to rig up a proof-of-concept when I can find the | time. | | Side-effect: Does not work for libraries without a significantly | more complex wrapper that certainly could not work for all | libraries. Though, you _could_ re-order the objects within a | static library fairly easily. | xxpor wrote: | That'd make process startup EXTREMELY slow | jchw wrote: | It's pretty much what OpenBSD is doing at bootup. | | Truthfully though you're right, using typical linkers, this | would be pretty slow; at least a few seconds for large | binaries, to minutes for things as large as web browsers. | However, for _many_ binaries, linking can be done _much_ | faster; mold claims to be only 50% the runtime of using `cp` | on the object files, which is fast enough to even re-link | Firefox on-the-fly without it being unusable. | | You could imagine writing a linker _specifically_ for this | case, that encodes information about the object files | directly into the resulting bundle. | blibble wrote: | I thought openbsd did it after boot? | dfox wrote: | OpenBSD relinks sshd. Which is relatively small thing | that is linked from relatively large objects (ie. it is | the typical modern C code). Relinking thing like glibc on | demand is going to be problematic, because it is | structured as to allow small binary sizes for static | linking and thus almost every function that is part of | glibc API is a separate compilation unit and object file. | Linking that into .so is slow, no mater what kind of | optimalization tricks you implement in the linker. | jchw wrote: | Yeah. That said, I'm suggesting that if it was really too | slow, though, it'd probably be infeasible to relink libc, | the kernel, etc. at bootup. It's not a direct comparison | to be sure. | somat wrote: | Openbsd also puts a fair amount of work into removing ROP | gadgets. | | For example. | | https://marc.info/?l=openbsd-cvs&m=152824407931917 | rtev wrote: | Very cool, thank you for sharing! Not only does ROP facilitate | traditional binary exploitation, but it's also used in cutting- | edge evasive techniques. By abusing ROP instead of direct | calls, red teamers are able to heavily obfuscate activities | from endpoint detection and response. | rtepopbe wrote: | Uh, yeah... The post opens with a mention of being inspired by | OpenBSD and goes into some detail on differences between their | approach and OpenBSD's throughout. | vlovich123 wrote: | I wonder if just shuffling it on every release (even minor) isn't | sufficient (and actually even publishing that order). That | doesn't have full security benefit (attackers have a finite set | of options) but keeps reproducible builds and the ability to | distribute pre-linked binaries while raising the attack | complexity significantly since no two machines are likely running | the exact same version. That means an exploit has to try several | different versions. Taking this a step further, create link N | randomly sorted copies per version and randomly distribute those. | Now the space to search through is large and the probability of | picking the correct gadget variant goes down with 1/MN where | there are M releases being attacked and N variants per release | that might be installed (a targeted attack or an attack of a | specific version only gets 1/N). Additionally, deterministic | builds maintain your ability to audit binaries and their | providence fairly easily (only grows linearly) while the risk of | noticing the attempt without a successful exploit is N-1/N. | | I'm not saying it's perfect but it seems like a reasonable | defense for binary distribution. As someone who used to run | Gentoo, I'd say most people are in favor of the faster times to | install a new package. | | EDIT: extending this idea further, I wonder if compilers can't | offer a random seed to supply that causes a random layout of the | sections within a built execution so that even statically linked | binaries benefit from this. | notpushkin wrote: | For binary distributions, how about shipping object files and | linking them on install with mold? This should be faster than | compiling from source, just marginally slower than installing | pre-linked binaries, and each build will be as unique as it | gets. | vlovich123 wrote: | The size of the distributed binary gets very large because | you're shipping a lot of code that ends up getting eliminated | by the linker. Also if you want to do any kind of LTO, then I | don't see how you do it in your model. (which is significant | for the larger applications like Chrome that have the likely | attack surface). Not every binary on the system actually | needs this either. | | Finally, the main problem with this idea is that you can't | audit malware because there's no way to maintain a source of | truth about what the binary on a given system should be. | Distributing randomly linked copies solves that because you | can have a deterministic mapping based on machine | characteristics (you do have to keep this hash secure but | it's feasible). You'd basically be maintaining N copies of | your distro with randomly built binaries with the user being | given a random one to install. | | And to be clear, my better idea is to do this at the compiler | level so that you randomize the relative location of | functions. That way it's impossible to find any gadget to | grab onto and you have to get information leakage from the | machine you're attacking & this information leakage has to be | repeated for each machine you want to compromise. | dfox wrote: | Randomizing the link order per release does not solve | anything, for this to really work as an mitigation layer, | you need to have few different randomly linked versions and | randomly give these to the end users. Just randomizing the | build does not solve anything as there still is exactly one | layout that everyone uses. | | On another note: automating this on gentoo is cool | exercise, but almost certainly if you just build everything | locally, the memory layout will be random enough that | writing shellcode blindly presents an interesting | challenge. (different compiler flags, various probabilistic | optimization passes... all that leads to the functions in | same object file having different sizes) | [deleted] | matzf wrote: | Don't try this with C++, unless you're certain that there are no | interdependencies or side-effects in global variable | initialisation. The link order (usually) affects the order in | which initialisers are executed. | londons_explore wrote: | Does the C++ spec guarantee initialization order? Or is any | application that depends on it relying on undefined behaviour? | theg5prank wrote: | There's no mandated order between compilation units. It's a | problem significant enough to have its own snarky name: the | Static Initialization Order Fiasco | https://en.cppreference.com/w/cpp/language/siof | Asooka wrote: | On the contrary: _do_ do this and if you observe your program | crashing due to linking order, fix the damn bug. | matzf wrote: | Fair enough :) I just meant to point out what could go wrong. | jchw wrote: | Developer PoV vs User/Distro PoV here :P you're not wrong, | though... | phkahler wrote: | >> As a side-effect, reproducible builds, which this technique | breaks, are less of a concern anyway (because you've compiled | your system from source). | | Reproducible builds verify the source code and build process | (including options) were the same. Not sure how important each | aspect is. | | Also, if for some reason you rebuild a dependency, you'll need to | relink everything that depends on that. This could get messy, but | it's still interesting. | cbrozefsky wrote: | Control over the RNG seed, and tracking that seed as an | 'input', would be a way to get reproducible builds while still | having randomization. | Hydraulix989 wrote: | Why? If the dependencies are dynamically loaded libraries it | shouldn't matter? | withinboredom wrote: | Isn't it impossible to have truly from-scratch reproducible | builds? IIRC, you have to trust the compiler which can't be | built from scratch. | ectopod wrote: | You can bootstrap the compiler. It's a chore but not | impossible. More usefully, you can check that your builds are | identical to other people's, so at least your compiler isn't | uniquely compromised. | londons_explore wrote: | > You can bootstrap the compiler. It's a chore but not | impossible. | | And specifically, only one person needs to do this once... | I'm surprised there isn't some project doing this... | yjftsjthsd-h wrote: | There is: https://savannah.nongnu.org/projects/stage0/ | withinboredom wrote: | I don't think it's possible since you'd need the original | compilers from the 70's and bootstrap other compilers up to | a modern one. Otherwise your existing compiler could taint | your new one. | ectopod wrote: | Many years ago I wrote a C compiler in assembly language. | It wasn't hard, and C hasn't changed that much. The | complexity in modern compilers is in the optimisation, | which you don't need if you're bootstrapping. It's not | impossible. | chameco wrote: | It'd be a fun exercise to write a tiny Forth in machine | code (sans assembler) and use it to write enough of a C | compiler to build tcc, or something along those lines. | From there I think you can chain old (but accessible) gcc | versions up to modern gcc. | kwhitefoot wrote: | ROP gadgets? | Karellen wrote: | https://en.wikipedia.org/wiki/Return-oriented_programming | | > Return-oriented programming (ROP) is a computer security | exploit technique that allows an attacker to execute code in | the presence of security defenses[1][2] such as executable | space protection and code signing.[3] | | > In this technique, an attacker gains control of the call | stack to hijack program control flow and then executes | carefully chosen machine instruction sequences that are already | present in the machine's memory, called "gadgets".[4][nb 1] | Each gadget typically ends in a return instruction and is | located in a subroutine within the existing program and/or | shared library code.[nb 1] Chained together, these gadgets | allow an attacker to perform arbitrary operations on a machine | employing defenses that thwart simpler attacks. | atlgator wrote: | I remember my Gentoo days freshman year in college. I spent more | time compiling updates than actually using the computer. | batman-farts wrote: | These days my 5950X can get through some of the big scary | packages quite rapidly. Firefox is done in about 8 minutes, a | new point release of Rust seems to take about 15. | | I still haven't decided whether or not I should be embarrassed | that I mainly bought a 16-core CPU to run Gentoo. | aquafox wrote: | I remember praying before every 'emerge -uDav world' that I | won't have to deal with fixing my system for the next 2 hours. | eMPee584 wrote: | Same thing for me. 2003 it was .. and gentoo was a well good | entry vehicle into linux | atlgator wrote: | We're the same age! I remember printing off a ~20 page | runbook of instructions to manually build and configure grub | and gentoo. Took hours to set up. | TylerE wrote: | Why did I never think of printing it? I'd open it in lynx | on a 2nd framebuffer (I forget the proper term... the | things that were like Alt+Shift+an Fkey or something) | gerdesj wrote: | Console 8) | TylerE wrote: | I remember installing from stage1 on a 1ghz-ish single core. | Just something like kde2 would take hours, and that's not even | counting the dependencies. Anything bigger than a command line | tool was something you'd kick off before going to bed and pray | it didn't error. (Spoiler: It almost always did) | dzmien wrote: | I do all world updates overnight for this very reason. But on | my R5 3600, the longest emerge is, by far, qtwebengine, which | takes just under 1.5 hours. Plus, Gentoo provides -bin versions | of many packages notorious for protracted build times, such as | Rust, Chromium, Firefox, etc... | gerdesj wrote: | -bin seems like a strange thing when you are doing Gentoo, | which is all about compile locally. Gentoo has always been | about choice and -bin is a choice. However you lose USE flag | choice decision with a -bin. | | The possible combinations that Gentoo allows looks to me like | a sort of Linux immune system in action. Quite a few | "unpopular" flags will get used (lol USEd) somewhere by | someone that will be more motivated on average to log a bug | somewhere. | | Gentoo also got the console shell look (colours, fonts etc) | right way before any other distro. It's copied widely. | gerdesj wrote: | I used to keep using the boxes whilst steam billowed out the | sides until things started crashing. | | I recall gcc3 -> 4. The prevailing "wisdom" was emerge --deep | (etc) world ... twice! My laptop was left for around a week | trundling through 1500 odd packages. I think I did system | first, twice too. I left it running on a glass table in an | unheated study, propped up to allow some better airflow. | | One of the great things about Gentoo is that a completely | fragged system doesn't faze you anymore. Screwed glibc? Never | mind. Broken python? lol! Scrambled portage? Hold my beer. | | I have a VM running in the attic that got a bit behind. OK it | was around eight? years out of date. I ended up putting in a | new portage tree under git and reverting it into the past and | then winding it forwards after getting the thing up to date at | that point in time. It took quite a while. I could have started | again but it was fun to do as an exercise. ___________________________________________________________________ (page generated 2023-01-26 23:01 UTC)