Dumping a NSEC enabled DNS Zone with nsecdump
Source and Compiled Output Available
One of the problems with protecting DNS is mitigating people sitting on typographical error names. For example if accounting.contoso.com is a known valid site, how do I prevent someone from spoofing acounting.contoso.com?
How this problem was solved with NSEC was by having the DNS server report what the next alphabetical names are, so there is no way to squeeze between the lines.

This of course leads to the next problem; can’t people easily enumerate my DNS zone? Seems like a fairly straight forward attack, right? It turns out that fairly straight forward is 60 lines of C code and comments.
//
// Dump a NSEC enabled domain
// NSEC records contain a pointer to NextDomainName;
// This allows for a very easy dictionary attack to transfer a zone
//
// Usage: nsecdump "fish.com"
//
int _tmain(int argc, wchar_t* argv[])
{
if(argc != 2)
{
wprintf(L"nsecdump zone.name.com\n");
wprintf(L"Dump a dns zone that is nsec enabled");
wprintf(L"http://wasnate.com");
return 0;
}
//
// Record the starting point
wchar_t* startPoint = argv[1];
bool firstIteration = true;
// Copy the first name to start with
SIZE_T nextNameSize = (lstrlenW(argv[1])+1) * sizeof(wchar_t);
wchar_t* nextName = (wchar_t*)malloc(nextNameSize);
ZeroMemory(nextName,nextNameSize);
memcpy(nextName, argv[1], nextNameSize);
while(true) {
//
// The last record will point to the first record
if(lstrcmpW(nextName,startPoint) == 0) {
if(firstIteration) firstIteration=false;
else break;
}
//
// Dump out the present hostname
wprintf(L"HostName: %s\n",nextName);
PDNS_RECORD ppQueryResultsSet=(PDNS_RECORD)malloc(sizeof(PDNS_RECORD));
ZeroMemory(ppQueryResultsSet, sizeof(PDNS_RECORD));
//
// Query the DNS name
DNS_STATUS status = DnsQuery(nextName, DNS_TYPE_NSEC, DNS_QUERY_STANDARD, NULL,
&ppQueryResultsSet, NULL);
if(status != 0) {
wprintf(L"Crap! DnsQuery(name=%s, NSEC,STANDARD) returned %d\n",nextName,status);
return -1;
}
//
// Copy the next name
nextNameSize = (lstrlenW(ppQueryResultsSet->Data.NSEC.pNextDomainName)+1)
* sizeof(wchar_t);
free(nextName);
nextName = (wchar_t*)malloc(nextNameSize);
ZeroMemory(nextName,nextNameSize);
memcpy(nextName, ppQueryResultsSet->Data.NSEC.pNextDomainName, nextNameSize);
//
// Free the list and repeat
DnsRecordListFree(ppQueryResultsSet, DnsFreeRecordList);
}
free(nextName);
return 0;
}
If you switch to NSEC 3; this attack is mitigated by all of the names being hashed.
Interview Question: Process a Binary Tree Programming Is Easy, Accept the Pattern