Example #1
0
 def getPdns(self, data):
     
     if chk_ip(data) or chk_domain(data):
         p = pdns()
         as_owners, uri, downloads, comms, c2 = p.findPassive(data)
         self.savePdnsC2(c2, 'VirusTotal')
         self.savePdns(uri, downloads, comms, c2)
     else:
         msg = "[*] %s is neither an IP Address or a domain" % (data)
         logging.error(msg)
Example #2
0
    def saveSample(self, hashs, tag, detection_date=None):
        #   init variables, assume hashs is passed as a list from get_hashes(path)
        md5 = ''
        sha1 = ''
        sha256 =''
        if detection_date is None:
            detection_date = self.today
        crc32 = hashs[0]
        md5 = hashs[1]
        sha1 = hashs[2]
        sha256 = hashs[3]
        
        #   Updating malware_sample table
        self.cursor.execute("select * from malware_sample where md5 = %s or sha1 = %s or sha256 = %s", (md5,sha1,sha256))
        found2 = self.cursor.fetchone()
        if found2:
            sample_id = found2[0]
            msg = '[-] Sample %s found, no update' % (md5)
            #logging.info(msg)
            if (not found2[3]) or (not found2[1]):
                self.cursor.execute("update malware_sample set md5=%s, sha1=%s, sha256=%s, source=%s where id=%s", (md5, sha1, sha256, tag, sample_id))
        else:
            self.cursor.execute("INSERT ignore INTO malware_sample(md5, sha1, sha256, source) values (%s,%s,%s,%s)", (md5, sha1, sha256, tag))
            sample_id = int(self.cursor.lastrowid)
            msg = "[+] Sample from %s (%s), updated" % (tag, sample_id)
            logging.info(msg)

        #   update av_classification from VirusTotal
        p = pdns()
        if md5:
            hash = md5
        elif sha1:
            hash = sha1
        else:
            hash = sha256
        #   check if av_classification added
        self.cursor.execute("select * from av_classification where sample_id=%s", (sample_id,))
        found2 = self.cursor.fetchone()
        if found2:
            msg = "[-] sample's av_classification found, no update..."
            #logging.info(msg)
        else:
            #   *** consider to download the samples ***
            result = p.getClassification(hash)
            for i in range(0, len(result)):
                av_vendor = result[i].get('av_vendor')
                detection_name = result[i].get('name')
                self.cursor.execute("INSERT ignore INTO av_classification(av_vendor, detection_name, sample_id) values (%s,%s,%s)", (av_vendor, detection_name, sample_id))

        self.db.commit()
Example #3
0
    def savePdns(self, uri, downloads, comms, c2):
        #   init variables
        source = 'rPdns'

        #   check if data is an ip address or a domain
        for i in range(0, len(c2)):
            
            try:
                sha256 = ''
                scan_date = c2[i].get('date')
                if chk_ip(c2[i].get('from_')):
                    ip = c2[i].get('from_')
                    domain = c2[i].get('_to')
                else:
                    domain = c2[i].get('from_')
                    ip = c2[i].get('_to')
                
                dns_id = self.findDns_id(domain, ip)
                #   find if sample in download *** insert a global var to auto download the samples ***
                for j in range(0, len(downloads)):
                    src = downloads[j].get('source')
                    sample_date = downloads[j].get('date')
                    hash = downloads[j].get('hash')
                    if src == c2[i].get('_to'):
                        sha256 = hash
                        #   Updating malware_sample table
                        self.cursor.execute("select id from malware_sample where sha256 = %s", (sha256,))
                        found2 = self.cursor.fetchone()
                        if found2:
                            sample_id = found2[0]
                            msg = '[-] Sample found, no update'
                            #logging.info(msg)
                        else:
                            msg = "[+] Updating sample: %s (%s) on %s" % (domain, ip, scan_date)
                            logging.info(msg)
                            self.cursor.execute("INSERT ignore INTO malware_sample(sha256, source) values (%s,%s)", (sha256, source))
                            sample_id = int(self.cursor.lastrowid)
                        #   Updating c2 table
                        self.cursor.execute("select id from c2 where dns_id = %s and sample_id = %s", (dns_id, sample_id))
                        found2 = self.cursor.fetchone()
                        if found2:
                            c2_id = int(found2[0])
                        else:
                            msg = "[+] Updating c2: %s [%s] (dns_id=%s, sample_id=%s)" % (domain, ip, dns_id, sample_id)
                            logging.info(msg)
                            self.cursor.execute("INSERT INTO c2(dns_id, sample_id, detection_date, source) values (%s,%s,%s,%s)", (dns_id, sample_id, scan_date, source))
                            c2_id = int(self.cursor.lastrowid)
        
                        #   *** PDNSDOWNLOAD is to be set in MalProfile.ini ***
                        PDNSDOWNLOAD = True
                        if PDNSDOWNLOAD:
                            msg = '... Trying to download sample: %s' % (hash)
                            logging.info(msg)
                            sha256 = hash
                            repo = './repo'
                            folder = os.path.join(repo, source, 'binaries', sha256[0], sha256[1], sha256[2], sha256[3])
                            destination_file = os.path.join(folder, sha256)
                            if not os.path.exists(destination_file):
                                msg = '... Preparing download of %s' % (sha256)
                                logging.info(msg)
                                p = pdns()
                                p.get_download(sha256, source)
                            else:
                                msg = '[-] Sample is found in repository folder: %s' % (folder)
                                logging.info(msg)
                            if not os.path.exists(destination_file):
                                msg = "[*] No sample downloaded: %s" % (sha256)
                                logging.info(msg)
                            else:
                                hashs = get_hashes(destination_file)
                                self.saveSample(hashs, source)
                                msg = '[+] Saving download of %s to %s' % (sha256, folder)
                                logging.info(msg)
        
                msg = "[+] Updating urls (%s): %s (%s) on %s" % (dns_id, domain, ip, scan_date)
                logging.info(msg)
                
                #   updating urls and communicate_with tables
                for k in range(0, len(uri)):
                    src = uri[k].get('source')
                    detection_date = uri[k].get('date')
                    url = uri[k].get('url')
                    if src == c2[i].get('from_') and url is not None and url != '':
                        self.cursor.execute("INSERT ignore INTO urls(dns_id, detection_date, url) values (%s,%s,%s)", (dns_id, detection_date, url))
                
                for k in range(0, len(comms)):
                    src = comms[k].get('source')
                    detection_date = comms[k].get('date')
                    hash = comms[k].get('hash')
                    if src == c2[i].get('from_'):
                        self.cursor.execute("INSERT ignore INTO communicate_with(dns_id, detection_date, sha256) values (%s,%s,%s)", (dns_id, detection_date, hash))
                self.db.commit()

            except MySQLdb.Error, e:
                
                if self.db:
                    self.db.rollback()
                    msg = "Exception %d: %s" % (e.args[0],e.args[1])
                    logging.error(msg)
                pass
Example #4
0
def main():
    
    parser = argparse.ArgumentParser()
    
    parser.add_argument("-b", action="store_true", dest="batch", default=False, help="BATCH processing a [domain|ip|md5] file")
    parser.add_argument("-c", action="store_true", dest="case", default=False, help="create a CASE by providing [domain or ip] and a sample")
    parser.add_argument("-d", action="store_true", dest="download", help="to DOWNLOAD sample")
    parser.add_argument("-p", action="store", dest="parked", choices=['ip', 'subnet'], help="parse & update PARKED domains by a submitted ip")
    parser.add_argument("-q", action="store_true", dest="query", help="make & update recursive Pdns QUERIES")
    parser.add_argument("-u", action="store_true", dest="update", default=False, help="UPDATE [domain or ip]")
    parser.add_argument("-w", action="store_true", dest="web", help="parse domain's html details")
    parser.add_argument("-m", action="store_true", dest="monitor", default=False, help="specify if MONITORED")
    parser.add_argument("-r", action="store_true", dest="move", help="transfer file to REPOSITORY")
    
    parser.add_argument("--path", action="store", dest="path", help="specify PATH of sample, pcap or memory_dump file")
    parser.add_argument("--hash", action="store", dest="hash", help="specify HASH to download")
    
    parser.add_argument("--tlp", type=int, action="store", dest="tlp", choices=[1,2,3,4], default=3, help="specify a tlp code")
    parser.add_argument("--tag", type=str, action="store", dest="tag", help="provide a SOURCE tag")

    parser.add_argument("--target", type=str, nargs="*", action="store", dest="target", help="<domain>, <ip> or <dns ip>")
    
    args = parser.parse_args()
    
    #print args
    d = db()
    
    if args.update:
        #   check monitoring?
        if args.monitor:
            monitoring_code = 1
        else:
            monitoring_code = 0
        if args.target == None or args.tag ==None:
            print "[*] update: -u [-m] --target DOMAIN [IP] --tag TAG"
            sys.exit(1)
        elif len(args.target) == 1:
            if chk_ip(args.target[0]):
                ip = args.target[0]
                print '[+] Updating ip %s' % (ip)
                d.updateIP(ip, args.tag, args.tlp, monitoring_code)
            elif chk_domain(args.target[0]):
                domain = args.target[0]
                print '[+] Updating domain %s' % (domain)
                d.updateDomain(domain, args.tag, args.tlp, monitoring_code)
            else:
                print "[*] %s is neither a domain or an ip" % (args.target[0])
        elif len(args.target) == 2:
            if chk_ip(args.target[0]):
                ip = args.target[0]
                domain = args.target[1]
            else:
                domain = args.target[0]
                ip = args.target[1]
            msg = '[*] Updating domain-ip pair: %s, %s with tag=%s' % (domain, ip, args.tag)
            logging.info(msg)
            print msg
            d.updatePair(domain, ip, args.tag, args.tlp, monitoring_code)
        else:
            print "[*] update: -u [-m] --target DOMAIN [IP] --tag TAG"


    if args.parked:
        #   check monitoring?
        if args.monitor:
            monitoring_code = 1
        else:
            monitoring_code = 0
        if args.target == None or args.tag ==None:
            print "[*] check parked: -p [-m] {ip, subnet} --target [IP] --tag TAG"
            sys.exit(1)
        elif len(args.target) == 1:
            if chk_ip(args.target[0]) and args.parked == 'ip':
                ip = args.target[0]
                print '[+] Check parked domains of ip %s' % (ip)
                parked = get_parked(ip)
                #   save parked
                print '[+] Updating %s records' % (len(parked))
                for i in range(0, len(parked)):
                    domain = parked[i]['domain']
                    ip = parked[i]['data']
                    d.updatePair(domain, ip, args.tag, args.tlp, monitoring_code)
            elif args.parked == 'subnet':
                ip = args.target[0]
                print '[+] Updating parked domains from subnet of ip %s' % (ip)
                parknets = get_parkedSubnet(ip)
                #   save parknets
                print 'Updating %s records' % (len(parknets))
                bar = pyprind.ProgBar(len(parknets))
                for i in range(0, len(parknets)):
                    domain = parknets[i]['domain']
                    ip = parknets[i]['data']
                    d.updatePair(domain, ip, args.tag, args.tlp, monitoring_code)
                    bar.update()
            else:
                print "[*] %s is neither a subnet or an ip" % (args.target[0])

    
    if args.web:
        #   check --target to parse domain's web details
        if args.target == None:
            print "[*] parse web: -w --target DOMAIN"
            sys.exit(1)
        elif len(args.target) == 1:
            if chk_domain(args.target[0]):
                domain = args.target[0]
                print '[+] Parsing html page of %s' % (domain)
                d.getWeb(domain)
            else:
                print "[*] %s is not a domain" % (args.target[0])
        else:
            print "[*] parse web: -w --target DOMAIN"


    if args.query:
        #   make of recursive pdns queries
        if args.target == None:
            print "[*] query rPdns: -q --target DOMAIN or IP"
            sys.exit(1)
        elif len(args.target) == 1:
            if chk_ip(args.target[0]):
                ip = args.target[0]
                print '[+] Preparing rPdns %s' % (ip)
                d.getPdns(ip)
            elif chk_domain(args.target[0]):
                domain = args.target[0]
                print '[+] Preparing rPdns %s' % (domain)
                d.getPdns(domain)
            else:
                    print "[*] %s is neither a domain or an ip" % (args.target[0])


    if args.move:
        #   read source_file and write destination_file, then compute hashs and save to db
        if args.move == False or args.path ==None or args.tag == None:
            print "[*] move: -s --path PATH --tag TAG"
            sys.exit(1)
        elif os.path.exists(args.path):
            list = get_hashes(args.path)
            sha256 = list[3]
            repo = './repo'
            folder = os.path.join(repo, args.tag, 'binaries', sha256[0], sha256[1], sha256[2], sha256[3])
            if not os.path.exists(folder):
                os.makedirs(folder, 0750)
            destination_file = os.path.join(folder, sha256)
            if not os.path.exists(destination_file):
                with open(destination_file, 'wb') as reading:
                    for chunk in get_chunks(args.path):
                        reading.write(chunk)
                msg = '[+] Sample move to repository folder: %s' % (folder)
                logging.info(msg)
                print msg
            else:
                list = get_hashes(destination_file)
                msg = '[*] Sample is found in repository folder: %s' % (folder)
                logging.info(msg)
                print msg
            #   update to database
            d.saveSample(list, args.tag)
        else:
            print "[*] move: -s --path PATH --tag TAG"


    if args.case:
        
        #   check monitoring?
        if args.monitor:
            monitoring_code = 1
        else:
            monitoring_code = 0

        #   create a case by supplying domain [ip] and a sample with tag
        if args.case == False or (args.hash == None and args.tag == None) or args.target == None:
            print "[*] create case: -c [-m] --target DOMAIN [IP] --tag TAG --hash HASH [--path PATH]"
            sys.exit(1)
        elif len(args.target) == 1:
            if chk_ip(args.target[0]):
                ip = args.target[0]
                domain = ''
                #   if dns_id found, skip adding and grap ip_id, domain_id
                msg = '[+] Updating ip %s' % (ip)
                print msg
                d.updateIP(ip, args.tag, args.tlp, monitoring_code)
            elif chk_domain(args.target[0]):
                domain = args.target[0]
                ip, c_name = retIP(domain)
                #   try update dns-pair, if not update domain only
                if ip == '':
                    msg = '[+] Updating domain %s' % (domain)
                    print msg
                    d.updateDomain(domain, args.tag, args.tlp, monitoring_code)
                else:
                    msg = '[+] Updating current domain-ip pair: %s, %s with tag=%s' % (domain, ip, args.tag)
                    logging.info(msg)
                    print msg
                    d.updatePair(domain, ip, args.tag, args.tlp, monitoring_code)
            else:
                print "[*] %s is neither a domain or an ip" % (args.target[0])
        elif len(args.target) == 2:
            if chk_ip(args.target[0]):
                ip = args.target[0]
                domain = args.target[1]
            else:
                domain = args.target[0]
                ip = args.target[1]
            #   if dns_id found, skip adding and grap ip_id, domain_id
            print '[*] Updating domain-ip pair: %s, %s with tag=%s' % (domain, ip, args.tag)
            d.updatePair(domain, ip, args.tag, args.tlp, monitoring_code)

        #   try download from VirusTotal or save sample if PATH supplied. With sample, update malware_sample & check av_classification and create a case
        with_sample = 0

        if args.hash and not args.path:

            if len(args.hash) == 32 or len(args.hash) == 40 or len(args.hash) == 64:
                sha256 = args.hash
                source = args.tag
                repo = './repo'
                folder = os.path.join(repo, args.tag, 'binaries', sha256[0], sha256[1], sha256[2], sha256[3])
                destination_file = os.path.join(folder, sha256)
                if not os.path.exists(destination_file):
                    print '... Preparing download of: %s' % (sha256)
                    p = pdns()
                    p.get_download(sha256, source)
                else:
                    msg = '[-] Sample is found in repository folder: %s' % (folder)
                    logging.info(msg)
                    with_sample = 1
                if not os.path.exists(destination_file):
                    msg = "[*] Sample NOT saved in repository folder: %s" % (folder)
                    logging.info(msg)
                else:
                    with_sample = 1
            else:
                print "[*] %s is not a hash of: md5, sha1 or sha256" % (args.hash)


        if args.path and not args.hash:
            
            if os.path.exists(args.path):
                list = get_hashes(args.path)
                sha256 = list[3]
                repo = './repo'
                folder = os.path.join(repo, args.tag, 'binaries', sha256[0], sha256[1], sha256[2], sha256[3])
                if not os.path.exists(folder):
                    os.makedirs(folder, 0750)
                destination_file = os.path.join(folder, sha256)
                if not os.path.exists(destination_file):
                    with open(destination_file, 'wb') as reading:
                        for chunk in get_chunks(args.path):
                            reading.write(chunk)
                    msg = '[+] Sample saved in repository folder: %s' % (folder)
                    logging.info(msg)
                    print msg
                else:
                    msg = '[-] Sample is found in repository folder: %s' % (folder)
                    logging.info(msg)
                if os.path.exists(destination_file):
                    #   mark for d.saveSample(list, source)
                    with_sample = 1
                else:
                    msg = "[*] Sample NOT saved in repository folder: %s" % (folder)
                    logging.info(msg)

        if with_sample == 1:

            #   save sample, find dns_id, update c2
            hashs = get_hashes(destination_file)
            d.saveSample(hashs, args.tag)
            
            #   find sample_id
            sample_id = d.findSample_id(hashs)
            #   find dns_id
            dns_id = d.findDns_id(domain, ip)
            #   find domain_id
            domain_id = d.findDomain_id(domain)
            
            #   find ip_id
            ip_id = d.findIP_id(ip)
            
            if sample_id != 0 and dns_id != 0:

                #   update c2
                d.saveC2(sample_id, dns_id, args.tag)
                
                #   update cases
                d.saveCases(args.tag)
                msg = '... Trying to add cases, c2 & artefacts from source: %s' % (args.tag)
                logging.info(msg)
                
                #   find case_id
                case_id = d.findCase_id(args.tag)
                    
                #   update case_artefacts
                d.saveArtefacts(ip_id, domain_id, sample_id, case_id)


    if args.download:
        #   specify hash to download a sample from VirusTotal
        if args.hash == None or args.tag ==None:
            print "[*] download: -d --hash HASH --tag TAG"
            sys.exit(1)
        elif type(args.hash) is str:
            if len(args.hash) == 32 or len(args.hash) == 40 or len(args.hash) == 64:
                sha256 = args.hash
                source = args.tag
                repo = './repo'
                folder = os.path.join(repo, args.tag, 'binaries', sha256[0], sha256[1], sha256[2], sha256[3])
                destination_file = os.path.join(folder, sha256)
                if not os.path.exists(destination_file):
                    print '[+] Preparing download of %s' % (sha256)
                    p = pdns()
                    p.get_download(sha256, source)
                else:
                    msg = '[-] Sample is found in repository folder: %s' % (folder)
                    logging.info(msg)
                if not os.path.exists(destination_file):
                    print '[*] No sample downloaded: %s' % (sha256)
                else:
                    list = get_hashes(destination_file)
                    d.saveSample(list, source)
                msg = '[+] Saving download of %s' % (sha256)
                logging.info(msg)
                print msg

            else:
                print "[*] %s is not a hash of: md5, sha1 or sha256" % (args.hash)


    if args.batch:
        
        #   check monitoring?
        if args.monitor:
            monitoring_code = 1
        else:
            monitoring_code = 0

        #   check if batch file exist?
        if args.path == None or args.tag == None:
            print "[*] Batch Process: -b [-m] --path PATH --tag TAG"
            sys.exit(1)
        else:
            if os.path.exists(args.path):
                lines = readBatch(args.path)
                bar = pyprind.ProgBar(len(lines))
                for i in range(0, len(lines)):
                    #   check what's inside the lines[i]
                    domain = ip = md5 = ''
                    for k in lines[i].keys():
                        if k == 'domain':
                            domain = lines[i][k]
                        if k == 'ip':
                            ip = lines[i][k]
                        if k == 'md5':
                            md5 = lines[i][k]
                    if md5 != '':
                        msg = '[+] Download sample & open case of %s for %s:%s' % (md5, domain, ip)
                        #   download the sample
                        with_sample = 0
                        sha256 = md5
                        source = args.tag
                        repo = './repo'
                        folder = os.path.join(repo, args.tag, 'binaries', sha256[0], sha256[1], sha256[2], sha256[3])
                        destination_file = os.path.join(folder, sha256)
                        
                        if not os.path.exists(destination_file):
                            msg = '[+] Preparing download of %s' % (sha256)
                            logging.info(msg)
                            p = pdns()
                            p.get_download(sha256, source)
                        else:
                            msg = '[-] Sample is found in repository folder: %s' % (folder)
                            logging.info(msg)
                        if not os.path.exists(destination_file):
                            msg = "[*] No sample downloaded: %s" % (sha256)
                            logging.info(msg)
                        else:
                            hashs = get_hashes(destination_file)
                            d.saveSample(hashs, source)
                            msg = '[+] Saving download of %s' % (sha256)
                            logging.info(msg)
                            with_sample = 1

                        if domain !='' or ip !='':
                            msg = '[+] update dns-link with %s:%s' % (domain, ip)
                            logging.info(msg)
                            d.updatePair(domain, ip, args.tag, args.tlp, monitoring_code)

                            if not os.path.exists(destination_file):
                                sample_id = 0
                            else:
                                #   find sample_id
                                sample_id = d.findSample_id(hashs)
                            
                            #   find dns_id
                            dns_id = d.findDns_id(domain, ip)
                            #   find domain_id
                            domain_id = d.findDomain_id(domain)
                                
                            #   find ip_id
                            ip_id = d.findIP_id(ip)
                            
                            if sample_id != 0 and dns_id != 0:
                                
                                #   update c2
                                d.saveC2(sample_id, dns_id, args.tag)
                                
                                #   update cases
                                d.saveCases(args.tag)
                                msg = '[+] Cases, C2 & Artefacts is added from source: %s' % (args.tag)
                                logging.info(msg)
                                
                                #   find case_id
                                case_id = d.findCase_id(args.tag)
                                
                                #   update case_artefacts
                                d.saveArtefacts(ip_id, domain_id, sample_id, case_id)

                    else:
                        if domain or ip:
                            msg = '[+] update dns-link with %s:%s' % (domain, ip)
                            logging.info(msg)
                            if domain != '' and ip !='':
                                logging.info('updatePair')
                                d.updatePair(domain, ip, args.tag, args.tlp, monitoring_code)
                            if domain != '' and ip == '':
                                logging.info('updateDomain')
                                d.updateDomain(domain, args.tag, args.tlp, monitoring_code)
                            if domain == '' and ip != '':
                                logging.info('updateIP')
                                d.updateIP(ip, args.tag, args.tlp, monitoring_code)
                        else:
                            msg = "[*] No processing, data dropped"
                            logging.info(msg)
                            logging.info(lines[i])
                    bar.update()


    if args.batch==False and args.case==False and args.download==False and args.hash==None and args.monitor==False and  args.move==False and args.parked==None and args.path==None and args.query==False and args.tag==None and args.target==None and args.tlp==3 and args.update==False and args.web==False:
        #   all default value provided
        print "usage: Maltelligence.py [-h]\n"