1

Being gadgets change per each system and architecture (do they?), how would an attacker be able to determine the offsets of various Return Oriented Programming gadgets, would an attacker first need to determine:

-The operating system?

-The architecture the operating system is running on (i386, 64bit, arm, arm64, etc)

-Possibly even more information?

Is this solely in the realm of information gathering, or are offsets universal? Maybe perhaps I should download some virtual machines and test manually? I'm aware libc versions have universal offsets, does this hold true to binaries also?

asd40732
  • 11
  • 2

2 Answers2

1

ROP gadgets are based on the process' executable pages, which in practice usually means based on the process binary and the binaries of any libraries linked to it at runtime (there are exceptions with processes that use JIT compilers, like browsers, but mostly that's not the relevant situation). This does mean that building a ROP chain requires knowing the process being targeted pretty precisely, including which architecture it's compiled for.

So, how to do that? Well, for a lot of things, it's pretty easy: most software doesn't change very often, so you get target the current version, and there aren't that many architectures in use so you can usually target x86 (if Windows desktop), AMD64 (if Linux or MacOS desktop, if one of the programs where the default Windows version is AMD64 now, or any server), or ARM/ARM64 if targeting a specific mobile device. Most malware isn't trying to target a specific computer, it's just trying to spread as wide as possible, because the impetus behind most malware is money (ransomware and cryptominers, but also old-school spyware and even adware).

So, you just target the most common kind of system that runs the software you have an exploit for, and the most common version of that software (at least among vulnerable ones). Remember that ROP is a payload; if the correctness of your ROP chain matters at all, you already exploited the process and gained control of the instruction pointer! Typically, that means you know pretty precisely the relevant software version.

Some info gathering is also an option, especially for more targeted attacks. Software such as nmap will try to guess operating system identity and software version based on either explicit version strings or heuristics about behavior. Some software just outright tells you all the relevant details, such as browser user-agent strings. You can also look for data through other sources, like if you're targeting a company, have they published anything about their infrastructure?

Another option is a multi-payload target. You build a collection of payloads, and then you hide them behind a bootstrapper that extracts some information about the system after initial exploitation, and loads the appropriate payload. This is, practically speaking, often mandatory anyhow: ASLR is near-universal now, and it breaks most situations where you would use ROP unless you can extract some object from memory and compare it to what you expected in order to determine the ASLR mask. Since you then must apply that mask to your ROP chain (unless you found a library without ASLR to target), you're already sending a custom ROP chain anyhow.

Finally, you can just try 'em all! If you're attacking a server, odds are that it's on a cluster of machines all running identical hardware and software. So you exploit one (whichever the load balancer gives you first), and if that doesn't work you just try again. The first server will probably crash if the initial exploit succeeds at all, but that's OK; the load balancer will probably stop targeting it until the watchdog brings it online again. This kind of brute-force approach is very "noisy", of course - tons of servers crashing unexpectedly tends to set off alarms - but firing off network requests is fast and most exploits are even faster, so you might still be able to get in before any human response can be mustered. What you do then is hard to say - the defenders will be trying to kick you out ASAP - but it's totally possible to try something like grabbing some secrets (keys, credentials, private files, whatever) or just doing quick recon for a future attack once they stop watching so closely.

CBHacking
  • 40,303
  • 3
  • 74
  • 98
1

Would an attacker need to determine:

The operating system?

If you mean like Linux vs. Windows, then definitely, since the executable format itself is going to differ across these platforms, and there will be platform-specific code and libraries.

If you mean different Linux distributions or kernels, then the answer depends. The same exact binary will always have the same offsets (whether relative or absolute) regardless of what system it is run on. However, Linux distributions generally build their own versions of packages, sometimes with additional patches and features. For this reason, a program packaged with Debian may differ from the equivalent Ubuntu package, even if the package version string is the same. Also, different compilers and toolchains could cause offsets and gadgets themselves to differ. Certainly, if using gadgets from libc, you'd need to know which version the target system is running.

The architecture the operating system is running on (i386, amd64, arm, arm64, etc)

Yes, this is extremely important. Different instruction set architectures use completely different instructions. An operation that takes 3 instructions on amd64 could take 6 instructions on arm, and arm uses fixed-length instructions while amd64 uses variable-length. Also, since you are in a different architecture, you will need to find gadgets that look completely different. For these reasons and others, offsets will differ drastically between architectures.

Possibly even more information?

Traditionally, Linux binaries are compiled with fixed, static addresses that remain the same every time the program is run no matter what system (e.g. 0x400000 on x86_64). This means gadgets are easy to find and reuse across systems that have the exact same vulnerable binary. However, modern Linux distributions are moving towards compiling binaries with PIE (position independent executable) enabled. This means that, at run time, the base offset for the binary's executable image in memory will start at a random offset (i.e. basically ASLR for the binary). This makes it nearly impossible to know the address of a gadget, unless there is a possible partial overwrite or arbitrary read primitive available. That said, there may be a performance cost to PIE (at least historically), which has slowed the adoption for all binaries (some distributions only initially opted to use PIE on network daemons).

How do attackers determine ROP gadgets remotely?

Typically, they won't find them remotely, but will perform research locally to determine the gadgets and their offsets. However, this is only so useful unless you know the target's version, architecture, and addresses. If the target does not completely crash after attempting, they could keep trying different versions until it works.

There is also research on how to find ROP gadgets blindly.

multithr3at3d
  • 12,355
  • 3
  • 29
  • 42