[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)