Tags: sqli web 

Rating: 5.0


# Sea of Quills

## Description

```
Come check out our finest selection of quills!

app.rb

Author: JoshDaBosh
```

## Analysis

Here is the interesting part that handles the POST requests in `app.rb`:

```ruby
post '/quills' do
db = SQLite3::Database.new "quills.db"
cols = params[:cols]
lim = params[:limit]
off = params[:offset]

blacklist = ["-", "/", ";", "'", "\""]

blacklist.each { |word|
if cols.include? word
return "beep boop sqli detected!"
end
}

if !/^[0-9]+$/.match?(lim) || !/^[0-9]+$/.match?(off)
return "bad, no quills for you!"
end

@row = db.execute("select %s from quills limit %s offset %s" % [cols, lim, off])

p @row

erb :specific
end
```

Go to the main page, click "Explore", and then do a simple search:

![](1.png)

Intercept the POST request in Burp:

![](2.png)

We control `limit` and `offset` from the search page, but enter any non-numeric characters and you'll get a bad response.

```ruby
if !/^[0-9]+$/.match?(lim) || !/^[0-9]+$/.match?(off)
return "bad, no quills for you!"
end
```

But `cols` is fair game as long as we don't use any of these characters:

```ruby
blacklist = ["-", "/", ";", "'", "\""]
```

Can we do a `union`?

![](3.png)

That works. Now we need to see what other tables might be there, which we can do with `sql from sqlite_master`.

![](4.png)

And there's our flag table!

## Solution

All that's left to do is use the union to get `flag from flagtable`:

![](5.png)

The flag is:

```
actf{and_i_was_doing_fine_but_as_you_came_in_i_watch_my_regex_rewrite_f53d98be5199ab7ff81668df}
```

Original writeup (https://github.com/dobsonj/ctf/tree/master/writeups/2021/angstromctf/sea_of_quills).