Mailing List Archive

Windows Dns bug with TXT and multiple strings
This sort of looks like a cross post, or an off-topic message from the
subject, but it's not:

Can someone with a *nix SPF implmentation and DNS server please try a
simple test for me.

Create a DNS TXT record like:

Windnsbug TXT ( ""
"v=spf1 "
""
""
"+mx "
""
"-all"
""
"" )

And then use your spftest program to read it. I'd expect the record to
look like "v=spf1 +mx -all" when you get to your parser as I expect
many other people would, however on Windows, I can't even process the
TXT record under some circumstances because the Windows 2K Dns API
DnsQuery barfs when in ANSI mode and corrupts my heap when in UNICODE
mode - bletch!

I've opened a case with Microsoft on this because it looks like a
vulnerability that could cause an least a DOS attack on a system that
tries to read the DNS TXT record and at worst a way to deliver a
virus/worm to a Windows system simply by sending you an email - hideous
I think you'll agree.

If you don't have Windows, then stop reading now, otherwise, this is the
case file that I forwarded to MS...

















========================================================================
========

Bug in DnsQuery
===============

Symptoms
========

DnsQuery cannot read DNS TXT records with empty strings and will either
crash the process (DnsQuery_A) or return an invalid data set
(DnsQuery_W) which subsequently corrupts the callers heap.


Steps to reproduce
==================

On a DNS server, create a TXT record with the following shape.

windnsbug TXT ( ""
"abc"
""
"def"
""
"this is"
""
"gonna break"
""
"" )

Either enter this via the DNS UI, or create a TXT record named
"windnsbug" and then edit the
%WindowsSystem32%\dns\windnsbug.<domain>.dns file and replace the TXT
record with the above.


Next on a Windows 2000/Windows XP system with Visual Studio installed,
start a console project and replace the main code with:

------------------------------------------------------------------------
--

// DNSBug.cpp : Defines the entry point for the console application. //

#include "stdafx.h"

#include <windows.h>
#include <windns.h>
#pragma comment(lib, "Dnsapi.lib" )

int _tmain(int argc, _TCHAR* argv[])
{
// First go, this is OK until the DnsRecordListFree
try
{
PDNS_RECORD pRecs;
// Replace the domain below with your domain (or leave
it as is and use ours)
DNS_STATUS stat = DnsQuery_W( L"dnsbug.exclaimer.co.uk",
DNS_TYPE_TEXT, DNS_QUERY_STANDARD | DNS_QUERY_BYPASS_CACHE, NULL,
&pRecs, NULL );
if ( stat == 0 )
{
for(DWORD
dw=0;dw<pRecs->Data.TXT.dwStringCount;dw++)
printf( "DnsQuery_W: Text string %d is
%S\r\n", dw, pRecs->Data.TXT.pStringArray[dw] );

DnsRecordListFree( pRecs, DnsFreeRecordListDeep
);
}
}
catch( ... )
{
printf("DnsQuery_W faulted\r\n");
}

// Second go, this is gonna crash ...
try
{
PDNS_RECORD pRecs;
// Replace the domain below with your domain (or leave
it as is and use ours)
DNS_STATUS stat = DnsQuery_A( "dnsbug.exclaimer.co.uk",
DNS_TYPE_TEXT, DNS_QUERY_STANDARD | DNS_QUERY_BYPASS_CACHE, NULL,
&pRecs, NULL );
if ( stat == 0 )
{
for(DWORD
dw=0;dw<pRecs->Data.TXT.dwStringCount;dw++)
printf( "DnsQuery_A: Text string %d is
%s\r\n", dw, pRecs->Data.TXT.pStringArray[dw] );

DnsRecordListFree( pRecs, DnsFreeRecordListDeep
);
}
}
catch( ... )
{
printf("DnsQuery_A faulted\r\n");
}

printf("End\r\n");

return 0;
}
------------------------------------------------------

Compile and run the program. Notice that the output from the DnsQuery_W
part has strange characters at the start of each line.



Concerns
========

With the the coming of Microsoft CallerID and SPF (various tools in the
fight against spam), DNS servers are starting to publish more
information in TXT records.

Many MTAs on Windows will be reading this data (including our product
Exclaimer (www.exclaimer.net)) using the DnsQuery API.

These processes may encounter DNS TXT records setup by a malicious user
in such as fashion as to cause code of their own liking to be executed
on the target system just by simply delivering a piece of email to that
server.




========================================================================
==========================

Further research
================

As I understand it, the strings are supposed to have a length octet
leading them in followed by the string which is not null terminated. In
the example above I'd expect the data in the RR to be 33 bytes long in
this shape:


0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 3 |'a'|'b'|'c'|'0'|'3'|'d'|
+---+---+---+---+---+---+---+---+

8 9 10 11 12 13 14 15
+---+---+---+---+---+---+---+---+
|'e'|'f'| 0 | 7 |'t'|'h'|'i'|'s'|
+---+---+---+---+---+---+---+---+

16 17 18 19 20 21 22 23
+---+---+---+---+---+---+---+---+
|' '|'i'|'s'| 0 | a |'g'|'o'|'n'|
+---+---+---+---+---+---+---+---+

24 25 26 27 28 29 30 31
+---+---+---+---+---+---+---+---+
|'n'|'a'|' '|'b'|'r'|'e'|'a'|'k'|
+---+---+---+---+---+---+---+---+

32 33
+---+---+
| 0 | 0 |
+---+---+

Spookily, in the case where the API DnsQuery_W which doesn't crash
internally (but corrupts the heap during free), the strings returned
from the Windows API are:

p[0] = "\3abc"
p[1] = "abc"
p[2] = "\3"
p[3] = "def "
p[4] = "\7this is"
p[5] = "this is"
p[6] = "\agonna break"
p[7] = "gonna break"
p[8] = ""
p[9] = ""

NSLOOKUP in debug & d2 mode returns the fact that there were 33 bytes of
data, lending weight to the fact that the record is not invalid in the
DNS server, and it only printing out 4 strings (which is sort of
expected).

Thanks,
Gary

This message (and any associated files) is intended only for the use of spf-devel@v2.listbox.com and may contain information that is confidential, subject to copyright or constitutes a trade secret. If you are not spf-devel@v2.listbox.com you are hereby notified that any dissemination, copying or distribution of this message, or files associated with this message, is strictly prohibited. If you have received this message in error, please notify us immediately by replying to the message and deleting it from your computer. Messages sent to and from us may be monitored. Any views or opinions presented are solely those of the author gary@exclaimer.net and do not necessarily represent those of the company.

This disclaimer was added by eXclaimer for Microsoft Exchange 2000, a DCSL product. Please visit our web site at www.exclaimer.co.uk for more information.

-------
To unsubscribe, change your address, or temporarily deactivate your subscription,
please go to http://v2.listbox.com/member/?listname=spf-devel@v2.listbox.com
RE: Windows Dns bug with TXT and multiple strings [ In reply to ]
Hi Gary,
I don't think you can mix ANSI and UNICODE calls like you did. PDNS_RECORD is either a pointer to a DNS_RECORDA or DNS_RECORDW structure, depending if you're compiling for UNICODE or not. You should use DNS_RECORDA* in DnsQuery_A and DNS_RECORDW* in DnsQuery_W.

Best regards,
Dirk

> -----Original Message-----
> From: Gary Levell [mailto:gary@exclaimer.net]
> Sent: Monday, June 14, 2004 15:23
> To: spf-devel@v2.listbox.com
> Subject: [spf-devel] Windows Dns bug with TXT and multiple strings
>
>
> This sort of looks like a cross post, or an off-topic message from the
> subject, but it's not:
>
> Can someone with a *nix SPF implmentation and DNS server please try a
> simple test for me.
>
> Create a DNS TXT record like:
>
> Windnsbug TXT ( ""
> "v=spf1 "
> ""
> ""
> "+mx "
> ""
> "-all"
> ""
> "" )
>
> And then use your spftest program to read it. I'd expect the record to
> look like "v=spf1 +mx -all" when you get to your parser as I expect
> many other people would, however on Windows, I can't even process the
> TXT record under some circumstances because the Windows 2K Dns API
> DnsQuery barfs when in ANSI mode and corrupts my heap when in UNICODE
> mode - bletch!
>
> I've opened a case with Microsoft on this because it looks like a
> vulnerability that could cause an least a DOS attack on a system that
> tries to read the DNS TXT record and at worst a way to deliver a
> virus/worm to a Windows system simply by sending you an email
> - hideous
> I think you'll agree.
>
> If you don't have Windows, then stop reading now, otherwise,
> this is the
> case file that I forwarded to MS...
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> ==============================================================
> ==========
> ========
>
> Bug in DnsQuery
> ===============
>
> Symptoms
> ========
>
> DnsQuery cannot read DNS TXT records with empty strings and
> will either
> crash the process (DnsQuery_A) or return an invalid data set
> (DnsQuery_W) which subsequently corrupts the callers heap.
>
>
> Steps to reproduce
> ==================
>
> On a DNS server, create a TXT record with the following shape.
>
> windnsbug TXT ( ""
> "abc"
> ""
> "def"
> ""
> "this is"
> ""
> "gonna break"
> ""
> "" )
>
> Either enter this via the DNS UI, or create a TXT record named
> "windnsbug" and then edit the
> %WindowsSystem32%\dns\windnsbug.<domain>.dns file and replace the TXT
> record with the above.
>
>
> Next on a Windows 2000/Windows XP system with Visual Studio installed,
> start a console project and replace the main code with:
>
> --------------------------------------------------------------
> ----------
> --
>
> // DNSBug.cpp : Defines the entry point for the console
> application. //
>
> #include "stdafx.h"
>
> #include <windows.h>
> #include <windns.h>
> #pragma comment(lib, "Dnsapi.lib" )
>
> int _tmain(int argc, _TCHAR* argv[])
> {
> // First go, this is OK until the DnsRecordListFree
> try
> {
> PDNS_RECORD pRecs;
> // Replace the domain below with your domain (or leave
> it as is and use ours)
> DNS_STATUS stat = DnsQuery_W(
> L"dnsbug.exclaimer.co.uk",
> DNS_TYPE_TEXT, DNS_QUERY_STANDARD | DNS_QUERY_BYPASS_CACHE, NULL,
> &pRecs, NULL );
> if ( stat == 0 )
> {
> for(DWORD
> dw=0;dw<pRecs->Data.TXT.dwStringCount;dw++)
> printf( "DnsQuery_W: Text string %d is
> %S\r\n", dw, pRecs->Data.TXT.pStringArray[dw] );
>
> DnsRecordListFree( pRecs,
> DnsFreeRecordListDeep
> );
> }
> }
> catch( ... )
> {
> printf("DnsQuery_W faulted\r\n");
> }
>
> // Second go, this is gonna crash ...
> try
> {
> PDNS_RECORD pRecs;
> // Replace the domain below with your domain (or leave
> it as is and use ours)
> DNS_STATUS stat = DnsQuery_A(
> "dnsbug.exclaimer.co.uk",
> DNS_TYPE_TEXT, DNS_QUERY_STANDARD | DNS_QUERY_BYPASS_CACHE, NULL,
> &pRecs, NULL );
> if ( stat == 0 )
> {
> for(DWORD
> dw=0;dw<pRecs->Data.TXT.dwStringCount;dw++)
> printf( "DnsQuery_A: Text string %d is
> %s\r\n", dw, pRecs->Data.TXT.pStringArray[dw] );
>
> DnsRecordListFree( pRecs,
> DnsFreeRecordListDeep
> );
> }
> }
> catch( ... )
> {
> printf("DnsQuery_A faulted\r\n");
> }
>
> printf("End\r\n");
>
> return 0;
> }
> ------------------------------------------------------
>
> Compile and run the program. Notice that the output from the
> DnsQuery_W
> part has strange characters at the start of each line.
>
>
>
> Concerns
> ========
>
> With the the coming of Microsoft CallerID and SPF (various
> tools in the
> fight against spam), DNS servers are starting to publish more
> information in TXT records.
>
> Many MTAs on Windows will be reading this data (including our product
> Exclaimer (www.exclaimer.net)) using the DnsQuery API.
>
> These processes may encounter DNS TXT records setup by a
> malicious user
> in such as fashion as to cause code of their own liking to be executed
> on the target system just by simply delivering a piece of
> email to that
> server.
>
>
>
>
> ==============================================================
> ==========
> ==========================
>
> Further research
> ================
>
> As I understand it, the strings are supposed to have a length octet
> leading them in followed by the string which is not null
> terminated. In
> the example above I'd expect the data in the RR to be 33 bytes long in
> this shape:
>
>
> 0 1 2 3 4 5 6 7
> +---+---+---+---+---+---+---+---+
> | 0 | 3 |'a'|'b'|'c'|'0'|'3'|'d'|
> +---+---+---+---+---+---+---+---+
>
> 8 9 10 11 12 13 14 15
> +---+---+---+---+---+---+---+---+
> |'e'|'f'| 0 | 7 |'t'|'h'|'i'|'s'|
> +---+---+---+---+---+---+---+---+
>
> 16 17 18 19 20 21 22 23
> +---+---+---+---+---+---+---+---+
> |' '|'i'|'s'| 0 | a |'g'|'o'|'n'|
> +---+---+---+---+---+---+---+---+
>
> 24 25 26 27 28 29 30 31
> +---+---+---+---+---+---+---+---+
> |'n'|'a'|' '|'b'|'r'|'e'|'a'|'k'|
> +---+---+---+---+---+---+---+---+
>
> 32 33
> +---+---+
> | 0 | 0 |
> +---+---+
>
> Spookily, in the case where the API DnsQuery_W which doesn't crash
> internally (but corrupts the heap during free), the strings returned
> from the Windows API are:
>
> p[0] = "\3abc"
> p[1] = "abc"
> p[2] = "\3"
> p[3] = "def "
> p[4] = "\7this is"
> p[5] = "this is"
> p[6] = "\agonna break"
> p[7] = "gonna break"
> p[8] = ""
> p[9] = ""
>
> NSLOOKUP in debug & d2 mode returns the fact that there were
> 33 bytes of
> data, lending weight to the fact that the record is not invalid in the
> DNS server, and it only printing out 4 strings (which is sort of
> expected).
>
> Thanks,
> Gary
>
> This message (and any associated files) is intended only for
> the use of spf-devel@v2.listbox.com and may contain
> information that is confidential, subject to copyright or
> constitutes a trade secret. If you are not
> spf-devel@v2.listbox.com you are hereby notified that any
> dissemination, copying or distribution of this message, or
> files associated with this message, is strictly prohibited.
> If you have received this message in error, please notify us
> immediately by replying to the message and deleting it from
> your computer Messages sent to and from us may be monitored.
> Any views or opinions presented are solely those of the
> author gary@exclaimer.net and do not necessarily represent
> those of the company.
>
> This disclaimer was added by eXclaimer for Microsoft Exchange
> 2000, a DCSL product. Please visit our web site at
www.exclaimer.co.uk for more information.

-------
To unsubscribe, change your address, or temporarily deactivate your subscription,
please go to http://v2.listbox.com/member/?listname=spf-devel@v2.listbox.com

-------
To unsubscribe, change your address, or temporarily deactivate your subscription,
please go to http://v2.listbox.com/member/?listname=spf-devel@v2.listbox.com
RE: Windows Dns bug with TXT and multiple strings [ In reply to ]
Dirk,

Thanks. It doesn't actually make any difference to the outcome of the
behaviour, and I've updated the sample to take this into consideration.
The problem occurs in the DnsQuery API, not in my output or use of the
returned records (even if they aren't character set neutral).

Interesting point that if you call the DnsQuery_A API, the host names
returned only contain characters in the 7 bit set, but if you call the
_W version and your DNS server is setup to support (non-standard) UTF-8
characters then you can get them back with the API too, you might also
have noticed that there is a DnsQuery_UTF8 function which returns
(unsurpisingly) characters from that set.

My use of these two different APIs was (loosely) to show they have
different behaviour, and since at the end-of-the-day, a (void *) is a
(void *), it's up to the internals of Windows to determine how to fill
the structure and would only cause problems for me if I was expecting
one and not the other.

As it happens, since I use the string pointer in a call to printf, there
is no type checking going on, but you will see that the format string
uses the %S in the UNICODE version (as the program is compiled for MBCS)
and %s in the ANSI one.

Thanks for picking it up though...
-Gary

This message (and any associated files) is intended only for the use of spf-devel@v2.listbox.com and may contain information that is confidential, subject to copyright or constitutes a trade secret. If you are not spf-devel@v2.listbox.com you are hereby notified that any dissemination, copying or distribution of this message, or files associated with this message, is strictly prohibited. If you have received this message in error, please notify us immediately by replying to the message and deleting it from your computer. Messages sent to and from us may be monitored. Any views or opinions presented are solely those of the author gary@exclaimer.net and do not necessarily represent those of the company.

This disclaimer was added by eXclaimer for Microsoft Exchange 2000, a DCSL product. Please visit our web site at www.exclaimer.co.uk for more information.

-------
To unsubscribe, change your address, or temporarily deactivate your subscription,
please go to http://v2.listbox.com/member/?listname=spf-devel@v2.listbox.com
RE: Windows Dns bug with TXT and multiple strings [ In reply to ]
Gary,
I wasn't sure that the DNS_RECORDX structures were identical besides the different string type.

I tried a TXT DnsQuery_A on your domain and I get a "Heap block at 0007DAA8 modified at 0007DB32 past requested size of 82" warning in the debugger. After that, the "DNS Client" service can't be restarted anymore??? This is on a W2K SP4 server system.

Dirk

> -----Original Message-----
> From: Gary Levell [mailto:gary@exclaimer.net]
> Sent: Monday, June 14, 2004 15:59
> To: spf-devel@v2.listbox.com
> Subject: RE: [spf-devel] Windows Dns bug with TXT and multiple strings
>
>
> Dirk,
>
> Thanks. It doesn't actually make any difference to the outcome of the
> behaviour, and I've updated the sample to take this into
> consideration.
> The problem occurs in the DnsQuery API, not in my output or use of the
> returned records (even if they aren't character set neutral).
>
> Interesting point that if you call the DnsQuery_A API, the host names
> returned only contain characters in the 7 bit set, but if you call the
> _W version and your DNS server is setup to support
> (non-standard) UTF-8
> characters then you can get them back with the API too, you might also
> have noticed that there is a DnsQuery_UTF8 function which returns
> (unsurpisingly) characters from that set.
>
> My use of these two different APIs was (loosely) to show they have
> different behaviour, and since at the end-of-the-day, a (void *) is a
> (void *), it's up to the internals of Windows to determine how to fill
> the structure and would only cause problems for me if I was expecting
> one and not the other.
>
> As it happens, since I use the string pointer in a call to
> printf, there
> is no type checking going on, but you will see that the format string
> uses the %S in the UNICODE version (as the program is
> compiled for MBCS)
> and %s in the ANSI one.
>
> Thanks for picking it up though...
> -Gary
>
> This message (and any associated files) is intended only for
> the use of spf-devel@v2.listbox.com and may contain
> information that is confidential, subject to copyright or
> constitutes a trade secret. If you are not
> spf-devel@v2.listbox.com you are hereby notified that any
> dissemination, copying or distribution of this message, or
> files associated with this message, is strictly prohibited.
> If you have received this message in error, please notify us
> immediately by replying to the message and deleting it from
> your computer Messages sent to and from us may be monitored.
> Any views or opinions presented are solely those of the
> author gary@exclaimer.net and do not necessarily represent
> those of the company.
>
> This disclaimer was added by eXclaimer for Microsoft Exchange
> 2000, a DCSL product. Please visit our web site at
www.exclaimer.co.uk for more information.

-------
To unsubscribe, change your address, or temporarily deactivate your subscription,
please go to http://v2.listbox.com/member/?listname=spf-devel@v2.listbox.com

-------
To unsubscribe, change your address, or temporarily deactivate your subscription,
please go to http://v2.listbox.com/member/?listname=spf-devel@v2.listbox.com
Re: Windows Dns bug with TXT and multiple strings [ In reply to ]
> Gary Levell wrote:

> DnsQuery cannot read DNS TXT records with empty strings
> and will either crash the process (DnsQuery_A) or return an
> invalid data set (DnsQuery_W) which subsequently corrupts
> the callers heap.

This is a stupid bug and a dangerous one. It enables a malicious domain
owner to cause a buffer overrun in your program (using DNSAPI.DLL) whenever
your program does any DNS query (not only TXT) at his domain.

Since I did not want to wait until Microsoft releases a patch, I fixed this
bug myself.

If you want to fix it also, then copy DNSAPI.DLL to the folder with the
application that uses that DLL. Then open the DLL with a hexadecimal editor
and do following changes, depending on the version:

Version 5.0.2195.2785: at 07A20 change "74 51" to "90 90".
Version 5.0.2195.6012: at 157C5 change "75 22" to "EB 22".

Roger

-------
To unsubscribe, change your address, or temporarily deactivate your subscription,
please go to http://v2.listbox.com/member/?listname=spf-devel@v2.listbox.com