Programming, Topic: Security

All posts

Recent posts

Using CSP unsafe-hashes

So here’s a fun one: how does the unsafe-hashes directive in a content security police work?

In a perfect world, you don’t need it. You can write a CSP with a minimal script-src policy, including only scripts from your own domains (self) or a list of specific other scripts or at worst domains.

But sometimes real life (and third party libraries) get in the way.

It starts with inline scripts. So you have to add unsafe-inline. But there’s a better way to do that: CSP nonces. Specify a randomly generated (per request) nonce in the CSP header and then apply that same nonce to every script tag. Voila. Better.

The problem: inline JavaScript events

But what about something like this:

<button onClick="doSomething();">

Well, you might say, don’t do that. Write it as a script:

<button id="doSomethinger">

<script nonce="correctHorseBatteryStable">
document.getElementById("doSomethinger").addEventListener("click", function() {
  doSomething();
});
</script>

But like I said–third parties scripts can be imperfect. And sometimes, they just insist on embedding their own event handlers inline.

A solution: unsafe-hashes

Enter: unsafe-hashes.

Basically, you can add this to your CSP:

script-src 'unsafe-hashes' 'sha256-44558f2c36efd8163eac2903cec13ed1fafcca51abd91d9f696321ab895f1107'

This tells the browser that you are allowed to have event listeners directly on HTML elements… so long as the content of the JavaScript hashes exactly to any hash listed as an unsafe-hash:

$ echo -n "doSomething();" | sha256

44558f2c36efd8163eac2903cec13ed1fafcca51abd91d9f696321ab895f1107

It gets worse: dynamically generated JavaScript

There is, however, one problem with this that does come up unfortunately often. If you’re already dealing with third parties not doing things you wish they would, well then you have to deal with fun code like this:

<?php
foreach ($buttonIds as $buttonId) {
    echo '<button onclick="doSomething(' . $buttonId . ');">Button ' . $buttonId . '</button>' . PHP_EOL;
}
?>

Unfortunately… that completely blows up the CSP. Because…

$ echo -n "doSomething(1);" | sha256
2ef899c15aae95711855a45a5bb93c55363162e0e75e295aad4f189f20323d7c

$ echo -n "doSomething(5);" | sha256
0e03bb385169b89c95eb62659f50604ffb8283154bd58ab8cc7e692c4b5c05a3

$ echo -n "doSomething(42);" | sha256
4d9691449db6740ee19207c5bb52361eb97e18f06352ed400f83ae7caee270da

Just hashes doing hash things there. So basically, you have to be able to dynamically generate your CSP on the fly, including all of the hashes of all of the functions and with each of their arguments that are either possible or (even better) actually used.

And this isn’t fun at all.

Now, you might say: but you can do something like this:

<?php
foreach ($buttonIds as $buttonId) {
  echo '<button data-id="$buttonId" onclick="doSomething(this.dataset.id);">'
}
?>

After all

$ echo -n "doSomething(this.dataset.id);" | sha256

d2ecabb98b1bb7cc81cd75d43dcb7bac08ce31055339920976cb92aa2f5dd2f5

Only one hash!

But if you have that much control… then why are you using inline JavaScript in the first place?

Is it safe?

Is this safe?

No. It’s called unsafe-* for a reason. An attacker that controls input can theoretically take any of the hashed functions you’re including (like submitPayment…) and inject them in places they shouldn’t be. And heck, if you manage to find a SHA-256 hash collision? Well, then you have far more interesting things to do with that then attacking some site that found themselves force to used unsafe-hashes

But it’s better than unsafe-inline without nonces which allows arbitrary inline scripts. And unfortunately, there’s no way to actually use nonces with inline scripts.

And while a perfectly secure system would be the best case, it’s absolutely better to do as much as you can to secure a system rather than doing nothing waiting for the perfect solution to become possible.

Onward!

read more...


Rebuilding Streams with TShark

Another quick post in a list of CTF techniques: filtering streams with tshark. tshark is the command line half of the packet capture tool Wireshark. The advantage here is it let’s you do all manner of filtering on the command line.

read more...


Mongo DB Data Exfiltration via Search Conditions

I recently participated in a security capture the flag (CTF) exercise through work. The goal was–in a wide variety of ways–to find a hidden string of the form flag{...} somewhere in the problem. Some required exploiting sample websites, some parsing various data formats or captures, some required reverse engineering code or binaries, and (new this year) some required messing with LLMs.

As I tend to do for just about everything, I ended up writing up my own experiences. I won’t share that, since it’s fairly tuned to the specific problems and thus 1) not interesting and 2) probably not mine to share, but I did want want to share a few interesting techniques I found/used. If it helps anyone either defend against similar attacks in the real world or (more importantly 😄) someone comes across this while trying to solve a CTF of their own, all the better.

Okay, first technique: extracting data from a MongoDB database using search conditions.

read more...


A CLI Tool for Bulk Processing Github Dependabot Alerts (with GraphQL!)

Dependabot is … somewhat useful. When it comes to letting you know that there are critical issues in your dependencies that can be fixed simply by upgrading the package (they did all the work for you*). The biggest problem is that it can just be insanely noisy. In a busy repo with multiple Node.JS codebases (especially), you can get dozens to even hundreds of reports a week. And for each one, you optimally would update the code… but sometimes it’s just not practical. So you have to decide which updates you actually apply.

So. How do we do it?

Well the traditional rest based Github APIs don’t expose the dependabot data, but the newer GraphQL one does! I’ll admit, I haven’t used as much GraphQL as I probably should, it’s… a bit more complicated than REST. But it does expose what I need.

read more...


A simple Flask Logging/Echo Server

A very simple server that can be used to catch all incoming HTTP requests and just echo them back + log their contents. I needed it to test what a webhook actually returned to me, but I’m sure that there are a number of other things it could be dropped in for.

It will take in any GET/POST/PATCH/DELETE HTTP request with any path/params/data (optionally JSON), pack that data into a JSON object, and both log that to a file (with a UUID1 based name) plus return this object to the request.

Warning: Off hand, there is already a potential security problem in this regarding DoS. It will happily try to log anything you throw at it, no matter how big and will store those in memory first. So long running requests / large requests / many requests will quickly eat up your RAM/disk. So… don’t leave this running unattended? At least not without additional configuration.

That’s it! Hope it’s helpful.

read more...


Pulling more than 5000 logs from datadog

Datadog is pretty awesome. I wish I had it at my previous job, but better late than never. In particular, I’ve used it a lot for digging through recent logs to try to construct various events for various (security related) reasons.

One of the problems I’ve come into though is that eventually you’re going to hit the limits of what datadog can do. In particular, I was trying to reconstruct user’s sessions and then check if they made one specific sequence of calls or another one. So far as I know, that isn’t directly possible, so instead, I wanted to download a subset of the datadog logs and process them locally.

Easy enough, yes? Well: https://stackoverflow.com/questions/67281698/datadog-export-logs-more-than-5-000

Turns out, you just can’t export more than 5000 logs directly. But… they have an API with pagination!

read more...


SSRF Protection in Rails

One of the more subtle bugs that a lot of companies miss is Server Side Request Forgery (SSRF). Like it’s cousin CSRF (cross-site request forgery), SSRF involves carefully crafting a request that runs in a way that the original developers didn’t expect to do things that shouldn’t be done. In the case of CSRF, one site is making a request on behalf of another in a user’s browser (cross-site), but in SSRF, a request is being made by a server on behalf of a client, but you can trick it into making a request that wasn’t intended.

For a perhaps more obvious example, consider a website with a service that will render webpages as preview images–consider sharing links on a social network. A user makes a request such as /render?url=https://www.google.com. This goes to the server, which will then fetch https://www.google.com, render the page to a screenshot, and then return that as a thumbnail.

This seems like rather useful functionality, but what if instead, the user gives the url: /render?url=https://secret-internal-site.company.com. Normally, company.com would be an internal only domain that cannot be viewed by users, but in this case–the server is within the corporate network. Off the server goes, helpfully taking and returning a screenshot. Another option–if you’re hosted on AWS–is the AWS metadata endpoint: http://169.254.169.254/latest/meta-data/. All sorts of interesting private things there. Or even more insidious, /render?url=file:///etc/password. That shouldn’t work in most cases, since most libraries know better than to rener file:// protocol URLs, but… not always!

read more...


Rack::Cors Configuration Tricks

cyu’s Rack::Cors middleware is rather handy if want to control your CORS (Cross-Origin Resource Sharing) settings in a Ruby-on-Rails project. Previously, there was a fairly major issue where :credentials => true was the default (which you generally do not want), but there were also some more complicated tweaks that I wanted to make.

One problem I recently had to deal with was wanting to:

  • Allow CORS connections from arbitrary domains (this site functions as an API)
  • Do not allow CORS from http domains at all
  • Only allow cookies (Access-Control-Allow-Credentials) to be sent for sibling subdomains
  • Prevent cookies from being sent from specific sibling subdomains (that are actually run by a third party)
  • On development (non-production) versions of the site, allow credentials from localhost

read more...


Prevent JavaScript links by parsing URLs

If you have a website that allows users to submit URLs, one of the (many many) things people will try to do to break your site is to submit URLs that use the javascript: protocol (rather than the more expected http: or https:). This is almost never something that you want, since it allows users to submit essentially arbitrary code that other users will run on click in the context of your domain (same origin policy).

So how do you fix it?

First thought would be to try to check the protocol:

> safe_url = (url) => !url.match(/^javascript:/)
[Function: safe_url]

> safe_url('http://www.example.com')
true

> safe_url('javascript:alert(1)')
false

read more...