{"action":"created","issue":{"url":"https://api.github.com/repos/shadowsocks/shadowsocks-libev/issues/1641","repository_url":"https://api.github.com/repos/shadowsocks/shadowsocks-libev","labels_url":"https://api.github.com/repos/shadowsocks/shadowsocks-libev/issues/1641/labels{/name}","comments_url":"https://api.github.com/repos/shadowsocks/shadowsocks-libev/issues/1641/comments","events_url":"https://api.github.com/repos/shadowsocks/shadowsocks-libev/issues/1641/events","html_url":"https://github.com/shadowsocks/shadowsocks-libev/issues/1641","id":251498817,"number":1641,"title":"`udns` stub resolver vulnerable to DNS spoofing attacks","user":{"login":"cpu","id":292650,"avatar_url":"https://avatars1.githubusercontent.com/u/292650?v=4","gravatar_id":"","url":"https://api.github.com/users/cpu","html_url":"https://github.com/cpu","followers_url":"https://api.github.com/users/cpu/followers","following_url":"https://api.github.com/users/cpu/following{/other_user}","gists_url":"https://api.github.com/users/cpu/gists{/gist_id}","starred_url":"https://api.github.com/users/cpu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cpu/subscriptions","organizations_url":"https://api.github.com/users/cpu/orgs","repos_url":"https://api.github.com/users/cpu/repos","events_url":"https://api.github.com/users/cpu/events{/privacy}","received_events_url":"https://api.github.com/users/cpu/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":11,"created_at":"2017-08-20T16:22:25Z","updated_at":"2017-08-31T03:46:21Z","closed_at":null,"author_association":"NONE","body":"Hi folks :wave:,\r\n\r\nI spent some time over the past two weeks fiddling with `shadowsocks-libev` and the `udns` stub resolver it links in order to determine how viable it is to perform DNS spoofing attacks against a `shadowsocks-libev` server. My detailed findings are below. I apologize for the volume of text but I wanted to make this as concrete as possible to reduce the chance that the claims are dismissed as unrealistic. If interested I can provide more information about the experimental setup that I used to prove the viability of this against a mock server. For now I've stuck to findings.\r\n\r\nI apologize for not accompanying this with a pull request but my ability to write production quality C is fairly limited and I suspect replacing `udns` will be a more involved job.\r\n\r\nSummary\r\n------------\r\n\r\nThe stub resolver that shadowsocks-libev uses, [`udns`](http://www.corpit.ru/mjt/udns/), has made several design decisions that introduce increased vulnerability to [DNS spoofing attacks](https://en.wikipedia.org/wiki/DNS_spoofing) on the shadowsocks server. Further, `udns` does not implement protocol features key to modern DNS making the shadowsocks server fragile and holding back protocol advancement. The `udns` author intends for it to be used exclusively in LAN environments where a trusted recursive resolver is nearby, likely making it a bad fit for most shadowsocks-libev deployments.\r\n\r\nRecommendation\r\n-----------------------\r\n\r\nThe shadowsocks-libev codebase should migrate to a better supported stub resolver such as [`c-ares`](https://c-ares.haxx.se/) or [`ldns`](https://www.nlnetlabs.nl/projects/ldns/). Both projects randomize the UDP source port on a per-query basis and generate transaction IDs using a more robust design than `udns`. Similarly both projects support TCP and handle truncated replies correctly. \r\n\r\nDetailed Explanation\r\n---------------------------\r\n\r\nThe shortcomings of `udns` fall into two categories: robustness & security. Universally my findings and descriptions pertain to `udns` 0.4, the most current release from three years ago,  `2014-01-23`.\r\n\r\nFrom a robustness perspective, `udns` has made design choices that simplify the API but ignore real world protocol considerations that other stub resolvers handle with more care:\r\n\r\n1. `udns` only supports UDP. Where `EDNS-0` is not supported to negotiate larger UDP response packets all replies must fit within 512 bytes.\r\n1. where replies are larger and returned with the truncated (TC) bit set, `udns` treats the response as an error\r\n1. if there is an error in `recvfrom` in reading the response, `udns` does not retry the query and it fails silently.\r\n\r\nFrom a security perspective, `udns` has made two design choices that greatly increase the server's vulnerability to DNS spoofing:\r\n\r\n1. by design `udns` uses **one** UDP socket for all queries. The port is selected randomly by the OS but **remains static across all queries made**.\r\n1. `udns` uses a PRNG (not a CSRNG) to generate random transaction IDs, and by default on UNIX systems it is seeded with the number of microseconds since epoch, meaning there are only **one million possible seeds**.\r\n\r\n###  Issue 1 - Static UDP source port\r\nWe can see the first security issue in action with a live shadowsocks-libev server by first using `tshark` to capture DNS traffic:\r\n        \r\n     tshark -i any -w /tmp/dns.test.cap 'udp port 53'\r\n\r\nand then by pulling out the UDP source port, query ID and query name from the packet dump:\r\n\r\n     tshark -r /tmp/dns.test.cap -Y \"ip.src == $SERVER_IP\" -e udp.srcport -e dns.id -e dns.qry.name -T fields | sort\r\n\r\nMake sure `$SERVER_IP` is the public IP of your shadowsocks server. If there are Shadowsocks clients active you will see all of their DNS queries are sourced from the **same** port #, in this case **33630**:\r\n```\r\n33630\t0x00000178\timages.mentalfloss.com\r\n33630\t0x000001a3\twww.google.com\r\n33630\t0x000002f0\tfonts.gstatic.com\r\n33630\t0x0000046a\tassets-cdn.github.com\r\n33630\t0x00000626\timages.mentalfloss.com\r\n33630\t0x0000065a\timages.mentalfloss.com\r\n33630\t0x00000756\tcdn.static-economist.com\r\n33630\t0x000008fa\tcsync.yahooapis.com\r\n33630\t0x000008fa\tjs-sec.indexww.com\r\n33630\t0x00000b08\tassets-cdn.github.com\r\n33630\t0x00000bd1\twww.cbc.ca\r\n33630\t0x00000c8e\tcdn.static-economist.com\r\n<snipped>\r\n```\r\n\r\nUsing a random source port has been best practice since 2008 when the [\"Kaminski attack\"](http://unixwiz.net/techtips/iguide-kaminsky-dns-vuln.html) was in the news. It's also considered a best practice as part of [RFC 5452, \"Measures for Making DNS More Resilient against Forged Answers\"](https://tools.ietf.org/html/rfc5452). [Section \"9.2.  Extending the Q-ID Space by Using Ports and Addresses\"](https://tools.ietf.org/html/rfc5452#section-9.2) describes using a random source port as a MUST. Without using a random source port an attacker intending to spoof a response to a query only needs to guess the transaction ID of the query. In practice I found that if a server repeatedly queried a name I was able to spoof a response successfully in under a minute from a host in the same datacentre by randomly guessing transaction IDs.\r\n\r\nThe author of udns is aware of the security implications of this design and says the following in the NOTES file:\r\n\r\n> Ok, udns is a stub resolver, so it expects sorta friendly environment, but on LAN it's usually much easier to fire an attack, due to the speed of local network, where a bad guy can generate alot of packets in a short time.\r\n\r\nand:\r\n\r\n>  Udns is designed to work in a LAN, it needs full recursive resolver nearby, and modern LAN usually uses high-bandwidth equipment which makes the Kaminsky attack trivial.  The problem is that even with qID (16 bits) and random UDP port (about 20 bits available to a regular process) combined still can not hold enough randomness, so on a fast network it is still easy to flood the target with fake replies and hit the \"right\" reply before real reply comes.  So random qIDs don't add much protection anyway,           \r\neven if this feature is implemented in udns, and using all available techniques wont solve it either.\r\n\r\nI think this position is unique to the udns author. All other stub resolvers and recursive resolvers I've looked at **do** randomize the source port. Further, in the case of shadowsocks-libev it seems unwise to make assumptions that the datacenter the server is located in is a friendly LAN. Second, I believe common deployment practice will be to run shadowsocks-libev on a server that has no local recursive resolver and instead sets `/etc/resolv.conf` to `8.8.8.8` or equivalent, a recursive resolver that may not be nearby at all and greatly increases the chances an attacker nearby could race the responses from the real server successfully.\r\n\r\nAs we'll see next the method that `udns` uses to generate random transaction IDs is also lacking, further exacerbating the impact of using a static source port.\r\n\r\n###  Issue 2 - Transaction ID PRNG weak, seeded unsafely\r\n\r\n`udns` generates transaction IDs using a PRNG based on [`smallprng` by Bob Jenkins](http://burtleburtle.net/bob/rand/smallprng.html).\r\n> This is a small fast pseudorandom number generator, suitable for large statistical calculations, but not of cryptographic quality\r\n\r\nThe `udns` implementation is located in `src/udns_jran.c`.  The PRNG is initialized in `src/udns_resolver.c`'s `dns_init_rng()` function (beginning on L413). It invokes the `udns_jraninit` function of `udns_jran.c` providing it the return value of `dns_nonrandom_32` as the seed.\r\n\r\nThe implementation of `dns_nonrandom_32` is as follows:\r\n```\r\nstatic unsigned dns_nonrandom_32(void) {                                                       \r\n#ifdef WINDOWS                                                                           \r\n  FILETIME ft;                                                                           \r\n  GetSystemTimeAsFileTime(&ft);                                                          \r\n  return ft.dwLowDateTime;                                                               \r\n#else                                                                                    \r\n  struct timeval tv;                                                                     \r\n  gettimeofday(&tv, NULL);                                                               \r\n  return tv.tv_usec;                                                                     \r\n#endif                                                                                   \r\n}                                                                                    \r\n```\r\n\r\nOn a non-Windows system this means that ultimately `udns_jraninit` is given a seed value of `timeval.tv_usec`. The `timeval` struct and the `tv_usec` field are described in the man page for `gettimeofday`:\r\n```\r\n           struct timeval {\r\n               time_t      tv_sec;     /* seconds */\r\n               suseconds_t tv_usec;    /* microseconds */\r\n           };\r\n```\r\n\r\nThis means that the `udns` prng is only ever initialized with **one of 1,000,000 possible seeds**. A value of more than 1,000,000 would not be a microsecond and would reset `tv_usec` to 0 while incrementing `tv_sec`. This is an unsuitably small search space.\r\n\r\nFurther, since `udns` uses a PRNG not a CSRNG it is not designed to resist next-bit attacks or state compromise. In practice I found that if I could learn any two query transaction IDs from a running `udns` stub (not necessarily two immediately subsequent transaction IDs) I could brute force the seed that the server used within seconds using an unoptimized C program. I don't believe there is an easy way to learn transaction IDs without being on the network path but it will be catastrophic to the TX ID security if two are leaked.\r\n\r\nOnce the seed is known the attacker is able to guess transaction IDs for future queries with great accuracy. Combined with knowing the static source port the adversary could spoof DNS responses reliably using many, many fewer UDP guess packets.\r\n\r\nIn comparison `c-ares` generates a random RC4 key using the system randomness devices resulting in a construction that is safer to state leaks & initialized with a much larger seed space. Similarly `ldns` uses OpenSSL `RAND_bytes` to generate cryptographically secure pseudo-random data for transaction IDs.\r\n\r\nCountermeasures/Practical Experience\r\n----------------------------------------------------\r\n\r\nOne counter measure that `udns` properly implements is enforcing that the source IP address of the UDP packets it receives matches the IP address of the recursive resolver the question was sent to. This means that attacking the `udns` stub resolver requires spoofing source IP addresses. This is certainly possible but requires finding a host/environment that doesn't use the best practices from [BCP-38](http://www.bcp38.info/index.php/Main_Page). \r\n\r\n[RFC 5452 Section 4.4 \"Matching the Source Address of the Authentic Response\"](https://tools.ietf.org/html/rfc5452#section-4.4) says:\r\n> While two Best Current Practice documents ([RFC2827] and [RFC3013] specifically) direct Internet access providers to prevent their customers from assuming IP addresses that are not assigned to    them, these recommendations are not universally (nor even widely) implemented.\r\n\r\nI don't believe this countermeasure alone (e.g. without stronger TX ID generation and random source ports) is sufficient to protect the  shadowsocks-libev queries from spoofing.\r\n\r\nIn practice I found that `udns` in the most verbose configuration did not print any logs when receiving UDP packets with DNS responses that had an incorrect transaction ID. This meant that when guessing transaction IDs incorrectly the attacker is able to retain relative obscurity. The system administrator would have to be looking at the UDP traffic at a layer other than the shadowsocks-libev/udns logs to notice something amiss.\r\n\r\nThe primary countermeasure that helps this from being a more serious issue is the lack of caching in both `udns` and `shadowsocks-libev`. In practice this means that unlike when targetting a recursive resolver the traditional \"cache poisoning\" attacks do not work. The attacker instead has to poison on a per-query basis which raises the bar significantly. That said, I believe targetting a high-traffic shadowsocks-libev server and a specific high-value, high-traffic domain would be feasible. Certainly feasible if the attacker is able to observe transaction IDs and learn the server's PRNG seed.\r\n\r\n\r\n\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/shadowsocks/shadowsocks-libev/issues/comments/326181459","html_url":"https://github.com/shadowsocks/shadowsocks-libev/issues/1641#issuecomment-326181459","issue_url":"https://api.github.com/repos/shadowsocks/shadowsocks-libev/issues/1641","id":326181459,"user":{"login":"debiansid","id":9624961,"avatar_url":"https://avatars3.githubusercontent.com/u/9624961?v=4","gravatar_id":"","url":"https://api.github.com/users/debiansid","html_url":"https://github.com/debiansid","followers_url":"https://api.github.com/users/debiansid/followers","following_url":"https://api.github.com/users/debiansid/following{/other_user}","gists_url":"https://api.github.com/users/debiansid/gists{/gist_id}","starred_url":"https://api.github.com/users/debiansid/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/debiansid/subscriptions","organizations_url":"https://api.github.com/users/debiansid/orgs","repos_url":"https://api.github.com/users/debiansid/repos","events_url":"https://api.github.com/users/debiansid/events{/privacy}","received_events_url":"https://api.github.com/users/debiansid/received_events","type":"User","site_admin":false},"created_at":"2017-08-31T03:46:21Z","updated_at":"2017-08-31T03:46:21Z","author_association":"NONE","body":"@madeye coz 703n just has limited rom, i’d Like see it in simple-obfs. Thanks"}}
{"actor":{"display_login":"debiansid"}}