[Tips]Reducing the Effective Entropy of GS阅读笔记
天桥说书的: void#ph4nt0m.org
pub: 2007-06-29
last mod: 2007-06-29
http://www.ph4nt0m.org
附件链接: http://bootshell.googlepages.com/attack_gs.zip
Reducing the Effective Entropy of GS是uninformed.org的vol.7的第1篇文章.
看完收获就是这一句话:
While the results shown in this paper do not represent a complete break of GS, they do hint toward a general weakness in the way that GS cookies are generated.
为啥把这句放到结尾?! 这不是明摆着坑害读者嘛.
全文就不翻译了,把其中有点意思的记下来.
1. 预备知识
----------------------------------------------------
GS __security_cookie的生成方法:
Cookie ^= SystemTimeLow; // GetSystemTimeAsFileTime()
Cookie ^= ProcessId; // GetCurrentProcessId()
Cookie ^= ThreadId; // GetCurrentThreadId()
Cookie ^= TickCount; // GetTickCount()
Cookie ^= PerformanceCounterHigh; //
Cookie ^= PerformanceCounterLow; // QueryPerformanceCounter()
该生成过程在PE入口点处的第一个call里面执行.生成的__security_cookie是映象的全局cookie.
为了提高攻击难度,__security_cookie用于被保护函数的时候,微软做了如下处理:
.text:00402150 xor eax, ebp
.text:00402152 mov [ebp+2A8h+var_4], eax
上面这段取自被GS保护的函数的Prologue,可以看到var_4 =__security_cookie ^ ebp;因为ebp的不确定,增加了var_4的随机性.
.text:00402229 xor ecx, ebp
.text:0040222B pop esi
.text:0040222C call __security_check_cookie
上面这段取自被GS保护的函数的Epilogue,可以看到ecx = var_4 ^ ebp后,调用了__security_check_cookie函数.跟进:
.text:00406351 jnz short loc_406355
.text:00406353 rep retn
.text:00406355 loc_406355:
.text:00406355 jmp __report_gsfailure
如果ecx==__security_cookie,则直接ret;如果ecx!=__security_cookie,则跌入深渊__report_gsfailure: 进程结束,溢出失败.
2. 我猜,我猜,我猜猜猜
----------------------------------------------------
作者假定的攻击环境是:
能有个本地非特权用户的shell.(废话,要是administrator,还费那事干嘛,直接上锤子砸不就得了)
先来看看全局的__security_cookie,如果要猜出它的值,需要猜出为cookie提供熵值的SystemTimeHigh,SystemTimeLow,ProcessId,ThreadId,TickCount, PerformanceCounterHigh,PerformanceCounterLow. 初看起来这和中彩票概率没什么差别,但是如果注意到,这些值都是在进程启动的时候生成的,就有意思了,下面按顺序说明如何猜测这些值:
2.1 SystemTimeHigh, SystemTimeLow
----------------------------------------------------
SystemTime共64bit,High是高32bit,Low是低32bit.
猜这个值比较容易,因为SystemTime的精度没想像中的那么高,并非传说中的100纳秒,而是是15.625毫秒或10.1毫秒.在这段漫长的时间 (当然是对CPU而言),创建线程和生成cookie的过程早就"biu!"的一声就完成了.所以我们可以假定线程的创建时间与生成cookie的时间一致!因此,猜测SystemTime的值转换成了猜线程的创建时间.
猜线程的创建时间需要要借助Native API NtQuerySystemInformation.通过SystemProcessesAndThreadsInformation系统信息类,我们能够得知进程名,进程创建时间和进程里面每个线程的创建时间(我们要的东西),而且最重要的是这个函数允许非特权用户调用.
但在Vista下这种方法不可行,因为Vista已经去掉了这个信息类.
2.2 ProcessId, ThreadId
----------------------------------------------------
这两个值最容易得到.只要利用上面说过的NtQuerySystemInformation通过SystemProcessesAndThreadsInformation信息类获得进程id和线程id即可.
有趣的是ProcessId和ThreadId的高16bit为0x0000,也就是说对cookie的高16bit值改变没有任何贡献.
当然,Vista下这个方法不可行,理由同上.
2.3 Tick Count
----------------------------------------------------
GetTickCount()函数返回的是系统启动后经过的毫秒数,所以如果知道系统的启动时间,同时假定cookie的生成时间与线程创建时间 CreationTime一致,那么Tick Count的估计值EstTickCount可以通过如下公式计算得到: (/10000是将100纳秒转换成毫秒)
EstTickCount = (CreationTime - BootTime) / 10000
同样,调用NtQuerySystemInformation通过SystemTimeOfDayInformation系统信息类即可获得系统的启动时间BootTime.(为一个64bit数,精度为100纳秒)
据作者测试, 上面的公式加上个经验值修正后,能更准点:
EstTickCount = [(CreationTime - BootTime) / 10000] + 78
2.4 PerformanceCounterHigh, PerformanceCounterLow
----------------------------------------------------
PerformanceCounter共64bit,High是高32bit,Low是低32bit.其中Low 32bit很难猜准,可以看第3节的测试结果.
PerformanceCounter即性能计数器值.这到底是怎么一个值?它是系统启动后,不断增加的一个计数器值(变化频率是固定的),调用QueryPerformanceCounter()可以读出当前性能计数器的值(64bit).
调用QueryPerformanceFrequency()可以告诉我们性能计数器每秒的滴答数,即变化频率PerfFreq,而且PerfFreq值在系统启动后不能改变.
所以,用前面估测的SystemTime,即EstSystemTime减去BootTime得到估测的启动时间EstUptime,再乘上 PerfFreq,即可得到估测的性能计数值EstPerfCounter: (/10000000是把PerfFreq由秒转成100纳秒级别)
EstPerfCounter = EstUpTime * (PerfFreq / 10000000)
据作者测试,上述公式加个经验值-165000,猜得更准些:
EstPerfCounter = EstUpTime * (PerfFreq / 10000000) - 165000
3. 测试
----------------------------------------------------
先用VC2005 Express(免费的)编译附带的show_cookie.c,记住带上/GS编译参数.
用ollydbg载入show_cookie.exe,进入___security_init_cookie例程(就在show_cookie.exe的入口点处那个call调用).如下patch函数:
___security_init_cookie函数:
0040168A 90 NOP
0040168B > F7D6 NOT ESI ; 从补丁代码处跳回
0040168D . 8935 68314000 MOV DWORD PTR DS:[403168],ESI
补丁代码: (在.text节尾部空白处写)
004017E2 56 PUSH ESI ; push Cookie值
004017E3 55 PUSH EBP ; push 栈帧
004017E4 E8 17F8FFFF CALL 00401000 ; 调用DumpInfo函数显示全局Cookie值
004017E9 ^ E9 9DFEFFFF JMP 0040168B ; 跳回 ___security_init_cookie
在ollydbg中保存修改,得到可以显示cookie值及相关信息的show_cookie_mod.exe文件.运行之,显示自己的全局cookie值.
再编译原文附带的gencookie.c,运行gencookie.exe show_cookie_mod.exe,得到猜测的cookie值.
下面是我的一次测试结果:
图(1)
图(2)
如上图所示,绿色表明猜对的部分,黄色表明猜错的部分,最终得到的猜测cookie值只有最高1字节符合.-_-"
4. 居然还有结论
----------------------------------------------------
能看到这里,说明你被彻底忽悠了. hiahiahia :-P