wip
parent
2683ab6929
commit
b499781fc6
|
@ -4,4 +4,5 @@ analyzer-d4-passivessl fetch a redis feed of certificate and TLS sessions and ma
|
||||||
```bash
|
```bash
|
||||||
go get github.com/gomodule/redigo/redis
|
go get github.com/gomodule/redigo/redis
|
||||||
go get github.com/lib/pq
|
go get github.com/lib/pq
|
||||||
|
sudo apt install postgresal-plpython3-10
|
||||||
```
|
```
|
||||||
|
|
44
main.go
44
main.go
|
@ -100,12 +100,21 @@ func main() {
|
||||||
s := sessionRecord{}
|
s := sessionRecord{}
|
||||||
_ = json.Unmarshal([]byte(dat), &s)
|
_ = json.Unmarshal([]byte(dat), &s)
|
||||||
|
|
||||||
if len(s.Certificates) > 0 {
|
if isValid(&s){
|
||||||
|
// Create ja3* records
|
||||||
|
err = insertJA3(&s)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(fmt.Sprintf("Could not insert JA3 into DB: %q", err))
|
||||||
|
}
|
||||||
// Insert Session
|
// Insert Session
|
||||||
ids, err := insertSession(&s)
|
ids, err := insertSession(&s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(fmt.Sprintf("Insert Sessions into DB failed: %q", err))
|
log.Fatal(fmt.Sprintf("Insert Sessions into DB failed: %q", err))
|
||||||
}
|
}
|
||||||
|
err = insertFuzzyHash(&s, ids)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(fmt.Sprintf("Insert Fuzzy Hash into DB failed: %q", err))
|
||||||
|
}
|
||||||
// Attempt to roughly build a chain of trust
|
// Attempt to roughly build a chain of trust
|
||||||
session := buildChain(&s)
|
session := buildChain(&s)
|
||||||
|
|
||||||
|
@ -119,17 +128,23 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(fmt.Sprintf("Could not link Certs and Session into DB: %q", err))
|
log.Fatal(fmt.Sprintf("Could not link Certs and Session into DB: %q", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create ja3* records
|
|
||||||
err = insertJA3(session)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(fmt.Sprintf("Could not insert JA3 into DB: %q", err))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isValid(s * sessionRecord) bool {
|
||||||
|
// Relationships
|
||||||
|
if len(s.Certificates) < 1 || s.JA3Digest == "" || s.JA3 == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Basic informations
|
||||||
|
if s.ClientPort == "" || s.ServerPort == "" || s.ServerIP == "" || s.ClientIP == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func insertJA3(s *sessionRecord) error {
|
func insertJA3(s *sessionRecord) error {
|
||||||
q := `INSERT INTO "ja3" (hash, raw, type) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING`
|
q := `INSERT INTO "ja3" (hash, raw, type) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING`
|
||||||
_, err := db.Exec(q, s.JA3Digest, s.JA3, "ja3")
|
_, err := db.Exec(q, s.JA3Digest, s.JA3, "ja3")
|
||||||
|
@ -144,6 +159,15 @@ func insertJA3(s *sessionRecord) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func insertFuzzyHash(s *sessionRecord, ids int64) error {
|
||||||
|
q := `INSERT INTO "fuzzy_hash" (type, value, "id_sessionRecord") VALUES ($1, $2, $3) ON CONFLICT DO NOTHING`
|
||||||
|
_, err := db.Exec(q, "TLSH", s.TLSH, ids)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// insertPublicKeys insert each public key of each certificate of a session
|
// insertPublicKeys insert each public key of each certificate of a session
|
||||||
func insertPublicKey(c x509.Certificate) (string, error) {
|
func insertPublicKey(c x509.Certificate) (string, error) {
|
||||||
pub, err := x509.ParsePKIXPublicKey(c.RawSubjectPublicKeyInfo)
|
pub, err := x509.ParsePKIXPublicKey(c.RawSubjectPublicKeyInfo)
|
||||||
|
@ -321,9 +345,9 @@ func insertCertificates(s *sessionRecord) ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func insertSession(s *sessionRecord) (int64, error) {
|
func insertSession(s *sessionRecord) (int64, error) {
|
||||||
q := `INSERT INTO "sessionRecord" (dst_ip, src_ip, dst_port, src_port, timestamp) VALUES ($1, $2, $3, $4, $5) RETURNING id`
|
q := `INSERT INTO "sessionRecord" (dst_ip, src_ip, dst_port, src_port, timestamp, hash_ja3) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id`
|
||||||
var id int64
|
var id int64
|
||||||
err := db.QueryRow(q, s.ServerIP, s.ClientIP, s.ServerPort, s.ClientPort, s.Timestamp).Scan(&id)
|
err := db.QueryRow(q, s.ServerIP, s.ClientIP, s.ServerPort, s.ClientPort, s.Timestamp, s.JA3Digest).Scan(&id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -339,7 +363,7 @@ func initRedis() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func initDB() {
|
func initDB() {
|
||||||
connStr := "user=postgres password=postgres dbname=new_database"
|
connStr := "user=postgres password=postgres dbname=passive_ssl"
|
||||||
err := errors.New("")
|
err := errors.New("")
|
||||||
db, err = sql.Open("postgres", connStr)
|
db, err = sql.Open("postgres", connStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -4,12 +4,15 @@
|
||||||
-- Project Site: pgmodeler.com.br
|
-- Project Site: pgmodeler.com.br
|
||||||
-- Model Author: ---
|
-- Model Author: ---
|
||||||
|
|
||||||
|
SET check_function_bodies = false;
|
||||||
|
-- ddl-end --
|
||||||
|
|
||||||
|
|
||||||
-- Database creation must be done outside an multicommand file.
|
-- Database creation must be done outside an multicommand file.
|
||||||
-- These commands were put in this file only for convenience.
|
-- These commands were put in this file only for convenience.
|
||||||
-- -- object: new_database | type: DATABASE --
|
-- -- object: passive_ssl | type: DATABASE --
|
||||||
-- -- DROP DATABASE IF EXISTS new_database;
|
-- -- DROP DATABASE IF EXISTS passive_ssl;
|
||||||
-- CREATE DATABASE new_database
|
-- CREATE DATABASE passive_ssl
|
||||||
-- ;
|
-- ;
|
||||||
-- -- ddl-end --
|
-- -- ddl-end --
|
||||||
--
|
--
|
||||||
|
@ -64,7 +67,7 @@ CREATE TABLE public.certificate(
|
||||||
is_valid_chain bool NOT NULL DEFAULT false,
|
is_valid_chain bool NOT NULL DEFAULT false,
|
||||||
"notBefore" time,
|
"notBefore" time,
|
||||||
"notAfter" time,
|
"notAfter" time,
|
||||||
"isSS" bool NOT NULL DEFAULT false,
|
"is_SS" bool NOT NULL DEFAULT false,
|
||||||
CONSTRAINT certificate_pk PRIMARY KEY (hash)
|
CONSTRAINT certificate_pk PRIMARY KEY (hash)
|
||||||
|
|
||||||
);
|
);
|
||||||
|
@ -104,7 +107,7 @@ CREATE TABLE public."sessionRecord"(
|
||||||
src_ip inet NOT NULL,
|
src_ip inet NOT NULL,
|
||||||
dst_port int4 NOT NULL,
|
dst_port int4 NOT NULL,
|
||||||
src_port int4 NOT NULL,
|
src_port int4 NOT NULL,
|
||||||
hash_ja3 bytea,
|
hash_ja3 bytea NOT NULL,
|
||||||
"timestamp" time(0) with time zone,
|
"timestamp" time(0) with time zone,
|
||||||
CONSTRAINT "sessionRecord_pk" PRIMARY KEY (id)
|
CONSTRAINT "sessionRecord_pk" PRIMARY KEY (id)
|
||||||
|
|
||||||
|
@ -130,7 +133,7 @@ ALTER TABLE public.ja3 OWNER TO postgres;
|
||||||
-- ALTER TABLE public."sessionRecord" DROP CONSTRAINT IF EXISTS ja3_fk CASCADE;
|
-- ALTER TABLE public."sessionRecord" DROP CONSTRAINT IF EXISTS ja3_fk CASCADE;
|
||||||
ALTER TABLE public."sessionRecord" ADD CONSTRAINT ja3_fk FOREIGN KEY (hash_ja3)
|
ALTER TABLE public."sessionRecord" ADD CONSTRAINT ja3_fk FOREIGN KEY (hash_ja3)
|
||||||
REFERENCES public.ja3 (hash) MATCH FULL
|
REFERENCES public.ja3 (hash) MATCH FULL
|
||||||
ON DELETE SET NULL ON UPDATE CASCADE;
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
-- ddl-end --
|
-- ddl-end --
|
||||||
|
|
||||||
-- object: public."many_sessionRecord_has_many_certificate" | type: TABLE --
|
-- object: public."many_sessionRecord_has_many_certificate" | type: TABLE --
|
||||||
|
@ -162,8 +165,8 @@ ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
CREATE TABLE public.fuzzy_hash(
|
CREATE TABLE public.fuzzy_hash(
|
||||||
id bigserial NOT NULL,
|
id bigserial NOT NULL,
|
||||||
type text NOT NULL,
|
type text NOT NULL,
|
||||||
value public.hstore NOT NULL,
|
value text NOT NULL,
|
||||||
hash_ja3 bytea,
|
"id_sessionRecord" bigint,
|
||||||
CONSTRAINT fuzzy_hash_pk PRIMARY KEY (id)
|
CONSTRAINT fuzzy_hash_pk PRIMARY KEY (id)
|
||||||
|
|
||||||
);
|
);
|
||||||
|
@ -258,11 +261,62 @@ CREATE INDEX path_index ON public.certificate
|
||||||
WITH (BUFFERING = ON);
|
WITH (BUFFERING = ON);
|
||||||
-- ddl-end --
|
-- ddl-end --
|
||||||
|
|
||||||
-- object: ja3_fk | type: CONSTRAINT --
|
-- object: "sessionRecord_fk" | type: CONSTRAINT --
|
||||||
-- ALTER TABLE public.fuzzy_hash DROP CONSTRAINT IF EXISTS ja3_fk CASCADE;
|
-- ALTER TABLE public.fuzzy_hash DROP CONSTRAINT IF EXISTS "sessionRecord_fk" CASCADE;
|
||||||
ALTER TABLE public.fuzzy_hash ADD CONSTRAINT ja3_fk FOREIGN KEY (hash_ja3)
|
ALTER TABLE public.fuzzy_hash ADD CONSTRAINT "sessionRecord_fk" FOREIGN KEY ("id_sessionRecord")
|
||||||
REFERENCES public.ja3 (hash) MATCH FULL
|
REFERENCES public."sessionRecord" (id) MATCH FULL
|
||||||
ON DELETE SET NULL ON UPDATE CASCADE;
|
ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
-- ddl-end --
|
-- ddl-end --
|
||||||
|
|
||||||
|
-- object: plpython3u | type: LANGUAGE --
|
||||||
|
-- DROP LANGUAGE IF EXISTS plpython3u CASCADE;
|
||||||
|
CREATE LANGUAGE plpython3u;
|
||||||
|
-- ddl-end --
|
||||||
|
ALTER LANGUAGE plpython3u OWNER TO postgres;
|
||||||
|
-- ddl-end --
|
||||||
|
|
||||||
|
-- object: public.tlshc | type: FUNCTION --
|
||||||
|
-- DROP FUNCTION IF EXISTS public.tlshc(text,text) CASCADE;
|
||||||
|
CREATE FUNCTION public.tlshc ( a text, b text)
|
||||||
|
RETURNS int4
|
||||||
|
LANGUAGE plpython3u
|
||||||
|
VOLATILE
|
||||||
|
CALLED ON NULL INPUT
|
||||||
|
SECURITY INVOKER
|
||||||
|
COST 1
|
||||||
|
AS $$
|
||||||
|
import tlsh
|
||||||
|
return tlsh.diff(a, b)
|
||||||
|
$$;
|
||||||
|
-- ddl-end --
|
||||||
|
ALTER FUNCTION public.tlshc(text,text) OWNER TO postgres;
|
||||||
|
-- ddl-end --
|
||||||
|
|
||||||
|
-- object: public.tlsht | type: FUNCTION --
|
||||||
|
-- DROP FUNCTION IF EXISTS public.tlsht(IN text,IN text,IN int4,int4) CASCADE;
|
||||||
|
CREATE FUNCTION public.tlsht (IN filter text, IN hash text, IN threshold int4, maxrows int4)
|
||||||
|
RETURNS SETOF public.fuzzy_hash
|
||||||
|
LANGUAGE plpython3u
|
||||||
|
IMMUTABLE LEAKPROOF
|
||||||
|
RETURNS NULL ON NULL INPUT
|
||||||
|
SECURITY INVOKER
|
||||||
|
COST 1
|
||||||
|
ROWS 1000
|
||||||
|
AS $$
|
||||||
|
import tlsh
|
||||||
|
param = ["TLSH"]
|
||||||
|
#param[0] = filter
|
||||||
|
#plan = plpy.prepare("SELECT * FROM fuzzy_hash WHERE type <> $1", ["text"])
|
||||||
|
#rv = plan.execute(param, maxrows)
|
||||||
|
rv = plpy.execute("SELECT * FROM fuzzy_hash", 1000)
|
||||||
|
r = []
|
||||||
|
for x in rv:
|
||||||
|
if tlsh.diff(x["value"], hash) < threshold:
|
||||||
|
r.append(x)
|
||||||
|
return r
|
||||||
|
$$;
|
||||||
|
-- ddl-end --
|
||||||
|
ALTER FUNCTION public.tlsht(IN text,IN text,IN int4,int4) OWNER TO postgres;
|
||||||
|
-- ddl-end --
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue