Skip to content

Commit c27aaef

Browse files
committed
【Plugins.JS】支持JavaScript敏感内容检测,识别JavaScript包含的AK/SK、ApiKey、电话、邮箱等敏感内容
1 parent 1ec1829 commit c27aaef

File tree

7 files changed

+297
-2
lines changed

7 files changed

+297
-2
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,8 @@
163163

164164
# 1.0.28 2024-07-30
165165
## BUGFIX
166-
* 【1】修复动静态爬虫HTTP代理不生效的问题
166+
* 【1】修复动静态爬虫HTTP代理不生效的问题
167+
* 【2】修复临时文件残留的问题
168+
169+
# 1.0.29 2024-07-31
170+
* 【1】支持JavaScript敏感内容检测,识别JavaScript包含的AK/SK、ApiKey、电话、邮箱等敏感内容

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Wscan是一款专注于WEB安全的扫描器,它向Nmap致敬,而Nmap已经
4545
| `自定义FUZZ插件` || × | 对body、query中的参数进行模糊测试 |
4646
| `Waf绕过/Waf测试` || × | 自定义各种特殊的Payload,测试Waf是否能拦截 |
4747
| `WEB组件识别` || × | 识别网站应用的组件及相关技术 |
48+
| `JavaScript敏感内容检测` || × | 识别JavaScript包含的AK/SK、ApiKey、电话、邮箱等敏感内容 |
4849
# 最佳实践
4950

5051
⬇️[下载地址](https://github.com/chushuai/wscan/releases)
@@ -305,6 +306,7 @@ Wscan支持JSON、HTML等多种格式的扫描报告,其中包含详尽的漏
305306
* 2024.07.26 发布v1.0.26 二进制版,支持自定用户名密码字典,增强表单爆破能力
306307
* 2024.07.29 发布v1.0.27 二进制版,cmd-injection插件,新增ssti系列漏洞检测payload
307308
* 2024.07.30 发布v1.0.28 二进制版,修复动静态爬虫HTTP代理不生效的问题
309+
* 2024.08.04 发布v1.0.29 二进制版,支持JavaScript敏感内容检测,识别JavaScript包含的AK/SK、ApiKey、电话、邮箱等敏感内容
308310
309311
# 开源时间表
310312
Wscan的目标是创建一个开源且非盈利的项目。然而,由于Wscan的工作量庞大,代码仍在快速迭代中。

core/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ func main() {
204204
app := &cli.App{
205205
Name: "wscan",
206206
Usage: "A powerful scanner engine ",
207-
Version: "1.0.28",
207+
Version: "1.0.29",
208208
Authors: []*cli.Author{&author},
209209
Flags: []cli.Flag{
210210
&cli.StringFlag{

core/plugins/js/js.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
2 * @Author: shaochuyu
3+
3 * @Date: 8/4/24
4+
4 */
5+
6+
package js
7+
8+
import (
9+
"context"
10+
"wscan/core/plugins/base"
11+
logger "wscan/core/utils/log"
12+
)
13+
14+
type Config struct {
15+
base.PluginBaseConfig `json:",inline" yaml:",inline"`
16+
CustomSensitiveContentCheckRule string `json:"custom_sensitive_content_check_rule" yaml:"sensitive_content_check" #:"自定义JS敏感内容检测规则"`
17+
}
18+
19+
// BaseConfig 返回基本配置, 固定格式, 无需修改
20+
func (c *Config) BaseConfig() *base.PluginBaseConfig {
21+
return &c.PluginBaseConfig
22+
}
23+
24+
type JS struct {
25+
base.PluginMixinInitConfig
26+
base.PluginMixinClose
27+
rules []Rule
28+
}
29+
30+
// Close 关闭函数
31+
func (*JS) Close() error {
32+
return nil
33+
}
34+
35+
// DefaultConfig 返回默认配置, 需要填写插件的默认配置
36+
func (*JS) DefaultConfig() base.PluginConfigInterface {
37+
config := &Config{PluginBaseConfig: base.PluginBaseConfig{
38+
Name: "js",
39+
Enabled: true,
40+
}, CustomSensitiveContentCheckRule: ""}
41+
return config
42+
}
43+
44+
// Fingers 返回漏洞检测配置
45+
func (p *JS) Fingers() []*base.Finger {
46+
fingers := []*base.Finger{}
47+
fingers = append(fingers, (&SensitiveContentCheck{rules: p.rules}).Finger())
48+
return fingers
49+
}
50+
51+
// GetConfig 获取配置
52+
func (p *JS) GetConfig() base.PluginConfigInterface {
53+
return p.PluginMixinInitConfig.GetConfig()
54+
}
55+
56+
// Init 插件初始化
57+
func (p *JS) Init(ctx context.Context, pci base.PluginConfigInterface, ab *base.ApolloBase) error {
58+
logger.Info("JS init")
59+
p.PluginMixinInitConfig.Init(ctx, pci, ab)
60+
p.LoadRules()
61+
return p.PluginMixinInitConfig.Init(ctx, pci, ab)
62+
}
63+
64+
func (p *JS) LoadRules() {
65+
p.rules, _ = LoadRulesWithRaw(ruleYaml)
66+
cfg := p.GetConfig().(*Config)
67+
if cfg.CustomSensitiveContentCheckRule != "" {
68+
if customSensitiveContentCheckRules, err := LoadRulesFromFile(cfg.CustomSensitiveContentCheckRule); err == nil {
69+
p.rules = append(p.rules, customSensitiveContentCheckRules...)
70+
} else {
71+
logger.Errorf("customSensitiveContentCheckRules LoadRules %s", cfg.CustomSensitiveContentCheckRule)
72+
}
73+
}
74+
75+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/**
2+
2 * @Author: shaochuyu
3+
3 * @Date: 8/4/24
4+
4 */
5+
6+
package js
7+
8+
import (
9+
"context"
10+
_ "embed"
11+
"fmt"
12+
"gopkg.in/yaml.v2"
13+
"os"
14+
"regexp"
15+
"strings"
16+
"time"
17+
"wscan/core/http"
18+
"wscan/core/model"
19+
"wscan/core/plugins/base"
20+
logger "wscan/core/utils/log"
21+
)
22+
23+
//go:embed "sensitive_content_check.yml"
24+
var ruleYaml []byte
25+
26+
// Rule defines the structure of a single regex rule
27+
type Rule struct {
28+
Title string `yaml:"title"`
29+
Pattern string `yaml:"regex"`
30+
Compiled *regexp.Regexp `yaml:"-"`
31+
}
32+
33+
// RuleSet holds a slice of rules
34+
type RuleSet struct {
35+
Rules []Rule `yaml:"rules"`
36+
}
37+
38+
func LoadRulesFromFile(filename string) ([]Rule, error) {
39+
data, err := os.ReadFile(filename)
40+
if err != nil {
41+
return nil, err
42+
}
43+
return LoadRulesWithRaw(data)
44+
}
45+
46+
func LoadRulesWithRaw(raw []byte) ([]Rule, error) {
47+
var ruleSet RuleSet
48+
err := yaml.Unmarshal(raw, &ruleSet)
49+
if err != nil {
50+
return nil, err
51+
}
52+
53+
// Compile regexes
54+
for i := range ruleSet.Rules {
55+
re, err := regexp.Compile(ruleSet.Rules[i].Pattern)
56+
if err != nil {
57+
return nil, err
58+
}
59+
ruleSet.Rules[i].Compiled = re // Store compiled regex
60+
}
61+
62+
return ruleSet.Rules, nil
63+
}
64+
65+
// ScanText checks for sensitive information in the given text
66+
func ScanText(text string, rules []Rule) {
67+
for _, rule := range rules {
68+
if rule.Compiled == nil {
69+
logger.Printf("No compiled regex for rule %s", rule.Title)
70+
continue
71+
}
72+
73+
matches := rule.Compiled.FindAllString(text, -1)
74+
if matches != nil {
75+
for _, match := range matches {
76+
// Check if the match should be ignored based on the content
77+
ignored := []string{"function", "encodeURIComponent", "XMLHttpRequest"}
78+
shouldIgnore := false
79+
for _, ignore := range ignored {
80+
if strings.Contains(match, ignore) {
81+
shouldIgnore = true
82+
break
83+
}
84+
}
85+
if shouldIgnore {
86+
continue
87+
}
88+
fmt.Printf("Found sensitive information:\nType: %s\nMatch: %s\n", rule.Title, match)
89+
}
90+
}
91+
}
92+
}
93+
94+
type SensitiveContentCheck struct {
95+
rules []Rule
96+
}
97+
98+
func (p *SensitiveContentCheck) Finger() *base.Finger {
99+
return &base.Finger{
100+
CheckAction: func(ctx context.Context, ab *base.Apollo) error {
101+
flow := ab.GetTargetFlow()
102+
logger.Infof("Start js/sensitive-content-check, %s", flow.Request.URL())
103+
for _, rule := range p.rules {
104+
if rule.Compiled == nil {
105+
logger.Printf("No compiled regex for rule %s", rule.Title)
106+
continue
107+
}
108+
matches := rule.Compiled.FindAllString(flow.Response.Text, -1)
109+
if matches != nil {
110+
fp := &model.Vuln{
111+
Payload: rule.Pattern,
112+
Param: nil,
113+
Flow: []*http.Flow{{Request: flow.Request, Response: flow.Response}},
114+
Binding: &model.VulnBinding{Plugin: "js/sensitive-content-check", Category: "js/sensitive-content-check", ID: "js/sensitive-content-check"},
115+
Extra: map[string]interface{}{
116+
"title": rule.Title,
117+
"matches": matches,
118+
},
119+
CreateTime: time.Now().Unix(),
120+
}
121+
fp.SetTargetURL(flow.Request.URL())
122+
ab.OutputVuln(fp)
123+
124+
}
125+
}
126+
return nil
127+
},
128+
Channel: "javascript",
129+
Binding: &model.VulnBinding{ID: "js/sensitive-content-check", Plugin: "js/sensitive-content-check", Category: "js/sensitive-content-check"},
130+
}
131+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
rules:
2+
- regex: '[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)+'
3+
title: 邮箱信息
4+
- regex: \b(?:secret|secret_key|token|secret_token|auth_token|access_token|username|password|aws_access_key_id|aws_secret_access_key|secretkey|authtoken|accesstoken|access-token|authkey|client_secret|bucket|email|HEROKU_API_KEY|SF_USERNAME|PT_TOKEN|id_dsa|clientsecret|client-secret|encryption-key|pass|encryption_key|encryptionkey|secretkey|secret-key|bearer|JEKYLL_GITHUB_TOKEN|HOMEBREW_GITHUB_API_TOKEN|api_key|api_secret_key|api-key|private_key|client_key|client_id|sshkey|ssh_key|ssh-key|privatekey|DB_USERNAME|oauth_token|irc_pass|dbpasswd|xoxa-2|xoxrprivate-key|private_key|consumer_key|consumer_secret|access_token_secret|SLACK_BOT_TOKEN|slack_api_token|api_token|ConsumerKey|ConsumerSecret|SESSION_TOKEN|session_key|session_secret|slack_token|slack_secret_token|bot_access_token|passwd|api|eid|sid|api_key|apikey|userid|user_id|user-id)["\s]*(?::|=|=:|=>)["\s]*[a-z0-9A-Z]{8,64}"?
5+
title: Token或密码
6+
- regex: \b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b
7+
title: IP地址
8+
- regex: '[\w]+\.cloudfront\.net'
9+
title: Cloudfront云泄露
10+
- regex: '[\w\-.]+\.appspot\.com'
11+
title: Appspot云泄露
12+
- regex: '[\w\-.]*s3[\w\-.]*\.?amazonaws\.com\/?[\w\-.]*'
13+
title: 亚马逊云泄露
14+
- regex: ([\w\-.]*\.?digitaloceanspaces\.com\/?[\w\-.]*)
15+
title: Digitalocean云泄露
16+
- regex: (storage\.cloud\.google\.com\/[\w\-.]+)
17+
title: Google云泄露
18+
- regex: ([\w\-.]*\.?storage.googleapis.com\/?[\w\-.]*)
19+
title: Google存储API泄露
20+
- regex: (?:139|138|137|136|135|134|147|150|151|152|157|158|159|178|182|183|184|187|188|198|130|131|132|155|156|166|185|186|145|175|176|133|153|177|173|180|181|189|199|170|171)[0-9]{8}
21+
title: 手机号
22+
- regex: ([-]+BEGIN [^\s]+ PRIVATE KEY[-]+[\s]*[^-]*[-]+END [^\s]+ PRIVATE KEY[-]+)
23+
title: SSH密钥
24+
- regex: access_key.*?["'](.*?)["']
25+
title: Access Key
26+
- regex: accesskeyid.*?["'](.*?)["']
27+
title: Access Key ID 1
28+
- regex: accesskeyid.*?["'](.*?)["']
29+
title: Access Key ID 2
30+
- regex: AKIA[0-9A-Z]{16}
31+
title: 亚马逊AWS API
32+
- regex: s3\.amazonaws.com[/]+|[a-zA-Z0-9_-]*\.s3\.amazonaws.com
33+
title: 亚马逊AWS 3S API 1
34+
- regex: ([a-zA-Z0-9-\.\_]+\.s3\.amazonaws\.com|s3://[a-zA-Z0-9-\.\_]+|s3-[a-zA-Z0-9-\.\_\/]+|s3.amazonaws.com/[a-zA-Z0-9-\.\_]+|s3.console.aws.amazon.com/s3/buckets/[a-zA-Z0-9-\.\_]+)
35+
title: 亚马逊AWS 3S API 2
36+
- regex: amzn\\.mws\\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}
37+
title: 亚马逊AWS 3S API 3
38+
- regex: '@author[: ]+(.*?) '
39+
title: 作者信息
40+
- regex: api[key|_key|\s+]+[a-zA-Z0-9_\-]{5,100}
41+
title: API
42+
- regex: basic [a-zA-Z0-9=:_\+\/-]{5,100}
43+
title: 基础信息
44+
- regex: bearer [a-zA-Z0-9_\-\.=:_\+\/]{5,100}
45+
title: Bearer
46+
- regex: EAACEdEose0cBA[0-9A-Za-z]+
47+
title: Facebook Token
48+
- regex: '[a-zA-Z0-9_-]*:[a-zA-Z0-9_\-]+@github\.com*'
49+
title: Github Token
50+
- regex: AIza[0-9A-Za-z-_]{35}
51+
title: Google API
52+
- regex: 6L[0-9A-Za-z-_]{38}|^6[0-9a-zA-Z_-]{39}$
53+
title: Google验证码
54+
- regex: ya29\.[0-9A-Za-z\-_]+
55+
title: Google OAuth
56+
- regex: ey[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*$
57+
title: JWT鉴权
58+
- regex: key-[0-9a-zA-Z]{32}
59+
title: Mailgun服务密钥
60+
- regex: access_token\$production\$[0-9a-z]{16}\$[0-9a-f]{32}
61+
title: Paypal/Braintree访问凭证
62+
- regex: '-----BEGIN PGP PRIVATE KEY BLOCK-----'
63+
title: PGP密钥
64+
- regex: (?i)(password\s*[`=:\"]+\s*[^\s]+|password is\s*[`=:\"]*\s*[^\s]+|pwd\s*[`=:\"]*\s*[^\s]+|passwd\s*[`=:\"]+\s*[^\s]+)
65+
title: 密码泄露
66+
- regex: '-----BEGIN EC PRIVATE KEY-----'
67+
title: RSA密钥
68+
- regex: '-----BEGIN DSA PRIVATE KEY-----'
69+
title: DSA密钥
70+
- regex: rk_live_[0-9a-zA-Z]{24}
71+
title: Stripe账号泄露 1
72+
- regex: sk_live_[0-9a-zA-Z]{24}
73+
title: Stripe账号泄露 2
74+
- regex: AC[a-zA-Z0-9_\-]{32}
75+
title: Twillio 账号泄露 1
76+
- regex: SK[0-9a-fA-F]{32}
77+
title: Twillio 账号泄露 2
78+
- regex: AP[a-zA-Z0-9_\-]{32}
79+
title: Twillio 账号泄露 3

core/plugins/plugins.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
"wscan/core/plugins/custom"
1414
"wscan/core/plugins/dirscan"
1515
"wscan/core/plugins/fastjson"
16+
"wscan/core/plugins/fingerprint"
17+
"wscan/core/plugins/js"
1618
"wscan/core/plugins/jsonp"
1719
"wscan/core/plugins/path_traversal"
1820
"wscan/core/plugins/prometheus"
@@ -62,5 +64,7 @@ func All() []base.Plugin {
6264
plugins = append(plugins, &shiro.Shiro{})
6365
plugins = append(plugins, &custom.Custom{})
6466
plugins = append(plugins, &xstream.Xstream{})
67+
plugins = append(plugins, &fingerprint.Fingerprint{})
68+
plugins = append(plugins, &js.JS{})
6569
return plugins
6670
}

0 commit comments

Comments
 (0)