0x00前言
陆陆续续算是“看”完了这个系列吧 下来讲讲自己的经验总结还有反思吧
0x01关于每一篇
首先我的每一篇自己并没有写出来很多东西 只是自己在学习这个系列过程中 做了哪些事情 因为自己本就是初学者 接触的知识层面并不多 无法关联到更多的知识
栈溢出
关于栈溢出 在x86下函数参数就在函数返回地址的上方 而在x64的时候 前六个整型或者指针参数依次在RDI RSI RDX RCX R8 R9寄存器中 然后在往栈中存储 而我们为了方便 也是在win7 x86的环境下去了解漏洞的原理的 由于这里我们不需要考虑GS机制 将返回地址覆盖为shellcode的地址去执行
其实栈能在现在已经有很多机制导致以前的无法去利用了 而当我们使用的是64位机器的时候 又是另外一种情况 shellcode的布局 就不是如此的简单了 我们可以看 这里
其实我做的并不像一个教程吧 更像是一个分享 分享了自己的学习过程 也算是记录下来 方便自己看 详细教程的其实有很多师傅都写过
至于关于shellcode的话 wjllz师傅也提到过 其实自己实现这些也并不难 先获取当前线程的_KTHREAD结构 然后去找到_EPROCESS结构 循环找到system的_EPROCESS 然后替换当前线程的Token
1 | void ShellCode() |
栈溢出 当碰到的时候 首先应该去考虑栈空间的大小 所以我又去回头看了下我的调试 我需要的是 当我毫不知情的情况下去思考这个过程 而不是去一味的跟着师傅们“走过场” 虽然这是一个很简单的问题 但也不能如此来做
->查看栈空间的大小
将函数断在栈溢出之前 计算栈空间的大小为0x81c 再加4字节的ebp地址 接下来的4字节就是shellcode的地址了 所以说我们传入的栈空间的大小应为0x824
->覆盖返回地址
emmm 所以根据之前pwn的思路 就很简单了 我们可以填充0x820的padding 然后将后面4个字节覆盖为shellcode的地址 然后执行到漏洞函数返回的时候就可以执行我们的shellcode了
->平衡堆栈
接下来需要考虑的问题是 执行完shellcode需要继续正常执行程序的流程 所以我们需要去平衡堆栈 了解程序在正常执行时发生的事情 在执行完shellcode后执行即可正常执行 关于平衡堆栈 就需要自己手动区调试了 所以去执行了一个较小的buf 来看看程序正常执行的时候发生了什么
1 | #include<stdio.h> |
即我们在shellcode后面需要加上这两条语句来平衡堆栈
emmmmm 到这里基本就差不多了 我们就差不多可以执行我们的shellcode并且程序正常的执行下去了
UAF
UAF漏洞着实是蛮多的 貌似在网上一搜索 说的就是浏览器中居多了 原理确实简单 在ctfwiki我们可以看到解释 我自己并没有很多经验 不过看前辈们的博文总是可以收获到东西的 wjllz师傅的文章总是能让我收获到不少东西的 他关于UAF讲的是一个实际的漏洞 这里 亦或者说是在玉涵师傅的文章更加的详细一些
在这一次的实践中
->了解到UAF的原理(其实之前已经了解过了。。。)
->实践堆喷射技术(其实也没有怎么实践吧 只能算是了解 了解) 通过堆喷射 我们去增加我们控制程序的概率
->也想通了之前复现CVE-2014-1767时关于IO控制码的问题 其实IO控制码在实际漏洞中emmm 可以用wjllz师傅提到过的方法 我也在文章中说到过
type confusion
类型混淆我这也算是第一次接触这个概念 一段代码没有验证对象类型 并在没有进行类型检查的情况下使用 而我并没有很多的经验 所以并不能讲很多东西 只能讲讲在这个里面获得的经验吧 等以后有了机会 再补充吧
大致就是对象没有进行初始化 去使用这个对象 而默认使用这个值导致被利用 因为这个对象是没有初始化的 所以可以被随便使用 使其指向我们的shellcode
->待补充。。。
整数溢出
其实在官方exp中 是失败的 导致了蓝屏 在官方版本的栈溢出(GS)也是蓝屏的 因为整数溢出一般也是要依赖于其他溢出的 所以应该是覆盖长度的问题导致shellcode的地址出现错误 即就是覆盖长度过长了
确实是这样的 emmm
在看漏洞战争的时候 也看过整数溢出的部分 也进行过相关的复现 也了解到过一些新的知识 但其实那时候感觉并没有获得多少知识 而且也没有相应的漏洞利用 只能说是作为漏洞的入门而已
池溢出
池溢出也是在没有接触过的情况下去看的 自己需要去了解一些关于池的知识 其实在学习池的时候 并没有很懂池的知识 得写写关于池的有关学习
池的话 其实跟堆有点像 但机制不同 有分页池和非分页池之说 我们这个是在非分页池产生的 非分页池的话 我们申请内核非分页池中 将池铺满我们的对象 就可以控制我们的对象
我们需要修改的是下一个池块的首部 首部覆盖位我们的shellcode 我们需要在内核中申请大量的内存空间来满足我们的要求 使用CreateEventA 该函数产生一个Event对象 将它填充进我们的池
1 | HANDLE CreateEventA( |
之后就是一些结构了 在文章中页也提到过
空指针解引用
指针设置为NULL时 对数据再进行访问 导致程序崩溃 这不就是UAF? 在这种情况下 对指针进行引用 就会引发程序出现问题 而在我们的漏洞程序中 我们是可以控制这个指针的
师傅是说过的 uaf是相对来说好利用的 但这个的思路uaf又有点区别 不需要去申请大量内存来增加我们控制的概率 需要将第二项改为shellcode的地址 使用NtAllocateVirtualMemory分配0页内存将第二个值(+0x4)控制为shellcode的地址
任意内存覆盖
任意内存写入 即就是www功能 需要找到一个稳定的调用地址 将shellcode指针写到内核分发表中 调用即可
在内核中的NtQueryIntervalProfile调用了KeQueryintervalProfile函数 而这个函数又调用了HalDispatchTable+4 将shellcode的指针放在这里即可
先使用enumDeviceDrivers函数 枚举所有设备驱动地址 然后使用GetDeviceDriverBaseNameA函数获取驱动的名称 找到ntkrmlpa.exe的地址 再计算出HalDispatchTable的地址 使用LoadLibraryExA函数加载ntkrnlpa.exe到内存 然后使用Getprocess函数获得HalDispatchTable的地址
未初始化栈
当听到栈的时候 我又双以为是覆盖返回地址了。。。。 然而并不是 当我在提权验证的时候 发现不了shellcode的地址。。。。。emmm wjllz师傅貌似说到过?
这次最应该学到的 就是栈喷射了吧 我们还是将回调函数设置为shellcode的指针 我们在用户模式下干扰内核模式 用户态申请栈空间 进行栈喷射 填入我们的shellcode即可
未初始化堆
这不跟上一个差不多 只不过这个是堆喷射了 又有点类似于池溢出?不不不 这个是分页池了 所以不能那样 得重新使用函数来进行申请内存 在CreateEventA函数中 有个参数是在分页池中的LpName
1 | HANDLE CreateEventA( |
0x02关于以后
接下来可能就要进行一些自己的探索好些吧 总是这样 get不到多少东西的 自己确实菜 慢慢来吧 从这个系列入门 然后去了解更多的东西 师傅总是叮嘱我慢慢来 不急 确实 这是一个需要耐心的过程 急不得 需要学的东西很多 每天似乎也都在学 也似乎没在? 不能高效的使用时间确实是我的诟病 并且已经持续了很久了好像
这算是结束了HEVD系列 其中有很多做的不好的地方 自己能力有限 只能做这么些东西 记录了是自己学习的吧 也写的并不是很好 并不能像wj师傅那样 一篇文章可以收获到很多的东西
挖洞的话 自己的想法是 因为经验还很欠缺 所以首先需要的是大量的实践经验 师傅们的博客入手 得到一定的经验之后 再自己分析 再独立分析的过程中 碰到不会的问题了 在万不得已的情况下 不去看师傅们的思路 下一阶段就是自己堵路分析挖洞了(漫长的过程 可能得好几年吧。。。)