8000 209 anonymize ncrack passwords · secureCodeBox/secureCodeBox@2fc8bf0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2fc8bf0

Browse files
author
Paul
committed
209 anonymize ncrack passwords
1 parent b15bf71 commit 2fc8bf0

File tree

6 files changed

+173
-70
lines changed

6 files changed

+173
-70
lines changed

scanners/ncrack/README.md.gotmpl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,35 @@ EXAMPLES:
138138
SEE THE MAN PAGE (http://nmap.org/ncrack/man.html) FOR MORE OPTIONS AND EXAMPLES
139139
```
140140

141+
## Password encryption
142+
143+
Because **Ncrack** findings are very sensitive you probably don't want every SecureCodeBox user to see them. In order
144+
to address this issue we provide an option that lets you encrypt found passwords with public key crypto. Just
145+
generate a key pair with openssl:
146+
147+
```bash
148+
openssl genrsa -out key.pem 2048
149+
openssl rsa -in key.pem -outform PEM -pubout -out public.pem
150+
```
151+
152+
After you created the public key file you have to create a kubernetes secret from that
153+
file:
154+
```bash
155+
kubectl create secret generic --from-file="public.key=public.pem" <ncrack-secret-name>
156+
```
157+
Now you only need to set the value *encryptPasswords.existingSecret* to the
158+
secrets name when installing the scanner
159+
160+
```bash
161+
helm install ncrack secureCodeBox/ncrack --set="encryptPasswords.existingSecret=<ncrack-secret-name>"
162+
```
163+
164+
To decrypt a password from a finding use:
165+
166+
```bash
167+
base64 encryptedPassword -d | openssl rsautl -decrypt -inkey key.pem -out decryptedPassword.txt
168+
```
169+
141170
## Chart Configuration
142171

143172
{{ template "chart.valuesTable" . }}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDftYgZ2MhLWumXTylT/nEhZ3Ul
3+
rk8xuf8EFA3ffMRgyW3n9mEpVFHVXZCaEYz55/pZqnsffUosPnHtKDV4uGPVqPJk
4+
Mi5WUj6oUE9O/BXArK8pJfncOKYqCQN45hKc/Plt7uvTCTS/oFKoowv1MyzLzbrL
5+
AI4I7JPgFA1nOp8UDQIDAQAB
6+
-----END PUBLIC KEY-----

scanners/ncrack/parser/parser.js

Lines changed: 56 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,67 @@
11
const xml2js = require('xml2js');
2+
const crypto = require("crypto");
3+
const fs = require("fs");
4+
const util = require("util");
5+
const readFile = util.promisify(fs.readFile);
26

3-
async function parse(fileContent) {
4-
const { ncrackrun } = await transformXML(fileContent);
5-
const findings = transformToFindings(ncrackrun);
6-
return findings;
7+
async function parse (fileContent, scan, encryptionKeyLocation = process.env["ENCRYPTION_KEY_LOCATION"]) {
8+
const { ncrackrun } = await transformXML(fileContent);
9+
let publicKey = null;
10+
if (encryptionKeyLocation) {
11+
publicKey = await readPublicKey(encryptionKeyLocation);
12+
}
13+
return transformToFindings(ncrackrun, publicKey);
714
}
815

9-
function transformToFindings(ncrackrun) {
10-
const portFindings = ncrackrun.service.flatMap(({ address, port, credentials = [] }) => {
11-
const { addr: ipAddress } = address[0]['$'];
12-
const { protocol, portid, name: portName } = port[0]['$'];
13-
14-
return credentials.map(credential => {
15-
const { username, password } = credential['$'];
16-
17-
return {
18-
name: `Credentials for Service ${portName}://${ipAddress}:${portid} discovered via bruteforce.`,
19-
description: '',
20-
category: 'Discovered Credentials',
21-
location: `${portName}://${ipAddress}:${portid}`,
22-
osi_layer: 'APPLICATION',
23-
severity: 'HIGH',
24-
attributes: {
25-
port: portid,
26-
ip_address: ipAddress,
27-
protocol: protocol,
28-
service: portName,
29-
username,
30-
password,
31-
},
32-
};
33-
});
34-
});
16+
function transformToFindings (ncrackrun, publicKey) {
17+
return ncrackrun.service.flatMap(({ address, port, credentials = [] }) => {
18+
const { addr: ipAddress } = address[0]['$'];
19+
const { protocol, portid, name: portName } = port[0]['$'];
20+
21+
return credentials.map(credential => {
22+
let { username, password } = credential['$'];
23+
24+
if (publicKey) {
25+
password = crypto.publicEncrypt({
26+
key: publicKey,
27+
padding: crypto.constants.RSA_PKCS1_PADDING,
28+
}, Buffer.from(password)).toString("base64")
29+
}
3530

36-
return portFindings;
31+
return {
32+
name: `Credentials for Service ${portName}://${ipAddress}:${portid} discovered via bruteforce.`,
33+
description: '',
34+
category: 'Discovered Credentials',
35+
location: `${portName}://${ipAddress}:${portid}`,
36+
osi_layer: 'APPLICATION',
37+
severity: 'HIGH',
38+
attributes: {
39+
port: portid,
40+
ip_address: ipAddress,
41+
protocol: protocol,
42+
service: portName,
43+
username,
44+
password,
45+
},
46+
};
47+
});
48+
});
3749
}
3850

39-
function transformXML(fileContent) {
40-
return new Promise((resolve, reject) => {
41-
xml2js.parseString(fileContent, (err, xmlInput) => {
42-
if (err) {
43-
reject(new Error('Error converting XML to JSON in xml2js: ' + err));
44-
} else {
45-
resolve(xmlInput);
46-
}
47-
});
51+
function transformXML (fileContent) {
52+
return new Promise((resolve, reject) => {
53+
xml2js.parseString(fileContent, (err, xmlInput) => {
54+
if (err) {
55+
reject(new Error('Error converting XML to JSON in xml2js: ' + err));
56+
} else {
57+
resolve(xmlInput);
58+
}
4859
});
60+
});
61+
}
62+
63+
async function readPublicKey (keyLocation) {
64+
return readFile(keyLocation, "utf-8")
4965
}
5066

5167
module.exports.parse = parse;

scanners/ncrack/parser/parser.test.js

Lines changed: 64 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
const { parse } = require('./parser');
22
const fs = require('fs');
3+
const crypto = require("crypto")
34

45
it('should return no findings when ncrack has not found credentials', async () => {
5-
// eslint-disable-next-line security/detect-non-literal-fs-filename
6-
const ncrackXML = fs.readFileSync(__dirname + '/__testFiles__/ncrack_no_results.xml', {
7-
encoding: 'utf8',
8-
});
9-
const findings = await parse(ncrackXML);
6+
// eslint-disable-next-line security/detect-non-literal-fs-filename
7+
const ncrackXML = fs.readFileSync(__dirname + '/__testFiles__/ncrack_no_results.xml', {
8+
encoding: 'utf8',
9+
});
10+
const findings = await parse(ncrackXML);
1011

11-
expect(findings.length).toBe(0);
12+
expect(findings.length).toBe(0);
1213
});
1314

1415
it('should return findings when ncrack found credentials', async () => {
15-
// eslint-disable-next-line security/detect-non-literal-fs-filename
16-
const ncrackXML = fs.readFileSync(__dirname + '/__testFiles__/ncrack_with_results.xml', {
17-
encoding: 'utf8',
18-
});
19-
const [finding, ...otherFindings] = await parse(ncrackXML);
16+
// eslint-disable-next-line security/detect-non-literal-fs-filename
17+
const ncrackXML = fs.readFileSync(__dirname + '/__testFiles__/ncrack_with_results.xml', {
18+
encoding: 'utf8',
19+
});
20+
const [finding, ...otherFindings] = await parse(ncrackXML);
2021

21-
expect(finding).toMatchInlineSnapshot(`
22+
expect(finding).toMatchInlineSnapshot(`
2223
Object {
2324
"attributes": Object {
2425
"ip_address": "192.168.0.1",
@@ -36,32 +37,32 @@ it('should return findings when ncrack found credentials', async () => {
3637
"severity": "HIGH",
3738
}
3839
`);
39-
expect(otherFindings.length).toBe(0);
40+
expect(otherFindings.length).toBe(0);
4041
});
4142

4243
it('should return no findings when ncrack has not found credentials scanning two services', async () => {
43-
// eslint-disable-next-line security/detect-non-literal-fs-filename
44-
const ncrackXML = fs.readFileSync(
45-
__dirname + '/__testFiles__/ncrack_two_services_no_results.xml',
46-
{
47-
encoding: 'utf8',
48-
}
49-
);
50-
const findings = await parse(ncrackXML);
44+
// eslint-disable-next-line security/detect-non-literal-fs-filename
45+
const ncrackXML = fs.readFileSync(
46+
__dirname + '/__testFiles__/ncrack_two_services_no_results.xml',
47+
{
48+
encoding: 'utf8',
49+
}
50+
);
51+
const findings = await parse(ncrackXML);
5152

52-
expect(findings.length).toBe(0);
53+
expect(findings.length).toBe(0);
5354
});
5455

5556
it('should return findings when ncrack found two credentials scanning two services', async () => {
56-
// eslint-disable-next-line security/detect-non-literal-fs-filename
57-
const ncrackXML = fs.readFileSync(
58-
__dirname + '/__testFiles__/ncrack_two_services_with_results.xml',
59-
{
60-
encoding: 'utf8',
61-
}
62-
);
57+
// eslint-disable-next-line security/detect-non-literal-fs-filename
58+
const ncrackXML = fs.readFileSync(
59+
__dirname + '/__testFiles__/ncrack_two_services_with_results.xml',
60+
{
61+
encoding: 'utf8',
62+
}
63+
);
6364

64-
expect(await parse(ncrackXML)).toMatchInlineSnapshot(`
65+
expect(await parse(ncrackXML)).toMatchInlineSnapshot(`
6566
Array [
6667
Object {
6768
"attributes": Object {
@@ -99,3 +100,36 @@ it('should return findings when ncrack found two credentials scanning two servic
99100
`);
100101
});
101102

103+
it('should encrypt findings when a public key is set', async () => {
104+
// eslint-disable-next-line security/detect-non-literal-fs-filename
105+
const ncrackXML = fs.readFileSync(__dirname + '/__testFiles__/ncrack_with_results.xml', {
106+
encoding: 'utf8',
107+
});
108+
const [finding, ...otherFindings] = await parse(ncrackXML, null, __dirname + "/__testFiles__/public_key.pem");
109+
110+
decryptedData = crypto.privateDecrypt({
111+
key: privateKey,
112+
padding: crypto.constants.RSA_PKCS1_PADDING,
113+
}, Buffer.from(finding.attributes.password, "base64"));
114+
115+
expect(finding.attributes.password.length).toBe(172);
116+
expect(decryptedData.toString()).toBe("aaf076d4fe7cfb63fd1628df91")
117+
118+
});
119+
120+
const privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" +
121+
"MIICXQIBAAKBgQDftYgZ2MhLWumXTylT/nEhZ3Ulrk8xuf8EFA3ffMRgyW3n9mEp\n" +
122+
"VFHVXZCaEYz55/pZqnsffUosPnHtKDV4uGPVqPJkMi5WUj6oUE9O/BXArK8pJfnc\n" +
123+
"OKYqCQN45hKc/Plt7uvTCTS/oFKoowv1MyzLzbrLAI4I7JPgFA1nOp8UDQIDAQAB\n" +
124+
"AoGAV5tepkiX/7KlocS1eZg+M4exf8UobF/bd3xnBmt0+DZJ3TpGSIol1fnjRAK1\n" +
125+
"g7SN/QlfWDCXmIYH1YkWj6UeKvWim86OV+61QX4imLAOsi7fSA8fcNRxYVX73hhk\n" +
126+
"kxt10a4l+CPAb4cyJa4Ud3UHhLtRlanJtQyAXZtQ38fRSiECQQDxIhBjkU4Sf96t\n" +
127+
"wpEWr/RnOA2aHOUWH8GCB4DAcw5wrISDcvRsgKggjec2VAJPovqSri1lQS4hV28M\n" +
128+
"4iTcj+ylAkEA7YB0rAebUzbFXzMrxUPxBbjze+idw1COqCXkX+N9RYVY23D8mUlR\n" +
129+
"8cMru4Rauu6DluSWZCgR14+Hi0TNrUHlSQJBAJBoJgh67JaHnYPSEbHUjjmCiCLT\n" +
130+
"Sx6Exg5pD+IxBWTU7EcMgPS51/YnBWCzzu6CXC2bwfPxpP6yrf65L/om90ECQQDe\n" +
131+
"HGYAhFSkq/JFp+tlXrbHbUJ4PQFdqbbgVh+P9YYwQBbrkm0JReKWwLnjclIPxAPY\n" +
132+
"WAq1vCuDdr2CZ2QahifRAkBd9mv+G4WO0hOsTBypeoEnL6VECzSauDwfIP/kSdBz\n" +
133+
"bmkZ6DCScZa8gz1J5ZamBnP4N2dtQn/zDtNUkS+qK+s2\n" +
134+
"-----END RSA PRIVATE KEY-----";
135+

scanners/ncrack/templates/ncrack-parse-definition.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,15 @@ spec:
66
handlesResultsType: ncrack-xml
77
image: "{{ .Values.parserImage.repository }}:{{ .Values.parserImage.tag | default .Chart.Version }}"
88
ttlSecondsAfterFinished: {{ .Values.parseJob.ttlSecondsAfterFinished }}
9+
{{- if .Values.encryptPasswords.existingSecret }}
10+
volumes:
11+
- name: "ncrack-secret"
12+
secret:
13+
secretName: {{ .Values.encryptPasswords.existingSecret }}
14+
volumeMounts:
15+
- name: "ncrack-secret"
16+
mountPath: "/secrets/"
17+
env:
18+
- name: "ENCRYPTION_KEY_LOCATION"
19+
value: "/secrets/{{ .Values.encryptPasswords.key }}"
20+
{{- end }}

scanners/ncrack/values.yaml< 93D6 /h3>

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ parserImage:
1111
# @default -- defaults to the charts version
1212
tag: null
1313

14+
encryptPasswords:
15+
# encryptPasswords.existingSecret -- secret name with a pem encoded rsa public key to encrypt identified passwords
16+
existingSecret: null
17+
# encryptPasswords.key -- name of the property in the secret with the pem encoded rsa public key
18+
key: "public.key"
19+
1420
parseJob:
1521
# parseJob.ttlSecondsAfterFinished -- seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/
1622
ttlSecondsAfterFinished: null

0 commit comments

Comments
 (0)
0