Skip to content

Commit

Permalink
Linux安全配置扫描输出为HTML报告;协议识别增加rsync;识别JupyterLab未授权访问漏洞
Browse files Browse the repository at this point in the history
  • Loading branch information
selinuxG committed Sep 19, 2023
1 parent d1d7727 commit 25b87ce
Show file tree
Hide file tree
Showing 12 changed files with 774 additions and 205 deletions.
8 changes: 8 additions & 0 deletions Protocol/rsysnc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package Protocol

import "strings"

// IsRsyncProtocol Rsync 协议识别
func IsRsyncProtocol(line string) bool {
return strings.HasPrefix(line, "@RSYNCD")
}
1 change: 1 addition & 0 deletions Protocol/web_RuleDatas.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var RuleDatas = []RuleData{
{"Lightdash", "body", "(Lightdash)"},
{"Apache-storm", "body", "(Storm UI)"},
{"HiveServer", "body", "(HiveServer)"},
{"JupyterLab", "body", "(JupyterLab)"},
{"华测监测预警系统", "body", "(华测监测预警系统)"},
{"时空智友企业信息管理", "body", "(时空智友企业信息管理)"},
{"D-Link-Route", "server", "HTTPD_ac 1.0"},
Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# 主要功能
> 主机存活探测、漏洞扫描、子域名扫描、端口扫描、各类服务数据库爆破、poc扫描、xss扫描、webtitle探测、web指纹识别、web敏感信息泄露、web目录浏览、web文件下载、等保安全风险问题风险自查等
> 主机存活探测、漏洞扫描、子域名扫描、端口扫描、各类服务数据库爆破、poc扫描、xss扫描、webtitle探测、web指纹识别、web敏感信息泄露、web目录浏览、web文件下载、等保安全风险问题风险自查等;
> 弱口令/未授权访问:40余种;
> WEB组件识别:300余种;
> 漏洞扫描:XSS、任意文件访问、任意命令执行、敏感信息泄露、默认账户密码...;
> 资产扫描:扫描存活主机->判断存活端口->识别协议/组件->基于组件协议进行弱口令、漏洞扫描->输出报告
## web功能预览(仅支持运行等保功能)
![web](images/web.gif)
Expand Down Expand Up @@ -48,6 +52,7 @@
| 34 | Node-Exporter || 仅验证未授权访问 |
| 35 | ApacheDruid || 仅验证未授权访问 |
| 36 | Zabbix || 仅验证默认账户 |
| 37 | JupyterLab || 仅验证未授权访问 |

## 资产扫描现阶段支持功能
| 序号 | 功能 | 是否支持 | 备注 |
Expand All @@ -63,8 +68,8 @@
| 9 | 识别web || 目前支持识别server、title、ssl证书 |
| 10 | 结果保存 || 默认保存保存到portscan.xlsx |
| 11 | 主机操作系统识别 || 基于ttl |
| 12 | 组件识别 || 目前常用200+ |
| 13 | 自动扫描弱口令 || rdp、ssh、redis、mysql、oracle、es、telnet、pgsql等20种 |
| 12 | 组件识别 || 目前常用300+ |
| 13 | 自动扫描弱口令 || rdp、ssh、redis、mysql、oracle、es、telnet、pgsql等40种 |
| 14 | web自动扫描xss || |
| 15 | web自动扫描漏洞 || 扫描poc、未授权访问、目录泄露 |
| 16 | 快速扫描格式 || 支持格式:https://192.168.1.1:9090、http://192.168.1.1:9090、192.168.1.1:9090/login/index.php |
Expand Down
9 changes: 9 additions & 0 deletions poc/yaml-poc/poc-yaml-JupyterLab-unauth.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: poc-yaml-JupyterLab-unauth
description: "未授权访问"
method: GET
path:
- /lab?
expression:
status: 200
body_all:
- "settings"
3 changes: 3 additions & 0 deletions port/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ func parseProtocol(conn net.Conn, host, port string, Poc bool) string {
case Protocol2.IsPgsqlProtocol(host, port):
return "数据库|PostgreSQL"

case Protocol2.IsRsyncProtocol(line):
return "rsync|" + line

default:
isWeb := Protocol2.IsWeb(host, port, Timeout, Poc)
for _, v := range isWeb {
Expand Down
233 changes: 233 additions & 0 deletions run/Runssh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
package run

import (
"fmt"
"golang.org/x/crypto/ssh"
"golin/global"
"html/template"
"io/fs"
"os"
"path/filepath"
"regexp"
"strings"
"time"
)

// Runssh 通过调用ssh协议执行命令,写入到文件,并减一个线程数
func Runssh(sshname string, sshHost string, sshUser string, sshPasswrod string, sshPort int, cmd string) {
defer wg.Done()
// 创建ssh登录配置
configssh := &ssh.ClientConfig{
Timeout: time.Second * 3, // ssh连接timeout时间
User: sshUser,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
configssh.Auth = []ssh.AuthMethod{ssh.Password(sshPasswrod)}
//增加旧版本算法支持,部分机器会出现 ssh: handshake failed: ssh: packet too large 报错
//configssh.Ciphers = []string{"aes128-cbc", "aes256-cbc", "3des-cbc", "aes128-ctr", "aes192-ctr", "aes256-ctr", "[email protected]", "[email protected]"}

// dial 获取ssh client
addr := fmt.Sprintf("%s:%d", sshHost, sshPort)
sshClient, err := ssh.Dial("tcp", addr, configssh)
if err != nil {
errhost = append(errhost, sshHost)
//fmt.Println(err)
return
}
defer sshClient.Close()

// 创建ssh-session
session, err := sshClient.NewSession()
defer session.Close()
if err != nil {
errhost = append(errhost, sshHost)
return
}

firepath := filepath.Join(succpath, "Linux")

// 自定义命令存在则只执行自定义文件
if runcmd != "" {
combo, err := session.CombinedOutput(cmd)
if err != nil {
errhost = append(errhost, sshHost)
return
}

//判断是否进行输出命令结果
if echorun {
fmt.Printf("%s\n%s\n", "<输出结果>", string(combo))
}
datanew := []byte(string(combo))
err = os.WriteFile(filepath.Join(firepath, fmt.Sprintf("%s_%s.log", sshname, sshHost)), datanew, fs.FileMode(global.FilePer))
if err != nil {
errhost = append(errhost, sshHost)
return
}
return
}

// 执行模板文件
data := Data{
Name: fmt.Sprintf("%s_%s", sshname, sshHost),
Address: runCmd("ifconfig", sshClient),
Disk: runCmd("df -h", sshClient),
PamSSH: runCmd(`cat /etc/pam.d/sshd|grep -v "^#"|grep -v "^$"`, sshClient),
PamPasswd: runCmd(`cat /etc/pam.d/passwd|grep -v "^#"|grep -v "^$"`, sshClient),
IptablesInfo: runCmd("iptables -L", sshClient),
PS: runCmd("ps aux", sshClient),
Sudoers: runCmd(`cat /etc/sudoers|grep -v "^#"|grep -v "^$"`, sshClient),
Rsyslog: runCmd(`cat /etc/rsyslog.conf|grep -v "^#"|grep -v "^$"`, sshClient),
CronTab: runCmd(`cat /etc/contab|grep -v "^#"|grep -v "^$"`, sshClient),
Share: runCmd(`cat /etc/exports|grep -v "^#"|grep -v "^$"`, sshClient),
Env: runCmd(`cat /etc/profile |grep -v "^#" |grep -v "^$"`, sshClient),
Version: runCmd("cat /etc/centos-release;uname -r", sshClient),
Docker: runCmd("docker ps", sshClient),
ListUnit: runCmd("systemctl list-unit-files|grep enabled", sshClient),
User: make([]LinUser, 0),
CreateUser: make([]Logindefs, 0),
Port: make([]PortList, 0),
ConfigSSH: make([]SSH, 0),
FilePer: make([]FileListPer, 0),
FireWalld: make([]FireListWalld, 0),
}

// 通过/etc/passwd 以及结合chage命令获取用户基本信息
for _, v := range strings.Split(runCmd("cat /etc/passwd", sshClient), "\n") {
userinfo := strings.Split(v, ":")
if len(userinfo) != 7 {
continue
}
var Login bool
if userinfo[6] == "/bin/bash" || userinfo[6] == "/bin/zsh" {
Login = true
}
shadow := strings.Split(runCmd(fmt.Sprintf("chage -l %s", userinfo[0]), sshClient), "\n")
user := LinUser{
Name: userinfo[0],
Passwd: userinfo[1],
Uid: userinfo[2],
Gid: userinfo[3],
Description: userinfo[4],
Pwd: userinfo[5],
Bash: userinfo[6],
Login: Login,
LastPasswd: strings.Split(shadow[0], ":")[1],
PasswdExpired: strings.Split(shadow[1], ":")[1],
Lose: strings.Split(shadow[2], ":")[1],
UserExpired: strings.Split(shadow[3], ":")[1],
MaxPasswd: strings.Split(shadow[5], ":")[1],
}
data.User = append(data.User, user)
}

//读取/etc/login.defs获取新创建用户时的信息
CreateUserLogindefs := Logindefs{
PassMaxDays: runCmd(`cat /etc/login.defs |grep -v "^#" |grep "PASS_MAX_DAYS"|awk -F " " '{print $2}'`, sshClient),
PassMinDays: runCmd(`cat /etc/login.defs |grep -v "^#" |grep "PASS_MIN_DAYS"|awk -F " " '{print $2}'`, sshClient),
PassWarnAge: runCmd(`cat /etc/login.defs |grep -v "^#" |grep "PASS_WARN_AGE"|awk -F " " '{print $2}'`, sshClient),
UMASK: runCmd(`cat /etc/login.defs |grep -v "^#" |grep "UMASK"|awk -F " " '{print $2}'`, sshClient),
EncryptMethod: runCmd(`cat /etc/login.defs |grep -v "^#" |grep "ENCRYPT_METHOD"|awk -F " " '{print $2}'`, sshClient),
}
data.CreateUser = append(data.CreateUser, CreateUserLogindefs)

//通过ss -tulnp获取端口信息
for _, p := range strings.Split(runCmd(`ss -tulnp|grep -v "Netid"`, sshClient), "\n") {
re := regexp.MustCompile(`\s+`)
s := re.ReplaceAllString(p, " ")
listen := strings.Split(s, " ")
if len(listen) != 7 {
continue
}
listenPort := PortList{
Netid: listen[0],
State: listen[1],
Local: listen[4],
Process: listen[6],
}
data.Port = append(data.Port, listenPort)
}

//获取sshd_config配置
sshdconfig := NewSSH()
if runCmd(`cat /etc/ssh/sshd_config|grep -v "^#" |grep "PasswordAuthentication"|awk -F " " '{print $2}'`, sshClient) == "no" {
sshdconfig.PasswordAuthentication = false
}
if runCmd(`cat /etc/ssh/sshd_config|grep -v "^#" |grep "PermitRootLogin"|awk -F " " '{print $2}'`, sshClient) == "no" {
sshdconfig.PermitRootLogin = false
}
if runCmd(`cat /etc/ssh/sshd_config|grep -v "^#" |grep "PermitEmptyPasswords"|awk -F " " '{print $2}'`, sshClient) == "yes" {
sshdconfig.PermitEmptyPasswords = true
}
if runCmd(`cat /etc/ssh/sshd_config|grep -v "^#" |grep "Protocol"|awk -F " " '{print $2}'`, sshClient) != "" {
sshdconfig.Protocol = runCmd(`cat /etc/ssh/sshd_config|grep -v "^#" |grep "Protocol"|awk -F " " '{print $2}'`, sshClient)
}
if runCmd(`cat /etc/ssh/sshd_config|grep -v "^#" |grep "MaxAuthTries"|awk -F " " '{print $2}'`, sshClient) != "" {
sshdconfig.MaxAuthTries = runCmd(`cat /etc/ssh/sshd_config|grep -v "^#" |grep "MaxAuthTries"|awk -F " " '{print $2}'`, sshClient)
}
data.ConfigSSH = append(data.ConfigSSH, sshdconfig)

//读取地址限制
data.HostAllow = runCmd(` cat /etc/hosts.allow |grep -v "^#" |grep -v "^$"`, sshClient)
data.HostDeny = runCmd(` cat /etc/hosts.Deny |grep -v "^#" |grep -v "^$"`, sshClient)

//中文文件权限
var FileList = []string{"/etc/passwd", "/etc/shadow", "/etc/group", "/etc/rsyslog.conf", "/etc/sudoers", "/etc/hosts.allow", "/etc/hosts.deny", "/etc/ssh/sshd_config", "/etc/pam.d/sshd", "/etc/pam.d/passwd", "/var/log/messages", "/var/log/audit/audit.log"}
for _, name := range FileList {
FilePer := FileListPer{
Name: name,
Permission: runCmd(fmt.Sprintf("stat -c '%s' %s ", "%a", name), sshClient),
Size: runCmd(fmt.Sprintf("stat -c '%s' %s ", "%s", name), sshClient),
Uid: runCmd(fmt.Sprintf("stat -c '%s' %s ", "%U", name), sshClient),
Gid: runCmd(fmt.Sprintf("stat -c '%s' %s ", "%G", name), sshClient),
LastReadTime: runCmd(fmt.Sprintf("stat -c '%s' %s ", "%x", name), sshClient),
LastWriteTime: runCmd(fmt.Sprintf("stat -c '%s' %s ", "%y", name), sshClient),
}
data.FilePer = append(data.FilePer, FilePer)
}

//防火墙 selinux状态
data.FireWalld = append(data.FireWalld, FireListWalld{
Name: "firewalld",
Status: runCmd(fmt.Sprintf(`systemctl status firewalld |grep "Active"|awk -F " " '{print $2}'`), sshClient),
})
data.FireWalld = append(data.FireWalld, FireListWalld{
Name: "selinux",
Status: runCmd(fmt.Sprintf(`cat /etc/selinux/config |grep -v "^#"|grep -v "^$"|awk -F 'SELINUX=' '{print $2}'`), sshClient),
})

// 读取模板文件
tmpl, err := template.ParseFS(templateFile, "linux_html.html")
if err != nil {
errhost = append(errhost, sshHost)
return
}
// 创建一个新的文件
newFile, err := os.Create(fmt.Sprintf("%s/%s_%s.html", firepath, sshname, sshHost))
if err != nil {
errhost = append(errhost, sshHost)
return
}
defer newFile.Close()
// 将模板执行的结果写入新的文件
err = tmpl.Execute(newFile, data)
if err != nil {
errhost = append(errhost, sshHost)
return
}

}

func runCmd(cmd string, Client *ssh.Client) string {
newClient, err := Client.NewSession()
if err != nil {
fmt.Println(err)
return ""
}
defer newClient.Close()
combo, err := newClient.CombinedOutput(cmd)
if err != nil {
return ""
}
return string(combo)
}
Loading

0 comments on commit 25b87ce

Please sign in to comment.