CVE-2016-5426 (published 2016-09-21T14:25Z)

PowerDNS (aka pdns) Authoritative Server before 3.4.10 allows remote attackers to cause a denial of service (backend CPU consumption) via a long qname.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:N/A:P)
CVSS v3 base score: 7.5, HIGH (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-399
cwe_name: Resource Management Errors
description: Weaknesses in this category are related to improper management of system resources.

Fix information:
repo_url: https://github.com/PowerDNS/pdns
commit_details: repo_url: https://github.com/PowerDNS/pdns
author: Remi Gacogne
author_date: 2016-07-01 15:30:20+02:00
msg: Reject qname's wirelength > 255, `chopOff()` handle dot inside labels
repository: repo_url: https://github.com/PowerDNS/pdns
repo_name: PowerDNS/pdns
description: PowerDNS Authoritative, PowerDNS Recursor, dnsdist
repo_language: C++
owner: PowerDNS
file_changes: filename: dnsparser.cc
change_type: ModificationType.MODIFY
diff: @@ -402,8 +402,9 @@ uint8_t PacketReader::get8BitInt()
 string PacketReader::getLabel(unsigned int recurs)
 {
   string ret;
+  size_t wirelength = 0;
   ret.reserve(40);
-  getLabelFromContent(d_content, d_pos, ret, recurs++);
+  getLabelFromContent(d_content, d_pos, ret, recurs++, wirelength);
   return ret;
 }
 
@@ -453,7 +454,7 @@ string PacketReader::getText(bool multi)
 }
 
 
-void PacketReader::getLabelFromContent(const vector<uint8_t>& content, uint16_t& frompos, string& ret, int recurs) 
+void PacketReader::getLabelFromContent(const vector<uint8_t>& content, uint16_t& frompos, string& ret, int recurs, size_t& wirelength)
 {
   if(recurs > 100) // the forward reference-check below should make this test 100% obsolete
     throw MOADNSException("Loop");
@@ -462,6 +463,10 @@ void PacketReader::getLabelFromContent(const vector<uint8_t>& content, uint16_t&
   // it is tempting to call reserve on ret, but it turns out it creates a malloc/free storm in the loop
   for(;;) {
     unsigned char labellen=content.at(frompos++);
+    wirelength++;
+    if (wirelength > 255) {
+      throw MOADNSException("Overly long DNS name ("+lexical_cast<string>(wirelength)+")");
+    }
 
     if(!labellen) {
       if(ret.empty())
@@ -474,13 +479,17 @@ void PacketReader::getLabelFromContent(const vector<uint8_t>& content, uint16_t&
 
       if(offset >= pos)
         throw MOADNSException("forward reference during label decompression");
-      return getLabelFromContent(content, offset, ret, ++recurs);
+      /* the compression pointer does not count into the wire length */
+      return getLabelFromContent(content, offset, ret, ++recurs, --wirelength);
     }
     else if(labellen > 63) 
       throw MOADNSException("Overly long label during label decompression ("+lexical_cast<string>((unsigned int)labellen)+")");
     else {
+      if (wirelength + labellen > 255) {
+        throw MOADNSException("Overly long DNS name ("+lexical_cast<string>(wirelength)+")");
+      }
+      wirelength += labellen;
       // XXX FIXME THIS MIGHT BE VERY SLOW!
-
       for(string::size_type n = 0 ; n < labellen; ++n, frompos++) {
         if(content.at(frompos)=='.' || content.at(frompos)=='\\') {
           ret.append(1, '\\');

programming_language: C++
filename: dnsparser.hh
change_type: ModificationType.MODIFY
diff: @@ -133,7 +133,7 @@ public:
   void xfrHexBlob(string& blob, bool keepReading=false);
 
   static uint16_t get16BitInt(const vector<unsigned char>&content, uint16_t& pos);
-  static void getLabelFromContent(const vector<uint8_t>& content, uint16_t& frompos, string& ret, int recurs);
+  static void getLabelFromContent(const vector<uint8_t>& content, uint16_t& frompos, string& ret, int recurs, size_t& wirelength);
 
   void getDnsrecordheader(struct dnsrecordheader &ah);
   void copyRecord(vector<unsigned char>& dest, uint16_t len);

programming_language: C++
filename: misc.cc
change_type: ModificationType.MODIFY
diff: @@ -150,16 +150,27 @@ bool chopOff(string &domain)
   if(domain.empty())
     return false;
 
-  string::size_type fdot=domain.find('.');
-
-  if(fdot==string::npos) 
-    domain="";
-  else {
-    string::size_type remain = domain.length() - (fdot + 1);
-    char tmp[remain];
-    memcpy(tmp, domain.c_str()+fdot+1, remain);
-    domain.assign(tmp, remain); // don't dare to do this w/o tmp holder :-)
+  bool escaped = false;
+  const string::size_type domainLen = domain.length();
+  for (size_t fdot = 0; fdot < domainLen; fdot++)
+  {
+    if (domain[fdot] == '.' && !escaped) {
+      string::size_type remain = domainLen - (fdot + 1);
+      char tmp[remain];
+      memcpy(tmp, domain.c_str()+fdot+1, remain);
+      domain.assign(tmp, remain); // don't dare to do this w/o tmp holder :-)
+
+      return true;
+    }
+    else if (domain[fdot] == '\\' && !escaped) {
+      escaped = true;
+    }
+    else {
+      escaped = false;
+    }
   }
+
+  domain = "";
   return true;
 }
 
@@ -169,19 +180,31 @@ bool chopOffDotted(string &domain)
   if(domain.empty() || (domain.size()==1 && domain[0]=='.'))
     return false;
 
-  string::size_type fdot=domain.find('.');
-  if(fdot == string::npos)
-    return false;
-
-  if(fdot==domain.size()-1) 
-    domain=".";
-  else  {
-    string::size_type remain = domain.length() - (fdot + 1);
-    char tmp[remain];
-    memcpy(tmp, domain.c_str()+fdot+1, remain);
-    domain.assign(tmp, remain);
+  bool escaped = false;
+  const string::size_type domainLen = domain.length();
+  for (size_t fdot = 0; fdot < domainLen; fdot++)
+  {
+    if (domain[fdot] == '.' && !escaped) {
+      if (fdot==domain.size()-1) {
+        domain=".";
+      }
+      else {
+        string::size_type remain = domainLen - (fdot + 1);
+        char tmp[remain];
+        memcpy(tmp, domain.c_str()+fdot+1, remain);
+        domain.assign(tmp, remain); // don't dare to do this w/o tmp holder :-)
+      }
+      return true;
+    }
+    else if (domain[fdot] == '\\' && !escaped) {
+      escaped = true;
+    }
+    else {
+      escaped = false;
+    }
   }
-  return true;
+
+  return false;
 }
 
 

programming_language: C++