How to steal a developer's local database
bouk.co
bouk.co
The entire 127.0.0.0/8 block is dedicated to the loopback interface [1]. That's 2^24 - 2 unique IP addresses you can choose at random. This basically eliminates the feasibility of the DNS rebinding component, as it would take prohibitively long to find the actual loopback address that your services have bound to.
It's important to note that this is much more effective than not using the default port. It's much faster to iterate all 2^16 ports on the same IP address than it is to wait for DNS TTL to expire so you can rebind to another IP address.
As a bonus, you don't have to worry about port collisions when nobody's allowed to listen on 0.0.0.0. Everybody can use 8080 if they want.
How do I do that? Iptables? (Sorry if this is a dumb question.)
I thought he said use 127.3.99.7 or something of that sort to avoid using the default localhost
Fortunately, the whole 127.0.0.0/8 block is automatically added to the loopback interface (edit: on Linux at least), so you can bind away to your heart's content.
How will your PC know how to resolve 3.0.0.0/8?
The point is that the most commonly used IP is 127.0.0.1 but we can as well use 127.120.99.12 because the entire subnet is the loopback address.
https://github.com/thewhitetulip/web-dev-golang-anti-textboo... For an example in Go.
It's pretty much always a command line flag or config file parameter.
If you're running with docker, it's even more standard. When you expose a port, just use `-p 127.12.12.3:11211:11211` (with your chosen IP address, of course), and docker will set up the forwarding for you, only for that address.
>>> import ipaddress
>>> import random
>>> ipaddress.IPv4Address('127.0.0.1') + random.randrange(2**24 - 2)
IPv4Address('127.23.181.175')Bear in mind, I'm a front end developer, so this is absolutely not my forte. Thanks for the help.
The birthday paradox means that you're probably going to spend a lot less time than that, but it's certainly going to buy me a lot longer than 1 minute that I get if I'm using 127.0.0.1. The chances I stay on any random website longer than 15 minutes are pretty slim, and you've got really poor odds of finding the right IP in that time.
1. I roll 1 die. What are the odds of getting a 3? (Assuming the die is evenly distributed 1-n, and n >= 3, the answer is 1/n )
2. I roll 100 dice. What are the odds of getting, among those dice, at least one result of 3? ( 1 - [(n-1)/n]^100 )
3. I roll 2 dice. What are the odds of those dice matching each other? ( 1/n )
4. I roll 100 dice. What are the odds of there being, among those 100 dice, any 2 dice that rolled the same result?
The birthday paradox provides the answer to problems of type (4).
a) attacking multiple targets all using independent random IPs for their services
b) Users running multiple services, each on a separate random loopback IP
And if I'm correct there, then this would presumably extend to generic DNS rebinding attacks; the greater the number of IPs in a subnet, the greater the birthday attack advantage the attacker gains. (right?)
Your (b) is similar, but even more helpful to the attacker in that the loopback IPs are constrained not to overlap with each other, which makes searching for "any hit, I don't care which" easier.
It's a birthday attack if you generate a lot of values and hope for two of those values -- all of which you generated -- to be equal to each other. If you're generating values and hoping for one of those values to be equal to some externally-defined value, it's not a birthday attack, it's a guessing attack.
Right, but that assumes a new DNS record is created each time. An attacker might simply have records for the entire 127.0.0.0/8 range and would iterate over them.
It would still take a long time to make an exhaustive search, but it will be faster than waiting for a 1 minute TTL between each try.
Linux does, so this will probably work for the majority of users here, but, I believe, it will not work for FreeBSD, for example (I'm on an iPad and don't have an SSH client at the moment so I can't verify). In those cases, however, you can add "aliases" to the loopback interface, with specific IP addresses in the 127/8 subnet, and then use them.
sudo ifconfig lo0 alias 127.1.2.3 0xff000000
And then, when you're done: sudo ifconfig lo0 -alias 127.1.2.3
to tear it back down.You can manage this trivially in rc.conf and then restart the netif and route services.
For example to add extra interfaces to lo0 on FreeBSD you may use:
ipv4_addrs_lo0="127.0.0.2-5/8"
in rc.confAnd now those addresses are automatically assigned to lo0, the netmask will be set to 255.255.255.255 automatically for all but the first in that range (.2). Since 127.0.0.1 is unconditionally assigned to lo0, you could also use:
ipv4_addrs_lo0="127.0.0.2-5/32"
This is what I do on FreeBSD.The DNS rebinding is a workaround so that you can read the reply of a message.
Enumerating a list of services that are running is something you could do as a precursor to using the dns rebinding attack to attack it. I am not familiar with javascript networking enough to say what kind of rate limiting you would incur, but in theory at least you should be able to probe many IPs at once.
A web browser is a locally running program. You mean, of course, that access to the socket can be limited to programs running under a user account sufficiently privileged to read and/or write to the socket file. Can all services be made to communicate only through domain sockets?
I agree that choosing randomly from the available loopback addresses and/or ports isn't providing any real security; it surely wouldn't take the web browser very long to make a connection to every port in the 127/8 address range.
That is of course correct. I should have worded it a bit more technical, I guess :-)
Support of unix domain sockets depends on the program, so not all support that, but Redis and memcache do. At least for those you can prevent the browser from connecting to them using TCP.
I guess another option would be to just limit what ports a browser can access. Some ports a blocked by default, but I feel like a whitelist instead of a blacklist makes more sense and I'm fairly certain that most of my daily browser usage would still work if only 80, 443 and maybe 8080 would be whitelisted. Is there a way to archive this (in chrome/Firefox) without reverting to some outside sandbox?
After briefly looking into this:-
- there doesn't seem to be a way to configure firefox with a whitelist of ports it may connect to
- AppArmor doesn't appear to yet be capable of constraining network access at the level of IP address or port number
- The good news is that it should be possible to use linux network namespaces along with iptables - I've not attempted it
They might mean instead that browsers can’t access domain sockets.
The real solution is for tools to not assume local host connections are secure.
It was very easy to write some javascript that hangs out in the browser, gets the updated DNS host as the 192.168.0.1 address (sure sure, you can go crazy guessing other addresses) and then about 60% of everyone was on admin:admin or something as common; the first 12 or so bits of an ethernet address are the vendor identifier, which makes the process even easier to assume. Then you just start posting data to well-known web admin interfaces and update the router password.
I have no idea how well this works, three or four years later...
I'm sorry, but I disagree. A browser allowing externally loaded scripts to access private ip ranges is not a reasonable decision.
PSA: To protect yourself from this, and some more bad browser defaults, use NoScript with "allow all scripts globally." Keep the JS, but filter out some bad stuff. Also enable ABE (application boundaries enforcer, made to solve exactly this problem) for good measure.
So you think a browser should pin IP addresses to DNS names and not follow DNS updates? That's what you're implying.
There are plenty of legitimate systems in the ecommerce world that rely on doing this.
Take Ariba Punchout, for example. The idea is for the store to pass the user's shopping cart back into the customer's internal ERP system to be turned into a purchase order and sent for approval. The way this is handled is that the initial request sets up a session including a URL to a customer-internal service that's intended to handle the transaction. When the user completes checkout, a page is returned to the user containing an XML payload, and script to submit that XML to the specified internal service URL. That way, the store is able to provide the PO data to what is otherwise a completely internal system.
Too many things run on localhost (or intranet) and assume that localhost=safe. I know, "that's wrong" and "don't listen on 127.0.0.1" and all that, but that horse has left the barn, emigrated, founded a family and died happy. We can't put the 127/192/10 genie back in the bottle.
Prevent access to all private resources from the outside and whitelist on a need-to-access basis. Or be ready to keep monkey patching your system against these exploits forever.
A few months ago there was a post [0] by antirez about how dangerous it is to leave a redis instance open to the world, in that an attacker could, for instance, authorize an SSH key on your machine and gain remote connectivity.
While the average workstation is not usually reachable from the outside network, you could probably combine some variant of that attack (the first thing that comes to mind: overwrite .bash_profile) with the attack of this article, causing a lot of fun.
[1] http://danwalsh.livejournal.com/31146.html [2] http://www.bress.net/blog/archives/195-Firefox-in-a-sandbox-...
Doesn't have to be SELinux, any of the frameworks will do. Or run it in a new network namespace.
If you were attacking a local webapp interface instead of a non-http daemon like redis, you would need your browser to be able to access the web service. At that point, this kind of attack would still allow an attacker to also access that web service.
Yes: remote DNS servers have no business serving up loopback addresses. Yes: browsers shouldn't let remote scripts access resources on the local network.
But WTF are you guys doing running services bound to network ports (even if only accessible from the local machine) that apparently have no authentication whatsoever? Have none of you ever used a multi-user machine?
When I was in university we had just three SunOS boxen shared amongst all undergrads in my faculty, and all three were directly accessible from the whole of the internet - there was no firewall of any kind. Even back in those rather more innocent days you learned real quick not to put up services which didn't authenticate every incoming connection.
A good firewall is not a substitute for having individual machines be secure.
A machine having only one (intended) user is not an excuse to run services that are not secure against local users.
Edit: Now that I think of it and especially with containerized dev environments and VMs, I'd bet it quite common. I'm sure I've opened up a DB or search container more than I needed to just because I couldn't get the damn things to talk. I still would have a firewall, but not everyone does.
But yeah, binding services to 0.0.0.0 on your machine is a recipe for disaster too.
DNS rebinding could definitely get around the PID check, but could it spoof an origin to something like "safari-extension://com.agilebits.onepassword4-safari-2bua8c4s2c"?
That being said, I don't think we're suggesting the same thing. I'm saying one could write a webpage that uses DNS rebinding to make requests on localhost, like OP. Then, the webpage, completely bypassing the browser's built-in extension system, makes a request to 1password over localhost (which they're already using for IPC).
The reason that DNS rebinding is relevant is twofold; first, you need the request to hijack existing IPC between the browser extension and the standalone desktop app, and two, because you need to have the request come from the browser's PID. That should all work.
The question here revolves around 1password's verification that the origin is coming from, for example, something similar to
> safari-extension://com.agilebits.onepassword4-safari-2bua8c4s2c
So then your webpage would also need to be able to spoof the origin of your localhost requests to look like they were coming from that origin. I don't know if that's possible or not, but if it is, it would imply that this technique could get you illegitimate access to 1password.
It would probably be easier to simply keep a IS_LOOPBACK flag for every DNS name resolved and kill any connection attempts if the flag changes while the page is loaded. Then you can keep using the OS DNS resolver logic.
DNS might legitimately return a different CDN but it sure as hell won't flip between private IP spaces and the public internet.
[0] https://en.wikipedia.org/wiki/Captive_portal#Redirection_by_...
xip.io is one of those services that doesn't work on my home network because I have unbound block all RFC1918 space.
# Enforce privacy of these addresses. Strips them away from answers.
# It may cause DNSSEC validation to additionally mark it as bogus.
# Protects against 'DNS Rebinding' (uses browser as network proxy).
# Only 'private-domain' and 'local-data' names are allowed to have
# these private addresses. No default.
private-address: 10.0.0.0/8
private-address: 172.16.0.0/12
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: fd00::/8
private-address: fe80::/10
private-address: 127.0.0.0/8I realize this is contrary to "be conservative in what you do, be liberal in what you accept from others", but I never thought that was a good way to write software.
But if the choice, after a Redis update, is between: a) my software breaking and, hopefully, an error message saying "redis failure parsing 'XYZ'" or b) my software /maybe/ continuing to function, while passing commands to Redis that it's ignoring
I would always pick (a), and I think most programmers would think likewise.
Now the cache server has a security update, so you apply it right away. But now when it gets your invalid command it not only returns an error but it drops the connection. Your client doesn't handle this well, and now your caching is fully broken and your server falls over from the load.
Also remember this with all your IoT appliances running on your local network. Even if it has a local IP-address, as long as you have a computer with a browser on the same network, you might as well consider your devices being publicly accessible from the rest of the internet.
edit: to people linking me safari CVEs (can't reply to you all) - these are memory corruptions. Not accessing ports on localhost.
I think one of the first sites to do it was this one: https://jailbreakme.qoid.us/
The devs seem to have gotten around the App Store by using an approach like TestFlight, which allows apps to be deployed for development and wider testing purposes without going through the App Store.
CVE-2016-4657 [2]: Visiting a maliciously crafted website may lead to arbitrary code execution
CVE-2016-4655 [3]: An application may be able to disclose kernel memory
CVE-2016-4656 [4]: An application may be able to execute arbitrary code with kernel privileges
[1] https://citizenlab.org/2016/08/million-dollar-dissident-ipho...
[2] http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=2016-4657
[3] http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=2016-4655
[4] http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=2016-4656
So unless somebody has crafted a page specifically targeting me and my naming convention for local sites, this wouldn't be an issue. And of course, once you hit a site, you'd still need to deal with the same security that the public facing version sees. You certainly wouldn't go out of your way to disable that on your local machine.
Databases are named, and often live within named database server instances, so they'd need to be specifically targeted as well. And, again, they have authorization to deal with. It's not like you'd leave that open either.
Is it common to do it any other way?
In fact, I've not worked at a company (in 12 years of web-app dev) that used different domain names on dev machines until very recently, when it became a requirement of the software itself to have subdomain-per-client code.
> All my actual sites listen for custom hostnames (since otherwise you only get one site per machine or have to do silly things with port numbers on the url).
It's not just web servers though, every from databases to caches servers.
>Databases are named, and often live within named database server instances, so they'd need to be specifically targeted as well
I think most would stick with the defaults. With mssql server it's "." or "sqlexpress" or whatever.
>And, again, they have authorization to deal with.
I've always used windows authentication, but that now seems like a terrible idea.
This DNS Rebinding hack should be easy to eliminate: only etc/hosts can make records that point to localhost.
I don't care if a HUGE swath of applications are broken by this, DNS just seems broken to allow remote DNS servers to make localhost records.
Please excuse my awkward use of words, I am a combination of sick and delightfully ignorant about DNS.
UNSAFE_ALLOW_REMOTE_DNS_RECORDS_DRAGONS = true
where the default setting is always FALSE.Can someone who does kernel or DNS client development in operating systems comment? Am I nuts or is DNS nuts?
There should be some central network policy in the enterprise that says a DNS record coming from outside the company can't point to addresses on the 10.xx.xx.xx or 192.168.xx.xx spaces. I'm surprised that isn't already the common configuration.
Super simple to set up too:
# Enforce privacy of these addresses. Strips them away from answers.
# It may cause DNSSEC validation to additionally mark it as bogus.
# Protects against 'DNS Rebinding' (uses browser as network proxy).
# Only 'private-domain' and 'local-data' names are allowed to have
# these private addresses. No default.
private-address: 10.0.0.0/8
private-address: 172.16.0.0/12
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: fd00::/8
private-address: fe80::/10
private-address: 127.0.0.0/8But then, you could also use 127.x.y.z (so you'd have to "blacklist" all the 127.0.0.0/8 range).
And also blacklist the 192.168.x.y range (more specifically an attacker could be that your router serves 192.168.1.x with DNS starting at x=100, so serve the 192.168.1.100).
Then at this point you realise that its not DNS that's broken but the underlying applications.
Just because a localhost address is in the IPv4 address space doesn't make it just some ordinary address. And applications aren't broken, the local operating system's network software is being hacked in this case of DNS Rebinding.
I do remember, however, that either my ISP or whatever DNS voodoo DD-WRT was doing blocked resolution of things in 127.0.0.0/8.
Scripts from the public Internet shouldn't be able to access private or local networks as a matter of policy.
Similarly, in a high-security environment, scripts from a private network shouldn't be able to access the public Internet - to help prevent exfiltration of private data.
I agree, and it's encouraged some pretty stupid practices where it is used for things other than espionage or malicious intent.
Lenovo has a driver detection / update tool on their website, to run it you download a helper application that opens up a HTTP endpoint on localhost, then their website uses it to scan your system and (hopefully) shuts it down afterward. Why was this done in the first place, forcing users to download and run an executable (which has to be restarted each time you scan) that has no UI except for the web browser tab you already have open is dumb.
If you are going to go to that much effort, better to just spend a little bit of time on security.
I couldn't convince anyone else to use this stuff. They were happy to just run some homebrew commands every now and then.
I was away from the project for about six months. When I got back I thought "great, I'll just `vagrant up` and be back in business."
Parallels had upgraded itself to some version that wasn't compatible with vagrant. I was eventually able to find an old parallels installer and downgraded.
My laptop had a newer version of ansible, and it couldn't run the old ansible scripts. There was no easy fix to something that should be easy (ansible could no longer create postgres users when ansible was running through a non-root account. With vagrant you log in as the `vagrant` user and that user has sudo).
I deleted all that stuff, and now I do all my development locally. I do run my DB's in docker containers, but that's it. Now it is super easy to add any version of any database to any project. Just a few lines in docker-compose.yml. But my docker-compose.yml was written for an older version of docker-compose. I tried to upgrade it to the latest syntax and nothing worked. I reverted that and things are still running. It is only a matter of time before some docker update renders my db and redis useless.
At that point I'll just `brew install redis` and `brew install postgres` and be done with it. Everything will run, and run at native speeds. Yay!
First, we use Ansible, not just for development, but for deployment. Because of this, if the time comes to upgrade Ansible, everybody upgrades, and any incompatible playbooks are immediately addressed.
Second, Parallels is a bad choice. They do not have a sufficient enterprise focus, and their product is updated too often. It's great for desktop virtualization, but not for much else.
We use VMWare Fusion, have no performance issues, and have never experienced the issue of hypervisor bit rot. On the one occasion when we upgraded VMWare, we also upgraded the corresponding Vagrant plugin, and everything worked fine.
A better mitigation than that mentioned in the article is that browsers should ignore a DNS update if it goes from a public IP to a private IP range. DNS pinning as suggested would cause havoc with most wifi captive portals, especially for those not computer savvy or the badly configured/implemented captive portals.
It will require extra page reloads. Everybody is used to reload pages for any random reason by now (and you can reload by javascript too). I don't think it would cause much havoc.
I typically tell people if they can't connect to services on our VPN, to make sure they're using google's DNS servers because they don't protect against DNS rebinding "attacks."
www.dropboxlocalhost.com -> 127.0.0.1
I don't know if dropbox are still using that in their client though.
The author notes that write access could be used to inject dangerous objects (e.g. malicious pickles) into the database. This is arguably a much more serious bug because it does not require DNS rebinding (such a request can be performed cross-origin) nor can it be mitigated by refusing to read the response (as Chrome is proposing to do).
In short: the database modification attack is potentially much more severe, but as of yet no precise attack chain has been identified. However, I think it's very likely that some server software uses e.g. pickles in the database.
[0] https://bugs.chromium.org/p/project-zero/issues/detail?id=67...
The PoC failed to work when using TorBrowser (with the security slider set to High) and letting NoScript temporarily allow.
In essence, the resolved address of a request will be checked if it lies in a reserved block. If so, further policy checks will be made for the resolved address, and the IP address will be pinned for that HTTP request.
Would appreciate feedback here, or on the issue.
[1] http://blog.fanout.io/2014/01/27/how-to-safely-invoke-webhoo...
Probably best to attack this on the DNS rebind level. Encapsulating the browser network context somehow and firewalling this might help mitigating this attack too.
By "well-constructed" I mean that the backend services for a given project should only be available on the container network, and not be exposed to the host network.
They absolutely should not, but I've seen it at companies I've worked at, at clients, and I'll admit I've done it at least a few times myself over the years.
AFAIK we're only allowed to have them on our work machines and we encourage clients to sanitize them before handing them over but not many do.
I prefer to have an annonymization script but many companies are too cheap to allocate time for one.
Fix the services, require authentication and permission enforcement and the problem is gone.
If the attack is coming from a browser it would authenticate with the users credentials.
How can I trace the source and ensure it doesn't restart on reboot?
For example:
POST / HTTP/1.1 << Ignored
Host: localhost:6379 << Ignored
SET abc 123 << Processed
QUIT << Processed
::1/128
Unlike IPv4 there is not an entire /8.
I have this enabled on my router.
dnsmasq has many other uses like tunneling all your DNS traffic through dnscrypt (https://www.opendns.com/about/innovations/dnscrypt/)
ssh <router>
configure
set service dns forwarding options stop-dns-rebind
commit
save
/var/log/dnsmasq.log should contain the resolution failures after that. e.g.: Sep 1 21:48:41 dnsmasq[26479]: possible DNS-rebind attack detected: www.dropboxlocalhost.comI know there are ways to do that as well, but this is the easiest and so it's most often the way it's done.
I see percentage of us here locking things down.
The rest, and the rest of the developers out there likely won't.
This needs to be fixed at the browser level I don't see it being solved any other way that would have the net benefit of having it fixed in the browser.
Simply pinning public and local and not allowing local rebinding should have most of the issues resolved.
EDIT: I'm not thinking straight. Please ignore me.
> Tunneling
Yes, that is what GP was describing.
Local VMs are definitely vulnerable to this as well.
Integration with IDEs.
That's a fair point, but Rsync or shared folders should have you covered for this.
config.vm.network :forwarded_port, guest: 8080, host: 8080 config.vm.network "public_network", :bridge => 'en0: Wi-Fi (AirPort)'
So there is some justification to having real data available - this is why in the finance space at least developer machines have relatively stringent restrictions placed on them compared to a lot of other organisations.