1
+ import time
2
+ import asyncpg
3
+
1
4
from bbot .modules .templates .subdomain_enum import subdomain_enum
2
5
3
6
@@ -11,30 +14,53 @@ class crt(subdomain_enum):
11
14
"author" : "@TheTechromancer" ,
12
15
}
13
16
14
- base_url = "https://crt.sh"
17
+ deps_pip = ["asyncpg" ]
18
+
19
+ db_host = "crt.sh"
20
+ db_port = 5432
21
+ db_user = "guest"
22
+ db_name = "certwatch"
15
23
reject_wildcards = False
16
24
17
25
async def setup (self ):
18
- self .cert_ids = set ()
26
+ self .db_conn = None
19
27
return await super ().setup ()
20
28
21
29
async def request_url (self , query ):
22
- params = {"q" : f"%.{ query } " , "output" : "json" }
23
- url = self .helpers .add_get_params (self .base_url , params ).geturl ()
24
- return await self .api_request (url , timeout = self .http_timeout + 30 )
25
-
26
- async def parse_results (self , r , query ):
27
- results = set ()
28
- j = r .json ()
29
- for cert_info in j :
30
- if not type (cert_info ) == dict :
31
- continue
32
- cert_id = cert_info .get ("id" )
33
- if cert_id :
34
- if hash (cert_id ) not in self .cert_ids :
35
- self .cert_ids .add (hash (cert_id ))
36
- domain = cert_info .get ("name_value" )
37
- if domain :
38
- for d in domain .splitlines ():
39
- results .add (d .lower ())
30
+ if not self .db_conn :
31
+ self .db_conn = await asyncpg .connect (
32
+ host = self .db_host , port = self .db_port , user = self .db_user , database = self .db_name
33
+ )
34
+
35
+ sql = """
36
+ WITH ci AS (
37
+ SELECT array_agg(DISTINCT sub.NAME_VALUE) NAME_VALUES
38
+ FROM (
39
+ SELECT DISTINCT cai.CERTIFICATE, cai.NAME_VALUE
40
+ FROM certificate_and_identities cai
41
+ WHERE plainto_tsquery('certwatch', $1) @@ identities(cai.CERTIFICATE)
42
+ AND cai.NAME_VALUE ILIKE ('%.' || $1)
43
+ LIMIT 50000
44
+ ) sub
45
+ GROUP BY sub.CERTIFICATE
46
+ )
47
+ SELECT DISTINCT unnest(NAME_VALUES) as name_value FROM ci;
48
+ """
49
+ start = time .time ()
50
+ results = await self .db_conn .fetch (sql , query )
51
+ end = time .time ()
52
+ self .verbose (f"SQL query executed in: { end - start } seconds with { len (results ):,} results" )
40
53
return results
54
+
55
+ async def parse_results (self , results , query ):
56
+ domains = set ()
57
+ for row in results :
58
+ domain = row ["name_value" ]
59
+ if domain :
60
+ for d in domain .splitlines ():
61
+ domains .add (d .lower ())
62
+ return domains
63
+
64
+ async def cleanup (self ):
65
+ if self .db_conn :
66
+ await self .db_conn .close ()
0 commit comments