View Issue Details

IDProjectCategoryView StatusLast Update
0001389Anope Development (1.9.x series)Otherpublic2012-03-15 03:06
Reporternenolod Assigned ToAdam  
PrioritynormalSeverityminorReproducibilityhave not tried
Status resolvedResolutionfixed 
Summary0001389: DNS query IDs are no longer random allowing predictability for hostile DNSBL replies

in commit 086790d6331357022f4da17c76b26b9fc6e2ad90, you made DNS queries sequential:

dns.cpp, lines 54-59:

    static unsigned short cur_id = 0;
    this->id = cur_id++;
while (DNSEngine->requests.count(this->id));

this needs to be reverted, because an attacker who knows the IP that Anope's DNS resolver is listening for replies on can spam the box with hostile replies for the purpose of getting specific IPs akilled.

to ensure DNS query hijacking is difficult, random 16-bit query IDs must always be used. with this change, once one query ID is learned successfully, all other subsequent query IDs are known forever.
TagsNo tags attached.



2012-03-14 08:08

administrator   ~0006117

Thanks but no thanks... I believe what we have now is secure enough.


2012-03-14 07:47

reporter   ~0006116

i am uncertain, this was a year ago. i just saw logs from #anope of people being egged on, and this sort of thing bugs me.

regarding PRNG security, my suggestion would be to reseed when your eventloop ('socket engine') iterates.

in pseudo-code:

while (1) {
    srand(rand() ^ time(NULL));

this should be good enough to provide acceptable security levels for the PRNG with little computational impact.


2012-03-14 07:40

administrator   ~0006115

I can't control people close to the project, but I can control those who are a part of it. Were any team members or contributors doing this?

If the configuration was leaked to an attacker, you have bigger problems like how your configuration was leaked to an attacker... I don't view that as a valid problem.


2012-03-14 07:38

reporter   ~0006114


commit a26f4b9a9a4e96ba88214e50dd49783aa1695559 is worse.

if the configuration is leaked to an attacker, he can learn all query IDs always, because:

first_query_id is rand(), which if srand() is called with the same seed will always be the same.

as a result, all subsequent query ids are known, as they are incremented from first_query_id. you just have to know the amount of queries that have been made, which is easier to guess than one may think.


2012-03-14 07:33

reporter   ~0006113


regarding this:

<+nenolod> hey, they publically disclosed 0days in atheme without giving any opportunity to fix them
<+nenolod> so i figure, you know, turnabout is fair play?

the atheme-6 < 6.0.8 FLAGS overflow vulnerability was discussed on #anope and people were crashing networks, much to the amusement of people who were very close to the project.


2012-03-13 23:53

administrator   ~0006111

Alright, from what I understand getting the initial query ID as you claim that would be used to send spoofed packets back to us would be rather difficult. To do that, assuming your name server wasn't compromised and the DNSBL you are using wasn't screwing with you, you would have to control a box between the name server and the DNSBL *and* be able to pick out the DNS query from services going through it, meaning you would have to know the services IP. This seems extremely unlikely, but if you could do it then it shouldn't matter at all what our query ID is as a spoofed reply could be sent back by whatever middle box successfully sniffed the packet using the ID it found.

However immediately after services are started, the query ID would be 0 or "close to" 0, so I have made it start with a randomly generated query ID.

I have also readded a configuration directive for a value to seed the RNG with, so people can't determine what it was off of /stats u.

This is all in a26f4b9a9a4e96ba88214e50dd49783aa1695559.

If there is a way you can pick up the query ID reliably please do reopen the bug and I will address it, I do not want to srand() and rand() for every query.



2012-03-13 21:31

administrator   ~0006110

I also do not appreciate you doing stuff like this:

[18:42] <+nenolod>
[18:42] <+nenolod> i'm sorry; i lied. 2 0days today.

This is hardly a "0day". Even less so because it is in GIT and not in, or even close to, a release.

As for:

<+nenolod> hey, they publically disclosed 0days in atheme without giving any opportunity to fix them
<+nenolod> so i figure, you know, turnabout is fair play?

I honestly don't have a clue what you are talking about, nor does anyone else on the team. I don't know what your problem is with us but we aren't out to get you or your products.


2012-03-13 21:20

administrator   ~0006108

How would even one query ID even be learned successfully?


2012-03-13 21:02

reporter   ~0006107

Related to this -- you're using rand() without reseeding it every so often. This is an insecure practice, as rand() is predictable if the seed is known (which is the time that services was started).

You should probably srand() every time the eventloop iterates.


2012-03-13 20:52

reporter   ~0006106

The checks are for lame DNS forging attempts caused by DRDOS attacks -- so that the IRCd can simply drop the bogus requests into /dev/null and ignore them succinctly.

InspIRCd's resolver uses secure query IDs, so it is secure against this specific attack -- where the attacker is fairly determined and knows enough about your infrastructure that she can send UDP packets from her box with the correct source address of a 'trusted' nameserver.

(Both of these are also why Hybrid, Charybdis, and Ratbox all recommend running an DNS resolver on localhost and just using that as the solely trusted nameserver. The IP stack on the box will happily reject forged UDP packets for if they come from an external interface.)


2012-03-13 20:47

administrator   ~0006104

Why does inspircd, hybrid, etc, even bother checking this then? I based ours off of inspircd's mostly.


2012-03-13 20:44

reporter   ~0006103

Uh, no.

You can spoof UDP source packets very easily.


2012-03-13 20:43

administrator   ~0006102

The source of the reply is checked here:

        if (this->addrs != from_server)
                Log(LOG_DEBUG_2) << "Resolver: Received an answer from the wrong nameserver, Bad NAT or DNS forging attempt? '" << this->addrs.addr() << "' != '" << from_server.addr() << "'";
                return true;

So they would have to compromise the DNS server the queries are being sent to.

Issue History

Date Modified Username Field Change
2012-03-13 20:40 nenolod New Issue
2012-03-13 20:43 Adam Note Added: 0006102
2012-03-13 20:44 nenolod Note Added: 0006103
2012-03-13 20:47 Adam Note Added: 0006104
2012-03-13 20:52 nenolod Note Added: 0006106
2012-03-13 21:02 nenolod Note Added: 0006107
2012-03-13 21:20 Adam Note Added: 0006108
2012-03-13 21:31 Adam Note Added: 0006110
2012-03-13 23:53 Adam Note Added: 0006111
2012-03-13 23:53 Adam Status new => resolved
2012-03-13 23:53 Adam Resolution open => fixed
2012-03-13 23:53 Adam Assigned To => Adam
2012-03-14 07:33 nenolod Note Added: 0006113
2012-03-14 07:33 nenolod Status resolved => feedback
2012-03-14 07:33 nenolod Resolution fixed => reopened
2012-03-14 07:38 nenolod Note Added: 0006114
2012-03-14 07:38 nenolod Status feedback => assigned
2012-03-14 07:40 Adam Note Added: 0006115
2012-03-14 07:47 nenolod Note Added: 0006116
2012-03-14 08:08 Adam Note Added: 0006117
2012-03-15 03:06 Adam Status assigned => resolved
2012-03-15 03:06 Adam Resolution reopened => fixed