icoa-cli 2.19.357 → 2.19.359
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/dist/commands/ai4ctf.js +1 -1
- package/dist/commands/ctf.js +1 -787
- package/dist/commands/ctf4ai-demo.js +1 -1
- package/dist/commands/ctf4vla.js +1 -1
- package/dist/commands/demo2.js +1 -1502
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +1 -0
- package/dist/commands/exam.js +1 -1
- package/dist/commands/files.js +1 -59
- package/dist/commands/lang.js +1 -202
- package/dist/commands/log.js +1 -171
- package/dist/commands/shell.js +1 -151
- package/dist/commands/sim.js +1 -389
- package/dist/index.js +1 -355
- package/dist/lib/access.js +1 -184
- package/dist/lib/aienv.js +1 -205
- package/dist/lib/arena-submit.js +1 -21
- package/dist/lib/banner.js +1 -31
- package/dist/lib/budget.js +1 -6
- package/dist/lib/challenge-dir.js +1 -16
- package/dist/lib/colors.js +1 -17
- package/dist/lib/comms.js +1 -212
- package/dist/lib/config.js +1 -93
- package/dist/lib/countdown.js +1 -43
- package/dist/lib/country-lang.js +1 -39
- package/dist/lib/ctfd-client.js +1 -417
- package/dist/lib/demo-exam.js +1 -478
- package/dist/lib/demo-flags.js +1 -27
- package/dist/lib/demo-stats.js +1 -62
- package/dist/lib/demo2-progress.js +1 -102
- package/dist/lib/docker-probe.js +1 -118
- package/dist/lib/editor-spawn.js +1 -53
- package/dist/lib/exam-client.js +1 -54
- package/dist/lib/exam-sandbox.js +1 -201
- package/dist/lib/exam-setup.js +1 -36
- package/dist/lib/exam-state.js +1 -273
- package/dist/lib/gemini.js +1 -247
- package/dist/lib/i18n.js +1 -302
- package/dist/lib/integrity-snapshot.js +1 -88
- package/dist/lib/interactive-spawn.js +1 -55
- package/dist/lib/ipynb-input.js +1 -65
- package/dist/lib/kernel-protocol.js +1 -88
- package/dist/lib/kernel.js +2 -146
- package/dist/lib/learn-curricula.js +1 -309
- package/dist/lib/learn-i18n.js +1 -184
- package/dist/lib/learn-input.js +1 -101
- package/dist/lib/learn-render.js +1 -863
- package/dist/lib/learn-state.js +1 -103
- package/dist/lib/log-sync.js +1 -155
- package/dist/lib/logger.js +1 -49
- package/dist/lib/main-rl.js +1 -7
- package/dist/lib/menu-nav.js +1 -105
- package/dist/lib/notebook-doc.js +1 -137
- package/dist/lib/open-file.js +1 -55
- package/dist/lib/paper-upgrade.js +1 -119
- package/dist/lib/platform.js +1 -99
- package/dist/lib/render-card.js +1 -112
- package/dist/lib/repl-asker.js +1 -67
- package/dist/lib/sample-runner.js +1 -227
- package/dist/lib/sandbox.js +1 -144
- package/dist/lib/shell-split.js +1 -69
- package/dist/lib/sim-cooldown.js +1 -75
- package/dist/lib/theme.js +1 -119
- package/dist/lib/token-format.js +1 -74
- package/dist/lib/tool-man.js +1 -418
- package/dist/lib/toolset-hash.js +1 -48
- package/dist/lib/translation.js +1 -80
- package/dist/lib/translations-fetcher.js +1 -95
- package/dist/lib/ui.js +1 -99
- package/dist/lib/update-check.js +1 -114
- package/dist/lib/version.js +1 -24
- package/dist/postinstall.js +1 -48
- package/dist/repl.js +1 -2391
- package/dist/types/index.js +1 -63
- package/package.json +1 -1
package/dist/lib/tool-man.js
CHANGED
|
@@ -1,418 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* `man <tool>` — quick CTF usage cheatsheet for the frozen sandbox toolset.
|
|
3
|
-
*
|
|
4
|
-
* This is the *usage* counterpart to `env` (which only reports install status).
|
|
5
|
-
* Scope = the curated ~40 tools players actually use to SOLVE challenges, not
|
|
6
|
-
* all 110 (a `man ls` stub is noise). Every tool here is confirmed present in
|
|
7
|
-
* `src/commands/env.ts` SYSTEM_TOOLS / PYTHON_LIBS and the sandbox image.
|
|
8
|
-
*
|
|
9
|
-
* Bundled offline (tiny text, universal to every CTF player) — plane-1 OK,
|
|
10
|
-
* same class as i18n strings; NOT a dataset (Thin-Client three-plane rule).
|
|
11
|
-
*
|
|
12
|
-
* Black-bg colour rule: chalk only; cyan = tool/command, gray = note,
|
|
13
|
-
* bold.yellow = the "important" lifeline.
|
|
14
|
-
*/
|
|
15
|
-
import chalk from 'chalk';
|
|
16
|
-
// Ordered roughly by how often a CTF player reaches for them.
|
|
17
|
-
export const TOOL_DOCS = [
|
|
18
|
-
// ── Recon / first look ───────────────────────────────────────────
|
|
19
|
-
{
|
|
20
|
-
name: 'file',
|
|
21
|
-
cat: 'Forensics',
|
|
22
|
-
summary: 'identify a file type (always step 1)',
|
|
23
|
-
ex: [['file mystery', 'what is this really? (extension lies)']],
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
name: 'strings',
|
|
27
|
-
cat: 'Forensics',
|
|
28
|
-
summary: 'pull printable text out of any binary',
|
|
29
|
-
ex: [
|
|
30
|
-
['strings -n 8 bin', 'runs ≥8 chars (cuts noise)'],
|
|
31
|
-
['strings bin | grep -i flag', 'hunt the flag directly'],
|
|
32
|
-
['strings -e l bin', '16-bit little-endian (wide) strings'],
|
|
33
|
-
],
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
name: 'xxd',
|
|
37
|
-
cat: 'Data',
|
|
38
|
-
summary: 'hex dump / reverse hex back to bytes',
|
|
39
|
-
ex: [
|
|
40
|
-
['xxd file | head', 'inspect magic bytes / header'],
|
|
41
|
-
['xxd -r hex.txt out.bin', 'rebuild a file from a hex dump'],
|
|
42
|
-
['xxd -s 0x100 -l 64 file', 'dump 64 bytes from offset 0x100'],
|
|
43
|
-
],
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: 'hexdump',
|
|
47
|
-
aliases: ['od'],
|
|
48
|
-
cat: 'Data',
|
|
49
|
-
summary: 'byte/octal dump (od is the BSD cousin)',
|
|
50
|
-
ex: [
|
|
51
|
-
['hexdump -C file | head', 'canonical hex+ASCII view'],
|
|
52
|
-
['od -An -tx1 file', 'raw hex bytes, no offsets'],
|
|
53
|
-
],
|
|
54
|
-
},
|
|
55
|
-
// ── Stego / carving ──────────────────────────────────────────────
|
|
56
|
-
{
|
|
57
|
-
name: 'binwalk',
|
|
58
|
-
cat: 'Forensics',
|
|
59
|
-
summary: 'find + extract files embedded in files',
|
|
60
|
-
ex: [
|
|
61
|
-
['binwalk file', 'scan for embedded signatures'],
|
|
62
|
-
['binwalk -e file', 'auto-extract everything it finds'],
|
|
63
|
-
['binwalk --dd=".*" file', 'carve every match (greedy)'],
|
|
64
|
-
],
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
name: 'foremost',
|
|
68
|
-
cat: 'Forensics',
|
|
69
|
-
summary: 'carve files by header (recover deleted/hidden)',
|
|
70
|
-
ex: [['foremost -i disk.img -o out/', 'recover into out/ by type']],
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
name: 'steghide',
|
|
74
|
-
cat: 'Forensics',
|
|
75
|
-
summary: 'hide/extract data in JPG/BMP/WAV/AU (passphrase)',
|
|
76
|
-
ex: [
|
|
77
|
-
['steghide info cat.jpg', 'is anything embedded?'],
|
|
78
|
-
['steghide extract -sf cat.jpg', 'pull it out (asks passphrase; blank often works)'],
|
|
79
|
-
],
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
name: 'exiftool',
|
|
83
|
-
cat: 'Forensics',
|
|
84
|
-
summary: 'read/write image & doc metadata (flags hide here)',
|
|
85
|
-
ex: [
|
|
86
|
-
['exiftool pic.png', 'dump all metadata'],
|
|
87
|
-
['exiftool -Comment pic.png', 'one specific tag'],
|
|
88
|
-
],
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
name: 'pngcheck',
|
|
92
|
-
cat: 'Forensics',
|
|
93
|
-
summary: 'validate PNG chunks (find tampering / appended data)',
|
|
94
|
-
ex: [['pngcheck -v flag.png', 'verbose chunk-by-chunk check']],
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
name: 'pdftotext',
|
|
98
|
-
cat: 'Data',
|
|
99
|
-
summary: 'extract text (and hidden layers) from a PDF',
|
|
100
|
-
ex: [['pdftotext doc.pdf -', 'print extracted text to stdout']],
|
|
101
|
-
},
|
|
102
|
-
// ── Disk / memory forensics ──────────────────────────────────────
|
|
103
|
-
{
|
|
104
|
-
name: 'sleuthkit',
|
|
105
|
-
aliases: ['mmls', 'fls', 'icat', 'tsk'],
|
|
106
|
-
cat: 'Forensics',
|
|
107
|
-
summary: 'disk-image forensics (partition → files → recover)',
|
|
108
|
-
ex: [
|
|
109
|
-
['mmls disk.img', 'list partitions + offsets'],
|
|
110
|
-
['fls -o 2048 -r disk.img', 'list files (use the partition offset)'],
|
|
111
|
-
['icat -o 2048 disk.img 12 > out', 'dump file by inode number'],
|
|
112
|
-
],
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
name: 'volatility3',
|
|
116
|
-
aliases: ['vol', 'volatility'],
|
|
117
|
-
cat: 'Forensics',
|
|
118
|
-
summary: 'memory-dump analysis',
|
|
119
|
-
ex: [
|
|
120
|
-
['vol -f mem.raw windows.pslist', 'list processes'],
|
|
121
|
-
['vol -f mem.raw windows.cmdline', 'recover command lines'],
|
|
122
|
-
],
|
|
123
|
-
},
|
|
124
|
-
// ── Crypto / passwords ───────────────────────────────────────────
|
|
125
|
-
{
|
|
126
|
-
name: 'openssl',
|
|
127
|
-
cat: 'Crypto',
|
|
128
|
-
summary: 'swiss-army crypto (decode, hash, enc/dec, certs)',
|
|
129
|
-
ex: [
|
|
130
|
-
['openssl enc -d -aes-256-cbc -in f -k pass', 'decrypt AES'],
|
|
131
|
-
['openssl x509 -in cert.pem -text -noout', 'read a certificate'],
|
|
132
|
-
['echo -n txt | openssl dgst -sha256', 'hash it'],
|
|
133
|
-
],
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
name: 'john',
|
|
137
|
-
aliases: ['john-the-ripper'],
|
|
138
|
-
cat: 'Crypto',
|
|
139
|
-
summary: 'crack password hashes (CPU)',
|
|
140
|
-
ex: [
|
|
141
|
-
['john --wordlist=rockyou.txt hashes', 'dictionary attack'],
|
|
142
|
-
['zip2john x.zip > h ; john h', 'crack a zip password'],
|
|
143
|
-
['john --show hashes', 'print cracked results'],
|
|
144
|
-
],
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
name: 'hashcat',
|
|
148
|
-
cat: 'Crypto',
|
|
149
|
-
summary: 'fast hash cracker (modes by -m)',
|
|
150
|
-
ex: [['hashcat -m 0 -a 0 hash rockyou.txt', 'MD5 dictionary (-m 0 = MD5)']],
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
name: 'gpg',
|
|
154
|
-
cat: 'Crypto',
|
|
155
|
-
summary: 'PGP decrypt / symmetric decrypt',
|
|
156
|
-
ex: [['gpg -d secret.gpg', 'decrypt (prompts for passphrase/key)']],
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
name: 'base64',
|
|
160
|
-
aliases: ['base32'],
|
|
161
|
-
cat: 'Data',
|
|
162
|
-
summary: 'decode/encode base64 (base32 alike)',
|
|
163
|
-
ex: [
|
|
164
|
-
['echo -n SGk= | base64 -d', 'decode'],
|
|
165
|
-
['base64 -d f.txt', 'decode a file blob'],
|
|
166
|
-
],
|
|
167
|
-
},
|
|
168
|
-
// ── Network ──────────────────────────────────────────────────────
|
|
169
|
-
{
|
|
170
|
-
name: 'nmap',
|
|
171
|
-
cat: 'Networking',
|
|
172
|
-
summary: 'port/service scan a target host',
|
|
173
|
-
ex: [
|
|
174
|
-
['nmap -sV -p- host', 'all ports + version detection'],
|
|
175
|
-
['nmap -sC -p 80,443 host', 'default scripts on web ports'],
|
|
176
|
-
],
|
|
177
|
-
},
|
|
178
|
-
{
|
|
179
|
-
name: 'nc',
|
|
180
|
-
aliases: ['netcat', 'ncat'],
|
|
181
|
-
cat: 'Networking',
|
|
182
|
-
summary: 'raw TCP/UDP connect (talk to a service)',
|
|
183
|
-
ex: [
|
|
184
|
-
['nc host 1337', 'connect to a challenge service'],
|
|
185
|
-
['nc -lvnp 4444', 'listen (catch a callback)'],
|
|
186
|
-
],
|
|
187
|
-
},
|
|
188
|
-
{
|
|
189
|
-
name: 'socat',
|
|
190
|
-
cat: 'Networking',
|
|
191
|
-
summary: 'flexible socket relay (TLS, pty, bridging)',
|
|
192
|
-
ex: [['socat - OPENSSL:host:443', 'connect over TLS interactively']],
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
name: 'curl',
|
|
196
|
-
cat: 'Web',
|
|
197
|
-
summary: 'HTTP client — the web-CTF workhorse',
|
|
198
|
-
ex: [
|
|
199
|
-
['curl -i url', 'show response headers'],
|
|
200
|
-
['curl -b "session=..." url', 'send a cookie'],
|
|
201
|
-
['curl -X POST -d "a=1" url', 'POST form data'],
|
|
202
|
-
],
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
name: 'wget',
|
|
206
|
-
cat: 'Web',
|
|
207
|
-
summary: 'download files / mirror a site',
|
|
208
|
-
ex: [['wget -r url', 'recursive download']],
|
|
209
|
-
},
|
|
210
|
-
{
|
|
211
|
-
name: 'tshark',
|
|
212
|
-
aliases: ['wireshark-cli'],
|
|
213
|
-
cat: 'Networking',
|
|
214
|
-
summary: 'CLI Wireshark — read/filter pcaps',
|
|
215
|
-
ex: [
|
|
216
|
-
['tshark -r cap.pcap -Y http', 'show only HTTP packets'],
|
|
217
|
-
['tshark -r cap.pcap -T fields -e data', 'dump a field across packets'],
|
|
218
|
-
],
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
name: 'tcpdump',
|
|
222
|
-
cat: 'Networking',
|
|
223
|
-
summary: 'capture/read packets (pcap)',
|
|
224
|
-
ex: [['tcpdump -r cap.pcap -A', 'print packets as ASCII']],
|
|
225
|
-
},
|
|
226
|
-
{
|
|
227
|
-
name: 'dig',
|
|
228
|
-
aliases: ['whois'],
|
|
229
|
-
cat: 'Networking',
|
|
230
|
-
summary: 'DNS lookups (whois = registration data)',
|
|
231
|
-
ex: [
|
|
232
|
-
['dig TXT domain', 'read TXT records (flags hide here)'],
|
|
233
|
-
['whois domain', 'ownership info'],
|
|
234
|
-
],
|
|
235
|
-
},
|
|
236
|
-
{
|
|
237
|
-
name: 'sqlmap',
|
|
238
|
-
cat: 'Web',
|
|
239
|
-
summary: 'automated SQL-injection exploitation',
|
|
240
|
-
ex: [
|
|
241
|
-
['sqlmap -u "url?id=1" --dbs', 'enumerate databases'],
|
|
242
|
-
['sqlmap -u url --dump -T users', 'dump a table'],
|
|
243
|
-
],
|
|
244
|
-
},
|
|
245
|
-
// ── Reverse engineering / binary ─────────────────────────────────
|
|
246
|
-
{
|
|
247
|
-
name: 'radare2',
|
|
248
|
-
aliases: ['r2'],
|
|
249
|
-
cat: 'Reverse Engineering',
|
|
250
|
-
summary: 'interactive RE framework (disasm/debug)',
|
|
251
|
-
ex: [
|
|
252
|
-
['r2 -A bin', 'open + auto-analyze'],
|
|
253
|
-
['(in r2) afl ; pdf @ main', 'list funcs; disasm main'],
|
|
254
|
-
],
|
|
255
|
-
},
|
|
256
|
-
{
|
|
257
|
-
name: 'rabin2',
|
|
258
|
-
cat: 'Reverse Engineering',
|
|
259
|
-
summary: 'binary info (radare2 family)',
|
|
260
|
-
ex: [
|
|
261
|
-
['rabin2 -z bin', 'list strings + offsets'],
|
|
262
|
-
['rabin2 -I bin', 'arch/bits/protections'],
|
|
263
|
-
],
|
|
264
|
-
},
|
|
265
|
-
{
|
|
266
|
-
name: 'objdump',
|
|
267
|
-
cat: 'Reverse Engineering',
|
|
268
|
-
summary: 'disassemble / inspect object files',
|
|
269
|
-
ex: [['objdump -d -M intel bin', 'Intel-syntax disassembly']],
|
|
270
|
-
},
|
|
271
|
-
{
|
|
272
|
-
name: 'readelf',
|
|
273
|
-
cat: 'Reverse Engineering',
|
|
274
|
-
summary: 'ELF header / sections / symbols',
|
|
275
|
-
ex: [
|
|
276
|
-
['readelf -a bin', 'everything'],
|
|
277
|
-
['readelf -d bin', 'dynamic section / RPATH'],
|
|
278
|
-
],
|
|
279
|
-
},
|
|
280
|
-
{
|
|
281
|
-
name: 'gdb',
|
|
282
|
-
aliases: ['pwndbg'],
|
|
283
|
-
cat: 'Debuggers',
|
|
284
|
-
summary: 'debugger (pwndbg plugin preloaded for pwn)',
|
|
285
|
-
ex: [
|
|
286
|
-
['gdb ./bin', 'start; then: break main / run / info registers'],
|
|
287
|
-
['(pwndbg) checksec', 'show binary mitigations'],
|
|
288
|
-
['(pwndbg) cyclic 200', 'gen pattern to find offset'],
|
|
289
|
-
],
|
|
290
|
-
},
|
|
291
|
-
{
|
|
292
|
-
name: 'ltrace',
|
|
293
|
-
aliases: ['strace'],
|
|
294
|
-
cat: 'Debuggers',
|
|
295
|
-
summary: 'trace library (ltrace) / syscalls (strace)',
|
|
296
|
-
ex: [
|
|
297
|
-
['ltrace ./bin', 'watch libc calls (e.g. strcmp the flag)'],
|
|
298
|
-
['strace ./bin', 'watch syscalls'],
|
|
299
|
-
],
|
|
300
|
-
},
|
|
301
|
-
{
|
|
302
|
-
name: 'upx',
|
|
303
|
-
cat: 'Reverse Engineering',
|
|
304
|
-
summary: 'un/pack executables',
|
|
305
|
-
ex: [['upx -d packed', 'unpack before analyzing']],
|
|
306
|
-
},
|
|
307
|
-
// ── Pwn ──────────────────────────────────────────────────────────
|
|
308
|
-
{
|
|
309
|
-
name: 'pwntools',
|
|
310
|
-
aliases: ['pwn'],
|
|
311
|
-
cat: 'CTF Core',
|
|
312
|
-
summary: 'Python exploit framework (+ pwn CLI)',
|
|
313
|
-
ex: [
|
|
314
|
-
['pwn checksec ./bin', 'show mitigations'],
|
|
315
|
-
['pwn cyclic 100 / pwn cyclic -l 0x6161', 'pattern + find offset'],
|
|
316
|
-
['python3: from pwn import *; p=process("./bin")', 'scripting entrypoint'],
|
|
317
|
-
],
|
|
318
|
-
},
|
|
319
|
-
{
|
|
320
|
-
name: 'ROPgadget',
|
|
321
|
-
aliases: ['ropper'],
|
|
322
|
-
cat: 'Binary & RE',
|
|
323
|
-
summary: 'find ROP gadgets in a binary',
|
|
324
|
-
ex: [
|
|
325
|
-
['ROPgadget --binary bin | grep "pop rdi"', 'find a gadget'],
|
|
326
|
-
['ropper -f bin --search "pop rdi"', 'ropper equivalent'],
|
|
327
|
-
],
|
|
328
|
-
},
|
|
329
|
-
// ── Data wrangling ───────────────────────────────────────────────
|
|
330
|
-
{
|
|
331
|
-
name: 'jq',
|
|
332
|
-
cat: 'Data',
|
|
333
|
-
summary: 'slice/query JSON',
|
|
334
|
-
ex: [
|
|
335
|
-
['curl url | jq .', 'pretty-print'],
|
|
336
|
-
['jq -r ".data[].flag" f.json', 'extract a field'],
|
|
337
|
-
],
|
|
338
|
-
},
|
|
339
|
-
{
|
|
340
|
-
name: 'sqlite3',
|
|
341
|
-
cat: 'Data',
|
|
342
|
-
summary: 'open/query a SQLite DB file',
|
|
343
|
-
ex: [
|
|
344
|
-
['sqlite3 app.db ".tables"', 'list tables'],
|
|
345
|
-
['sqlite3 app.db "select * from users"', 'query'],
|
|
346
|
-
],
|
|
347
|
-
},
|
|
348
|
-
];
|
|
349
|
-
// alias / name → doc
|
|
350
|
-
const INDEX = new Map();
|
|
351
|
-
for (const d of TOOL_DOCS) {
|
|
352
|
-
INDEX.set(d.name.toLowerCase(), d);
|
|
353
|
-
for (const a of d.aliases ?? [])
|
|
354
|
-
INDEX.set(a.toLowerCase(), d);
|
|
355
|
-
}
|
|
356
|
-
function printOne(d) {
|
|
357
|
-
const alias = d.aliases?.length ? chalk.gray(' (' + d.aliases.join(', ') + ')') : '';
|
|
358
|
-
console.log();
|
|
359
|
-
console.log(' ' + chalk.bold.cyan(d.name) + alias + chalk.gray(' · ' + d.cat));
|
|
360
|
-
console.log(' ' + chalk.white(d.summary));
|
|
361
|
-
console.log();
|
|
362
|
-
for (const [cmd, note] of d.ex) {
|
|
363
|
-
console.log(' ' + chalk.cyan(cmd));
|
|
364
|
-
console.log(' ' + chalk.gray(note));
|
|
365
|
-
}
|
|
366
|
-
console.log();
|
|
367
|
-
console.log(chalk.gray(' More: ') + chalk.cyan('man tools') + chalk.gray(' · not a real judge — verify your own output.'));
|
|
368
|
-
console.log();
|
|
369
|
-
}
|
|
370
|
-
function printIndex() {
|
|
371
|
-
const byCat = new Map();
|
|
372
|
-
for (const d of TOOL_DOCS) {
|
|
373
|
-
const k = d.cat;
|
|
374
|
-
if (!byCat.has(k))
|
|
375
|
-
byCat.set(k, []);
|
|
376
|
-
byCat.get(k).push(d);
|
|
377
|
-
}
|
|
378
|
-
console.log();
|
|
379
|
-
console.log(chalk.bold.yellow(' ★ Tool cheatsheet') +
|
|
380
|
-
chalk.gray(' — type ') +
|
|
381
|
-
chalk.cyan('man <tool>') +
|
|
382
|
-
chalk.gray(' for usage, e.g. ') +
|
|
383
|
-
chalk.cyan('man steghide'));
|
|
384
|
-
console.log();
|
|
385
|
-
for (const [cat, list] of byCat) {
|
|
386
|
-
console.log(' ' + chalk.bold.white(cat));
|
|
387
|
-
console.log(' ' + chalk.cyan(list.map((d) => d.name).join(' ')));
|
|
388
|
-
}
|
|
389
|
-
console.log();
|
|
390
|
-
console.log(chalk.gray(' These are the frozen sandbox tools — no pip/apt install. ') +
|
|
391
|
-
chalk.cyan('env') +
|
|
392
|
-
chalk.gray(' shows what is installed.'));
|
|
393
|
-
console.log();
|
|
394
|
-
}
|
|
395
|
-
/** Render `man <query>`: empty/"tools"/"list" → index; a tool → its sheet; else nearest hints. */
|
|
396
|
-
export function renderToolMan(query) {
|
|
397
|
-
const q = (query || '').trim().toLowerCase();
|
|
398
|
-
if (!q || q === 'tools' || q === 'list' || q === 'all') {
|
|
399
|
-
printIndex();
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
const hit = INDEX.get(q);
|
|
403
|
-
if (hit) {
|
|
404
|
-
printOne(hit);
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
// fuzzy: prefix then substring on names+aliases
|
|
408
|
-
const keys = [...INDEX.keys()];
|
|
409
|
-
const near = keys.filter((k) => k.startsWith(q)).concat(keys.filter((k) => k.includes(q) && !k.startsWith(q)));
|
|
410
|
-
const uniq = [...new Set(near.map((k) => INDEX.get(k).name))].slice(0, 5);
|
|
411
|
-
console.log();
|
|
412
|
-
console.log(chalk.yellow(` No cheatsheet for "${query}".`));
|
|
413
|
-
if (uniq.length) {
|
|
414
|
-
console.log(chalk.gray(' Did you mean: ') + chalk.cyan(uniq.join(' ')) + '?');
|
|
415
|
-
}
|
|
416
|
-
console.log(chalk.gray(' Type ') + chalk.cyan('man tools') + chalk.gray(' for the full list.'));
|
|
417
|
-
console.log();
|
|
418
|
-
}
|
|
1
|
+
import chalk from"chalk";export const TOOL_DOCS=[{name:"file",cat:"Forensics",summary:"identify a file type (always step 1)",ex:[["file mystery","what is this really? (extension lies)"]]},{name:"strings",cat:"Forensics",summary:"pull printable text out of any binary",ex:[["strings -n 8 bin","runs ≥8 chars (cuts noise)"],["strings bin | grep -i flag","hunt the flag directly"],["strings -e l bin","16-bit little-endian (wide) strings"]]},{name:"xxd",cat:"Data",summary:"hex dump / reverse hex back to bytes",ex:[["xxd file | head","inspect magic bytes / header"],["xxd -r hex.txt out.bin","rebuild a file from a hex dump"],["xxd -s 0x100 -l 64 file","dump 64 bytes from offset 0x100"]]},{name:"hexdump",aliases:["od"],cat:"Data",summary:"byte/octal dump (od is the BSD cousin)",ex:[["hexdump -C file | head","canonical hex+ASCII view"],["od -An -tx1 file","raw hex bytes, no offsets"]]},{name:"binwalk",cat:"Forensics",summary:"find + extract files embedded in files",ex:[["binwalk file","scan for embedded signatures"],["binwalk -e file","auto-extract everything it finds"],['binwalk --dd=".*" file',"carve every match (greedy)"]]},{name:"foremost",cat:"Forensics",summary:"carve files by header (recover deleted/hidden)",ex:[["foremost -i disk.img -o out/","recover into out/ by type"]]},{name:"steghide",cat:"Forensics",summary:"hide/extract data in JPG/BMP/WAV/AU (passphrase)",ex:[["steghide info cat.jpg","is anything embedded?"],["steghide extract -sf cat.jpg","pull it out (asks passphrase; blank often works)"]]},{name:"exiftool",cat:"Forensics",summary:"read/write image & doc metadata (flags hide here)",ex:[["exiftool pic.png","dump all metadata"],["exiftool -Comment pic.png","one specific tag"]]},{name:"pngcheck",cat:"Forensics",summary:"validate PNG chunks (find tampering / appended data)",ex:[["pngcheck -v flag.png","verbose chunk-by-chunk check"]]},{name:"pdftotext",cat:"Data",summary:"extract text (and hidden layers) from a PDF",ex:[["pdftotext doc.pdf -","print extracted text to stdout"]]},{name:"sleuthkit",aliases:["mmls","fls","icat","tsk"],cat:"Forensics",summary:"disk-image forensics (partition → files → recover)",ex:[["mmls disk.img","list partitions + offsets"],["fls -o 2048 -r disk.img","list files (use the partition offset)"],["icat -o 2048 disk.img 12 > out","dump file by inode number"]]},{name:"volatility3",aliases:["vol","volatility"],cat:"Forensics",summary:"memory-dump analysis",ex:[["vol -f mem.raw windows.pslist","list processes"],["vol -f mem.raw windows.cmdline","recover command lines"]]},{name:"openssl",cat:"Crypto",summary:"swiss-army crypto (decode, hash, enc/dec, certs)",ex:[["openssl enc -d -aes-256-cbc -in f -k pass","decrypt AES"],["openssl x509 -in cert.pem -text -noout","read a certificate"],["echo -n txt | openssl dgst -sha256","hash it"]]},{name:"john",aliases:["john-the-ripper"],cat:"Crypto",summary:"crack password hashes (CPU)",ex:[["john --wordlist=rockyou.txt hashes","dictionary attack"],["zip2john x.zip > h ; john h","crack a zip password"],["john --show hashes","print cracked results"]]},{name:"hashcat",cat:"Crypto",summary:"fast hash cracker (modes by -m)",ex:[["hashcat -m 0 -a 0 hash rockyou.txt","MD5 dictionary (-m 0 = MD5)"]]},{name:"gpg",cat:"Crypto",summary:"PGP decrypt / symmetric decrypt",ex:[["gpg -d secret.gpg","decrypt (prompts for passphrase/key)"]]},{name:"base64",aliases:["base32"],cat:"Data",summary:"decode/encode base64 (base32 alike)",ex:[["echo -n SGk= | base64 -d","decode"],["base64 -d f.txt","decode a file blob"]]},{name:"nmap",cat:"Networking",summary:"port/service scan a target host",ex:[["nmap -sV -p- host","all ports + version detection"],["nmap -sC -p 80,443 host","default scripts on web ports"]]},{name:"nc",aliases:["netcat","ncat"],cat:"Networking",summary:"raw TCP/UDP connect (talk to a service)",ex:[["nc host 1337","connect to a challenge service"],["nc -lvnp 4444","listen (catch a callback)"]]},{name:"socat",cat:"Networking",summary:"flexible socket relay (TLS, pty, bridging)",ex:[["socat - OPENSSL:host:443","connect over TLS interactively"]]},{name:"curl",cat:"Web",summary:"HTTP client — the web-CTF workhorse",ex:[["curl -i url","show response headers"],['curl -b "session=..." url',"send a cookie"],['curl -X POST -d "a=1" url',"POST form data"]]},{name:"wget",cat:"Web",summary:"download files / mirror a site",ex:[["wget -r url","recursive download"]]},{name:"tshark",aliases:["wireshark-cli"],cat:"Networking",summary:"CLI Wireshark — read/filter pcaps",ex:[["tshark -r cap.pcap -Y http","show only HTTP packets"],["tshark -r cap.pcap -T fields -e data","dump a field across packets"]]},{name:"tcpdump",cat:"Networking",summary:"capture/read packets (pcap)",ex:[["tcpdump -r cap.pcap -A","print packets as ASCII"]]},{name:"dig",aliases:["whois"],cat:"Networking",summary:"DNS lookups (whois = registration data)",ex:[["dig TXT domain","read TXT records (flags hide here)"],["whois domain","ownership info"]]},{name:"sqlmap",cat:"Web",summary:"automated SQL-injection exploitation",ex:[['sqlmap -u "url?id=1" --dbs',"enumerate databases"],["sqlmap -u url --dump -T users","dump a table"]]},{name:"radare2",aliases:["r2"],cat:"Reverse Engineering",summary:"interactive RE framework (disasm/debug)",ex:[["r2 -A bin","open + auto-analyze"],["(in r2) afl ; pdf @ main","list funcs; disasm main"]]},{name:"rabin2",cat:"Reverse Engineering",summary:"binary info (radare2 family)",ex:[["rabin2 -z bin","list strings + offsets"],["rabin2 -I bin","arch/bits/protections"]]},{name:"objdump",cat:"Reverse Engineering",summary:"disassemble / inspect object files",ex:[["objdump -d -M intel bin","Intel-syntax disassembly"]]},{name:"readelf",cat:"Reverse Engineering",summary:"ELF header / sections / symbols",ex:[["readelf -a bin","everything"],["readelf -d bin","dynamic section / RPATH"]]},{name:"gdb",aliases:["pwndbg"],cat:"Debuggers",summary:"debugger (pwndbg plugin preloaded for pwn)",ex:[["gdb ./bin","start; then: break main / run / info registers"],["(pwndbg) checksec","show binary mitigations"],["(pwndbg) cyclic 200","gen pattern to find offset"]]},{name:"ltrace",aliases:["strace"],cat:"Debuggers",summary:"trace library (ltrace) / syscalls (strace)",ex:[["ltrace ./bin","watch libc calls (e.g. strcmp the flag)"],["strace ./bin","watch syscalls"]]},{name:"upx",cat:"Reverse Engineering",summary:"un/pack executables",ex:[["upx -d packed","unpack before analyzing"]]},{name:"pwntools",aliases:["pwn"],cat:"CTF Core",summary:"Python exploit framework (+ pwn CLI)",ex:[["pwn checksec ./bin","show mitigations"],["pwn cyclic 100 / pwn cyclic -l 0x6161","pattern + find offset"],['python3: from pwn import *; p=process("./bin")',"scripting entrypoint"]]},{name:"ROPgadget",aliases:["ropper"],cat:"Binary & RE",summary:"find ROP gadgets in a binary",ex:[['ROPgadget --binary bin | grep "pop rdi"',"find a gadget"],['ropper -f bin --search "pop rdi"',"ropper equivalent"]]},{name:"jq",cat:"Data",summary:"slice/query JSON",ex:[["curl url | jq .","pretty-print"],['jq -r ".data[].flag" f.json',"extract a field"]]},{name:"sqlite3",cat:"Data",summary:"open/query a SQLite DB file",ex:[['sqlite3 app.db ".tables"',"list tables"],['sqlite3 app.db "select * from users"',"query"]]}];const e=new Map;for(const a of TOOL_DOCS){e.set(a.name.toLowerCase(),a);for(const s of a.aliases??[])e.set(s.toLowerCase(),a)}export function renderToolMan(a){const s=(a||"").trim().toLowerCase();if(!s||"tools"===s||"list"===s||"all"===s)return void function(){const e=new Map;for(const a of TOOL_DOCS){const s=a.cat;e.has(s)||e.set(s,[]),e.get(s).push(a)}console.log(),console.log(chalk.bold.yellow(" ★ Tool cheatsheet")+chalk.gray(" — type ")+chalk.cyan("man <tool>")+chalk.gray(" for usage, e.g. ")+chalk.cyan("man steghide")),console.log();for(const[a,s]of e)console.log(" "+chalk.bold.white(a)),console.log(" "+chalk.cyan(s.map(e=>e.name).join(" ")));console.log(),console.log(chalk.gray(" These are the frozen sandbox tools — no pip/apt install. ")+chalk.cyan("env")+chalk.gray(" shows what is installed.")),console.log()}();const t=e.get(s);if(t)return void function(e){const a=e.aliases?.length?chalk.gray(" ("+e.aliases.join(", ")+")"):"";console.log(),console.log(" "+chalk.bold.cyan(e.name)+a+chalk.gray(" · "+e.cat)),console.log(" "+chalk.white(e.summary)),console.log();for(const[a,s]of e.ex)console.log(" "+chalk.cyan(a)),console.log(" "+chalk.gray(s));console.log(),console.log(chalk.gray(" More: ")+chalk.cyan("man tools")+chalk.gray(" · not a real judge — verify your own output.")),console.log()}(t);const n=[...e.keys()],o=n.filter(e=>e.startsWith(s)).concat(n.filter(e=>e.includes(s)&&!e.startsWith(s))),r=[...new Set(o.map(a=>e.get(a).name))].slice(0,5);console.log(),console.log(chalk.yellow(` No cheatsheet for "${a}".`)),r.length&&console.log(chalk.gray(" Did you mean: ")+chalk.cyan(r.join(" "))+"?"),console.log(chalk.gray(" Type ")+chalk.cyan("man tools")+chalk.gray(" for the full list.")),console.log()}
|
package/dist/lib/toolset-hash.js
CHANGED
|
@@ -1,48 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { platform } from 'node:os';
|
|
5
|
-
import { getIcoaDir } from './config.js';
|
|
6
|
-
// T3 — environment fingerprint (FORENSIC, not a gate).
|
|
7
|
-
//
|
|
8
|
-
// Hashes the {name=version} of the sanctioned toolset detected on this host.
|
|
9
|
-
// Reported at exam start and stored server-side so a reviewer can compare
|
|
10
|
-
// environments across students (and spot a session whose env fingerprint
|
|
11
|
-
// changed). Honest limits: this proves which sanctioned tools/versions are
|
|
12
|
-
// PRESENT — it does NOT prove the ABSENCE of forbidden tools, and the expected
|
|
13
|
-
// value differs per platform (mac/ubuntu/WSL ship different versions). So it is
|
|
14
|
-
// a comparison fingerprint, never a pass/fail check. See icoa2026event/07.
|
|
15
|
-
const CACHE = () => join(getIcoaDir(), 'toolset-hash.json');
|
|
16
|
-
/** Compute the hash over (name=version) pairs, persist it, return the hash.
|
|
17
|
-
* Called once at the end of `env setup` so the exam-start path only READS. */
|
|
18
|
-
export function computeAndCacheToolsetHash(entries) {
|
|
19
|
-
const norm = entries
|
|
20
|
-
.map((e) => `${e.name}=${(e.version || '').trim()}`)
|
|
21
|
-
.sort()
|
|
22
|
-
.join('\n');
|
|
23
|
-
const hash = createHash('sha256').update(norm).digest('hex').slice(0, 16);
|
|
24
|
-
const rec = {
|
|
25
|
-
hash,
|
|
26
|
-
platform: platform(),
|
|
27
|
-
count: entries.length,
|
|
28
|
-
computedAt: new Date().toISOString(),
|
|
29
|
-
};
|
|
30
|
-
try {
|
|
31
|
-
writeFileSync(CACHE(), JSON.stringify(rec));
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
/* best-effort — a missing cache just means no env fingerprint is reported */
|
|
35
|
-
}
|
|
36
|
-
return hash;
|
|
37
|
-
}
|
|
38
|
-
/** Read the cached env-fingerprint hash. Returns undefined if `env setup` has
|
|
39
|
-
* not run (so the exam start path never blocks or spawns 30 version probes). */
|
|
40
|
-
export function getCachedToolsetHash() {
|
|
41
|
-
try {
|
|
42
|
-
const rec = JSON.parse(readFileSync(CACHE(), 'utf-8'));
|
|
43
|
-
return typeof rec.hash === 'string' && rec.hash ? rec.hash : undefined;
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
1
|
+
import{createHash as t}from"node:crypto";import{readFileSync as o,writeFileSync as r}from"node:fs";import{join as e}from"node:path";import{platform as n}from"node:os";import{getIcoaDir as s}from"./config.js";const h=()=>e(s(),"toolset-hash.json");export function computeAndCacheToolsetHash(o){const e=o.map(t=>`${t.name}=${(t.version||"").trim()}`).sort().join("\n"),s=t("sha256").update(e).digest("hex").slice(0,16),a={hash:s,platform:n(),count:o.length,computedAt:(new Date).toISOString()};try{r(h(),JSON.stringify(a))}catch{}return s}export function getCachedToolsetHash(){try{const t=JSON.parse(o(h(),"utf-8"));return"string"==typeof t.hash&&t.hash?t.hash:void 0}catch{return}}
|
package/dist/lib/translation.js
CHANGED
|
@@ -1,80 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { join, dirname } from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { getIcoaDir } from './config.js';
|
|
5
|
-
import { translateText } from './gemini.js';
|
|
6
|
-
import { ensureLangCache } from './translations-fetcher.js';
|
|
7
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
// Bundled translations shipped with the package (translations/<lang>/<id>.json)
|
|
9
|
-
function getBundledPath(lang, challengeId) {
|
|
10
|
-
return join(__dirname, '..', '..', 'translations', lang, `${challengeId}.json`);
|
|
11
|
-
}
|
|
12
|
-
// User-local cache (~/.icoa/translations/)
|
|
13
|
-
function getCachePath(lang, challengeId) {
|
|
14
|
-
const dir = join(getIcoaDir(), 'translations', lang);
|
|
15
|
-
mkdirSync(dir, { recursive: true });
|
|
16
|
-
return join(dir, `${challengeId}.json`);
|
|
17
|
-
}
|
|
18
|
-
export async function getTranslation(text, challengeId, targetLang) {
|
|
19
|
-
if (targetLang === 'en')
|
|
20
|
-
return text;
|
|
21
|
-
// 1. Check bundled static translations — only present in dev (npm-link) trees;
|
|
22
|
-
// v2.19.197+ no longer ships translations/ in the npm package.
|
|
23
|
-
const bundledPath = getBundledPath(targetLang, challengeId);
|
|
24
|
-
if (existsSync(bundledPath)) {
|
|
25
|
-
try {
|
|
26
|
-
const bundled = JSON.parse(readFileSync(bundledPath, 'utf-8'));
|
|
27
|
-
if (bundled.translation)
|
|
28
|
-
return bundled.translation;
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
/* corrupt, fall through */
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
// 2. Safety net: if this language hasn't been fetched yet (e.g. lang was set
|
|
35
|
-
// by country auto-detect rather than the `lang` command), pull the pack
|
|
36
|
-
// once. No-op when already cached.
|
|
37
|
-
await ensureLangCache(targetLang, { silent: false });
|
|
38
|
-
// 3. Check user-local cache (populated by step 2 or earlier `lang` calls)
|
|
39
|
-
const cachePath = getCachePath(targetLang, challengeId);
|
|
40
|
-
if (existsSync(cachePath)) {
|
|
41
|
-
try {
|
|
42
|
-
const cached = JSON.parse(readFileSync(cachePath, 'utf-8'));
|
|
43
|
-
if (cached.translation)
|
|
44
|
-
return cached.translation;
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
/* corrupt, fall through */
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
// 4. Translate via Gemini API (last resort — covers untranslated tail content)
|
|
51
|
-
const translation = await translateText(text, getLangName(targetLang));
|
|
52
|
-
writeFileSync(cachePath, JSON.stringify({ original: text, translation, timestamp: new Date().toISOString() }));
|
|
53
|
-
return translation;
|
|
54
|
-
}
|
|
55
|
-
function getLangName(code) {
|
|
56
|
-
const names = {
|
|
57
|
-
en: 'English',
|
|
58
|
-
zh: 'Chinese (Simplified)',
|
|
59
|
-
ja: 'Japanese',
|
|
60
|
-
ko: 'Korean',
|
|
61
|
-
es: 'Spanish',
|
|
62
|
-
ar: 'Arabic',
|
|
63
|
-
fr: 'French',
|
|
64
|
-
pt: 'Portuguese (Brazilian)',
|
|
65
|
-
ru: 'Russian',
|
|
66
|
-
hi: 'Hindi',
|
|
67
|
-
de: 'German',
|
|
68
|
-
id: 'Indonesian',
|
|
69
|
-
th: 'Thai',
|
|
70
|
-
vi: 'Vietnamese',
|
|
71
|
-
tr: 'Turkish',
|
|
72
|
-
uk: 'Ukrainian',
|
|
73
|
-
ht: 'Haitian Creole',
|
|
74
|
-
bn: 'Bengali',
|
|
75
|
-
kk: 'Kazakh',
|
|
76
|
-
uz: 'Uzbek',
|
|
77
|
-
lo: 'Lao',
|
|
78
|
-
};
|
|
79
|
-
return names[code] || 'English';
|
|
80
|
-
}
|
|
1
|
+
import{existsSync as n,mkdirSync as t,readFileSync as i,writeFileSync as r}from"node:fs";import{join as a,dirname as o}from"node:path";import{fileURLToPath as e}from"node:url";import{getIcoaDir as s}from"./config.js";import{translateText as f}from"./gemini.js";import{ensureLangCache as u}from"./translations-fetcher.js";const c=o(e(import.meta.url));export async function getTranslation(o,e,l){if("en"===l)return o;const m=function(n,t){return a(c,"..","..","translations",n,`${t}.json`)}(l,e);if(n(m))try{const n=JSON.parse(i(m,"utf-8"));if(n.translation)return n.translation}catch{}await u(l,{silent:!1});const h=function(n,i){const r=a(s(),"translations",n);return t(r,{recursive:!0}),a(r,`${i}.json`)}(l,e);if(n(h))try{const n=JSON.parse(i(h,"utf-8"));if(n.translation)return n.translation}catch{}const p=await f(o,(g=l,{en:"English",zh:"Chinese (Simplified)",ja:"Japanese",ko:"Korean",es:"Spanish",ar:"Arabic",fr:"French",pt:"Portuguese (Brazilian)",ru:"Russian",hi:"Hindi",de:"German",id:"Indonesian",th:"Thai",vi:"Vietnamese",tr:"Turkish",uk:"Ukrainian",ht:"Haitian Creole",bn:"Bengali",kk:"Kazakh",uz:"Uzbek",lo:"Lao"}[g]||"English"));var g;return r(h,JSON.stringify({original:o,translation:p,timestamp:(new Date).toISOString()})),p}
|