Zone Transfer with Mutual Tls
How Zone Transfer Manifests in Mutual TLS
A DNS zone transfer (AXFR) operation is intended to replicate zone data between authorized name servers. When an API exposes this operation, it should be protected by mutual TLS (mTLS) so that only trusted peers presenting valid client certificates can request the transfer. If the mTLS handshake is misconfigured, the server may fall back to server‑only authentication or accept connections without verifying the client certificate. An attacker who can reach the endpoint without presenting a valid client cert can then issue an AXFR query and obtain the entire DNS zone, revealing internal hostnames, service records, and potentially sensitive infrastructure details.
Typical vulnerable code paths look like the following examples. In Go, a TLS configuration that omits ClientAuth: tls.RequireAndVerifyClientCert allows any client to connect:
// Vulnerable: no client certificate verification
conf := &tls.Config{
Certificates: []tls.Certificate{cert},
// Missing ClientAuth setting – defaults to tls.NoClientCert
}
ln, _ := tls.Listen("tcp", ":8443", conf)
// Handler for /dns/zone-transfer
http.HandleFunc("/dns/zone-transfer", zoneTransferHandler)
http.Serve(ln, nil)
In Node.js, forgetting to set requestCert: true and rejectUnauthorized: true results in a similar exposure:
// Vulnerable: client cert not required
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt'),
// requestCert and rejectUnauthorized omitted
};
const server = tls.createServer(options, (socket) => {
// socket.authorized will be false for clients without cert
handleZoneTransfer(socket);
});
server.listen(8443);
Python’s ssl.SSLContext suffers the same issue when verify_mode is set to ssl.CERT_NONE:
# Vulnerable: no client cert verification
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile='server.crt', keyfile='server.key')
context.verify_mode = ssl.CERT_NONE # Should be CERT_REQUIRED
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.bind(('0.0.0.0', 8443))
sock.listen(5)
while True:
conn, addr = sock.accept()
ssock = context.wrap_socket(conn, server_side=True)
process_zone_transfer(ssock)
These snippets illustrate the exact Mutual TLS‑specific code paths where a missing or lax client‑certificate check enables an unauthenticated zone transfer.
Mutual TLS‑Specific Detection
middleBrick performs unauthenticated black‑box scanning of the API surface. When it encounters an endpoint that appears to perform a DNS zone transfer (e.g., a GET or POST to /dns/zone-transfer with parameters like domain=example.com), it attempts the request without presenting any TLS client certificate. If the server responds with the zone data (or a successful AXFR response), middleBrick flags the finding under the Authentication category, noting that mutual TLS enforcement is missing or misconfigured.
The scanner does not need any prior knowledge of the server’s TLS configuration; it simply observes whether the endpoint is reachable and responsive under an unauthenticated TLS handshake. This mirrors real‑world attack steps: an attacker discovers the endpoint, initiates a TLS handshake without a client cert, and if the server proceeds, they can issue the AXFR query.
Example of detecting the issue with the middleBrick CLI:
# Scan a target API for zone‑transfer exposure
middlebrick scan https://api.internal.example.com/dns/zone-transfer?domain=corp.internal
The output will include a finding such as:
- Finding: Unauthenticated DNS zone transfer accessible via mTLS‑protected endpoint
- Severity: High
- Evidence: Response contains SOA and NS records for zone corp.internal
- Remediation Guidance: Enforce client certificate verification in the TLS configuration; ensure
ClientAuthis set toRequireAndVerifyClientCert(or equivalent) and disable any fallback to server‑only authentication.
Because middleBrick tests the unauthenticated attack surface, it will also catch cases where the server incorrectly allows a TLS handshake to succeed but then fails to validate the client certificate during the application‑level check.
Mutual TLS‑Specific Remediation
The fix is to configure the Mutual TLS stack to require and validate a client certificate for every connection to the zone‑transfer endpoint. Below are corrected versions of the vulnerable snippets shown earlier.
Go – enforce client cert verification:
// Fixed: require and verify client certificate
conf := &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
// Optional: specify the CA pool that issued client certs
ClientCAs: caPool,
}
ln, _ := tls.Listen("tcp", ":8443", conf)
http.HandleFunc("/dns/zone-transfer", zoneTransferHandler)
http.Serve(ln, nil)
Node.js – request and reject unauthorized clients:
// Fixed: require client cert and reject if invalid
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt'),
ca: fs.readFileSync('ca.crt'), // CA that issued client certs
requestCert: true, // ask for client cert
rejectUnauthorized: true // abort handshake if cert invalid
};
const server = tls.createServer(options, (socket) => {
// socket.authorized is true only for valid client certs
if (!socket.authorized) {
socket.write('TLS handshake failed: client certificate required\n');
socket.end();
return;
}
handleZoneTransfer(socket);
});
server.listen(8443);
Python – set verify mode to require client certs:
# Fixed: require client certificate verification
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile='server.crt', keyfile='server.key')
context.load_verify_locations(cafile='ca.crt') # CA that issued client certs
context.verify_mode = ssl.CERT_REQUIRED # mandate client cert
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.bind(('0.0.0.0', 8443))
sock.listen(5)
while True:
conn, addr = sock.accept()
ssock = context.wrap_socket(conn, server_side=True)
if ssock.cipher() is None:
# Handshake failed – client did not provide valid cert
ssock.close()
continue
process_zone_transfer(ssock)
After applying these changes, repeat the middleBrick scan. The scanner will no longer be able to retrieve the zone data without a valid client certificate, and the finding will be marked as resolved.