Skip to content

Commit f6bcb06

Browse files
committed
started fixing the JARM implementation
1 parent 3cbf8c2 commit f6bcb06

File tree

4 files changed

+180
-6
lines changed

4 files changed

+180
-6
lines changed

engine/plugins/jarm.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// Copyright © by Jeff Foley 2017-2024. All rights reserved.
2+
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
package plugins
6+
7+
import (
8+
"errors"
9+
"log/slog"
10+
11+
"github.com/owasp-amass/amass/v4/engine/plugins/support"
12+
et "github.com/owasp-amass/amass/v4/engine/types"
13+
dbt "github.com/owasp-amass/asset-db/types"
14+
oam "github.com/owasp-amass/open-asset-model"
15+
"github.com/owasp-amass/open-asset-model/domain"
16+
"github.com/owasp-amass/open-asset-model/network"
17+
"github.com/owasp-amass/open-asset-model/property"
18+
"github.com/owasp-amass/open-asset-model/relation"
19+
"github.com/owasp-amass/open-asset-model/service"
20+
)
21+
22+
type jarmPlugin struct {
23+
name string
24+
log *slog.Logger
25+
source *et.Source
26+
}
27+
28+
func NewJARMFingerprints() et.Plugin {
29+
return &jarmPlugin{
30+
name: "JARM-Fingerprint",
31+
source: &et.Source{
32+
Name: "JARM-Fingerprint",
33+
Confidence: 90,
34+
},
35+
}
36+
}
37+
38+
func (j *jarmPlugin) Name() string {
39+
return j.name
40+
}
41+
42+
func (j *jarmPlugin) Start(r et.Registry) error {
43+
j.log = r.Log().WithGroup("plugin").With("name", j.name)
44+
45+
if err := r.RegisterHandler(&et.Handler{
46+
Plugin: j,
47+
Name: j.name + "-Handler",
48+
MaxInstances: 25,
49+
Transforms: []string{string(oam.Service)},
50+
EventType: oam.Service,
51+
Callback: j.check,
52+
}); err != nil {
53+
return err
54+
}
55+
56+
j.log.Info("Plugin started")
57+
return nil
58+
}
59+
60+
func (j *jarmPlugin) Stop() {
61+
j.log.Info("Plugin stopped")
62+
}
63+
64+
func (j *jarmPlugin) check(e *et.Event) error {
65+
_, ok := e.Entity.Asset.(*service.Service)
66+
if !ok {
67+
return errors.New("failed to extract the Service asset")
68+
}
69+
70+
if !j.hasCertificate(e) {
71+
return nil
72+
}
73+
74+
since, err := support.TTLStartTime(e.Session.Config(), string(oam.Service), string(oam.Service), j.name)
75+
if err != nil {
76+
return err
77+
}
78+
79+
src := j.source
80+
if !support.AssetMonitoredWithinTTL(e.Session, e.Entity, src, since) {
81+
j.query(e)
82+
support.MarkAssetMonitored(e.Session, e.Entity, src)
83+
}
84+
return nil
85+
}
86+
87+
func (j *jarmPlugin) hasCertificate(e *et.Event) bool {
88+
if edges, err := e.Session.Cache().OutgoingEdges(e.Entity, e.Session.Cache().StartTime(), "certificate"); err == nil && len(edges) > 0 {
89+
for _, edge := range edges {
90+
if a, err := e.Session.Cache().FindEntityById(edge.ToEntity.ID); err == nil && a != nil {
91+
if a.Asset.AssetType() == oam.TLSCertificate {
92+
return true
93+
}
94+
}
95+
}
96+
}
97+
return false
98+
}
99+
100+
type fingerprint struct {
101+
asset *dbt.Entity
102+
port *dbt.Edge
103+
hash string
104+
}
105+
106+
func (j *jarmPlugin) query(e *et.Event) {
107+
var targets []*fingerprint
108+
109+
if edges, err := e.Session.Cache().IncomingEdges(e.Entity, e.Session.Cache().StartTime(), "port"); err == nil && len(edges) > 0 {
110+
for _, edge := range edges {
111+
portrel, ok := edge.Relation.(*relation.PortRelation)
112+
if !ok {
113+
continue
114+
}
115+
if a, err := e.Session.Cache().FindEntityById(edge.FromEntity.ID); err == nil && a != nil {
116+
switch a.Asset.(type) {
117+
case *domain.FQDN:
118+
if portrel.Protocol == "https" {
119+
t := &fingerprint{
120+
asset: e.Entity,
121+
port: edge,
122+
}
123+
targets = append([]*fingerprint{t}, targets...)
124+
}
125+
case *network.IPAddress:
126+
if portrel.Protocol == "https" {
127+
targets = append(targets, &fingerprint{
128+
asset: e.Entity,
129+
port: edge,
130+
})
131+
}
132+
}
133+
}
134+
}
135+
}
136+
137+
var results []*fingerprint
138+
for _, target := range targets {
139+
portrel := target.port.Relation.(*relation.PortRelation)
140+
if fp, err := support.JARMFingerprint(target.asset.Asset, portrel); err == nil && fp != "" {
141+
results = append(results, &fingerprint{
142+
asset: target.asset,
143+
port: target.port,
144+
hash: fp,
145+
})
146+
}
147+
}
148+
149+
if len(results) > 0 {
150+
j.store(e, results)
151+
}
152+
}
153+
154+
func (j *jarmPlugin) store(e *et.Event, fps []*fingerprint) {
155+
for _, fp := range fps {
156+
_, _ = e.Session.Cache().CreateEdgeProperty(fp.port, &property.SimpleProperty{
157+
PropertyName: "JARM",
158+
PropertyValue: fp.hash,
159+
})
160+
}
161+
}

engine/plugins/load.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ var pluginNewFuncs = []func() et.Plugin{
5454
scrape.NewSiteDossier,
5555
whois.NewWHOIS,
5656
NewIPNetblock,
57-
//NewJARMFingerprint,
57+
NewJARMFingerprints,
5858
NewKnownFQDN,
5959
NewVerifiedEmail,
6060
}

engine/plugins/service_discovery/http_probes/plugin.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,15 +156,17 @@ func (hp *httpProbing) store(e *et.Event, resp *http.Response, entity *dbt.Entit
156156
serv.BannerLen = int(resp.Length)
157157
serv.Headers = resp.Header
158158

159+
proto := "http"
159160
var c *oamcert.TLSCertificate
160161
if firstAsset != nil {
162+
proto = "https"
161163
c = firstAsset.Asset.(*oamcert.TLSCertificate)
162164
}
163165

164166
portrel := &relation.PortRelation{
165167
Name: "port",
166168
PortNumber: port,
167-
Protocol: "tcp",
169+
Protocol: proto,
168170
}
169171

170172
s, err := support.CreateServiceAsset(e.Session, entity, portrel, serv, c)

engine/plugins/support/fingerprinting.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,32 @@ import (
1414
jarm "github.com/caffix/jarm-go"
1515
"github.com/owasp-amass/amass/v4/utils/net"
1616
oam "github.com/owasp-amass/open-asset-model"
17+
"github.com/owasp-amass/open-asset-model/domain"
18+
"github.com/owasp-amass/open-asset-model/network"
19+
"github.com/owasp-amass/open-asset-model/relation"
1720
)
1821

19-
func JARMFingerprint(target oam.Asset) (string, error) {
20-
var port int
22+
func JARMFingerprint(target oam.Asset, portrel *relation.PortRelation) (string, error) {
2123
var ipv6 bool
2224
var host string
2325

26+
if fqdn, ok := target.(*domain.FQDN); ok {
27+
host = fqdn.Name
28+
} else if ip, ok := target.(*network.IPAddress); ok {
29+
ipv6 = ip.Address.Is6()
30+
host = ip.Address.String()
31+
} else {
32+
return "", errors.New("target must be a FQDN or IPAddress")
33+
}
34+
2435
addr := host
2536
if ipv6 {
2637
addr = "[" + addr + "]"
2738
}
28-
addr += ":" + strconv.Itoa(port)
39+
addr += ":" + strconv.Itoa(portrel.PortNumber)
2940

3041
var results []string
31-
for _, probe := range jarm.GetProbes(host, port) {
42+
for _, probe := range jarm.GetProbes(host, portrel.PortNumber) {
3243
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
3344
defer cancel()
3445

0 commit comments

Comments
 (0)