pdknockr

- Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.acid.vegas/-c.git
Log | Files | Refs | Archive | README

commit 50cf4464fae24f649d563dd8a41eb05e866dbbf6
parent eadfc02dbfc60fb862f435340b394c4d32ae35cf
Author: acidvegas <acid.vegas@acid.vegas>
Date: Mon, 27 Nov 2023 19:47:21 -0500

Test was a success! Script is working, just need to refine everything, clean it up, fix the arguments.

Diffstat:
Mpdknockr.py | 122++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Arandom_subdomains.txt | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

2 files changed, 303 insertions(+), 45 deletions(-)

diff --git a/pdknockr.py b/pdknockr.py
@@ -2,7 +2,11 @@
 # Passive DNS Knocker (PDK) - developed by acidvegas in python (https://git.acid.vegas/pdknockr)
 
 import asyncio
+import json
+import logging
+import logging.handlers
 import random
+import time
 
 try:
     import aiodns
@@ -10,55 +14,60 @@ except ImportError:
     raise SystemExit('missing required \'aiodns\' module (pip install aiodns)')
 
 
-async def dns_lookup(domain: str, subdomain: str, dns_server: str, dns_type: str, semaphore: asyncio.Semaphore):
+async def dns_lookup(domain: str, subdomain: str, dns_server: str, dns_type: str, timeout: int, semaphore: asyncio.Semaphore):
     '''
     Perform a DNS lookup on a target domain.
     
     :param domain: The target domain to perform the lookup on.
     :param subdomain: The subdomain to look up.
     :param dns_server: The DNS server to perform the lookup on.
+    :param dns_type: The DNS record type to look up.
+    :param timeout: The timeout for the DNS lookup.
+    :param semaphore: The semaphore to use for concurrency.
     '''
     async with semaphore:
         target = f'{subdomain}.{domain}'
-        resolver = aiodns.DNSResolver(nameservers=[dns_server])
+        resolver = aiodns.DNSResolver(nameservers=[dns_server], timeout=timeout)
+        logging.info(f'\033[96mKnocking {target}\033[0m on \033[93m{dns_server}\033[0m (\033[90m{dns_type}\033[0m)')
         try:
             await resolver.query(target, dns_type)
-            print(f'[\033[92mDONE\033[0m] Knocking \033[96m{target}\033[0m on \033[93m{dns_server}\033[0m')
         except Exception as e:
-            print(f'[\033[31mFAIL\033[0m] Knocking \033[96m{target}\033[0m on \033[93m{dns_server}\033[0m \033[90m({e})\033[0m')
+            pass
 
 
+def generate_subdomain(sub_domains: list) -> str:
+    '''
+    Generate a random subdomain.
+
+    :param sub_domains: The list of subdomains to use.
+    '''
+    chosen_domains = random.sample(sub_domains, 2)
+    if random.choice([True, False]):
+        chosen_index = random.choice([0, 1])
+        chosen_domains[chosen_index] =  chosen_domains[chosen_index] + str(random.randint(1, 99))
+    return random.choice(['.', '-']).join(chosen_domains)
+
+    
 async def main(args):
     '''
     Main function for the program.
     
     :param args: The arguments passed to the program.
     '''
-    global dns_servers
+    global dns_keys
 
     semaphore = asyncio.BoundedSemaphore(args.concurrency)
     tasks = []
 
-    if args.domains:
-        for domain in args.domains.split(','):
-            for dns_server in dns_servers:
-                if len(tasks) < args.concurrency:
-                    task = asyncio.create_task(dns_lookup(domain, args.subdomain, dns_server, args.rectype, semaphore))
-                    tasks.append(task)
-                else:
-                    done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
-                    tasks = list(pending)
-
-    elif args.input:
-        async with asyncio.open_file(args.input, 'r') as file:
-            for domain in file:
-                for dns_server in dns_servers:
-                    if len(tasks) < args.concurrency:
-                        task = asyncio.create_task(dns_lookup(domain, args.subdomain, dns_server, args.rectype, semaphore))
-                        tasks.append(task)
-                    else:
-                        done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
-                        tasks = list(pending)
+    for domain in args.domains.split(','):
+        for dns_server in dns_keys:
+            if len(tasks) < args.concurrency:
+                query_record = random.choice(args.rectype)
+                task = asyncio.create_task(dns_lookup(domain, dns_keys[dns_server], dns_server, query_record, args.timeout, semaphore))
+                tasks.append(task)
+            else:
+                done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
+                tasks = list(pending)
 
 
 
@@ -68,38 +77,61 @@ if __name__ == '__main__':
     import urllib.request
 
     parser = argparse.ArgumentParser(description='Passive DNS Knocking Tool')
-    parser.add_argument('-d', '--domains', help='Comma seperate list of domains')
-    parser.add_argument('-i', '--input', help='File containing list of domains')
-    parser.add_argument('-s', '--subdomain', help='Subdomain to look up')
+    parser.add_argument('-d', '--domains', help='Comma seperate list of domains or file containing list of domains')
+    #parser.add_argument('-s', '--subdomain', help='Subdomain to look up')
     parser.add_argument('-c', '--concurrency', type=int, default=50, help='Concurrency limit (default: 50)')
     parser.add_argument('-r', '--resolvers', help='File containing list of DNS resolvers (uses public-dns.info if not specified)')
-    parser.add_argument('-rt', '--rectype', default='A', help='DNS record type (default: A)')
+    parser.add_argument('-rt', '--rectype', default='A,AAAA', help='Comma-seperated list of  DNS record type (default: A)')
+    parser.add_argument('-t', '--timeout', type=int, default=3, help='Timeout for DNS lookup (default: 3)')
     args = parser.parse_args()
 
-    if not args.input and not args.domains:
+    sh = logging.StreamHandler()
+    sh.setFormatter(logging.Formatter('%(asctime)s | %(levelname)9s | %(message)s', '%I:%M %p'))
+    os.makedirs('logs', exist_ok=True)
+    log_filename = time.strftime('pdk_%Y-%m-%d_%H-%M-%S.log')
+    fh = logging.handlers.RotatingFileHandler(f'logs/{log_filename}.log', maxBytes=2500000, backupCount=3, encoding='utf-8')
+    fh.setFormatter(logging.Formatter('%(asctime)s | %(levelname)9s | %(filename)s.%(funcName)s.%(lineno)d | %(message)s', '%Y-%m-%d %I:%M %p')) 
+    logging.basicConfig(level=logging.NOTSET, handlers=(sh,fh))
+
+    if not args.domains:
         raise SystemExit('no domains specified')
     
-    elif args.input and args.domains:
-        raise SystemExit('cannot specify both domain and input file')
-    
-    elif args.input and not os.path.exists(args.input):
-        raise SystemExit('input file does not exist')
-
-    elif args.rectype and args.rectype not in ['A', 'AAAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT']:
-        raise SystemExit('invalid record type')
+    if args.rectype:
+        valid_record_types = ('A', 'AAAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT')
+        if ',' in args.rectype:
+            args.rectype = args.rectype.split(',')
+            for record_type in args.rectype:
+                if record_type not in valid_record_types:
+                    logging.fatal('invalid record type')
+        elif args.rectype not in valid_record_types:
+            logging.fatal('invalid record type')
+        else:
+            args.rectype = [args.rectype]
     
-    elif args.resolvers:
+    if args.resolvers:
         if os.path.exists(args.resolvers):
             with open(args.resolvers, 'r') as file:
                 dns_servers = [item.strip() for item in file.readlines() if item.strip()]
-                if not dns_servers:
-                    raise SystemExit('no DNS servers found in file')
-                else:
-                     print(f'Loaded {len(dns_servers):,} DNS servers from file')
+                logging.info(f'Loaded {len(dns_servers):,} DNS servers from file')
         else:
-            raise SystemExit('DNS servers file does not exist')
+            logging.fatal('DNS servers file does not exist')
     else:
         dns_servers = urllib.request.urlopen('https://public-dns.info/nameservers.txt').read().decode().split('\n')
-        print(f'Loaded {len(dns_servers):,} DNS servers from public-dns.info')
+        logging.info(f'Loaded {len(dns_servers):,} DNS servers from public-dns.info')
 
+    # Command line argument needed for this still
+    if os.path.exists('random_subdomains.txt'):
+        with open('random_subdomains.txt', 'r') as file:
+            sub_domains = [item.strip() for item in file.readlines() if item.strip()]
+            logging.info(f'Loaded {len(sub_domains):,} subdomains from file')
+    else:
+        logging.fatal('random_subdomains.txt is missing')
+   
+    dns_keys = dict()
+    for dns_server in dns_servers:
+        dns_keys[dns_server] = generate_subdomain(sub_domains)
+        logging.info(f'{dns_server} will have a key of {dns_keys[dns_server]}')
+    with open('dns_keys.txt', 'w') as file:
+        json.dump(dns_keys, file)
+    
     asyncio.run(main(args))
 \ No newline at end of file
diff --git a/random_subdomains.txt b/random_subdomains.txt
@@ -0,0 +1,226 @@
+accounts
+ad
+admin
+affiliates
+analytics
+api
+app
+archive
+asset
+backup
+beta
+billing
+blog
+booking
+calendar
+campaign
+careers
+catalog
+cdn
+chat
+cloud
+club
+code
+core
+community
+console
+cpanel
+customer
+dashboard
+data
+db
+de
+delivery
+demo
+design
+dev
+developer
+digital
+directory
+dns
+docs
+donate
+download
+edit
+education
+elastic
+email
+en
+engage
+enterprise
+es
+eu
+event
+explore
+faq
+feed
+feedback
+file
+files
+finance
+forum
+fr
+ftp
+game
+gateway
+git
+gitlab
+grafana
+guide
+help
+home
+host
+hosting
+hr
+id
+image
+images
+imap
+import
+info
+intranet
+invest
+investor
+irc
+jenkins
+jobs
+kb
+kibana
+lab
+learn
+lecture
+legal
+library
+link
+links
+live
+login
+m
+mail
+mail1
+mail2
+manage
+manager
+map
+marketing
+media
+members
+message
+mobile
+mta
+music
+mx
+mx1
+mx2
+my
+network
+news
+newsletter
+newsroom
+ns
+ns1
+ns2
+ns3
+office
+online
+order
+origin
+page
+partner
+payment
+photo
+podcast
+pop
+portal
+press
+privacy
+prod
+product
+production
+profile
+project
+promo
+proxies
+proxy
+public
+radio
+recruit
+redirect
+register
+relay
+remote
+report
+repository
+research
+reset
+resource
+review
+rss
+ru
+sale
+sales
+scan
+scanner
+search
+secure
+security
+server
+service
+services
+setting
+sftp
+shop
+showcase
+signin
+signup
+site
+smtp
+social
+software
+solution
+source
+ssl
+stage
+staging
+start
+stat
+static
+stats
+status
+store
+stream
+student
+studio
+support
+survey
+system
+team
+tech
+test
+ticket
+time
+tool
+tools
+tracking
+trade
+traffic
+training
+translate
+update
+upload
+video
+view
+virtual
+vpn
+web
+webinar
+webmail
+website
+wiki
+work
+workshop
+www
+www1
+www2
+xml
+zone