Full solution of Batman Investigation II - Gotham Underground Corruption from bi0sctf 2024
tl;dr
- Challenge 2 of Batman Investigation series
- Memory Forensics - WinDBG Dump Debugging - Malware Analysis - Blockchain Forensics - Password Retrieval - MAC Artefact Analysis
Challenge Points: 1000
No. of solves: 0
Challenge Author: Azr43lKn1ght
Challenge Description
After getting in hold of Carmine Falcone’s Laptop, Bruce Wayne has to investigate his laptop but unfortnatly he can’t get the files nor the encrypted disk and its key, so he went ahead to get the memory dumped. After investigating and getting the data from the guy hired by Falcone for analysis, it seems corrupted!. Now He have to find and solve the things uncovering the Gotham’s underground secrets, Help him solve the case of biggest Gotham corruption that is yet to happen!! They need to be stopped!
Password : freIsEprOMPOLICkLERA
Challenge Files:
- Primary Link
- Mirror Link
- nc link
Solution
Question 1:
Q1) who asked Carmine falcon to contact someone and who is falcone disguised as?
Format: disguse-name_person-contacted (all lowercase)
We are given a Raw file which is a memory dump, so we can use volatility to analyze the memory dump. First, we need to find the profile of the memory dump. We can use the imageinfo
plugin to find the profile of the memory dump if we are using vol2.
as it was Win10x64_19041, we can either use vol2 or vol3 to analyze the memory dump.
Looking into active process list using windows.pslist.PsList
1 | vol -f Carmine-Falcone.raw windows.pslist.PsList |
1 | Volatility 3 Framework 2.4.1 |
For the messages sent we can see that Thunderbird.exe is an active process. So analysing Inbox file will give us all the conversation data. So lets scan and list all files in the system using
1 | vol -f Carmine-Falcone.raw windows.filescan.FileScan |
1 | [snip] |
Lets dump the files from memory using the following command
1 | vol -f Carmine-Falcone.raw windows.dumpfiles.DumpFiles --virtaddr 0xab0b10cde210 |
let’s analyze the file, on analysis on the contents of the file, the answer to the first question is found.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39[snip]
X-Gm-Message-State: AOJu0Yyv8yhr8N0SSfAETgbGKlwAAXHBQ8dPe0iUUHmbk4oykMYSTuoR
ErKG++vrWZ5uSwIcHfIukc3n4Q98KhcEOA==
X-Google-Smtp-Source: AGHT+IEaR13fr5QwtA2EVAeeqRlt5MHwDILGsc17qMCuDr64g/xiFSQi2/n9+TmXEoQztdSMzBFY3Q==
X-Received: by 2002:a17:902:e54a:b0:1d4:da6d:c121 with SMTP id n10-20020a170902e54a00b001d4da6dc121mr2644778plf.13.1704622743610;
Sun, 07 Jan 2024 02:19:03 -0800 (PST)
Return-Path: <salvatoremaroni3376@gmail.com>
Received: from [10.0.2.15] ([59.89.233.32])
by smtp.gmail.com with ESMTPSA id ji5-20020a170903324500b001d3ffb7c4c7sm4214767plb.40.2024.01.07.02.18.49
for <hugostrange581@gmail.com>
(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);
Sun, 07 Jan 2024 02:18:59 -0800 (PST)
Content-Type: multipart/mixed; boundary="------------I30YCObF3eWdYu0uKKgStigo"
Message-ID: <d3a63f93-a0ee-4718-a71a-d9646de2698b@gmail.com>
Date: Sun, 7 Jan 2024 10:17:39 +0000
MIME-Version: 1.0
User-Agent: Mozilla Thunderbird
Content-Language: en-US
To: hugostrange581@gmail.com
From: Salvatore Maroni <salvatoremaroni3376@gmail.com>
Subject: Confidential!!
This is a multi-part message in MIME format.
--------------I30YCObF3eWdYu0uKKgStigo
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit
Contact the guy and the key for his reply is his private key!!
--------------I30YCObF3eWdYu0uKKgStigo
Content-Type: application/octet-stream; name="contact.rar"
Content-Disposition: attachment; filename="contact.rar"
Content-Transfer-Encoding: base64
UmFyIRoHAQC+0emJDQEFCQAIAQGb1umAgAAblo4KLwIDC+eEAASEwAAgz4NtOoA7ABFjb250
YWN0Ly5EU19TdG9yZQoDAp8i9ZMvP9oBzfZjAjB1QzNC9HBGxzabsqp/ESASvESqR1DS3aIR
UcaXdOB0Nog4aCpZbC2Gyx0sl2iVVSvY34OfBd6nxI+LnITbtzHJBWyyoR1OZu9clcxuvPZj
[snip]hugo-strange_salvatore-maroni
is the answer to the first question.
1 | Answer1:hugo-strange_salvatore-maroni |
but we also found an attachment that was sent with the email. Let’s extract the file contact.rar which is the attachment and analyze it as well look into the second question.
but this can also be solved by taking a memory dump of the process and listing all the strings.
Question 2:
Q2) what is the hash of the file that he sent him to contact
Format: sha1sum
After decoding the base64 encoded attachment file as well removing all trailing white spaces, we get the attachment file, now we can extract and compute the sha1sum of the attachment.
1 | Answer2: 61ca5520263677e648297e1aefd90343fdd0388f |
Question 3:
Q3) what is the password of his password manager
Format: password_manager_name_with_version:md5(password)
As we can see KeePass was the only password manager that was running on the system, we can conclude it was the password manager. Now we have to find its version.
From filescan, we can see that KeePass 2.53.1 was present (version : 2.53.1)
1 | 0xab0b10a0f1b0 \Users\Carmine Falcone\Downloads\KeePass-2.53.1-Setup.exe |
this can also be found from the following config file
0xab0b10a3b3f0 \Program Files\KeePass Password Safe 2\KeePass.exe.config 216
So i went ahead and dumped KeePass.exe.config
and its contents were:
1 |
|
as well this can be done by printing the hive registry or dumping hives and analysing the registry.
now we want the password, searching for password recovery/retrieval of the database of KeePass which is .kdbx file, we come across a CVE dated 2023. That is CVE-2023-32784 as well we can find this article.
Now let’s search for .kdbx files using filescan and dump the file from memory.
1 | [snip] |
1 | vol -f Carmine-Falcone.raw windows.dumpfiles.DumpFiles --virtaddr 0xab0b138c5bc0 |
The plugin we found from the article above shows a missing character in the middle.
1 | Possible password: ●i&Y●Q)8h<)1T2j |
So it’s better we go with the .net tool which is better.
1 | Password candidates (character positions): |
lets write a easy script to bruteforce the password
1 | #!/usr/bin/python |
before that we analyse the kdbx file for a structure check and remove the trailing null bytes added up during filedump.
After running the script on it, we get the correct password:
1 | [snip] |
so the password is di&YéQ)8h<)1T2j
and now the answer will be
1 | Answer3: keepass_2.53.1:4e896dbb5120c2c33e0965174c6036c2 |
Question 4:
Q4) what is the key he stored in his password manager that he deleted?
Format: (format: user_name:key)
now we can open the kdbx file using the password we retrieved.
now lets look for the key he stored in his password manager which was deleted.
let’s view it
1 | Answer4: Azr43lKn1ght:sw1zwJjvpqVvFNANws1v |
Question 5:
Q5) what is the cryptocurrency wallet installed and when is it installed?
Format: wallet_name-date-time(in format YYYY:MM:DD:HH:MM)
We can see from the active processes that Exodus is running, so we can conclude that Exodus is the cryptocurrency wallet installed. Now we have to find the installation time.
From Uninstall reg entry of exodus, we can get the install date
1 | vol.py -f Carmine-Falcone.raw --profile=Win10x64_19041 printkey -K "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\exodus" |
as well we can get it from exodus file installer’s prefetch execution time or as well look into mft.
1 | vol -f Carmine-Falcone.raw windows.mftscan.MFTScan |
1 | * 0xab0b0769c920 FILE 89142 2 File Archive FILE_NAME 2024-01-07 08:39:03.000000 2024-01-07 08:39:03.000000 2024-01-07 08:39:03.000000 2024-01-07 08:39:03.000000 EXODUS-WINDOWS-X64-24.1.1.EXE-5FE34CD7.pf |
1 | 2024-01-07 08:38:08 UTC+0000 2024-01-07 08:38:32 UTC+0000 2024-01-07 08:38:32 UTC+0000 2024-01-07 08:38:32 UTC+0000 Users\CARMIN~1\DOWNLO~1\exodus-windows-x64-24.1.1.exe |
And we can see there is a prefetch entry for the installer which has the time 08:39:03
1 | Answer5: exodus-2024:01:07:08:39 |
Question 6:
Q6) what is the malware that wants to steal the cryptocurrency wallet and ip it’s trying to send to with port number?
Format: malware_ip:port)
let’s look through the contents of Downloads, Desktop etc. There didn’t seem to be anything suspicious present in the downloads folder since all that there was the installers for the applications present in his system. But in desktop folder, there was a services.exe which looked suspicious which also has a execution/run in prefetch files as well as other Evidence of Executions.
1 | 0xab0b0d34fe90 \Users\Carmine Falcone\Desktop\services.exe 216 |
So assuming the ip and port is present in the strings of the binary, I made a quick code snippet find all occurances of ip addresses in the strings.
1 | import re |
which gives the output,
['127.0.0.1', '172.30.145.189']
First one is localhost, hence the second one should be the one we are after.
ding strings grep with the ip, we get the port too.
1 | Answer6: services.exe_172.30.145.189:6969 |
Question 7:
Q7) Eventhough Falcone’s Network Moniter blocked the file sent, falcone made sure to zip with a password and noted it down temporaily in his screen and sent it to a detective for analysis.what is the password of the zip file?
as he noted it down and only notepad.exe was running and as it not get saved as well it’s mentioned temporarily, we have to retrieve it from notepad memory.
So let’s dump the VAD structures using vaddump vol2 plugin.
1 | vol.py -f Carmine-Falcone.raw --profile=Win10x64_19041 vaddump -p 5372 -D vaddumps |
But unless we can pinpoint to the heap dumps in the VAD structures, its going to be hectic to go through the whole process’s memory dump. So i used vadtree plugin in vol2 to look at the VAD (Virtual Address Descriptor) tree.
1 | vol.py -f Carmine-Falcone.raw --profile=Win10x64_19041 vadtree -p 5372 --output-file=./vadtree.dot --output=dot |
here the colours represent
1 | Red: Heaps |
We can see there are 4 heaps within the process structure as denoted by the red blocks. We can also achieve the same with volshell.
1 | vol.py -f Carmine-Falcone.raw --profile=Win10x64_19041 volshell -p 5372 |
Now that we have the addresses of the heaps, we narrow down our analysing to just the heap dumps.
1 | strings -e l notepad.exe.4775c080.<address>.dmp |
3 out of 4 dumps we got didn’t contain much information. But while going through the strings of the dump of the address 0x000001ea80b30000-0x000001ea80c2ffff
, along with other interesting strings, we can find this.
And we can confirm its just a whitespace. So now that we have a possible candidate for a password w&31D,%4> C6'nT
We can also get this using MemProcFS by looking into the heaps of notepad.
and as well we can verify this by debugging the memory dump using windbg.
we can get the dmp file of the original raw memory file with mounting the device using memprocfs and then we can use windbg to debug the memory dump.
!process 0 0 notepad.exe command displays detailed information about the notepad.exe
.process /r /p ffffab0b152e8080 command sets the process context to the notepad.exe process with the specified address. The /r option reloads the user-mode symbols for the process, and the /p option specifies a physical address.
After running this command, the debugger’s process context is set to the notepad.exe process, and the user-mode symbols for the process are loaded.
now let’s list all heaps using!heap -s -v -a
we have to look int heaps with extra user_flag
and lets display the heap content using
1 | db 000001ea80b6ec80 L80 |
w&31D,%4> C6'nT
and we finally find the valid string that could be the noted down one.
1 | Answer7: 63eeb3a466f8bab52b44e182e919b512 |
Question 8:
Q8) what is the hash of the original files in the zip file before the stealer modified them?
Format: sha1sum:sha1sum
now we can either dump the zip file from memory or get it from the handle of 7z.
but apparently when we look into the file’s hex it is hollowed out eventhough it is a handle to a process, the middle is only null bytes, so we can’t extract the file either. dumping it from memory also has the same effect.
1 | vol.py -f Carmine-Falcone.raw --profile=Win10x64_19041 filescan | grep "Desktop" |
so looking into why the file unknown.data is given as well this zip file that is inside the 7z was dropped by the services.exe which is found using timelining, so from the previous question as well as description we can conclude that the unknown.data is the original file that was zipped and sent to the detective it’s corrupted
now we can try and fix the file using the hollowed out 7z.
fixing both after comparing, we are able to fix the file and extract it using the above obtained password w&31D,%4> C6'nT
and we get 2 .seco files.
but as mentioned by the question as well as looking into the timeline as the stealer modified the seco files, lets statically analyse the binary with IDA to fix it as the hash is also verified as wrong.
We can see that it’s a PE32+ executable, pretty standard stuff.
We get to the start function from which we follow start_0
As it’s windows go binary, the offset in line 47 in start_0 should point to the offset of runtime_main.
then looking into the runtime_main.
we can immediately see that it is loading the path of a file called exodus.wallet
, by joining it with an environment variable USERPROFILE
.
Then, it’s concatenating “.zip” to the filename which is stored in the environment variable.
Then, it calls a function getFilesInFolder
(which I renamed in IDA) after looking at it.
getFilesInFolder
is defined as follows
Here we can see that it’s just walking the entire directory and getting every file in it.
After that, it enters a for loop, where first it enters a function (on the right branch of the jump) which first opens and reads the file (which is clear from the assembly)
(Note that the buffer of the file is stored as a pointer inside rdx
after the function returns)
Then, once that is done, a function called runtime_makeslice
(IDA’s renaming convention) is called. This essentially allocates new memory on the heap for a fresh object and initialises its values to 0.
After that, it enters this for loop
Notice how the file’s buffer is being stored in esi
, then XORed with 0x33, then stored as a pointer inside rax
. This is done for the entire length of the buffer (basically the whole file)
Then, again, from the assembly itself, it is pretty clear that this new “encrypted” buffer (which is just the original file XORed with 0x33), that is then written to a fresh file. This is done for every file in the directory (this is evident also from the fact that the entire directory was previously walked to get every file handle).
After the for loop to encrypt every file, another function (on the left branch of the jump) is called, let’s take a look at what it does.
Again, like the previous function, it first opens the file (the encrypted files this time), stats them, creates a zip header, and copies the file content to the zip archive.
After that, we enter this part of the binary
It enters a for loop to remove every encrypted file that was created.
After that, it gets the current working directory of the zip file that was created (containing all the encrypted files).
After that, it initiates a tcp
connection to 172.30.145.189:6969
, to which it sends our zipped archive of the encrypted files.
So the binary itself does something pretty simple overall. It reads every single file in the directory, “encrypts” it by XORing with 0x33, then zips it and sends it to that IP and port.
Here we can see, the data is begin xored with 0x33, which is also evident when looking back to the .seco files in hex editor where there are a lot of 0x33 bytes present.
so wrote a quick script to fix up the .seco files by xoring them with 0x33,
1 | with open('enc/seed.seco','rb') as file: |
So now i have the fixed both seco files. So taking the sha1 of both the files.
1 | Answer8: 3ba6011f5e1b6941f893df6579f87f1c823e7493:5e38ffd5e6d6115a6f4bb21b5216c37392eb544f |
Question 9:
Q9) what is the password that Carmine Falcone protected the wallet with
Format: md5(password)
After doing research, tests and analysis on exodus wallet, we are able to find that the password is stored in plaintext between exodus.wallet%22%2C%22passphrase%22%3A%22
and %22%7D
in memory. So we can extract the password from the memory dump.
so now we got the password as yb%23uO7%26n%25%24%C2%A36%5BE310
, converting the url encoded characters to ascii we get yb#uO7&n%$£6[E310
password is yb#uO7&n%$£6[E310
1 | Answer9: 93bbca2406298b9cb574ac06abf113e4 |
Question 10:
Q10) who did Carmine falcone recive money from?
Format: name_person:curreny_used(3 or 4 letter significance):amount(in dollars with precision of 2 decimal places):transaction_id
after transferring the wallet to exodus , we can use the password we found to open the wallet and look into the transactions.
let’s check the received logs.
he have received money from Edward Nigma (Riddler) in USD 15.31 with a transaction id of 0xab42fc3fb81fca7bec9e49058d80dc928ed5de2e780a54de41d3c1a8587c4ab7.
1 | Answer10 edward_nigma:eth:15.31:0xab42fc3fb81fca7bec9e49058d80dc928ed5de2e780a54de41d3c1a8587c4ab7 |
Question 11
Q11) who did Carmine falcone send money to?
Format: name_person:curreny_used(3 or 4 letter significance):amount(in dollars with precision of 2 decimal places):senderaddress
the same way we look into the sent log.
He sent the money to Oswald Cobblepot, which was USD 13.65 with a transaction id of 0xab42fc3fb81fca7bec9e49058d80dc928ed5de2e780a54de41d3c1a8587c4ab7 with a wallet address of 0xcfb6b082B7296FaA5aD18695e1F554AFC23724eD
1 | Answer11: oswald_cobblepot:eth:13.65:0xcfb6b082B7296FaA5aD18695e1F554AFC23724eD |
Question 12
Q12) what is the name and client private key that Salvatore asked for contact verification?
Format: infobreakage_dbkey:client_email
Now looking into the email attachment, we have few folders, on analysis we can find that it is dropbox files.
looking into info.json gives that an info from the path that it is either linux or mac .dropbox folder
looking into files, analysing as well after researching, we are able to come across these tools that can give us the required information.
but apparently we have to edit it so it can work in our case.
so I made a cpp script to do that.
1 |
|
and we get the output as
as well this can be made efficient by
1 | import sys |
now we can use this on the tool and get the details, here is what I edited in the tool.
1 | #!/usr/bin/env python |
1 | KEYSTORE: got user key (0, e747b9c8cc2f80b88c6b511f26682b66) |
so dbkey: 89839f76862bd89b369d99f41ca51cd7
now we can use this tool to decrypt and view the dbx file.
1 | sqlite -key b7b04158ee71df20a80d1a70113798c1 <path to config.dbx> |
from which we can get the required information that is:
1 | client email :twoface2681988@gmail.com |
1 | Answer12: 89839f76862bd89b369d99f41ca51cd7:twoface2681988@gmail.com |
Question 13
Q13) what is the information interpreted further from previous question
Format: name_client:private_key
so we can answer this from the above analysis
1 | Answer13: harvey_dent:MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgJGmnt3gVtSX2iswJYf9S3I5X4vPaS1xKHAvFdN5A2tWhRANCAASO63HNoO6rpC9Gfpi5tR+F5kEiRoTzbV/xWscN9McLCTlj2gWtZ+LSrkQkuaO5/XYiHXxCF/1vRWuNspXwY/K9 |
Question 14
Q14) what is the userkey of the encrypted dbx database?
Format: userkey
userkey:e747b9c8cc2f80b88c6b511f26682b66
1 | Answer14 : e747b9c8cc2f80b88c6b511f26682b66 |
and after this, we finally finish the Adventure! and yeah we made Batman uncover the most underground corruption that was happening in Gotham.
flag
1 | bi0sctf{b4tm4n_und3rgr0und_inv3st1g4t10n_Succ3ssful_f98a12405d41746836bb4e7df69e44f6} |
Closing Note
Thank you to everyone who tried the challenge. We thank you for playing the CTF and would love to hear some feedback about the challenge.
If you have any queries regarding the challenge, feel free to hit me up over twitter/discord.