首先解释一下GOT表和PLT表
GOT表是全局函数表(动态函数表,全局函数偏移量表)而PLT表则是局部函数表,GOT表中具有这个程序或者进程引用的所有的数据(包括函数和全局变量),PLT表中则是局部函数的表目,PLT表与GOT表是一一对应的,即PLT表中存储的是GOT表中的相对位置,而GOT表中存储的是函数的真正地址。在程序加载的时候,动态链接器会重定位GOT表中函数得到地址,即达到动态加载的过程。
PLT表与GOT表通过延时绑定,即将过程地址的绑定推迟到第一次调用该函数的时候,为了实现延时绑定,GOT表中的三个位置是特殊的:GOT[0]包含.dynamic段的地址,.dynamic段包含了动态链接器用来绑定过程地址的信息,比如符号的位置和重定位信息;GOT[1]包含动态链接器的标识;GOT[2]包含动态链接器的延迟绑定代码的入口点。GOT的其他表目为本模块要引用的一个全局变量或函数的地址。同样PLT表中有一个位置是特殊的,PLT[0]中保存的是相当于一个函数的一串代码,他跳到动态链接库中执行。每一个数据的PLT表目中的数据是由三部分组成的:第一条指令是跳转到相应的GOT存储的地址值中.第二条指令把函数相应的ID压入栈中,第三条指令跳转到PLT[O]中调用动态链接器解析函数地址,并把函数真正地址存入相应的GOT表目中。GOT表的初始化函数地址是PLT表中的第二条指令的地址,在函数被调用一次之后,GOT表中的函数地址的值就会变为真正的函数地址的值,在下一次调用函数的时候,只会花费一条指令和一个间接的存储器引用。
下面是具体的函数第一次调用的过程
首先是跳转到相应的PLT表中,此时该PLT表中的指令为
0x000001 jmp GOT
0x000002 push id
0x000003 jmp PLT[0]
GOT表中的内容
id:
0x000002
首先是程序执行到jmp GOT 进入GOT表中之后又跳转到0x000002位置即push id处将函数的id压入栈中,接着跳转到jmp PLT[0]的位置执行相应的动态链接库中查找该函数的真实地址的操作,并且把真实地址写入到GOT表中,在下一次调用函数的时候直接就进入GOT表中的相应位置,这是跳转的地址就直接到了真是函数地址的入口处,开始执行相应的代码
在执行完6之后GOT表中的id为3的函数的地址就变为函数的真实入口地址
再看这个题目
查看源代码发现漏洞很明显,scanf函数后面没有加 &所产生的漏洞原理
输入scanf(“%d”,&passcode)时读取的是passcode的地址0x70707070
当输入scanf(“%d”,passcode)时读取的是passcode这个地址的存储内容即0x80808080
如果我们可以控制0x80808080这个内容,那我们就可以利用这个漏洞进行GOT表的覆写
首先是检查一下有没有加壳,发现没有加壳
然后查看是32位的程序
用gdb调试一下程序在各个函数入口处下一个断点运行
我们写入数据存储的地方是0xffffd268 地址为edp-0x70 此时ebp的地址为
其中红色标识的部分是启用了栈溢出保护措施,再继续向下执行
进入到login函数中,发现两者的ebp相同
继续向下看,反编译一下login函数
发现scanf要读取的地址就是ebp-0x10(前面的两个参数从右向左依次压栈),两者ebp地址又相同,也就是scanf要写入的地址会从输入的name变量中读取,这就给了我们控制写入地址的机会
在IDA反编译中也可以看到passcode1的地址
两者的距离为0x60 即96个字节,最后的四个字节恰好是要写入的地址,那么我们怎么知道哪些地址可以写入呢,答案就是GOT表可以写入,我们可以将scanf后面的函数在GOT表中的真实函数地址写为我们想要执行的函数地址如(system)就可以获得相应的权限或者直接拿到flag
看到后面运行了fflush函数 puts函数 exit函数
这三个函数都可以,我们的目的是通过覆写GOT表在执行完scanf后直接执行system函数
我们看一下函数的地址
选这三个函数都可以,因为都在login中调用,我们选择最近的fflush函数,他的地址为0x0804a004
也就是说我们输入的name字段的最后四位是这个地址
我们再来查找system函数的地址,我们的目的是获取flag,因此要执行的是验证成功之后的system('/bin/bash cat flag')而不是单纯的system函数,他是有参数的于是我们选择地址0x80485e3,参数地址0x80487af
这样playload就出来了
由于scanf输入passcode1时是\d因此我们要先把地址转换为十进制
'a'*96 + '\x00\xa0\x04\x08'+ '\n'+'134514147\n'
连上服务器
python -c print('a'*96 + '\x00\xa0\x04\x08'+ '\n'+'134514147\n') | ./passcode
成功拿到flag