☕
Writeups
TryHackMeHackTheBoxReferralsDonateLinkedIn
  • Writeups
  • TryHackme
    • 2025
      • Security Footage
      • Ledger
      • Moebius
      • Mayhem
      • Robots
      • Billing
      • Crypto Failures
      • Rabbit Store
      • Decryptify
      • You Got Mail
      • Smol
      • Light
      • Lo-Fi
      • Silver Platter
    • 2024
      • Advent of Cyber '24 Side Quest
        • T1: Operation Tiny Frostbite
        • T2: Yin and Yang
        • T3: Escaping the Blizzard
        • T4: Krampus Festival
        • T5: An Avalanche of Web Apps
      • The Sticker Shop
      • Lookup
      • Mouse Trap
      • Hack Back
      • SeeTwo
      • Whiterose
      • Rabbit Hole
      • Mountaineer
      • Extracted
      • Backtrack
      • Brains
      • Pyrat
      • K2
        • Base Camp
        • Middle Camp
        • The Summit
      • The London Bridge
      • Cheese CTF
      • Breakme
      • CERTain Doom
      • TryPwnMe One
      • Hammer
      • U.A. High School
      • IronShade
      • Block
      • Injectics
      • DX2: Hell's Kitchen
      • New York Flankees
      • NanoCherryCTF
      • Publisher
      • W1seGuy
      • mKingdom
      • Airplane
      • Include
      • CyberLens
      • Profiles
      • Whats Your Name?
      • Capture Returns
      • TryHack3M
        • TryHack3M: Burg3r Bytes
        • TryHack3M: Bricks Heist
        • TryHack3M: Sch3Ma D3Mon
        • TryHack3M: Subscribe
      • Creative
      • Bypass
      • Clocky
      • El Bandito
      • Hack Smarter Security
      • Summit
      • Chrome
      • Exfilibur
      • Breaking RSA
      • Kitty
      • Reset
      • Umbrella
      • WhyHackMe
      • Dodge
    • 2023
      • Advent of Cyber '23 Side Quest
        • The Return of the Yeti
        • Snowy ARMageddon
        • Frosteau Busy with Vim
        • The Bandit Surfer
      • Stealth
      • AVenger
      • Dreaming
      • DockMagic
      • Hijack
      • Bandit
      • Compiled
      • Super Secret TIp
      • Athena
      • Mother's Secret
      • Expose
      • Lesson learned?
      • Grep
      • Crylo
      • Forgotten Implant
      • Red
    • Obscure
    • Capture
    • Prioritise
    • Weasel
    • Valley
    • Race Conditions
    • Intranet
    • Flip
    • Cat Pictures 2
    • Red Team Capstone Challenge
      • OSINT
      • Perimeter Breach
      • Initial Compromise of Active Directory
      • Full Compromise of CORP Domain
      • Full Compromise of Parent Domain
      • Full Compromise of BANK Domain
      • Compromise of SWIFT and Payment Transfer
  • HackTheBox
    • 2025
      • Certified
    • 2024
      • BoardLight
      • Crafty
      • Devvortex
      • Surveillance
      • Codify
      • Manager
      • Drive
      • Zipping
    • 2023
      • Topology
Powered by GitBook
On this page
  • Recon
  • Source Code Analysis
  • Admin Access
  • Obtaining The Secret: ENC_SECRET_KEY

Was this helpful?

  1. TryHackme
  2. 2025

Crypto Failures

Implementing your own military-grade encryption is usually not the best idea. - by shamollash

PreviousBillingNextRabbit Store

Last updated 2 months ago

Was this helpful?

The following post by 0xb0b is licensed under


Recon

We start with a Nmap scan and find two open ports. We have SSH on port 22 and a web server on port 80.

We visit the page and see that we are logged in as a guest. There is talk of an SSO cookie that is supposed to be secured by military grade encryption.

We find the cookie as follows:

In the source, we find a comment that the .bak files should still be removed. Furthermore crypt is written in bold.

We intercept a request to the index page using burp and check whether index.php can also be called. And we get the index page.

If we now request index.php.bak, we find the source of the index page and thus also the generation of the cookie. The config.php is unfortunately not available as a backup, with that we might have had access to the key.

HTTP/1.1 200 OK
Date: Fri, 28 Feb 2025 21:18:26 GMT
Server: Apache/2.4.59 (Debian)
Last-Modified: Tue, 18 Jun 2024 06:23:20 GMT
ETag: "7bb-61b241e413aab"
Accept-Ranges: bytes
Content-Length: 1979
Connection: close
Content-Type: application/x-trash

<?php
include('config.php');

function generate_cookie($user,$ENC_SECRET_KEY) {
    $SALT=generatesalt(2);
    
    $secure_cookie_string = $user.":".$_SERVER['HTTP_USER_AGENT'].":".$ENC_SECRET_KEY;

    $secure_cookie = make_secure_cookie($secure_cookie_string,$SALT);

    setcookie("secure_cookie",$secure_cookie,time()+3600,'/','',false); 
    setcookie("user","$user",time()+3600,'/','',false);
}

function cryptstring($what,$SALT){

return crypt($what,$SALT);

}


function make_secure_cookie($text,$SALT) {

$secure_cookie='';

foreach ( str_split($text,8) as $el ) {
    $secure_cookie .= cryptstring($el,$SALT);
}

return($secure_cookie);
}


function generatesalt($n) {
$randomString='';
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
for ($i = 0; $i < $n; $i++) {
    $index = rand(0, strlen($characters) - 1);
    $randomString .= $characters[$index];
}
return $randomString;
}



function verify_cookie($ENC_SECRET_KEY){


    $crypted_cookie=$_COOKIE['secure_cookie'];
    $user=$_COOKIE['user'];
    $string=$user.":".$_SERVER['HTTP_USER_AGENT'].":".$ENC_SECRET_KEY;

    $salt=substr($_COOKIE['secure_cookie'],0,2);

    if(make_secure_cookie($string,$salt)===$crypted_cookie) {
        return true;
    } else {
        return false;
    }
}


if ( isset($_COOKIE['secure_cookie']) && isset($_COOKIE['user']))  {

    $user=$_COOKIE['user'];

    if (verify_cookie($ENC_SECRET_KEY)) {
        
    if ($user === "admin") {
   
        echo 'congrats: ******flag here******. Now I want the key.';

            } else {
        
        $length=strlen($_SERVER['HTTP_USER_AGENT']);
        print "<p>You are logged in as " . $user . ":" . str_repeat("*", $length) . "\n";
	    print "<p>SSO cookie is protected with traditional military grade en<b>crypt</b>ion\n";    
    }

} else { 

    print "<p>You are not logged in\n";
   

}

}
  else {

    generate_cookie('guest',$ENC_SECRET_KEY);
    
    header('Location: /');


}
?>

Source Code Analysis

If a cookie secure_cookie is not set, it gets generated by generate_cookie(). The structure of the cookie is user:UserAgent:ENC_SECRET_KEY. So we are at least in control of the User-Agent parameter, but more on that later. The generate_cookie function first calls generatesalt(), to generates a random 2-byte salt from an alphanumeric character set. This salt and the cookie string is then passed to the function make_secure_cookie.

function generate_cookie($user,$ENC_SECRET_KEY) {
    $SALT=generatesalt(2);
    
    $secure_cookie_string = $user.":".$_SERVER['HTTP_USER_AGENT'].":".$ENC_SECRET_KEY;

    $secure_cookie = make_secure_cookie($secure_cookie_string,$SALT);

    setcookie("secure_cookie",$secure_cookie,time()+3600,'/','',false); 
    setcookie("user","$user",time()+3600,'/','',false);
}

The make_secure_cookie() function essentially applies crypt() in chunks of 8 characters of the passed cookie string including the salt.

function cryptstring($what,$SALT){

return crypt($what,$SALT);

}


function make_secure_cookie($text,$SALT) {

$secure_cookie='';

foreach ( str_split($text,8) as $el ) {
    $secure_cookie .= cryptstring($el,$SALT);
}

return($secure_cookie);
}

The verification process extracts the salt from the first two characters of the stored cookie (substr($_COOKIE['secure_cookie'],0,2)). Which is then used to generate a secure cookie to compare it with the one passed.

function verify_cookie($ENC_SECRET_KEY){


    $crypted_cookie=$_COOKIE['secure_cookie'];
    $user=$_COOKIE['user'];
    $string=$user.":".$_SERVER['HTTP_USER_AGENT'].":".$ENC_SECRET_KEY;

    $salt=substr($_COOKIE['secure_cookie'],0,2);

    if(make_secure_cookie($string,$salt)===$crypted_cookie) {
        return true;
    } else {
        return false;
    }
}

Once a valid cookie is verified, the user is either given access or denied based on their username. The username is set by the user cookie. If the user is admin a flag will be shown.

if ($user === "admin") {
   
        echo 'congrats: ******flag here******. Now I want the key.';

            } else {
        
        $length=strlen($_SERVER['HTTP_USER_AGENT']);
        print "<p>You are logged in as " . $user . ":" . str_repeat("*", $length) . "\n";
	    print "<p>SSO cookie is protected with traditional military grade en<b>crypt</b>ion\n";    
    }

Admin Access

Since we are able to extract the salt of a hash, and we know that every chunks of 8 characters of the cookie string will be hashed with the salt, it is easy to impersonate the admin. All we have to do is to replace the first hash with the one representing the admin user and setting the user cookie to admin.

admin_cookie.php
<?php

$cookie = "lb7NSQVKII/xMlbSWUALKQkTdclbqNHCHlMb2IglbVqJC01B.ixolbB7iJhmQibUUlb4sPbTVMReDYlbUs7NJp6wQkIlbkhj828r0JCclbyFRIEvG3Ik2lbABrF.l0qTDYlblaFxGrwRKMMlbph0SXuMCedklbjfONPBmXZSIlbjh4lifLVPY.lbFlQbRJviwKolbd9gbgcYvhQglb77NNFoZAUBklb7JS3G1oOhQIlbN/BsekmCM0olbkQb.FfO/DBolbGQ1M.CnpZYQlbt49SHHVbIp2lbctBi8ETlzjIlbLXG3tP9wJA2lbUfn3Z7bPyc6lb6hhaY67E6/glb0UPWjIZpfw2lbnXRE.VVii1Mlb0pXLxIYnSsQ";


$salt = substr($cookie,0,2);

$text = "guest:Mo";
$guest_part = crypt($text, $salt);

$admin_text = "admin:Mo";
$admin_part = crypt($admin_text, $salt);


$modified_cookie = str_replace($guest_part, $admin_part, $cookie);

print($modified_cookie);

?>

We run our script to generate a new secure_cookie.

Next, we replace the secure_cookie with ours and set the user cookie to admin and reload the page. We are now logged in as admin and get the first flag.

Obtaining The Secret: ENC_SECRET_KEY

The next step is to extract the secret key. Since we know the salt, we can now bruteforce any 8 chunk block. With a character set of 65 characters, that would be 65^8 permutations that we would have to try for each block. That would not be feasible.

Instead, we could reduce it to one byte. Because we have control over the User-Agent and could create a block of which we already know 7 bytes and would only have to bruteforce one byte.

We hash the block with the 7 known bytes and the unknown character and when this resulting hash reappears in the cookie, we know that the unknown byte must be part of the key.

So we could brute-force the second chunk with the following string, generated by the user agent = AAAAAAAA guest:AAAAAAAA:_ and thus determine the first character of the key.

Chunk A: guest:AA

Chunk B: AAAAAA:_

So, we use the User-Agent as padding, that allows us to brute-force a single character at a time.

We chose a padding of 256 since the flag is really long.

We then just need to reduce the padding and update our know string $test_string with the character sequence found until we finally uncovered the entire key.

We illustrate this in the following graphic. The test string is outlined. If we get a hit with a hash, we reduce the user-agent by one A and adjust the test string by removing the first character and appending the next known one.

extreact_key.php
<?php

// Target URL
$target_url = "http://cryptofailures.thm/index.php";

// Possible characters
$charset = implode('', array_merge(
    range('a', 'z'),     // Lowercase letters
    range('A', 'Z'),     // Uppercase letters
    range('0', '9'),     // Digits
    str_split("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") // Special symbols
));


// Initial User-Agent
$user_agent = str_repeat("A", 256); // Adjusted length
$known_prefix = "guest:" . $user_agent . ":"; // Base structure
$test_string = substr($known_prefix, -8);

// Function to fetch secure_cookie with a specific User-Agent
function get_secure_cookie($user_agent) {
    global $target_url;

    $context = stream_context_create([
        "http" => [
            "method" => "GET",
            "header" =>
                "Host: cryptofailures.thm\r\n" .
                "User-Agent: $user_agent\r\n" .
                "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\n" .
                "Accept-Language: en-US,en;q=0.5\r\n" .
                "Accept-Encoding: gzip, deflate, br\r\n" .
                "Connection: close\r\n" .
                "Upgrade-Insecure-Requests: 1\r\n"
        ]
    ]);

    // Fetch response
    $response = file_get_contents($target_url, false, $context);

    // Extract "secure_cookie" from headers
    foreach ($http_response_header as $header) {
        if (stripos($header, "Set-Cookie: secure_cookie=") !== false) {
            preg_match('/secure_cookie=([^;]+)/', $header, $matches);
            
            if (!isset($matches[1])) {
                return null;
            }

            // URL-decode the cookie value before using it
            $decoded_cookie = urldecode($matches[1]);

            // Ensure no URL-encoded characters remain
            if (preg_match('/%[0-9A-Fa-f]{2}/', $decoded_cookie)) {
                die("❌ URL-encoded characters detected in secure_cookie!\n");
            }

            return $decoded_cookie;
        }
    }
    return null;
}


// Start brute-force process
$found_text = "";
while (true) {
    echo "\nCurrent known part: {$found_text}\n";

    // Get new secure_cookie for the current prefix
    $secure_cookie = get_secure_cookie($user_agent);
    $user_agent = substr($user_agent, 1);
    if (!$secure_cookie) {
        die("❌ Failed to retrieve secure_cookie!\n");
    }

    echo "✅ Retrieved (Decoded) secure_cookie: $secure_cookie\n";

    // Extract salt
    $salt = substr($secure_cookie, 0, 2);

    // Brute-force the next character
    $found_char = null;
    foreach (str_split($charset) as $char) {
        $test_string_temp = substr($test_string, 1) . $char;
	print($test_string_temp . "\n");
        
        $hashed_test = crypt($test_string_temp, $salt);
        if (str_contains($secure_cookie, $hashed_test)) {
            echo "✅ Found character: $char\n";
            $found_text .= $char;
            $test_string = $test_string_temp;
            echo $test_string . "\n";
            echo $hashed_test . "\n";
            break;
        }
    }
}

?>

After running our script, we get the key after a short time.

CC BY 4.0
Crypto FailuresTryHackMe
Logo