再利用Chakra引擎绕过CFG

xlab · 2015/12/24 15:00

Author:[email protected]

0x00 前言


本文源自一次与TK闲聊,期间得知成功绕过CFG的经过与细节(参考:[利用Chakra JIT绕过DEP和CFG])。随即出于对技术的兴趣,也抽出一些时间看了相关的东西,结果发现了另一处绕过CFG的位置。所以这篇文章中提到的思路与技术归根结底是来自TK提示的,在此特别感谢。

关于CFG的分析文章已经有很多了,想要了解的话可以参考我之前在HitCon 2015上的演讲(spartan 0day & exploit)。要说明的是,本文的内容即为我演讲中马赛克的部分,至此通过一次内存写实现edge的任意代码执行方法就全部公开了。

0x01 Chakra调用函数的逻辑


chakra引擎在函数调用时,会根据所调用函数状态的不同进行不同的处理。比如第一次调用的函数、多次调用的函数、DOM接口函数及经过jit编译后的函数。不同的函数类型会有不同的处理流程,而这些不同的处理都会通过Js::InterpreterStackFrame::OP_CallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallI<Js::LayoutSizePolicy<0> > > >函数调用Js::JavascriptFunction::CallFunction<1>函数来实现。

1.函数的首次调用与多次调用

当调用如下脚本时,Js::JavascriptFunction::CallFunction<1>函数会被Js::InterpreterStackFrame::OP_CallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallI<Js::LayoutSizePolicy<0> > > >函数调用。

#!js
function test(){}test();
复制代码

如果函数是第一次被调用,则执行流程如下。

#!bash
chakra!Js::InterpreterStackFrame::OP_CallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallI<Js::LayoutSizePolicy<0> > > >|-chakra!Js::JavascriptFunction::CallFunction<1>|-chakra!Js::JavascriptFunction::DeferredParsingThunk|-chakra!Js::JavascriptFunction::DeferredParse|-chakra!NativeCodeGenerator::CheckCodeGenThunk|-chakra!Js::InterpreterStackFrame::DelayDynamicInterpreterThunk|-jmp_code|-chakra!Js::InterpreterStackFrame::InterpreterThunk
复制代码

如果再次调用这个函数的话,调用流程如下。

#!bash
chakra!Js::InterpreterStackFrame::OP_CallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallI<Js::LayoutSizePolicy<0> > > >|-chakra!Js::JavascriptFunction::CallFunction<1>|-chakra!NativeCodeGenerator::CheckCodeGenThunk|-chakra!Js::InterpreterStackFrame::DelayDynamicInterpreterThunk|-jmp_code|-chakra!Js::InterpreterStackFrame::InterpreterThunk
复制代码

两次的调用流程大致是相同的,其中主要不同是因为,函数在第一次调用时候需要通过DeferredParsingThunk函数对其进行解析。其实函数只有在第一次调用时才进行进一步的初始化解析操作,这样设计主要是为了效率。而后续调用再直接解释执行。

分析发现,Js::JavascriptFunction::CallFunction<1>函数调用的子函数是通过Js::ScriptFunction对象中的数据获得的。后续调用的函数Js::JavascriptFunction::DeferredParsingThunkNativeCodeGenerator::CheckCodeGenThunk都存在于Js::ScriptFunction对象中。两次调用中Js::ScriptFunction对象的变化。

第一次调用时的Js::ScriptFunction对象。

#!bash
0:010> u poi(06eaf050 )
chakra!Js::ScriptFunction::`vftable':0:010> dd 06eaf050 
06eaf050  5f695580 06eaf080 00000000 000000000:010> dd poi(06eaf050+4) 
06eaf080  00000012 00000000 06e26c00 06e1fea0
06eaf090  5f8db3f0 00000000 5fb0b454 000001010:010> u poi(poi(06eaf050+4)+0x10)
chakra!Js::JavascriptFunction::DeferredParsingThunk:
复制代码

第二次调用时的Js::ScriptFunction对象。

#!bash
0:010> u poi(06eaf050 )
chakra!Js::ScriptFunction::`vftable':0:010> dd 06eaf050 
06eaf050  5f695580 1ce1a0c0 00000000 000000000:010> dd poi(06eaf050+4)
1ce1a0c0  00000012 00000000 06e26c00 06e1fea0
1ce1a0d0  5f8db9e0 00000000 5fb0b454 000001010:010> u poi(poi(06eaf050+4)+0x10)
chakra!NativeCodeGenerator::CheckCodeGenThunk:
复制代码

所以函数在第一次调用与后续调用的不同,是通过修改Js::ScriptFunction对象中的函数指针来实现的。

2.函数的jit

接下来我们看一下函数的jit。测试脚本代码如下,多次调用test1函数触发其jit。

#!js
function test1(num)
{return num + 1 + 2 + 3;
}//触发jittest1(1);
复制代码

经过jit的Js::ScriptFunction对象。

#!bash
//新的调试,对象内存地址会不同0:010> u poi(07103050 )
chakra!Js::ScriptFunction::`vftable':0:010> dd 07103050 
07103050  5f695580 1d7280c0 00000000 000000000:010> dd poi(07103050+4)
1d7280c0  00000012 00000000 07076c00 071080a0
1d7280d0  0a510600 00000000 5fb0b454 000001010:010> u poi(poi(07103050+4)+0x10)              //jit code
0a510600 55              push    ebp
0a510601 8bec            mov     ebp,esp
0a510603 81fc5cc9d005    cmp     esp,5D0C95Ch
0a510609 7f21            jg      0a51062c
0a51060b 6a00            push    0
0a51060d 6a00            push    0
0a51060f 68d0121b04      push    41B12D0h
0a510614 685c090000      push    95Ch
0a510619 e802955b55      call    chakra!ThreadContext::ProbeCurrentStack2 (5fac9b20)
0a51061e 0f1f4000        nop     dword ptr [eax]
0a510622 0f1f4000        nop     dword ptr [eax]
0a510626 0f1f4000        nop     dword ptr [eax]
0a51062a 6690            xchg    ax,ax
0a51062c 6a00            push    0
0a51062e 8d6424ec        lea     esp,[esp-14h]
0a510632 56              push    esi
0a510633 53              push    ebx
0a510634 b8488e0607      mov     eax,7068E48h
0a510639 8038ff          cmp     byte ptr [eax],0FFh
0a51063c 7402            je      0a510640
0a51063e fe00            inc     byte ptr [eax]
0a510640 8b450c          mov     eax,dword ptr [ebp+0Ch]
0a510643 25ffffff08      and     eax,8FFFFFFh
0a510648 0fbaf01b        btr     eax,1Bh
0a51064c 83d802          sbb     eax,2
0a51064f 7c2f            jl      0a510680
0a510651 8b5d14          mov     ebx,dword ptr [ebp+14h] //ebx = num
0a510654 8bc3            mov     eax,ebx        //eax = num (num << 1 & 1)
0a510656 d1f8            sar     eax,1          //eax = num >> 1
0a510658 732f            jae     0a510689
0a51065a 8bf0            mov     esi,eax
0a51065c 8bc6            mov     eax,esi
0a51065e 40              inc     eax            //num + 1
0a51065f 7040            jo      0a5106a1
0a510661 8bc8            mov     ecx,eax
0a510663 83c102          add     ecx,2          //num + 2
0a510666 7045            jo      0a5106ad
0a510668 8bc1            mov     eax,ecx
0a51066a 83c003          add     eax,3          //num + 3
0a51066d 704a            jo      0a5106b9
0a51066f 8bc8            mov     ecx,eax
0a510671 d1e1            shl     ecx,1          //ecx = num << 1
0a510673 7050            jo      0a5106c5
0a510675 41              inc     ecx            //ecx = num += 1
0a510676 8bd9            mov     ebx,ecx
0a510678 8bc3            mov     eax,ebx
0a51067a 5b              pop     ebx
0a51067b 5e              pop     esi
0a51067c 8be5            mov     esp,ebp
0a51067e 5d              pop     ebp
0a51067f c3              ret
复制代码

Js::ScriptFunction对象中原本指向NativeCodeGenerator::CheckCodeGenThunk函数的指针,在jit之后变为指向jit code的指针。实现了直接调用函数jit code。

这里简单说明一下,在调用函数传递参数时,是先将参数左移一位,然后将最低位置1之后的值进行传递的(parameter = (num << 1) & 1)。所以其在获取参数之后的第一件事是将其右移1位,获取参数原始的值。至于为什么要这样,我想应该是因为脚本引擎垃圾回收机制导致的,引擎通过最低位来区分对象与数据。

#!bash
chakra!Js::InterpreterStackFrame::OP_CallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallI<Js::LayoutSizePolicy<0> > > >|-chakra!Js::JavascriptFunction::CallFunction<1>|-jit code
复制代码

调用jit函数时的调用栈如上所示,这就是chakra引擎调用jit函数的方法。

3.DOM接口函数

最后为了完整性,还有一类函数需要简单介绍一下,就是DOM接口函数,由其它引擎如渲染引擎提供的函数(理论上可以为任何其它引擎)。

#!js
document.createElement("button");
复制代码

执行上面脚本则会通过下面的函数调用流程,最后调用到提供接口函数的引擎中。

#!bash
chakra!Js::InterpreterStackFrame::OP_CallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallI<Js::LayoutSizePolicy<0> > > >|-chakra!Js::JavascriptFunction::CallFunction<1>|-chakra!Js::JavascriptExternalFunction::ExternalFunctionThunk //调用dom接口函数|-dom_interface_function    //EDGEHTML!CFastDOM::CDocument::Trampoline_createElement
复制代码

当调用dom接口函数时,Js::InterpreterStackFrame::OP_CallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallI<Js::LayoutSizePolicy<0> > > >函数及后续的处理流程中所使用的Function对象与前面不同,使用的是Js::JavascriptExternalFunction对象。然后与前面的函数调用类似,也是通过解析对象内的函数指针,并对其进行调用,最终进入到想要调用的DOM接口函数中。

#!bash
0:010> u poi(06f2cea0)
chakra!Js::JavascriptExternalFunction::`vftable':0:010> dd 06f2cea0 
06f2cea0  5f696c4c 06e6f7a0 00000000 000000000:010> dd poi(06f2cea0+4)
06e6f7a0  00000012 00000000 06e76c00 06f040a0
06e6f7b0  5f8c6130 00000000 5fb0b454 000001010:010> u poi(poi(06f2cea0+4)+0x10)
chakra!Js::JavascriptExternalFunction::ExternalFunctionThunk:
复制代码

这就是chakra引擎对不同类型函数的不同调用的方式。

0x02 漏洞与利用


经过前面对chakra引擎各种调用函数方法的介绍,我们再来看一下本文的重点绕过cfg的漏洞。前面提到的在第一次调用脚本创建的函数时与后续调用此函数会有不同的流程。这里我们再看一下此处的逻辑,调用栈如下。

#!bash
//第一次调用
chakra!Js::InterpreterStackFrame::OP_CallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallI<Js::LayoutSizePolicy<0> > > >|-chakra!Js::JavascriptFunction::CallFunction<1>|-chakra!Js::JavascriptFunction::DeferredParsingThunk|-chakra!Js::JavascriptFunction::DeferredParse    //获取NativeCodeGenerator::CheckCodeGenThunk函数|-chakra!NativeCodeGenerator::CheckCodeGenThunk|-chakra!Js::InterpreterStackFrame::DelayDynamicInterpreterThunk|-jmp_code  |-chakra!Js::InterpreterStackFrame::InterpreterThunk
复制代码

在前面没有提到的是,上面调用流程中的Js::JavascriptFunction::DeferredParse函数。此函数内部会进行函数解析相关的工作,并且返回NativeCodeGenerator::CheckCodeGenThunk函数的指针,然后在返回Js::JavascriptFunction::DeferredParsingThunk函数后对其进行调用。NativeCodeGenerator::CheckCodeGenThunk函数的指针也是通过解析Js::JavascriptFunction对象获得的。代码如下。

#!js
int __cdecl Js::JavascriptFunction::DeferredParsingThunk(struct Js::ScriptFunction *p_script_function)
{NativeCodeGenerator_CheckCodeGenThunk = Js::JavascriptFunction::DeferredParse(&p_script_function);return NativeCodeGenerator_CheckCodeGenThunk();
}.text:002AB3F0 push    ebp
.text:002AB3F1 mov     ebp, esp
.text:002AB3F3 lea     eax, [esp+p_script_function]
.text:002AB3F7 push    eax             ; struct Js::ScriptFunction **
.text:002AB3F8 call    Js::JavascriptFunction::DeferredParse
.text:002AB3FD pop     ebp
.text:002AB3FE jmp     eax
复制代码

在这个跳转位置上并没有对eax中的函数指针进行CFG检查。所以可以利用其进行eip劫持。不过还首先还要知道Js::JavascriptFunction::DeferredParse函数返回的NativeCodeGenerator::CheckCodeGenThunk函数指针是如何通过Js::ScriptFunction对象何解析出来的。解析过程如下。

#!bash
0:010> u poi(070af050)
chakra!Js::ScriptFunction::`vftable':0:010> dd 070af050 + 14
070af064  076690e0 5fb11ef4 00000000 000000000:010> dd 076690e0 + 10
076690f0  076690e0 04186628 07065f90 000000000:010> dd 076690e0 + 28
07669108  07010dc0 000001a8 00000035 000000000:010> dd 07010dc0 
07010dc0  5f696000 05a452b8 00000000 5f8db9e00:010> u 5f8db9e0
chakra!NativeCodeGenerator::CheckCodeGenThunk:
复制代码

如上所述,Js::JavascriptFunction::DeferredParse通过解析Js::ScriptFunction对象获取NativeCodeGenerator::CheckCodeGenThunk函数指针,解析方法简写为[[[Js::ScriptFunction+14]+10]+28]+0c。所以只要伪造此处内存中的数据,即可通过调用函数来间接触发Js::JavascriptFunction::DeferredParse函数的调用,进而劫持eip,具体如下。

#!bash
0:010> g
Breakpoint 0 hit
eax=603ba064 ebx=063fba10 ecx=063fba40 edx=063fba40 esi=00000001 edi=058fc6b0
eip=603ba064 esp=058fc414 ebp=058fc454 iopl=0         nv up ei ng nz na po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000283
chakra!`dynamic initializer for 'DOMFastPathInfo::getterTable''+0x734:
603ba064 94              xchg    eax,esp
603ba065 c3              ret
复制代码

这样就绕过了cfg,成功劫持了eip。这种方法简单稳定,在获得了内存读写能力时使用是很方便的。此漏洞已于2015年7月25日报告微软。

0x03 修补方案


本文所述的漏洞微软已经修补,修补方案也比较简单就是对此处跳转增加cfg检查。

#!bash
.text:002AB460 push    ebp
.text:002AB461 mov     ebp, esp
.text:002AB463 lea     eax, [esp+arg_0]
.text:002AB467 push    eax
.text:002AB468 call    Js::JavascriptFunction::DeferredParse
.text:002AB46D mov     ecx, eax        ; this
.text:002AB46F call    ds:___guard_check_icall_fptr  //增加cfg检查
.text:002AB475 mov     eax, ecx
.text:002AB477 pop     ebp
.text:002AB478 jmp     eax
.text:00
复制代码

0x04 参考


  • 利用Chakra JIT绕过DEP和CFG
  • spartan 0day & exploit

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/391082.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

论文搜索源

中国科学院文献情报中心 见下图 中国计算机学会推荐国际学术会议和期刊目录 EI学术会议中心,        engieer village 转载于:https://www.cnblogs.com/cxy-941228/p/7693097.html

重学TCP协议(10)SYN flood 攻击

1.SYN flood 攻击 SYN Flood&#xff08;半开放攻击&#xff09;是一种拒绝服务&#xff08;DDoS&#xff09;攻击&#xff0c;其目的是通过消耗所有可用的服务器资源使服务器不可用于合法流量。通过重复发送初始连接请求&#xff08;SYN&#xff09;数据包&#xff0c;攻击者能…

大数据入门课程_我根据数千个数据点对互联网上的每门数据科学入门课程进行了排名...

大数据入门课程by David Venturi大卫文图里(David Venturi) A year ago, I dropped out of one of the best computer science programs in Canada. I started creating my own data science master’s program using online resources. I realized that I could learn everyt…

python 数据框缺失值_Python:处理数据框中的缺失值

python 数据框缺失值介绍 (Introduction) In the last article we went through on how to find the missing values. This link has the details on the how to find missing values in the data frame. https://medium.com/kallepalliravi/python-finding-missing-values-in-…

Spring Cloud 5分钟搭建教程(附上一个分布式日志系统项目作为参考) - 推荐

http://blog.csdn.net/lc0817/article/details/53266212/ https://github.com/leoChaoGlut/log-sys 上面是我基于Spring Cloud ,Spring Boot 和 Docker 搭建的一个分布式日志系统. 目前已在我司使用. 想要学习Spring Cloud, Spring Boot以及Spring 全家桶的童鞋,可以参考学习,如…

51nod1832(二叉树/高精度模板+dfs)

题目链接: http://www.51nod.com/onlineJudge/questionCode.html#!problemId1832 题意: 中文题诶~ 思路: 若二叉树中有 k 个节点只有一个子树, 则答案为 1 << k. 详情参见:http://blog.csdn.net/gyhguoge01234/article/details/77836484 代码: 1 #include <iostream&g…

重学TCP协议(11)TFO(Tcp Fast Open)

1. TFO 为了改善web应用相应时延&#xff0c;google发布了通过修改TCP协议利用三次握手时进行数据交换的TFO(TCP fast open&#xff0c;RFC 7413)。 TFO允许在TCP握手期间发送和接收初始SYN分组中的数据。如果客户端和服务器都支持TFO功能&#xff0c;则可以减少建立到同一服…

[网络安全] 远程登录

远程登录方式: 1.图像化远程登录 做法: 运行"窗口"输入 "mstsc " 输入ip地址 注意: 被远程计算机&#xff0c;必须打开远程登录服务: 信息面板–系统–允许远程访问。被远程计算机&#xff0c;必须存在拥有远程桌面权限的用户。 2.命令行远程登录 teln…

外星人图像和外星人太空船_卫星图像:来自太空的见解

外星人图像和外星人太空船By Christophe Restif & Avi Hoffman, Senior Software Engineers, Crisis Response危机应对高级软件工程师Christophe Restif和Avi Hoffman Editor’s note: In 2019, we piloted a new feature in Search SOS Alerts for major California wild…

chrome恐龙游戏_如何玩没有互联网的Google Chrome恐龙游戏-在线和离线

chrome恐龙游戏Several years ago, Google added a fun little Easter egg to Chrome: if your internet went down and you tried to visit a web page, youd see the message "Unable to connect to the Internet" or "No internet" with a little pixi…

Hotpatch潜在的安全风险

屎蛋 2016/06/22 10:11author:[email protected]0x00 “Hotpatch”简介IOS App的开发者们经常会出现这类问题&#xff1a;当一个新版本上线后发现存在一个严重的bug&#xff0c;有可能因为一个逻辑问题导致支付接口存在被薅羊毛的风险&#xff0c;这个时候能做的只能是赶快修复…

spring中@Inject和@Autowired的区别?分别在什么条件下使用呢?

问题&#xff1a;spring中Inject和Autowired的区别&#xff1f;分别在什么条件下使用呢&#xff1f; 我在浏览SpringSource上的一些博客&#xff0c;在其他一个博客中&#xff0c;那个作者用了Inject&#xff0c;但是我觉得他用Autowired也行 下面是一部分代码&#xff1a; …

Objective-C语言的动态性

Objective-C具有相当多的动态特性&#xff0c;基本的&#xff0c;也是经常被提到和用到的有动态类型&#xff08;Dynamic typing&#xff09;&#xff0c;动态绑定&#xff08;Dynamic binding&#xff09;和动态加载&#xff08;Dynamic loading&#xff09; 一、编译时和运行…

内存泄漏和内存溢出的区别

原文地址https://www.zhihu.com/question/40560123 简单来说&#xff0c;操作系统就像资源分配人员&#xff0c;你要使用内存的时候分给你&#xff0c;你用完了还给它。如果你使用了没有分配给你的内存就是内存溢出&#xff0c;如果你用完了没有还就是内存泄漏。会引起的问题&a…

怎么注销笔记本icloud_如何在笔记本电脑或台式机的Web浏览器中在线查看Apple iCloud照片

怎么注销笔记本icloudPicture this: you just returned from a beautiful vacation and want to show all those gorgeous photos to your family. But your phone just died. And since youre at a family dinner your laptop is nowhere to be found.想象一下&#xff1a;您刚…

棒棒糖 宏_棒棒糖图表

棒棒糖 宏AKA: lollipop plot又名&#xff1a;棒棒糖情节 WHY: a lollipop chart (LC) is a handy variation of a bar chart where the bar is replaced with a line and a dot at the end. Just like bar graphs, lollipop plots are used to make comparisons between diff…

ubuntu上如何安装tomcat

1. 在官网下载linux里面的tomcat 2. 放到DownLoads下面--把tomcat的压缩包放到DownLoads3. sudo mkdir /usr/local/tomcat/ -在usr/local/路径下新建一个tomcat的文件夹4 sudo tar zxvf tomcat。。。。tar.gz -C /usr/local/tomcat/---把解压后的tomcat放到usr/local/下的tomca…

leetcode 1734. 解码异或后的排列(位运算)

给你一个整数数组 perm &#xff0c;它是前 n 个正整数的排列&#xff0c;且 n 是个 奇数 。 它被加密成另一个长度为 n - 1 的整数数组 encoded &#xff0c;满足 encoded[i] perm[i] XOR perm[i 1] 。比方说&#xff0c;如果 perm [1,3,2] &#xff0c;那么 encoded [2,…

ZooKeeper3.4.5-最基本API开发

2019独角兽企业重金招聘Python工程师标准>>> package cn.itcast.bigdata.zk;import java.io.IOException; import java.util.List;import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEven…

字符串转换整数python_将Python字符串转换为Int:如何在Python中将字符串转换为整数

字符串转换整数pythonUnlike many other programming languages out there, Python does not implicitly typecast integers (or floats) to strings when you concatenate them to strings.与现有的许多其他编程语言不同&#xff0c;Python在将整数连接到字符串时不会隐式地将…