pages/txt/rop-on-arm.txt (view raw)
1 06 June, 2019
2
3Return Oriented Programming on ARM (32-bit)
4
5Making stack-based exploitation great again!
6
7 Before we start anything, you're expected to know the basics of ARM
8 assembly to follow along. I highly recommend [1]Azeria's series on
9 [2]ARM Assembly Basics. Once you're comfortable with it, proceed with
10 the next bit -- environment setup.
11
12Setup
13
14 Since we're working with the ARM architecture, there are two options to
15 go forth with:
16 1. Emulate -- head over to [3]qemu.org/download and install QEMU. And
17 then download and extract the ARMv6 Debian Stretch image from one
18 of the links [4]here. The scripts found inside should be
19 self-explanatory.
20 2. Use actual ARM hardware, like an RPi.
21
22 For debugging and disassembling, we'll be using plain old gdb, but you
23 may use radare2, IDA or anything else, really. All of which can be
24 trivially installed.
25
26 And for the sake of simplicity, disable ASLR:
27$ echo 0 > /proc/sys/kernel/randomize_va_space
28
29 Finally, the binary we'll be using in this exercise is [5]Billy Ellis'
30 [6]roplevel2.
31
32 Compile it:
33$ gcc roplevel2.c -o rop2
34
35 With that out of the way, here's a quick run down of what ROP actually
36 is.
37
38A primer on ROP
39
40 ROP or Return Oriented Programming is a modern exploitation technique
41 that's used to bypass protections like the NX bit (no-execute bit) and
42 code sigining. In essence, no code in the binary is actually modified
43 and the entire exploit is crafted out of pre-existing artifacts within
44 the binary, known as gadgets.
45
46 A gadget is essentially a small sequence of code (instructions), ending
47 with a ret, or a return instruction. In our case, since we're dealing
48 with ARM code, there is no ret instruction but rather a pop {pc} or a
49 bx lr. These gadgets are chained together by jumping (returning) from
50 one onto the other to form what's called as a ropchain. At the end of a
51 ropchain, there's generally a call to system(), to acheive code
52 execution.
53
54 In practice, the process of executing a ropchain is something like
55 this:
56 * confirm the existence of a stack-based buffer overflow
57 * identify the offset at which the instruction pointer gets
58 overwritten
59 * locate the addresses of the gadgets you wish to use
60 * craft your input keeping in mind the stack's layout, and chain the
61 addresses of your gadgets
62
63 [7]LiveOverflow has a [8]beautiful video where he explains ROP using
64 "weird machines". Check it out, it might be just what you needed for
65 that "aha!" moment :)
66
67 Still don't get it? Don't fret, we'll look at actual exploit code in a
68 bit and hopefully that should put things into perspective.
69
70Exploring our binary
71
72 Start by running it, and entering any arbitrary string. On entering a
73 fairly large string, say, "A" � 20, we see a segmentation fault occur.
74
75 string and segfault
76
77 Now, open it up in gdb and look at the functions inside it.
78
79 gdb functions
80
81 There are three functions that are of importance here, main, winner and
82 gadget. Disassembling the main function:
83
84 gdb main disassembly
85
86 We see a buffer of 16 bytes being created (sub sp, sp, #16), and some
87 calls to puts()/printf() and scanf(). Looks like winner and gadget are
88 never actually called.
89
90 Disassembling the gadget function:
91
92 gdb gadget disassembly
93
94 This is fairly simple, the stack is being initialized by pushing {r11},
95 which is also the frame pointer (fp). What's interesting is the pop
96 {r0, pc} instruction in the middle. This is a gadget.
97
98 We can use this to control what goes into r0 and pc. Unlike in x86
99 where arguments to functions are passed on the stack, in ARM the
100 registers r0 to r3 are used for this. So this gadget effectively allows
101 us to pass arguments to functions using r0, and subsequently jumping to
102 them by passing its address in pc. Neat.
103
104 Moving on to the disassembly of the winner function:
105
106 gdb winner disassembly
107
108 Here, we see a calls to puts(), system() and finally, exit(). So our
109 end goal here is to, quite obviously, execute code via the system()
110 function.
111
112 Now that we have an overview of what's in the binary, let's formulate a
113 method of exploitation by messing around with inputs.
114
115Messing around with inputs :^)
116
117 Back to gdb, hit r to run and pass in a patterned input, like in the
118 screenshot.
119
120 gdb info reg post segfault
121
122 We hit a segfault because of invalid memory at address 0x46464646.
123 Notice the pc has been overwritten with our input. So we smashed the
124 stack alright, but more importantly, it's at the letter `F'.
125
126 Since we know the offset at which the pc gets overwritten, we can now
127 control program execution flow. Let's try jumping to the winner
128 function.
129
130 Disassemble winner again using disas winner and note down the offset of
131 the second instruction -- add r11, sp, #4. For this, we'll use Python
132 to print our input string replacing FFFF with the address of winner.
133 Note the endianness.
134$ python -c 'print("AAAABBBBCCCCDDDDEEEE\x28\x05\x01\x00")' | ./rop2
135
136 jump to winner
137
138 The reason we don't jump to the first instruction is because we want to
139 control the stack ourselves. If we allow push {rll, lr} (first
140 instruction) to occur, the program will pop those out after winner is
141 done executing and we will no longer control where it jumps to.
142
143 So that didn't do much, just prints out a string "Nothing much
144 here...". But it does however, contain system(). Which somehow needs to
145 be populated with an argument to do what we want (run a command,
146 execute a shell, etc.).
147
148 To do that, we'll follow a multi-step process:
149 1. Jump to the address of gadget, again the 2nd instruction. This will
150 pop r0 and pc.
151 2. Push our command to be executed, say "/bin/sh" onto the stack. This
152 will go into r0.
153 3. Then, push the address of system(). And this will go into pc.
154
155 The pseudo-code is something like this:
156string = AAAABBBBCCCCDDDDEEEE
157gadget = # addr of gadget
158binsh = # addr of /bin/sh
159system = # addr of system()
160
161print(string + gadget + binsh + system)
162
163 Clean and mean.
164
165The exploit
166
167 To write the exploit, we'll use Python and the absolute godsend of a
168 library -- struct. It allows us to pack the bytes of addresses to the
169 endianness of our choice. It probably does a lot more, but who cares.
170
171 Let's start by fetching the address of /bin/sh. In gdb, set a
172 breakpoint at main, hit r to run, and search the entire address space
173 for the string "/bin/sh":
174(gdb) find &system, +9999999, "/bin/sh"
175
176 gdb finding /bin/sh
177
178 One hit at 0xb6f85588. The addresses of gadget and system() can be
179 found from the disassmblies from earlier. Here's the final exploit
180 code:
181import struct
182
183binsh = struct.pack("I", 0xb6f85588)
184string = "AAAABBBBCCCCDDDDEEEE"
185gadget = struct.pack("I", 0x00010550)
186system = struct.pack("I", 0x00010538)
187
188print(string + gadget + binsh + system)
189
190
191 Honestly, not too far off from our pseudo-code :)
192
193 Let's see it in action:
194
195 the shell!
196
197 Notice that it doesn't work the first time, and this is because /bin/sh
198 terminates when the pipe closes, since there's no input coming in from
199 STDIN. To get around this, we use cat(1) which allows us to relay input
200 through it to the shell. Nifty trick.
201
202Conclusion
203
204 This was a fairly basic challenge, with everything laid out
205 conveniently. Actual ropchaining is a little more involved, with a lot
206 more gadgets to be chained to acheive code execution.
207
208 Hopefully, I'll get around to writing about heap exploitation on ARM
209 too. That's all for now.
210
211References
212
213 1. https://twitter.com/fox0x01
214 2. https://azeria-labs.com/writing-arm-assembly-part-1/
215 3. https://www.qemu.org/download/
216 4. https://blahcat.github.io/qemu/
217 5. https://twitter.com/bellis1000
218 6. https://icyphox.sh/static/files/roplevel2.c
219 7. https://twitter.com/LiveOverflow
220 8. https://www.youtube.com/watch?v=zaQVNM3or7k&list=PLhixgUqwRTjxglIswKp9mpkfPNfHkzyeN&index=46&t=0s