Abusing DNS: Part 6, Its all about who you know

One of the benefits we saw for using DNS in Part 1 was the ability to bounce our traffic through different systems that we don't control. Up until now we have been going directly to the DNS server we control.

When your host computers doesn't know how to resolve a DNS address it will ask the DNS resolver it has been configured to use. This resolver is often called a nameserver
. That nameserver
knows who to ask if it doesn't know the answer and so on until the question gets to your server. What we really want is something like this.

Awesome we just need to ask the nameserver
So far we have used localhost
Okay sooo... we just give the client the nameservers
IP...? That is kind of lame, you don't specify an IP in your browser, and dig
doesn't take an IP by default. Turns out your system is configured with a nameserver
look at the network configuration file and we are good to go right... right

Not so fast my dear reader. While finding the configured DNS resolver for most unix systems is relatively straight forward, to have some real fun we are going to be making our client support both linux and windows. Thats right this post is actually about cross platform support and conditional compilation! 😎
Lets take advantage of some more rust features. Thankfully the rust compiler rustc
supports multiple platforms and it lets you configure what your code does based on options at compile time.
Linux
Linux and most unixes have been nice enough to standardize on /etc/resolv.conf
as a place to find the systems configured resolver. Lets parse resolv.conf
and find the name server.
#[cfg(target_os = "linux")]
fn find_dns_resolvers() -> Result<Vec<String>> {
let mut servers = vec![];
// Read /etc/resolv.conf to get DNS servers
if let Ok(content) = std::fs::read_to_string("/etc/resolv.conf") {
for line in content.lines() {
let line = line.trim();
if line.starts_with("nameserver") {
if let Some(entry) = line.split_whitespace().nth(1) {
servers.push(format!("{}:53", entry));
}
}
}
}
Ok(servers)
}
find the resolvers for linux
The magic here is the very first line #[cfg(target_os = "linux")]
The comThis tells the rust compiler to build this version of find_dns_resolvers
when building for linux.
Windows
Now we just need to write the same function for windows ensure the function name and return types are the same and we should be good to go.
#[cfg(target_os = "windows")]
fn find_dns_resolvers(domain: String) -> Result<String> {
let adapters = ipconfig::get_adapters()?;
let mut servers = vec![];
for dns_server in adapters
.iter()
.flat_map(|adapter| adapter.dns_servers().iter())
{
servers.push(format!("{}:53", dns_server));
}
Ok(servers)
}
Here we use the ipconfig crate which makes it very simple to find the DNS server for each of the host systems network adapters. To use ipconfig, we just need to add a conditional dependency to our Cargo.toml
and we are all set.
[target.'cfg(windows)'.dependencies]
ipconfig = "0.3.2"
Call it
Finally we need to modify the code to call our new function.
let server = match matches.get_one::<String>("server") {
Some(s) => s,
None => {
let resolvers = find_dns_resolvers()?;
if resolvers.is_empty() {
return Err("Unable to find nameserver!".into());
} else {
&resolvers[0].to_owned()
}
}
};
tracing::debug!("found: {server}");
While verifying the command line arguments instead of having the default DNS server be 127.0.0.1
we will now call out to find_dns_resolvers
in order to lookup what the client system is configured to use as a resolver. When you compile this code for linux you get a version of the binary that looks at /etc/resolv.conf
and when you compile it for windows you get the version that looks at what is configured for network adapters.
Wrap up
There we have it folks, another one. You may have noticed I didn't cover actually compiling for linux and windows. You can either compile the client on both a linux and a windows machine... or you can do what is called cross compiling (let me know if I should cover cross compiling). Take note, this version will blow up after finding your DNS server but that is okay, next week we will look at how to get DNS traffic to actually route to our DNS server on the internet.