One Shellcode to Rule Them All: Cross-Platform Exploitation
•
5 likes•5,254 views
As the internet of things becomes less a buzzword, and more a reality, we're noticing that it's growing increasingly common to see embedded software which runs across different architectures -whether that's the same router firmware running across different models, or the operating system for a smart TV being used by different manufacturers. In a world where even your toaster might have internet access, we suspect that the ability to write cross-platform shellcode is going transition from being a merely neat trick, to a viable tool for attackers.
Writing cross-platform shellcode is tough, but there's a few techniques you can use to simplify the problem. We discuss one such method, which we used to great success during the DEFCON CTF qualifiers this year.
Presented by Tinfoil Security founder Michael Borohovski and engineer Shane Wilton at Secuinside 2014, in Seoul.
https://www.tinfoilsecurity.com/blog/cross-platform-exploitation
1 of 22
Download to read offline
More Related Content
One Shellcode to Rule Them All: Cross-Platform Exploitation
2. Who are we
• Michael “Borski” Borohovski
• Co-Founder / CTO @ Tinfoil Security
• Member of “Samurai” CTF team
• MIT Computer Science
• Hacking since 13, won Defcon 20 CTF
• Shane “ShaneWilton” Wilton
• Engineer @ Tinfoil Security
• Member of “Samurai” CTF team
• University of Waterloo Computer Science
• Hacking since he was just a wee little baby
3. Who are we
• Best web application scanner on the market
• Focused on Dev. and DevOps integrations
• Empower developers to find and fix vulnerabilities
before they’re deployed
• Enable security teams to focus on the hard problems
• Email secuinside@tinfoilsecurity.com for 2 free months
4. What is shellcode?
• Small piece of (assembled) code used as payload to exploit a vulnerability
• Common goals
• Launch a shell
• Read a file
• Stage a larger payload
• ???
• Lots of public examples of shellcode
• Shell-storm, metasploit, etc.
5. Why write your own?
• Often you need to perform unique actions.
• Unlock a door, call out to a different binary, etc.
• May have unique constraints
• Can’t contain the $ character
• Only alphanumeric characters
• Runs under both little and big endian (Dalvik?)
• Fun!
6. How do you write shellcode?
• Learn the system calls for your platform
• Man pages are your friend
• Start simple, then build more complexity
• First, just call the _exit syscall
• Then, “hello world” followed by _write followed by _exit
• …
• Familiarize yourself with different calling conventions
• x86 - cdecl, fastcall, etc.
• PowerPC - registers
• SPARC - register windows
● Have fun with this one, because we didn’t
• Most importantly…
7. How do you write shellcode?
• Comment
• Comment
• Comment
• Commenting with ; is your best friend
• If you think understanding your Ruby code a month later is tough, try
deciphering shellcode you’ve optimized to fit into a tiny buffer
8. Why is multiplatform shellcode useful?
• Deploy once, pwn always
• Consider the recent futex bug
• Allowed for priv. esc. on linux
• The original proof of concept (PoC) was for x86
• Geohot used the bug to root an android phone (ARM)
• Theoretically, a multi-platform payload could root any linux device
• Difficult to probe architectures in the wild
• Same version of software can run on completely
different architectures
• Common with routers, smart devices, etc.
• Guess wrong, and the target crashes
• Crashes lead to detection
9. Why is multiplatform shellcode useful?
• Malware (but that’s bad, don’t do it!)
• Internet of things – everything connected, built differently, lots of cheap
hardware choices
• “100,000 Refrigerators and other home appliances hacked
to perform cyber attack”
• Internet census 2012 attacked 1.2M devices
• Exploit/binary targeted 9 different platforms/architectures.
10. Compiling your pieces
• QEMU or Virtual Machine (VMWare, Parallels, etc.)
• Write shellcode once
• Load image for desired architecture in QEMU
• qemu-img create -f qcow2 linuxppc.qcow2 5G
• qemu-system-ppc -hda linuxppc.qcow2
-cdrom debian-ppc.iso
-boot d
-m 512
• Use nasm to assemble once in qemu
• nasm –f bin shellcode.asm
11. Compiling your pieces
• Capstone
• Programmable disassembly framework
• http://www.capstone-engine.org/
• Arm, Arm64 (Armv8), Mips, PowerPC, SPARC, SystemZ,
XCore & Intel
• Written in C but bindings for Ruby, Python, etc.
• Useful for seeing how opcodes disassemble in different
architectures
• Same opcode under different architectures lead to
different behaviors
• Take shellcode, print out disassembly for ARM, PPC,
X86, etc.
12. • Different architectures require different payloads
• Each architecture has its own nuances
• x86 has variable length instructions
• SPARC has fixed-length 32-bit instructions
• Shellcode must not crash on any platform
• We have three goals
• Write payloads for each architecture
• Determine the architecture of the CPU
• Jump to the payload for that architecture
• How do we determine the architecture of the CPU?
Multi-Platform Payloads
13. • The same bytes decode to different instructions on different
architectures
• A jump instruction on x86 might be a NOP on PowerPC
• Example - “x37x37xebx78”
• x86
• aaa; aaa; jmp 116+4
• MIPS
• ori $s7, $t9, 0xeb78
• SPARC
• sethi %hi(0xdfade000), %i3
CPU Switch Header
14. • Needs to jump in one architecture, and be NOP-like in all
others
• Can’t crash any architectures
• Can’t modify PC
• We don’t care about most other register state
• Most architectures encode branch instructions in predictable
formats.
• SPARC - 00-a-bbbbb-010-<22-bit offset>
• a - 1-bit annulment flag
• bbbbb - 5-bit condition
• We can fuzz all of the possible branch instructions!
Finding “switch” instructions
15. • Compute all of the branch instructions for an architecture
• Use Capstone to decode them in all other targeted
architectures
• Look for instructions which decode harmlessly in most other
architectures
• Easier than it sounds!
• We structure our switch-table like an onion
• “Peel” off an architecture with each instruction
• i.e. an instruction can’t crash MIPS, if MIPS has already
jumped to its payload by that point
Choosing Jump Candidates
16. • Consider the case on two architectures, A and B
• Let IA
and IB
be the sets of possible branch instructions for A
and B
• IA
= {a1
, a2
, a3
}
• IB
= {b1
, b2
, b3
}
• Let Din
be the set of dependencies for instruction in
• i.e. if a1
crashes on architecture B then Da1
= IB
• We need an instruction from IA
and an instruction from IB
such that there exists an evaluation order which resolves all
dependencies
• Called a topological ordering on the dependency graph
Dependency Resolution
17. 1. Let S be the cartesian product of the sets of branch
instructions
2. For each s ∈ S = (a, b, c, …)
a. Construct a graph G with vertex set given by the
elements of s
b. Create a directed edge from vertex i to vertex j if
instruction i crashes under the architecture for which j
originates from
c. Check for a topological ordering on G
i. If one exists, return it, we are done
ii. Otherwise, continue
3. If no ordering exists, we need to be clever
a. Consider multi-stage payloads which split the targeted
architectures into more manageable groupings
Algorithmically…
18. • Polyglot @ DEFCON 22 CTF Quals
• Construct a payload which reads a flag on x86, ARMEL (little
endian), ARMEB (big endian), and PPC
• The dependencies are resolvable as:
• x86 -> PPC -> ARMEB -> ARMEL
• tsort can do most of this work from the command-line!
Putting It Together
73 12 00 00 48 00 01 70 9A 00 00 40 13 00 00 EA
x86 jae 0x14 - - -
PPC andi r1, r0, 72 bdnzfa- lt, 0x98 - -
ARMEB tstvc r2, #0 stmdami r0, ... bls 0x110 -
ARMEL ... ... ... b 0x60
19. • Each architecture is jumping to a different point, so we can
simply insert our platform-specific shellcode at the correct
offsets
• Note the strange instructions
• 48 00 01 70 -> bdnzfa- lt, 0x98
• Not a terribly useful instruction, but acts like a simple
branch in our case
• You just owned four different platforms with one payload
• Congratulations!
Putting It Together Cont.
20. • Hardware is becoming more and more varied, and will only
get further fragmented over time
• Knowing and being able to fingerprint one architecture will
become a thing of the past
• Writing one payload that works across many architectures
was once a luxury, but is quickly becoming a requirement for
launching attacks in the wild
To sum it all up
21. • Basic idea: set up a jump table at the beginning of your
shellcode, with one architecture falling through with each
instruction
• Find jmp/branch instructions in one architecture that are
NOPs or NOP-like instructions in all others you’re targeting
• To automate this search, you can reduce the problem to one
of dependency resolution
To sum it all up