Dns Cache Poisoning with Basic Auth
How DNS Cache Poisoning Manifests in Basic Auth
DNS cache poisoning (also known as DNS spoofing) tricks a resolver into returning an attacker‑controlled IP address for a legitimate domain. When an application builds an outbound request that uses HTTP Basic Auth, the resolver’s poisoned answer can cause the Authorization header – containing base64‑encoded username:password – to be sent to the attacker’s server instead of the intended service.
Typical vulnerable code paths involve taking a user‑supplied hostname (e.g., from a query parameter, header, or configuration service) and using it to construct a request that includes a Basic Auth header. If the application does not validate or whitelist the resolved host, an attacker who has poisoned the DNS cache can redirect the request to a malicious endpoint and harvest credentials. This pattern has been observed in real‑world incidents such as CVE-2008-1447 (the Kaminsky attack), where poor source‑port randomization made cache poisoning practical, and in API‑gateway plugins that forward requests to user‑specified back‑ends using Basic Auth for upstream authentication.
// Vulnerable Node.js example: hostname taken directly from request query
const http = require('http');
function handle(req, res) {
const targetHost = req.query.host; // user‑controlled
const auth = Buffer.from(`${process.env.USERNAME}:${process.env.PASSWORD}`).toString('base64');
const options = {
hostname: targetHost, // <-- DNS lookup performed here
port: 443,
path: '/api/data',
method: 'GET',
headers: {
Authorization: `Basic ${auth}`
}
};
const httpsReq = https.request(options, (apiRes) => {
// pipe response back to client
apiRes.pipe(res);
});
httpsReq.on('error', (err) => res.statusCode = 502, res.end());
httpsReq.end();
}
In the snippet above, if an attacker has poisoned the DNS entry for targetHost, the outbound request will be sent to the attacker’s IP, leaking the Basic Auth credentials.
Basic Auth‑Specific Detection
middleBrick performs unauthenticated, black‑box scanning of the API surface. It detects DNS cache poisoning risk in Basic Auth flows by:
- Sending requests that inject user‑controlled host values (via query string, headers, or body) and observing whether the resulting outbound traffic contains a Basic Auth header directed to an external host.
- Checking for missing host validation: if the reflector echoes the supplied host without whitelist verification, the scan flags a potential DNS‑poisoning vector.
- Correlating findings with the Basic Auth detection module to ensure the leaked header is indeed an Authorization: Basic … value.
The scan does not need any agents or credentials; you simply provide the public URL of the API. For example, using the middleBrick CLI:
# Scan an API endpoint for DNS cache poisoning and other risks
middlebrick scan https://api.example.com/v1/resources
The resulting report will list a finding under the "Input Validation" category (or similar) with severity "high", describing that user‑supplied host values are used to construct outbound Basic Auth requests without validation. The finding includes remediation guidance, such as implementing an allow‑list of trusted hosts or disabling outbound requests to user‑provided domains.
Basic Auth‑Specific Remediation
The most effective defense is to never let untrusted input dictate the destination of an outbound request that carries Basic Auth credentials. Apply the following mitigations:
- Validate or whitelist hostnames before performing DNS lookups. Only allow domains that are explicitly approved for upstream communication.
- Enforce HTTPS and verify the server’s TLS certificate; reject connections to hosts with mismatched or self‑signed certificates unless explicitly pinned.
- Avoid following redirects to untrusted hosts; disable automatic redirect handling or validate each redirect destination against the same whitelist.
- If possible, avoid using Basic Auth altogether for external calls; prefer token‑based authentication with short‑lived, scoped credentials that are useless if intercepted.
- Deploy DNSSEC‑validating resolvers on the host running the application to reduce the chance of successful cache poisoning.
Below is a corrected version of the Node.js example that implements a simple host allow‑list:
// Fixed Node.js example with host whitelist
const https = require('http');
const ALLOWED_HOSTS = new Set(['api.internal.example.com', 'payments.provider.net']);
function handle(req, res) {
const userHost = req.query.host;
if (!ALLOWED_HOSTS.has(userHost)) {
return res.statusCode = 400, res.end('Invalid host');
}
const auth = Buffer.from(`${process.env.USERNAME}:${process.env.PASSWORD}`).toString('base64');
const options = {
hostname: userHost, // DNS lookup now only for trusted host
port: 443,
path: '/api/data',
method: 'GET',
headers: {
Authorization: `Basic ${auth}`
}
};
const httpsReq = https.request(options, (apiRes) => {
apiRes.pipe(res);
});
httpsReq.on('error', (err) => {
console.error(err);
res.statusCode = 502;
res.end();
});
httpsReq.end();
}
In Python with the requests library, the same principle applies:
import requests
ALLOWED = {'api.internal.example.com', 'payments.provider.net'}
def call_api(user_host):
if user_host not in ALLOWED:
raise ValueError('Host not allowed')
auth = requests.auth.HTTPBasicAuth('user', 'pass')
resp = requests.get(f'https://{user_host}/api/data', auth=auth, timeout=5, verify=True)
resp.raise_for_status()
return resp.json()
These changes ensure that even if an attacker succeeds in poisoning the DNS cache, the application will refuse to connect to the malicious host, keeping Basic Auth credentials confined to legitimate services.