How to hack an Angular app?
ng-conf 2018 · Salt Lake City, USA
by Asim Hussain · 1 November 2018
Despite the title, this isn’t really an Angular talk — it’s a web security talk, and the honest subtitle is “real-life hacking stories”. I’m not a security expert; I’m a web developer who got hacked one day and learned a thing or two. So I teach the basics the only way I find sticks: by walking through three real breaches and what each one should have taught us.
AI-generated summary of my talk
Jump into the talk
A little terminology first
Two words do a lot of work in this talk. A vulnerability is just a hole in your security — not configuring a firewall is a vulnerability. An exploit is the series of steps you take to take advantage of that hole and do something bad. And a zero-day is an exploit nobody knows about yet — it’s private, secret, and valuable. The moment it becomes public we stop calling it a zero-day; it’s just an exploit, and its value decays over time because vendors start shipping patches. Getting hold of a real zero-day is hard and expensive. Getting hold of a six-month-old public exploit? You just google it.
Equifax: the patch they didn’t install
Everyone remembers Equifax — one of the largest breaches in history, roughly 184 million records, from a billion-dollar company with ten thousand employees. So how did they get in? Not a zero-day. They got hacked through a known vulnerability in Apache Struts, their web framework, that had already been fixed for two months. All Equifax had to do to protect themselves was install an update.
That gets a laugh in the room, but it’s far more common than people think. Snyk’s reports put numbers on it: around a quarter of the top 50 data breaches came through known vulnerabilities, and a huge share of sites were running vulnerable JavaScript libraries. This is exactly why, if you read the Angular docs — AngularJS or modern Angular — the number one security item listed is “keep updated to the latest version”. I know upgrading is a fight with your manager. But attacking a site through a known vulnerability is genuinely trivial: you google it on Exploit-DB, search by framework, click a link, and it hands you the exploit and instructions. If you host on GitHub, their dependency alerts will scan your package.json against those databases and warn you; if you live on the command line, NSP and Snyk do the same.
The GitHub heist: small bugs, chained
This is my favourite, because it reads like a heist movie. It comes from a researcher called Orange (Orange Tsai), who found it through GitHub’s bug bounty. To trigger his exploit on GitHub Enterprise, all you do is add a webhook URL to a repo and then run a search. That’s it — and an id command runs on the server behind the firewall. Here’s how the pieces stack:
- The webhook becomes an SSRF. Webhooks POST to a URL you give them. Orange found they’d forgotten to block
0, which resolves to localhost — so the server makes a request to itself, behind its own firewall, to an internal Graphite service. (Server-Side Request Forgery: tricking the server into making requests on your behalf.) - Graphite turns a POST into a GET. The internal code pulls a URL out of the request and does a GET to it. Two exploits chained already.
- A CRLF-injection bug in Python’s HTTP library. That GET goes through an HTTP library with a known carriage-return/line-feed injection flaw. CRLF (
\r\n) is how HTTP separates lines — so by smuggling those characters in, you can forge a malformed HTTP message. - Protocol smuggling into memcache. Point that forged message at the memcache port and it stops being HTTP. memcache reads your smuggled lines as memcache commands — so you can write whatever you like into the cache.
And the payload: we all serialize objects into memcache, then deserialize and use them. Orange overwrote a cached object with a crafted one, so the mere act of deserializing it executed his code on the server. The lesson I keep hammering: we imagine we’ll be attacked through one giant hole. We won’t. It’s small, subtle bugs chained together — which is exactly why hacking stories are so much fun to tell.
npm typosquatting: one missing hyphen
The last story is the one closest to home. Late in 2017 people thought Kent C. Dodds’ cross-env package had been corrupted to steal environment variables. It hadn’t. What actually happened was someone published crossenv — same code, no hyphen — with a malicious setup script that base64-encoded your environment variables and POSTed them to the attacker’s server. Install it, and you’ve handed over your passwords and connection strings, since that’s exactly where we stash sensitive config.
This is typosquatting, and it’s everywhere — every language with a package registry has it. And we all set ourselves up for it: I install from npm, can’t remember if a package has a hyphen, try it without, and if it works, great. But think about what npm install really is — you’re giving a stranger you’ve never met the ability to run code as you, behind your firewall, with access to all your keys. crossenv was live for two weeks and pulled down on a package downloaded millions of times a month. Partial defences exist: scoped packages (this is why Angular’s packages live under @angular — only the scope owner can publish there), and npm’s newer rules blocking names that differ from an existing package by punctuation alone.
So what do you actually do?
The summary is short and unglamorous: update all the things. Nobody wants to be the person explaining to their CEO why the company failed because they couldn’t be bothered to install an update. There’s no such thing as a small vulnerability — fix it. And as for the npm trust problem? I’ll be honest: I don’t have a clean solution for that one. That’s the bit that should still scare you.