示例#1
0
def test_feed_ipv4_confidence():
    x = Ipv4()
    cli = Client('http://localhost:5000', 1234)

    r = cli.aggregate(data, field='observable')
    r = x.process(r, whitelist)

    s = set()
    for rr in r:
        if rr['observable'] not in s:
            s.add(rr['confidence'])

    assert 75 not in s
示例#2
0
def make_cifs_connection():
    """Connect to CIFS instance."""
    cif_client = Client(token=psi_ops_config.CIF_TOKEN,
                        remote=psi_ops_config.CIF_REMOTE,
                        verify_ssl=psi_ops_config.CIF_VERIFY_SSL,
                        )
    return cif_client
示例#3
0
def main():
    p = ArgumentParser(
        description=textwrap.dedent('''\
        example usage:
            $ cif-apwg -v
        '''),
        formatter_class=RawDescriptionHelpFormatter,
        prog='cif-apwg'
    )

    p.add_argument("-v", "--verbose", dest="verbose", action="count",
                   help="set verbosity level [default: %(default)s]")
    p.add_argument('-d', '--debug', dest='debug', action="store_true")

    p.add_argument("--config", dest="config", help="specify a configuration file [default: %(default)s]",
                   default=os.path.join(os.path.expanduser("~"), DEFAULT_CONFIG))

    p.add_argument("--token", dest="token", help="specify token")
    p.add_argument("--remote", dest="remote", help="specify the CIF remote")
    p.add_argument("--group", dest="group", help="specify CIF group [default: %(default)s]", default="everyone")
    p.add_argument('--no-verify-ssl', action="store_true", default=False)

    # apwg options

    p.add_argument("--limit", help="limit the number of records processed", default=LIMIT)
    p.add_argument("--cache", default=os.path.join(os.path.expanduser("~"), ".cif/cleanmx"))
    p.add_argument("--cleanmx-remote",  default=CLEANMX_REMOTE)
    p.add_argument('--tlp', default=TLP)
    p.add_argument('--confidence', default=CONFIDENCE)

    p.add_argument("--dry-run", help="do not submit to CIF", action="store_true")

    p.add_argument("--no-last-run", help="do not modify lastrun file", action="store_true")

    args = p.parse_args()

    loglevel = logging.WARNING
    if args.verbose:
        loglevel = logging.INFO
    if args.debug:
        loglevel = logging.DEBUG

    console = logging.StreamHandler()
    logging.getLogger('').setLevel(loglevel)
    console.setFormatter(logging.Formatter(LOG_FORMAT))
    logging.getLogger('').addHandler(console)
    logger = logging.getLogger(__name__)

    options = vars(args)

    if os.path.isfile(args.config):
            f = file(args.config)
            config = yaml.load(f)
            f.close()
            if not config['client']:
                raise Exception("Unable to read " + args.config + " config file")
            config = config['client']
            for k in config:
                if not options.get(k):
                    options[k] = config[k]

    if not os.path.isdir(options["cache"]):
        os.makedirs(options["cache"])

    lastrun = os.path.join(options["cache"], "lastrun")
    logger.debug(lastrun)
    if os.path.exists(lastrun):
        with open(lastrun) as f:
            start = f.read().strip("\n")

        uri = "{0}?delta={1}".format(
            options["cleanmx_remote"],
            start
        )
    else:
        uri = "{0}?limit={1}".format(
            options["cleanmx_remote"],
            options['limit']
        )

    logger.debug("url: {0}".format(uri))

    session = requests.Session()
    session.headers['User-Agent'] = 'cif-cleanmx-py/0.0.0a1'
    logger.info("pulling data")

    try:
        body = session.get(uri)
    except requests.exceptions.ConnectionError as e:
        logger.critical(e)
        raise SystemExit

    if len(body.content) > 2:
        xmldoc = minidom.parseString(body.content)
        items = xmldoc.getElementsByTagName('entry')

    if len(items):
        body = []
        logger.debug('sending {0} items'.format(len(items)))
        import arrow
        reporttime = arrow.utcnow().format('YYYY-MM-DDTHH:mm:ss')
        reporttime = '{}Z'.format(reporttime)
        for e in reversed(items):
            start = e.getElementsByTagName('id')[0].childNodes[0].data
            body.append(
                {
                    "observable": e.getElementsByTagName('url')[0].childNodes[0].data,
                    "lasttime": datetime.fromtimestamp(int(e.getElementsByTagName('first')[0].childNodes[0].data)).strftime(
                        "%Y-%m-%dT%H:%M:%SZ"),
                    "reporttime": reporttime,
                    "tags": ["phishing"],
                    "confidence": options["confidence"],
                    "tlp": options["tlp"],
                    "group": options["group"],
                    "otype": "url",
                    "provider": "support.clean-mx.de",
                    "application": ["http", "https"],
                    "altid": "http://support.clean-mx.de/clean-mx/phishing",
                    "altid_tlp": "green",
                }
            )

        if not options.get("dry_run"):
            logger.info("submitting {0} observables to CIF: {1}".format(len(body), options["remote"]))
            cli = CIFClient(options['token'], remote=options['remote'], no_verify_ssl=options['no_verify_ssl'])
            ret = cli.submit(json.dumps(body))
        else:
            pprint(body)
            logger.info("dry run, skipping submit...")
    else:
        logger.info("nothing new to submit...")

    if not options.get("no_last_run") and not options.get("dry_run"):
        with open(os.path.join(options["cache"], "lastrun"), "w") as f:
            f.write(str(start))
示例#4
0
def test_client():
    cli = Client(token=1234, remote='https://localhost2:8443', verify_ssl=False)

    assert cli.verify_ssl is False
    assert cli.remote == 'https://localhost2:8443','remote-mismatch'
    assert cli.token == '1234', 'token mismatch'
示例#5
0
 def setUp(self):
     self.cli = Client(token=1234,
                       remote='https://localhost2:8443',
                       no_verify_ssl=1)
示例#6
0
def main():

    #
    # initialize module
    #

    p = ArgumentParser(
        description=textwrap.dedent('''\
        example usage:
            $ cat test.eml | cgmail
            $ cgmail --file test.eml
        '''),
        formatter_class=RawDescriptionHelpFormatter,
        prog='cgmail'
    )

    p.add_argument("-v", "--verbose", dest="verbose", action="count",
                   help="set verbosity level [default: %(default)s]")
    p.add_argument('-d', '--debug', dest='debug', action="store_true")

    p.add_argument("-f", "--file", dest="file", help="specify email file")

    # cif arguments
    p.add_argument("--confidence", help="specify confidence for submitting to CIF", default=CONFIDENCE)
    p.add_argument("--remote", help="specify CIF remote [default: %(default)s",
                   default=REMOTE_DEFAULT)
    p.add_argument("--token", help="specify CIF token")
    p.add_argument("--config", help="specify CIF config [default: %(default)s",
                   default=os.path.expanduser("~/.cif.yml"))
    p.add_argument("--tags", help="specify CIF tags [default: %(default)s", default=["phishing"])
    p.add_argument("--group", help="specify CIF group [default: %(default)s", default="everyone")
    p.add_argument("--tlp", help="specify CIF TLP [default: %(default)s", default=TLP)
    p.add_argument("--no-verify-ssl", action="store_true", default=False)
    p.add_argument("--raw", action="store_true", help="include raw message data")
    p.add_argument("--provider", help="specify feed provider [default: %(default)s]", default=PROVIDER)

    args = p.parse_args()

    loglevel = logging.WARNING
    if args.verbose:
        loglevel = logging.INFO
    if args.debug:
        loglevel = logging.DEBUG

    console = logging.StreamHandler()
    logging.getLogger('').setLevel(loglevel)
    console.setFormatter(logging.Formatter(LOG_FORMAT))
    logging.getLogger('').addHandler(console)
    logger = logging.getLogger(__name__)

    options = vars(args)

    if os.path.isfile(args.config):
        f = file(args.config)
        config = yaml.load(f)
        f.close()
        if not config['client']:
            raise Exception("Unable to read " + args.config + " config file")
        config = config['client']
        for k in config:
            if not options.get(k):
                options[k] = config[k]

        if config.get("remote") and options["remote"] == REMOTE_DEFAULT:
            options["remote"] = config["remote"]

    else:
        logger.info("{} config does not exist".format(args.config))

    #
    # get email from file or stdin
    #

    if not options.get("remote"):
        logger.critical("missing --remote")
        raise SystemExit

    if options.get("file"):
        with open(options["file"]) as f:
            email = f.read()
    else:
        email = sys.stdin.read()

    # extract urls from message body and mail parts
    urls = cgmail.extract_urls(email)

    #
    # submit urls to a CIF instance
    #

    # initialize cif client
    cli = Client(remote=options["remote"], token=options["token"], no_verify_ssl=options["no_verify_ssl"])

    for u in urls:
        logger.info("submitting: {0}".format(u))

        o = Observable(
            observable=u,
            confidence=options["confidence"],
            tlp=options["tlp"],
            group=options["group"],
            tags=options["tags"],
            provider=options.get('provider')
        )

        if options.get('raw'):
            o.raw = email

        r = cli.submit(str(o))
        logger.info("submitted: {0}".format(r))
示例#7
0
def main():

    #
    # initialize module
    #

    p = ArgumentParser(
        description=textwrap.dedent('''\
        example usage:
            $ cat test.eml | cgmail
            $ cgmail --file test.eml
        '''),
        formatter_class=RawDescriptionHelpFormatter,
        prog='cgmail'
    )

    p.add_argument("-v", "--verbose", dest="verbose", action="count",
                   help="set verbosity level [default: %(default)s]")
    p.add_argument('-d', '--debug', dest='debug', action="store_true")

    p.add_argument("-f", "--file", dest="file", help="specify email file")

    # cif arguments
    p.add_argument("--confidence", help="specify confidence for submitting to CIF", default=CONFIDENCE)
    p.add_argument("--remote", help="specify CIF remote")
    p.add_argument("--token", help="specify CIF token")
    p.add_argument("--config", help="specify CIF config [default: %(default)s",
                   default=os.path.expanduser("~/.cif.yml"))
    p.add_argument("--tags", help="specify CIF tags [default: %(default)s", default=["phishing"])
    p.add_argument("--group", help="specify CIF group [default: %(default)s", default="everyone")
    p.add_argument("--tlp", help="specify CIF TLP [default: %(default)s", default=TLP)
    p.add_argument("--no-verify-ssl", action="store_true", default=False)
    p.add_argument("--raw", action="store_true", help="include raw message data")
    p.add_argument("--provider", help="specify feed provider [default: %(default)s]", default=PROVIDER)

    p.add_argument('--exclude', help='url patterns to exclude [default: %(default)s', default=EXCLUDE)
    p.add_argument('--confidence-lower', help='patterns to automatically lower confidence', default=CONFIDENCE_LOWER)
    p.add_argument('-n', '--not-really', help='do not submit', action='store_true')
    p.add_argument('--cache', help='location to cache whitelist [default: %(default)s', default=WHITELIST_CACHE)
    p.add_argument('--blacklist-cache', default=BLACKLIST_CACHE)

     # Process arguments
    args = p.parse_args()
    setup_logging(args)
    logger = logging.getLogger(__name__)

    exclude = None
    if args.exclude:
        exclude = re.compile(args.exclude)

    confidence_lower = None
    if args.confidence_lower:
        confidence_lower = re.compile(args.confidence_lower)

    o = read_config(args)
    options = vars(args)
    for v in options:
        if options[v] is None:
            options[v] = o.get(v)

    if not options.get('token'):
        raise RuntimeError('missing --token')

    if options.get("file"):
        with open(options["file"]) as f:
            email = f.read()
    else:
        email = sys.stdin.read()

    # extract urls from message body and mail parts
    bits = cgmail.parse_email_from_string(email)
    urls = set()

    for n in bits:
        if n.get('urls'):
            for u in n['urls']:
                urls.add(u)

    verify_ssl = True
    if options.get('no_verify_ssl'):
        verify_ssl = False

    # initialize cif client
    cli = Client(remote=options["remote"], token=options["token"], verify_ssl=verify_ssl)

    update_cache = True
    if os.path.isfile(args.cache):
        modified = os.path.getmtime(args.cache)
        if arrow.utcnow() < arrow.get(modified + 84600):
            update_cache = False

    if update_cache:
        # pull FQDN whitelist

        filters = {
            'tags': 'whitelist',
            'otype': 'fqdn',
            'confidence': 25,
        }
        now = arrow.utcnow()
        filters['reporttimeend'] = '{0}Z'.format(now.format('YYYY-MM-DDTHH:mm:ss'))
        now = now.replace(days=-7)
        filters['reporttime'] = '{0}Z'.format(now.format('YYYY-MM-DDTHH:mm:ss'))

        ret = cli.search(limit=50000, filters=filters, sort='reporttime', sort_direction='desc')
        with open(args.cache, 'w') as f:
            for r in ret:
                f.write("{0}\n".format(r['observable']))

    update_cache = True
    if os.path.isfile(args.blacklist_cache):
        modified = os.path.getmtime(args.blacklist_cache)
        if arrow.utcnow() < arrow.get(modified + 84600):
            update_cache = False

    if update_cache:
        filters = {
            'tags': 'phishing,suspicious,malware',
            'otype': 'fqdn',
            'confidence': 75,
        }
        now = arrow.utcnow()
        filters['reporttimeend'] = '{0}Z'.format(now.format('YYYY-MM-DDTHH:mm:ss'))
        now = now.replace(days=-7)
        filters['reporttime'] = '{0}Z'.format(now.format('YYYY-MM-DDTHH:mm:ss'))

        ret = cli.search(limit=50000, filters=filters, sort='reporttime', sort_direction='desc')
        with open(args.blacklist_cache, 'w') as f:
            for r in ret:
                f.write("{0}\n".format(r['observable']))

    fqdns = set()
    with open(args.cache) as f:
        for l in f:
            fqdns.add(l.rstrip("\n"))

    fqdns_blacklist = set()
    with open(args.blacklist_cache) as f:
        for l in f:
            fqdns_blacklist.add(l.rstrip("\n"))

    for u in urls:
        u = u.rstrip('\/')
        u = urlparse(u)

        fqdn = url_to_fqdn(u.geturl())
        if exclude and exclude.search(fqdn):
            continue

        confidence = options['confidence']

        if match_whitelist(fqdns, u.netloc):
            if (u.netloc not in URL_SHORTNERS) and (not match_whitelist(HOSTING_PROVIDERS, u.netloc)):
                confidence = options['confidence'] - 15
            else:
                confidence = options['confidence'] + 5
        elif match_whitelist(fqdns_blacklist, u.netloc):
            confidence = options['confidence'] + 10
        else:
            confidence = options['confidence'] + 5

        # else
        # raise confidence
        logger.info("submitting: {0}".format(u.geturl()))

        o = Observable(
            observable=u.geturl(),
            confidence=confidence,
            tlp=options["tlp"],
            group=options["group"],
            tags=options["tags"],
            provider=options.get('provider')
        )

        o = o.__dict__
        del o['logger']

        if options.get('raw'):
            o.raw = email

        if not args.not_really:
            r = cli.submit(o)
            logger.info("submitted: {0}".format(r))
示例#8
0
def main():
    p = ArgumentParser(
        description=textwrap.dedent('''\
        example usage:
            $ export APWG_REMOTE=https://api.apwg.org/endpoint
            $ export APWG_TOKEN=123123123
            
            $ export CIF_REMOTE=https://csirtgadgets.org
            $ export CIF_TOKEN=123412341234
            $ cif-apwg-submit -v
        '''),
        formatter_class=RawDescriptionHelpFormatter,
        prog='cif-apwg-submit'
    )

    p.add_argument("-v", "--verbose", dest="verbose", action="count",
                   help="set verbosity level [default: %(default)s]")
    p.add_argument('-d', '--debug', dest='debug', action="store_true")

    p.add_argument('--no-verify-ssl', action="store_true", default=False)

    # apwg options

    p.add_argument("--limit", dest="limit", help="limit the number of records processed")
    p.add_argument("--cache", default=os.path.join(os.path.expanduser("~"), ".cif/apwg_submit"))
    p.add_argument("--past-minutes", help="number of hours to go back and retrieve", default=10)
    p.add_argument("--apwg-confidence", default="90")

    # cif options
    p.add_argument('--tlp', default=TLP)
    p.add_argument('--altid-tlp', default=TLP)
    p.add_argument('--altid')
    p.add_argument('--confidence', default=CONFIDENCE)
    p.add_argument('--tags', default="phishing")
    p.add_argument('--otype', default='url')
    p.add_argument('--itype', default='url')
    p.add_argument('--filters', default=None)
    p.add_argument('--group', default="everyone")

    p.add_argument("--dry-run", help="do not submit to CIF", action="store_true")

    p.add_argument("--no-last-run", help="do not modify lastrun file", action="store_true")

    args = p.parse_args()

    filters = {
        "confidence": args.confidence,
        "tags": args.tags.split(','),
        "otype": args.otype,
        "itype": args.itype,
        "group": args.group,
    }

    if args.filters:
        for e in args.filters.split('&', 1):
            k = e.split('=', 1)
            filters[k[0]] = k[1]


    loglevel = logging.WARNING
    if args.verbose:
        loglevel = logging.INFO
    if args.debug:
        loglevel = logging.DEBUG

    console = logging.StreamHandler()
    logging.getLogger('').setLevel(loglevel)
    console.setFormatter(logging.Formatter(LOG_FORMAT))
    logging.getLogger('').addHandler(console)
    logger = logging.getLogger(__name__)

    if not APWG_REMOTE and not args.dry_run:
       print("\nAPWG_REMOTE missing.\nConsult with [email protected] for more information")
       raise SystemExit

    if not os.path.isdir(args.cache):
        os.makedirs(args.cache)

    end = datetime.utcnow()

    lastrun = os.path.join(args.cache, "lastrun")
    logger.debug(lastrun)
    if os.path.exists(lastrun):
        with open(lastrun) as f:
            start = f.read().strip("\n")
            start = datetime.strptime(start, '%Y-%m-%d %H:%M:%S.%f')
    else:
        minutes = int(args.past_minutes)
        start = end - timedelta(minutes=minutes, seconds=-1)

    start = start.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
    end = end.strftime("%Y-%m-%dT%H:%M:%S.%fZ")

    logger.info("start:{0}".format(start))
    logger.info("end:{0}".format(end))

    filters['reporttime'] = start
    filters['reporttimeend'] = end

    logger.debug(json.dumps(filters, indent=4))

    # get data
    verify_ssl = True
    if args.no_verify_ssl:
        verify_ssl = False

    from apwgsdk.client import Client as APWGClient
    apwg = APWGClient()
    cli = Client(CIF_TOKEN, remote=CIF_REMOTE, verify_ssl=verify_ssl)
    data = cli.search(limit=args.limit, filters=filters)

    for d in data:
        if d.get('observable'):
            d['indicator'] = d['observable']

        if args.altid and not d.get('altid', '').startswith(args.altid):
            logger.debug('skipping.. %s' % d['indicator'])
            # if the altid doesn't match, skip it..
            continue

        if logger.getEffectiveLevel() == logging.DEBUG:
            logger.debug("%s" % json.dumps(d, indent=4))

        if args.dry_run:
            logger.info(json.dumps(d, indent=4))
            continue

        if logger.getEffectiveLevel() == logging.DEBUG:
            logger.debug('sending: %s' % json.dumps(d, indent=4))
        else:
            logger.info('sending: %s' % d['indicator'])

        # submit to apwg
        try:
            r = apwg.indicators_create(indicator=d['indicator'], confidence=args.apwg_confidence,
                                       description='phishing', lasttime=d['lasttime'])

            logger.info('indicator created successfully: {}'.format(r['id']))
            if args.debug:
                pprint(r)

        except Exception as e:
            logger.debug(e)
            logger.error('error creating indicator')

    if not args.no_last_run and not args.dry_run:
        with open(os.path.join(args.cache, "lastrun"), "w") as f:
            f.write(str(end))
示例#9
0
def main():
    p = ArgumentParser(
        description=textwrap.dedent('''\
        example usage:
            $ export APWG_REMOTE=https://api.apwg.org/endpoint
            $ export APWG_TOKEN=123123123
            $ cif-apwg -v
        '''),
        formatter_class=RawDescriptionHelpFormatter,
        prog='cif-apwg'
    )

    p.add_argument("-v", "--verbose", dest="verbose", action="count",
                   help="set verbosity level [default: %(default)s]")
    p.add_argument('-d', '--debug', dest='debug', action="store_true")

    p.add_argument("--config", dest="config", help="specify a configuration file [default: %(default)s]",
                   default=os.path.join(os.path.expanduser("~"), DEFAULT_CONFIG))

    p.add_argument("--token", dest="token", help="specify token")
    p.add_argument("--remote", dest="remote", help="specify the CIF remote")
    p.add_argument("--group", dest="group", help="specify CIF group [default: %(default)s]", default="everyone")
    p.add_argument('--no-verify-ssl', action="store_true", default=False)

    # apwg options

    p.add_argument("--limit", dest="limit", help="limit the number of records processed")
    p.add_argument("--apwg-token", help="specify an APWG token", required=True, default=APWG_TOKEN)
    p.add_argument("--format", default="json")
    p.add_argument("--cache", default=os.path.join(os.path.expanduser("~"), ".cif/apwg"))
    p.add_argument("--apwg-remote",  default=APWG_REMOTE, required=True)
    p.add_argument("--past-hours", help="number of hours to go back and retrieve", default="24")
    p.add_argument("--apwg-confidence-low", default="65")
    p.add_argument("--apwg-confidence-high", default="100")
    p.add_argument('--tlp', default=TLP)
    p.add_argument('--altid-tlp', default=TLP)
    p.add_argument('--confidence', default=CONFIDENCE)

    p.add_argument("--dry-run", help="do not submit to CIF", action="store_true")

    p.add_argument("--no-last-run", help="do not modify lastrun file", action="store_true")

    args = p.parse_args()

    loglevel = logging.WARNING
    if args.verbose:
        loglevel = logging.INFO
    if args.debug:
        loglevel = logging.DEBUG

    console = logging.StreamHandler()
    logging.getLogger('').setLevel(loglevel)
    console.setFormatter(logging.Formatter(LOG_FORMAT))
    logging.getLogger('').addHandler(console)
    logger = logging.getLogger(__name__)

    if not args.apwg_remote:
        print("\nAPWG_REMOTE missing.\nConsult with [email protected] for more information")
        raise SystemExit

    options = vars(args)

    if os.path.isfile(args.config):
            f = file(args.config)
            config = yaml.load(f)
            f.close()
            if not config['client']:
                raise Exception("Unable to read " + args.config + " config file")
            config = config['client']
            for k in config:
                if not options.get(k):
                    options[k] = config[k]

    if not os.path.isdir(options["cache"]):
        os.makedirs(options["cache"])

    end = datetime.utcnow()

    lastrun = os.path.join(options["cache"], "lastrun")
    logger.debug(lastrun)
    if os.path.exists(lastrun):
        with open(lastrun) as f:
            start = f.read().strip("\n")
            start = datetime.strptime(start, '%Y-%m-%d %H:%M:%S.%f')
    else:
        hours = int(options["past_hours"])
        start = end - timedelta(hours=hours, seconds=-1)

    logger.info("start:{0}".format(start))
    logger.info("end:{0}".format(end))

    uri = "{}?t={}&dd_date_start={}&dd_date_end={}&confidence_low=90".format(
        options['apwg_remote'],
        options["apwg_token"],
        start.strftime('%s'),
        end.strftime('%s')
    )

    logger.debug("apwg url: {0}".format(uri))

    session = requests.Session()
    session.headers['User-Agent'] = 'cif-apwg-py/0.0.3a'
    logger.info("pulling apwg data")
    body = session.get(uri)
    body = json.loads(body.content)

    body = body['_embedded']['phish']

    if len(body):
        if options.get("limit"):
            body = body[-int(options["limit"]):]

        body = [
            {
                "observable": e["url"].lower().lstrip('3d'),
                "reporttime": datetime.fromtimestamp(e["modified"]).strftime("%Y-%m-%dT%H:%M:%SZ"),
                "firsttime": datetime.fromtimestamp(e["date_discovered"]).strftime("%Y-%m-%dT%H:%M:%SZ"),
                "lasttime": datetime.fromtimestamp(e['date_discovered']).strftime("%Y-%m-%dT%H:%M:%SZ"),
                "tags": ["phishing"],
                "description": e["brand"].lower(),
                "confidence": options["confidence"],
                "tlp": options["tlp"],
                "group": options["group"],
                "otype": "url",
                "provider": "apwg.org",
                "altid_tlp": options['altid_tlp'],
                "application": ["http", "https"]

            } for e in reversed(body)]

        logger.info("start of data: {0}".format(body[len(body)-1]["reporttime"]))
        logger.info("end of data: {0}".format(body[0]["reporttime"]))
        if not options.get("dry_run"):
            logger.info("submitting {0} observables to CIF: {1}".format(len(body), options["remote"]))
            verify_ssl = True
            if options['no_verify_ssl']:
                verify_ssl = False
            cli = Client(options['token'], remote=options['remote'], verify_ssl=verify_ssl)
            ret = cli.submit(body)
        else:
            pprint(body)
            logger.info("dry run, skipping submit...{}".format(len(body)))
    else:
        logger.info("nothing new to submit...")

    if not options.get("no_last_run") and not options.get("dry_run"):
        with open(os.path.join(options["cache"], "lastrun"), "w") as f:
            f.write(str(end))
示例#10
0
def main():

    #
    # initialize module
    #

    p = ArgumentParser(description=textwrap.dedent('''\
        example usage:
            $ cat test.eml | cgmail
            $ cgmail --file test.eml
        '''),
                       formatter_class=RawDescriptionHelpFormatter,
                       prog='cgmail')

    p.add_argument("-v",
                   "--verbose",
                   dest="verbose",
                   action="count",
                   help="set verbosity level [default: %(default)s]")
    p.add_argument('-d', '--debug', dest='debug', action="store_true")

    p.add_argument("-f", "--file", dest="file", help="specify email file")

    # cif arguments
    p.add_argument("--confidence",
                   help="specify confidence for submitting to CIF",
                   default=CONFIDENCE)
    p.add_argument("--remote", help="specify CIF remote")
    p.add_argument("--token", help="specify CIF token")
    p.add_argument("--config",
                   help="specify CIF config [default: %(default)s",
                   default=os.path.expanduser("~/.cif.yml"))
    p.add_argument("--tags",
                   help="specify CIF tags [default: %(default)s",
                   default=["phishing"])
    p.add_argument("--group",
                   help="specify CIF group [default: %(default)s",
                   default="everyone")
    p.add_argument("--tlp",
                   help="specify CIF TLP [default: %(default)s",
                   default=TLP)
    p.add_argument("--no-verify-ssl", action="store_true", default=False)
    p.add_argument("--raw",
                   action="store_true",
                   help="include raw message data")
    p.add_argument("--provider",
                   help="specify feed provider [default: %(default)s]",
                   default=PROVIDER)

    p.add_argument('--exclude',
                   help='url patterns to exclude [default: %(default)s',
                   default=EXCLUDE)
    p.add_argument('--confidence-lower',
                   help='patterns to automatically lower confidence',
                   default=CONFIDENCE_LOWER)
    p.add_argument('-n',
                   '--not-really',
                   help='do not submit',
                   action='store_true')
    p.add_argument('--cache',
                   help='location to cache whitelist [default: %(default)s',
                   default=WHITELIST_CACHE)
    p.add_argument('--blacklist-cache', default=BLACKLIST_CACHE)

    # Process arguments
    args = p.parse_args()
    setup_logging(args)
    logger = logging.getLogger(__name__)

    exclude = None
    if args.exclude:
        exclude = re.compile(args.exclude)

    confidence_lower = None
    if args.confidence_lower:
        confidence_lower = re.compile(args.confidence_lower)

    o = read_config(args)
    options = vars(args)
    for v in options:
        if options[v] is None:
            options[v] = o.get(v)

    if not options.get('token'):
        raise RuntimeError('missing --token')

    if options.get("file"):
        with open(options["file"]) as f:
            email = f.read()
    else:
        email = sys.stdin.read()

    # extract urls from message body and mail parts
    bits = cgmail.parse_email_from_string(email)
    urls = set()

    for n in bits:
        if n.get('urls'):
            for u in n['urls']:
                urls.add(u)

    verify_ssl = True
    if options.get('no_verify_ssl'):
        verify_ssl = False

    # initialize cif client
    cli = Client(remote=options["remote"],
                 token=options["token"],
                 verify_ssl=verify_ssl)

    update_cache = True
    if os.path.isfile(args.cache):
        modified = os.path.getmtime(args.cache)
        if arrow.utcnow() < arrow.get(modified + 84600):
            update_cache = False

    if update_cache:
        # pull FQDN whitelist

        filters = {
            'tags': 'whitelist',
            'otype': 'fqdn',
            'confidence': 25,
        }
        now = arrow.utcnow()
        filters['reporttimeend'] = '{0}Z'.format(
            now.format('YYYY-MM-DDTHH:mm:ss'))
        now = now.shift(days=-7)
        filters['reporttime'] = '{0}Z'.format(
            now.format('YYYY-MM-DDTHH:mm:ss'))

        ret = cli.search(limit=50000,
                         filters=filters,
                         sort='reporttime',
                         sort_direction='desc')
        with open(args.cache, 'w') as f:
            for r in ret:
                f.write("{0}\n".format(r['observable']))

    update_cache = True
    if os.path.isfile(args.blacklist_cache):
        modified = os.path.getmtime(args.blacklist_cache)
        if arrow.utcnow() < arrow.get(modified + 84600):
            update_cache = False

    if update_cache:
        filters = {
            'tags': 'phishing,suspicious,malware',
            'otype': 'fqdn',
            'confidence': 75,
        }
        now = arrow.utcnow()
        filters['reporttimeend'] = '{0}Z'.format(
            now.format('YYYY-MM-DDTHH:mm:ss'))
        now = now.shift(days=-7)
        filters['reporttime'] = '{0}Z'.format(
            now.format('YYYY-MM-DDTHH:mm:ss'))

        ret = cli.search(limit=50000,
                         filters=filters,
                         sort='reporttime',
                         sort_direction='desc')
        with open(args.blacklist_cache, 'w') as f:
            for r in ret:
                f.write("{0}\n".format(r['observable']))

    fqdns = set()
    with open(args.cache) as f:
        for l in f:
            fqdns.add(l.rstrip("\n"))

    fqdns_blacklist = set()
    with open(args.blacklist_cache) as f:
        for l in f:
            fqdns_blacklist.add(l.rstrip("\n"))

    for u in urls:
        u = u.rstrip('\/')
        u = urlparse(u)

        fqdn = url_to_fqdn(u.geturl())
        if exclude and exclude.search(fqdn):
            continue

        confidence = options['confidence']

        if match_whitelist(fqdns, u.netloc):
            if (u.netloc not in URL_SHORTNERS) and (not match_whitelist(
                    HOSTING_PROVIDERS, u.netloc)):
                confidence = options['confidence'] - 15
            else:
                confidence = options['confidence'] + 5
        elif match_whitelist(fqdns_blacklist, u.netloc):
            confidence = options['confidence'] + 10
        else:
            confidence = options['confidence'] + 5

        # else
        # raise confidence
        logger.info("submitting: {0}".format(u.geturl()))

        o = Observable(observable=u.geturl(),
                       confidence=confidence,
                       tlp=options["tlp"],
                       group=options["group"],
                       tags=options["tags"],
                       provider=options.get('provider'))

        o = o.__dict__
        del o['logger']

        if options.get('raw'):
            o.raw = email

        if not args.not_really:
            r = cli.submit(o)
            logger.info("submitted: {0}".format(r))
示例#11
0
def main():
    p = ArgumentParser(description=textwrap.dedent('''\
        example usage:
            $ export APWG_REMOTE=https://api.apwg.org/endpoint
            $ export APWG_TOKEN=123123123
            
            $ export CIF_REMOTE=https://csirtgadgets.org
            $ export CIF_TOKEN=123412341234
            $ cif-apwg-submit -v
        '''),
                       formatter_class=RawDescriptionHelpFormatter,
                       prog='cif-apwg-submit')

    p.add_argument("-v",
                   "--verbose",
                   dest="verbose",
                   action="count",
                   help="set verbosity level [default: %(default)s]")
    p.add_argument('-d', '--debug', dest='debug', action="store_true")

    p.add_argument('--no-verify-ssl', action="store_true", default=False)

    # apwg options

    p.add_argument("--limit",
                   dest="limit",
                   help="limit the number of records processed")
    p.add_argument("--cache",
                   default=os.path.join(os.path.expanduser("~"),
                                        ".cif/apwg_submit"))
    p.add_argument("--past-minutes",
                   help="number of hours to go back and retrieve",
                   default=10)
    p.add_argument("--apwg-confidence", default="90")

    # cif options
    p.add_argument('--tlp', default=TLP)
    p.add_argument('--altid-tlp', default=TLP)
    p.add_argument('--altid')
    p.add_argument('--confidence', default=CONFIDENCE)
    p.add_argument('--tags', default="phishing")
    p.add_argument('--otype', default='url')
    p.add_argument('--itype', default='url')
    p.add_argument('--filters', default=None)
    p.add_argument('--group', default="everyone")

    p.add_argument("--dry-run",
                   help="do not submit to CIF",
                   action="store_true")

    p.add_argument("--no-last-run",
                   help="do not modify lastrun file",
                   action="store_true")

    args = p.parse_args()

    filters = {
        "confidence": args.confidence,
        "tags": args.tags.split(','),
        "otype": args.otype,
        "itype": args.itype,
        "group": args.group,
    }

    if args.filters:
        for e in args.filters.split('&', 1):
            k = e.split('=', 1)
            filters[k[0]] = k[1]

    loglevel = logging.WARNING
    if args.verbose:
        loglevel = logging.INFO
    if args.debug:
        loglevel = logging.DEBUG

    console = logging.StreamHandler()
    logging.getLogger('').setLevel(loglevel)
    console.setFormatter(logging.Formatter(LOG_FORMAT))
    logging.getLogger('').addHandler(console)
    logger = logging.getLogger(__name__)

    if not APWG_REMOTE and not args.dry_run:
        print(
            "\nAPWG_REMOTE missing.\nConsult with [email protected] for more information"
        )
        raise SystemExit

    if not os.path.isdir(args.cache):
        os.makedirs(args.cache)

    end = datetime.utcnow()

    lastrun = os.path.join(args.cache, "lastrun")
    logger.debug(lastrun)
    if os.path.exists(lastrun):
        with open(lastrun) as f:
            start = f.read().strip("\n")
            start = datetime.strptime(start, '%Y-%m-%d %H:%M:%S.%f')
    else:
        minutes = int(args.past_minutes)
        start = end - timedelta(minutes=minutes, seconds=-1)

    start = start.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
    end = end.strftime("%Y-%m-%dT%H:%M:%S.%fZ")

    logger.info("start:{0}".format(start))
    logger.info("end:{0}".format(end))

    filters['reporttime'] = start
    filters['reporttimeend'] = end

    logger.debug(json.dumps(filters, indent=4))

    # get data
    verify_ssl = True
    if args.no_verify_ssl:
        verify_ssl = False

    from apwgsdk.client import Client as APWGClient
    apwg = APWGClient()
    cli = Client(CIF_TOKEN, remote=CIF_REMOTE, verify_ssl=verify_ssl)
    data = cli.search(limit=args.limit, filters=filters)

    for d in data:
        if d.get('observable'):
            d['indicator'] = d['observable']

        if args.altid and not d.get('altid', '').startswith(args.altid):
            logger.debug('skipping.. %s' % d['indicator'])
            # if the altid doesn't match, skip it..
            continue

        if logger.getEffectiveLevel() == logging.DEBUG:
            logger.debug("%s" % json.dumps(d, indent=4))

        if args.dry_run:
            logger.info(json.dumps(d, indent=4))
            continue

        if logger.getEffectiveLevel() == logging.DEBUG:
            logger.debug('sending: %s' % json.dumps(d, indent=4))
        else:
            logger.info('sending: %s' % d['indicator'])

        # submit to apwg
        try:
            r = apwg.indicators_create(indicator=d['indicator'],
                                       confidence=args.apwg_confidence,
                                       description='phishing',
                                       lasttime=d['lasttime'])

            logger.info('indicator created successfully: {}'.format(r['id']))
            if args.debug:
                pprint(r)

        except Exception as e:
            logger.debug(e)
            logger.error('error creating indicator')

    if not args.no_last_run and not args.dry_run:
        with open(os.path.join(args.cache, "lastrun"), "w") as f:
            f.write(str(end))
示例#12
0
def main():
    p = ArgumentParser(
        description=textwrap.dedent('''\
        example usage:
            $ cat test.eml | cif-procmail -v
            $ cif-procmail --file test.eml
        '''),
        formatter_class=RawDescriptionHelpFormatter,
        prog='cif-procmail'
    )

    p.add_argument("-v", "--verbose", dest="verbose", action="count",
                   help="set verbosity level [default: %(default)s]")
    p.add_argument('-d', '--debug', dest='debug', action="store_true")

    p.add_argument("-f", "--file", dest="file", help="specify email file")

    # cif arguments
    p.add_argument("--confidence", help="specify confidence for submitting to CIF", default="65")
    p.add_argument("--remote", help="specify CIF remote [default: %(default)s",
                   default=REMOTE_DEFAULT)
    p.add_argument("--token", help="specify CIF token")
    p.add_argument("--config", help="specify CIF config [default: %(default)s",
                   default=os.path.expanduser("~/.cif.yml"))
    p.add_argument("--tags", help="specify CIF tags [default: %(default)s", default=["phishing"])
    p.add_argument("--group", help="specify CIF group [default: %(default)s", default="everyone")
    p.add_argument("--tlp", help="specify CIF TLP [default: %(default)s", default="amber")
    p.add_argument("--no-verify-ssl", action="store_true", default=False)
    p.add_argument("--raw", help="include raw message data")
    p.add_argument("--raw-headers", help="include raw header data", action="store_true")
    p.add_argument("--provider", help="specify feed provider [default: %(default)s", default="localhost")

    args = p.parse_args()

    loglevel = logging.WARNING
    if args.verbose:
        loglevel = logging.INFO
    if args.debug:
        loglevel = logging.DEBUG

    console = logging.StreamHandler()
    logging.getLogger('').setLevel(loglevel)
    console.setFormatter(logging.Formatter(LOG_FORMAT))
    logging.getLogger('').addHandler(console)
    logger = logging.getLogger(__name__)

    options = vars(args)

    if os.path.isfile(args.config):
        f = file(args.config)
        config = yaml.load(f)
        f.close()
        if not config['client']:
            raise Exception("Unable to read " + args.config + " config file")
        config = config['client']
        for k in config:
            if not options.get(k):
                options[k] = config[k]

        if config.get("remote") and options["remote"] == REMOTE_DEFAULT:
            options["remote"] = config["remote"]

    else:
        logger.info("{} config does not exist".format(args.config))

    if not options.get("remote"):
        logger.critical("missing --remote")
        raise SystemExit

    if options.get("file"):
        with open(options["file"]) as f:
            msg = email.message_from_file(f)
    else:
        msg = sys.stdin.read()
        msg = email.message_from_string(msg)

    urls = set()
    # plain text vs html

    #print msg.get_content_type()
    if msg.is_multipart():
        msgs = msg.get_payload()
        for i, m in enumerate(msgs[1:]):
            html = str(m.get_payload())  ## TODO
            urls.update(extract_urls(html))
    else:
        content = msg.get_payload()
        if not msg.get_content_type.startswith("text/plain"):
            html = True
        urls.update(extract_urls(content, html=html))

    pprint(urls)

    cli = Client(remote=options["remote"], token=options["token"], no_verify_ssl=options["no_verify_ssl"])

    for u in urls:
        logger.info("submitting: {0}".format(u))
        raw = html
        if options.get("raw_headers"):
            raw = msg.as_string()

        o = Observable(
            observable=u,
            confidence=options["confidence"],
            tlp=options["tlp"],
            group=options["group"],
            tags=options["tags"],
            raw=raw,
            provider=options["provider"]
        )
        r = cli.submit(submit=str(o))
        logger.info("submitted: {0}".format(r))
示例#13
0
def main():
    p = ArgumentParser(description=textwrap.dedent('''\
        example usage:
            $ export APWG_REMOTE=https://api.apwg.org/endpoint
            $ export APWG_TOKEN=123123123
            $ cif-apwg -v
        '''),
                       formatter_class=RawDescriptionHelpFormatter,
                       prog='cif-apwg')

    p.add_argument("-v",
                   "--verbose",
                   dest="verbose",
                   action="count",
                   help="set verbosity level [default: %(default)s]")
    p.add_argument('-d', '--debug', dest='debug', action="store_true")

    p.add_argument("--config",
                   dest="config",
                   help="specify a configuration file [default: %(default)s]",
                   default=os.path.join(os.path.expanduser("~"),
                                        DEFAULT_CONFIG))

    p.add_argument("--token", dest="token", help="specify token")
    p.add_argument("--remote", dest="remote", help="specify the CIF remote")
    p.add_argument("--group",
                   dest="group",
                   help="specify CIF group [default: %(default)s]",
                   default="everyone")
    p.add_argument('--no-verify-ssl', action="store_true", default=False)

    # apwg options

    p.add_argument("--limit",
                   dest="limit",
                   help="limit the number of records processed")
    p.add_argument("--apwg-token",
                   help="specify an APWG token",
                   required=True,
                   default=APWG_TOKEN)
    p.add_argument("--format", default="json")
    p.add_argument("--cache",
                   default=os.path.join(os.path.expanduser("~"), ".cif/apwg"))
    p.add_argument("--apwg-remote", default=APWG_REMOTE, required=True)
    p.add_argument("--past-hours",
                   help="number of hours to go back and retrieve",
                   default="24")
    p.add_argument("--apwg-confidence-low", default="65")
    p.add_argument("--apwg-confidence-high", default="100")
    p.add_argument('--tlp', default=TLP)
    p.add_argument('--altid-tlp', default=TLP)
    p.add_argument('--confidence', default=CONFIDENCE)

    p.add_argument("--dry-run",
                   help="do not submit to CIF",
                   action="store_true")

    p.add_argument("--no-last-run",
                   help="do not modify lastrun file",
                   action="store_true")

    args = p.parse_args()

    loglevel = logging.WARNING
    if args.verbose:
        loglevel = logging.INFO
    if args.debug:
        loglevel = logging.DEBUG

    console = logging.StreamHandler()
    logging.getLogger('').setLevel(loglevel)
    console.setFormatter(logging.Formatter(LOG_FORMAT))
    logging.getLogger('').addHandler(console)
    logger = logging.getLogger(__name__)

    if not args.apwg_remote:
        print(
            "\nAPWG_REMOTE missing.\nConsult with [email protected] for more information"
        )
        raise SystemExit

    options = vars(args)

    if os.path.isfile(args.config):
        f = file(args.config)
        config = yaml.load(f)
        f.close()
        if not config['client']:
            raise Exception("Unable to read " + args.config + " config file")
        config = config['client']
        for k in config:
            if not options.get(k):
                options[k] = config[k]

    if not os.path.isdir(options["cache"]):
        os.makedirs(options["cache"])

    end = datetime.utcnow()

    lastrun = os.path.join(options["cache"], "lastrun")
    logger.debug(lastrun)
    if os.path.exists(lastrun):
        with open(lastrun) as f:
            start = f.read().strip("\n")
            start = datetime.strptime(start, '%Y-%m-%d %H:%M:%S.%f')
    else:
        hours = int(options["past_hours"])
        start = end - timedelta(hours=hours, seconds=-1)

    logger.info("start:{0}".format(start))
    logger.info("end:{0}".format(end))

    uri = "{}?t={}&dd_date_start={}&dd_date_end={}&confidence_low=90".format(
        options['apwg_remote'], options["apwg_token"], start.strftime('%s'),
        end.strftime('%s'))

    logger.debug("apwg url: {0}".format(uri))

    session = requests.Session()
    session.headers['User-Agent'] = 'cif-apwg-py/0.0.3a'
    logger.info("pulling apwg data")
    body = session.get(uri)
    body = json.loads(body.content)

    body = body['_embedded']['phish']

    if len(body):
        if options.get("limit"):
            body = body[-int(options["limit"]):]

        body = [{
            "observable":
            e["url"].lower().lstrip('3d'),
            "reporttime":
            datetime.fromtimestamp(
                e["modified"]).strftime("%Y-%m-%dT%H:%M:%SZ"),
            "firsttime":
            datetime.fromtimestamp(
                e["date_discovered"]).strftime("%Y-%m-%dT%H:%M:%SZ"),
            "lasttime":
            datetime.fromtimestamp(
                e['date_discovered']).strftime("%Y-%m-%dT%H:%M:%SZ"),
            "tags": ["phishing"],
            "description":
            e["brand"].lower(),
            "confidence":
            options["confidence"],
            "tlp":
            options["tlp"],
            "group":
            options["group"],
            "otype":
            "url",
            "provider":
            "apwg.org",
            "altid_tlp":
            options['altid_tlp'],
            "application": ["http", "https"]
        } for e in reversed(body)]

        logger.info("start of data: {0}".format(body[len(body) -
                                                     1]["reporttime"]))
        logger.info("end of data: {0}".format(body[0]["reporttime"]))
        if not options.get("dry_run"):
            logger.info("submitting {0} observables to CIF: {1}".format(
                len(body), options["remote"]))
            verify_ssl = True
            if options['no_verify_ssl']:
                verify_ssl = False
            cli = Client(options['token'],
                         remote=options['remote'],
                         verify_ssl=verify_ssl)
            ret = cli.submit(body)
        else:
            pprint(body)
            logger.info("dry run, skipping submit...{}".format(len(body)))
    else:
        logger.info("nothing new to submit...")

    if not options.get("no_last_run") and not options.get("dry_run"):
        with open(os.path.join(options["cache"], "lastrun"), "w") as f:
            f.write(str(end))