1. Digging the code

The first step is to reverse the code from NodeRED, simple steps
1. Clone the sources
git clone https://github.com/node-red/node-red.git
2. Dig with grep in the code
grep -ril crypto packages/

   packages/node_modules/@node-red/editor-api/lib/auth/index.js
   packages/node_modules/@node-red/editor-api/lib/auth/strategies.js
   packages/node_modules/@node-red/editor-api/lib/auth/tokens.js
   packages/node_modules/@node-red/editor-api/lib/editor/comms.js
   packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js
   packages/node_modules/@node-red/editor-client/src/types/README.md
   packages/node_modules/@node-red/editor-client/src/types/node/crypto.d.ts
   packages/node_modules/@node-red/editor-client/src/types/node/tls.d.ts
   packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/editor.js
   packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/html.worker.js
   packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/ts.worker.js
   packages/node_modules/@node-red/nodes/core/common/60-link.js
   packages/node_modules/@node-red/nodes/core/network/21-httprequest.js
   packages/node_modules/@node-red/runtime/lib/nodes/credentials.js
   packages/node_modules/@node-red/runtime/lib/storage/index.js
   packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/authServer.js
   packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/index.js
   packages/node_modules/node-red/red.js
  1. Find the interesting parts
    packages/node_modules/@node-red/runtime/lib/nodes/credentials.js file name looks promising.
function decryptCredentials(key,credentials) {
    var creds = credentials["$"];
    var initVector = Buffer.from(creds.substring(0, 32),'hex');
    creds = creds.substring(32);
    var decipher = crypto.createDecipheriv(encryptionAlgorithm, key, initVector);
    var decrypted = decipher.update(creds, 'base64', 'utf8') + decipher.final('utf8');
    return JSON.parse(decrypted);
}

and

            var defaultKey;
            try {
                defaultKey = settings.get('_credentialSecret');
            } catch(err) {
            }
            if (defaultKey) {
                defaultKey = crypto.createHash('sha256').update(defaultKey).digest();
                encryptionKeyType = "system";
            }

and

                // Check if we have a generated _credSecret to decrypt with and remove
                if (defaultKey) {
                    log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
                    if (credentialsEncrypted) {
                        try {
                            credentials = decryptCredentials(defaultKey,credentials)
                        } catch(err) {
                            credentials = {};
                            log.warn(log._("nodes.credentials.error",{message:err.toString()}))
                            var error = new Error("Failed to decrypt credentials");
                            error.code = "credentials_load_failed";
                            throw error;
                        }
                    }
                    dirty = true;
                    removeDefaultKey = true;
                }
  • The key come from _credentialSecret which is in the file .config.runtime.json
  • Then the key is hashed as sha256 and pass to decryptCredentials() function
  • decryptCredentials() extract the 32 first characters and taken them as the Initial Vector
  • The end of creds string is a base64 encoded, and the content is encrypted aes

2. Openssl is a pain

The command openssl enc -base64 -d DOESN’T DO THE SAME AS base64 -d

Why ?

Because you need -A one line weird option.

openssl enc -base64 -d -A does the same as base64 -d

So if you need to decrypt a encoded base64 payload you need the -a and -A options like

openssl enc -aes-256-ctr -d -a -A

Our key is jq -j '._credentialSecret' $1/.config.runtime.json | sha256sum | cut -c 1-64 ( pay attention to the -j option of jq command for not passing a breakline to sha256sum).

Our IV is jq -r '.["$"]' $1/flows_cred.json | cut -c 1-32.

And our aes data is jq '.["$"]' -j $1/flows_cred.json | cut -c 33-.

3. Final script

#!/bin/bash
#
# Decrypt flows_cred.json from a NodeRED data directory
#
# Usage
# ./node-red-decrypt-flows-cred.sh ./node_red_data
#
jq  '.["$"]' -j $1/flows_cred.json | \
  cut -c 33- | \
  openssl enc -aes-256-ctr -d -base64 -A -iv `jq  -r '.["$"]' $1/flows_cred.json | cut -c 1-32` -K `jq -j '._credentialSecret' $1/.config.runtime.json | sha256sum | cut -c 1-64`

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *