wasnt nate

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.
NSec Diagram

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.

Leave a Reply