moriarty-project 0.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- moriarty/__init__.py +5 -0
- moriarty/adapters/__init__.py +0 -0
- moriarty/agent/__init__.py +0 -0
- moriarty/assets/modules/.gitkeep +0 -0
- moriarty/assets/modules/asia/douban.yaml +19 -0
- moriarty/assets/modules/asia/kakao.yaml +19 -0
- moriarty/assets/modules/asia/line.yaml +19 -0
- moriarty/assets/modules/asia/mixi.yaml +19 -0
- moriarty/assets/modules/asia/naver.yaml +19 -0
- moriarty/assets/modules/asia/qq.yaml +19 -0
- moriarty/assets/modules/asia/vk.yaml +19 -0
- moriarty/assets/modules/asia/wechat.yaml +19 -0
- moriarty/assets/modules/asia/weibo.yaml +19 -0
- moriarty/assets/modules/asia/xiaohongshu.yaml +19 -0
- moriarty/assets/modules/behance.yaml +47 -0
- moriarty/assets/modules/business/crunchbase.yaml +27 -0
- moriarty/assets/modules/business/fiverr.yaml +32 -0
- moriarty/assets/modules/business/freelancer.yaml +27 -0
- moriarty/assets/modules/business/glassdoor.yaml +27 -0
- moriarty/assets/modules/business/guru.yaml +26 -0
- moriarty/assets/modules/business/indeed.yaml +25 -0
- moriarty/assets/modules/business/monster.yaml +25 -0
- moriarty/assets/modules/business/peopleperhour.yaml +26 -0
- moriarty/assets/modules/business/toptal.yaml +28 -0
- moriarty/assets/modules/business/upwork.yaml +27 -0
- moriarty/assets/modules/business/ziprecruiter.yaml +25 -0
- moriarty/assets/modules/content/buymeacoffee.yaml +27 -0
- moriarty/assets/modules/content/gumroad.yaml +27 -0
- moriarty/assets/modules/content/ko-fi.yaml +32 -0
- moriarty/assets/modules/content/onlyfans.yaml +27 -0
- moriarty/assets/modules/content/patreon.yaml +33 -0
- moriarty/assets/modules/content/substack.yaml +32 -0
- moriarty/assets/modules/creative/500px.yaml +31 -0
- moriarty/assets/modules/creative/artstation.yaml +33 -0
- moriarty/assets/modules/creative/deviantart.yaml +32 -0
- moriarty/assets/modules/creative/flickr.yaml +31 -0
- moriarty/assets/modules/creative/pexels.yaml +26 -0
- moriarty/assets/modules/creative/unsplash.yaml +26 -0
- moriarty/assets/modules/creative/vimeo.yaml +31 -0
- moriarty/assets/modules/crypto/binance.yaml +27 -0
- moriarty/assets/modules/crypto/bitcointalk.yaml +33 -0
- moriarty/assets/modules/crypto/coinbase.yaml +26 -0
- moriarty/assets/modules/crypto/etherscan.yaml +32 -0
- moriarty/assets/modules/crypto/foundation.yaml +28 -0
- moriarty/assets/modules/crypto/kraken.yaml +27 -0
- moriarty/assets/modules/crypto/mirror.yaml +27 -0
- moriarty/assets/modules/crypto/niftygateway.yaml +26 -0
- moriarty/assets/modules/crypto/opensea.yaml +32 -0
- moriarty/assets/modules/crypto/rarible.yaml +27 -0
- moriarty/assets/modules/crypto/superrare.yaml +29 -0
- moriarty/assets/modules/dating/bumble.yaml +25 -0
- moriarty/assets/modules/dating/grindr.yaml +27 -0
- moriarty/assets/modules/dating/happn.yaml +25 -0
- moriarty/assets/modules/dating/her.yaml +27 -0
- moriarty/assets/modules/dating/hinge.yaml +25 -0
- moriarty/assets/modules/dating/match.yaml +25 -0
- moriarty/assets/modules/dating/meetme.yaml +27 -0
- moriarty/assets/modules/dating/okcupid.yaml +25 -0
- moriarty/assets/modules/dating/pof.yaml +25 -0
- moriarty/assets/modules/dating/tinder.yaml +25 -0
- moriarty/assets/modules/dating-nsfw/adultfriendfinder.yaml +28 -0
- moriarty/assets/modules/dating-nsfw/ashley-madison.yaml +26 -0
- moriarty/assets/modules/design/adobe-portfolio.yaml +27 -0
- moriarty/assets/modules/design/carbonmade.yaml +27 -0
- moriarty/assets/modules/design/cgsociety.yaml +27 -0
- moriarty/assets/modules/design/coroflot.yaml +27 -0
- moriarty/assets/modules/design/figma.yaml +27 -0
- moriarty/assets/modules/design/sketch.yaml +26 -0
- moriarty/assets/modules/dev/bitbucket.yaml +35 -0
- moriarty/assets/modules/dev/codeforces.yaml +32 -0
- moriarty/assets/modules/dev/codepen.yaml +34 -0
- moriarty/assets/modules/dev/hackerone.yaml +32 -0
- moriarty/assets/modules/dev/hackthebox.yaml +27 -0
- moriarty/assets/modules/dev/huggingface.yaml +27 -0
- moriarty/assets/modules/dev/kaggle.yaml +32 -0
- moriarty/assets/modules/dev/leetcode.yaml +32 -0
- moriarty/assets/modules/dev/replit.yaml +31 -0
- moriarty/assets/modules/dribbble.yaml +53 -0
- moriarty/assets/modules/ecommerce/etsy.yaml +32 -0
- moriarty/assets/modules/education/duolingo.yaml +32 -0
- moriarty/assets/modules/education/edx.yaml +26 -0
- moriarty/assets/modules/education/khanacademy.yaml +26 -0
- moriarty/assets/modules/education/lynda.yaml +27 -0
- moriarty/assets/modules/education/memrise.yaml +27 -0
- moriarty/assets/modules/education/pluralsight.yaml +27 -0
- moriarty/assets/modules/education/skillshare.yaml +27 -0
- moriarty/assets/modules/education/udacity.yaml +27 -0
- moriarty/assets/modules/email/github_email.yaml +40 -0
- moriarty/assets/modules/email/gravatar.yaml +23 -0
- moriarty/assets/modules/europe/badoo.yaml +19 -0
- moriarty/assets/modules/europe/lovoo.yaml +19 -0
- moriarty/assets/modules/europe/myspace.yaml +19 -0
- moriarty/assets/modules/europe/netlog.yaml +19 -0
- moriarty/assets/modules/europe/ok.yaml +19 -0
- moriarty/assets/modules/europe/skyrock.yaml +19 -0
- moriarty/assets/modules/europe/studivz.yaml +19 -0
- moriarty/assets/modules/europe/tuenti.yaml +19 -0
- moriarty/assets/modules/europe/viadeo.yaml +19 -0
- moriarty/assets/modules/europe/xing.yaml +19 -0
- moriarty/assets/modules/fitness/fitbit.yaml +27 -0
- moriarty/assets/modules/fitness/garmin.yaml +27 -0
- moriarty/assets/modules/fitness/myfitnesspal.yaml +27 -0
- moriarty/assets/modules/fitness/strava.yaml +33 -0
- moriarty/assets/modules/fitness/zwift.yaml +28 -0
- moriarty/assets/modules/food/allrecipes.yaml +27 -0
- moriarty/assets/modules/food/tasty.yaml +27 -0
- moriarty/assets/modules/food/yelp.yaml +32 -0
- moriarty/assets/modules/food/zomato.yaml +28 -0
- moriarty/assets/modules/forums/4chan.yaml +26 -0
- moriarty/assets/modules/forums/8kun.yaml +26 -0
- moriarty/assets/modules/forums/9gag.yaml +26 -0
- moriarty/assets/modules/forums/discourse.yaml +26 -0
- moriarty/assets/modules/forums/disqus.yaml +31 -0
- moriarty/assets/modules/forums/hackernews.yaml +32 -0
- moriarty/assets/modules/forums/launchpad.yaml +27 -0
- moriarty/assets/modules/forums/phpbb.yaml +25 -0
- moriarty/assets/modules/forums/quora.yaml +32 -0
- moriarty/assets/modules/forums/serverfault.yaml +27 -0
- moriarty/assets/modules/forums/slashdot.yaml +28 -0
- moriarty/assets/modules/forums/stackexchange.yaml +32 -0
- moriarty/assets/modules/forums/superuser.yaml +27 -0
- moriarty/assets/modules/forums/vbulletin.yaml +25 -0
- moriarty/assets/modules/forums/xenforo.yaml +25 -0
- moriarty/assets/modules/forums-nsfw/kiwifarms.yaml +25 -0
- moriarty/assets/modules/forums-nsfw/lolcow.yaml +26 -0
- moriarty/assets/modules/gaming/apextracker.yaml +27 -0
- moriarty/assets/modules/gaming/battlenet.yaml +26 -0
- moriarty/assets/modules/gaming/chess.yaml +30 -0
- moriarty/assets/modules/gaming/discord-public.yaml +27 -0
- moriarty/assets/modules/gaming/dotabuff.yaml +32 -0
- moriarty/assets/modules/gaming/epicgames.yaml +25 -0
- moriarty/assets/modules/gaming/faceit.yaml +33 -0
- moriarty/assets/modules/gaming/fortnitetracker.yaml +32 -0
- moriarty/assets/modules/gaming/gog.yaml +26 -0
- moriarty/assets/modules/gaming/itch.yaml +32 -0
- moriarty/assets/modules/gaming/kongregate.yaml +25 -0
- moriarty/assets/modules/gaming/minecraft.yaml +31 -0
- moriarty/assets/modules/gaming/opgg.yaml +32 -0
- moriarty/assets/modules/gaming/origin.yaml +26 -0
- moriarty/assets/modules/gaming/playstation.yaml +30 -0
- moriarty/assets/modules/gaming/roblox.yaml +31 -0
- moriarty/assets/modules/gaming/xbox.yaml +25 -0
- moriarty/assets/modules/github.yaml +68 -0
- moriarty/assets/modules/gitlab.yaml +60 -0
- moriarty/assets/modules/instagram.yaml +48 -0
- moriarty/assets/modules/latam/fotolog.yaml +27 -0
- moriarty/assets/modules/latam/orkut.yaml +26 -0
- moriarty/assets/modules/latam/taringa.yaml +27 -0
- moriarty/assets/modules/learning/coursera.yaml +26 -0
- moriarty/assets/modules/learning/udemy.yaml +26 -0
- moriarty/assets/modules/linkedin.yaml +40 -0
- moriarty/assets/modules/marketplaces/depop.yaml +28 -0
- moriarty/assets/modules/marketplaces/ebay.yaml +32 -0
- moriarty/assets/modules/marketplaces/grailed.yaml +27 -0
- moriarty/assets/modules/marketplaces/mercari.yaml +26 -0
- moriarty/assets/modules/marketplaces/poshmark.yaml +27 -0
- moriarty/assets/modules/marketplaces/reverb.yaml +27 -0
- moriarty/assets/modules/marketplaces/vinted.yaml +28 -0
- moriarty/assets/modules/medium.yaml +44 -0
- moriarty/assets/modules/music/audiomack.yaml +26 -0
- moriarty/assets/modules/music/bandcamp.yaml +30 -0
- moriarty/assets/modules/music/beatport.yaml +28 -0
- moriarty/assets/modules/music/deezer.yaml +26 -0
- moriarty/assets/modules/music/discogs.yaml +32 -0
- moriarty/assets/modules/music/genius.yaml +26 -0
- moriarty/assets/modules/music/lastfm.yaml +30 -0
- moriarty/assets/modules/music/mixcloud.yaml +26 -0
- moriarty/assets/modules/music/reverbnation.yaml +31 -0
- moriarty/assets/modules/music/soundcloud.yaml +31 -0
- moriarty/assets/modules/music/spotify.yaml +26 -0
- moriarty/assets/modules/music/tidal.yaml +26 -0
- moriarty/assets/modules/nsfw/adultwork.yaml +27 -0
- moriarty/assets/modules/nsfw/bongacams.yaml +28 -0
- moriarty/assets/modules/nsfw/cam4.yaml +28 -0
- moriarty/assets/modules/nsfw/chaturbate.yaml +28 -0
- moriarty/assets/modules/nsfw/clips4sale.yaml +27 -0
- moriarty/assets/modules/nsfw/extralunchmoney.yaml +27 -0
- moriarty/assets/modules/nsfw/fansly.yaml +28 -0
- moriarty/assets/modules/nsfw/fetlife.yaml +28 -0
- moriarty/assets/modules/nsfw/iwantclips.yaml +27 -0
- moriarty/assets/modules/nsfw/justforfans.yaml +28 -0
- moriarty/assets/modules/nsfw/loyalfans.yaml +28 -0
- moriarty/assets/modules/nsfw/manyvids.yaml +27 -0
- moriarty/assets/modules/nsfw/myfreecams.yaml +28 -0
- moriarty/assets/modules/nsfw/niteflirt.yaml +26 -0
- moriarty/assets/modules/nsfw/pornhub.yaml +32 -0
- moriarty/assets/modules/nsfw/redtube.yaml +27 -0
- moriarty/assets/modules/nsfw/stripchat.yaml +28 -0
- moriarty/assets/modules/nsfw/xhamster.yaml +27 -0
- moriarty/assets/modules/nsfw/xvideos.yaml +27 -0
- moriarty/assets/modules/nsfw/youporn.yaml +27 -0
- moriarty/assets/modules/photography/eyeem.yaml +25 -0
- moriarty/assets/modules/photography/fotki.yaml +25 -0
- moriarty/assets/modules/photography/photobucket.yaml +26 -0
- moriarty/assets/modules/photography/smugmug.yaml +25 -0
- moriarty/assets/modules/photography/vsco.yaml +27 -0
- moriarty/assets/modules/pinterest.yaml +40 -0
- moriarty/assets/modules/podcasts/anchor.yaml +26 -0
- moriarty/assets/modules/podcasts/castbox.yaml +26 -0
- moriarty/assets/modules/podcasts/podbean.yaml +26 -0
- moriarty/assets/modules/professional/about.yaml +31 -0
- moriarty/assets/modules/professional/academia.yaml +27 -0
- moriarty/assets/modules/professional/angellist.yaml +27 -0
- moriarty/assets/modules/professional/calendly.yaml +26 -0
- moriarty/assets/modules/professional/issuu.yaml +27 -0
- moriarty/assets/modules/professional/mendeley.yaml +27 -0
- moriarty/assets/modules/professional/notion.yaml +27 -0
- moriarty/assets/modules/professional/orcid.yaml +27 -0
- moriarty/assets/modules/professional/producthunt.yaml +31 -0
- moriarty/assets/modules/professional/researchgate.yaml +32 -0
- moriarty/assets/modules/professional/scribd.yaml +27 -0
- moriarty/assets/modules/professional/slideshare.yaml +31 -0
- moriarty/assets/modules/professional/trello.yaml +26 -0
- moriarty/assets/modules/professional/typeform.yaml +27 -0
- moriarty/assets/modules/reddit.yaml +46 -0
- moriarty/assets/modules/regional/amino.yaml +27 -0
- moriarty/assets/modules/regional/ask-fm.yaml +32 -0
- moriarty/assets/modules/regional/babycenter.yaml +26 -0
- moriarty/assets/modules/regional/cafemom.yaml +27 -0
- moriarty/assets/modules/regional/care2.yaml +27 -0
- moriarty/assets/modules/regional/diaspora.yaml +26 -0
- moriarty/assets/modules/regional/ello.yaml +27 -0
- moriarty/assets/modules/regional/gaia.yaml +27 -0
- moriarty/assets/modules/regional/habbo.yaml +27 -0
- moriarty/assets/modules/regional/imvu.yaml +27 -0
- moriarty/assets/modules/regional/lemmy.yaml +27 -0
- moriarty/assets/modules/regional/peertube.yaml +26 -0
- moriarty/assets/modules/regional/pixelfed.yaml +27 -0
- moriarty/assets/modules/regional/plurk.yaml +26 -0
- moriarty/assets/modules/regional/recroom.yaml +27 -0
- moriarty/assets/modules/regional/secondlife.yaml +26 -0
- moriarty/assets/modules/regional/vine-archive.yaml +27 -0
- moriarty/assets/modules/regional/vrchat.yaml +27 -0
- moriarty/assets/modules/regional/weheartit.yaml +27 -0
- moriarty/assets/modules/social/anilist.yaml +27 -0
- moriarty/assets/modules/social/beacons.yaml +26 -0
- moriarty/assets/modules/social/blogger.yaml +27 -0
- moriarty/assets/modules/social/crunchyroll.yaml +27 -0
- moriarty/assets/modules/social/discord.yaml +27 -0
- moriarty/assets/modules/social/dreamwidth.yaml +26 -0
- moriarty/assets/modules/social/facebook.yaml +34 -0
- moriarty/assets/modules/social/goodreads.yaml +32 -0
- moriarty/assets/modules/social/imdb.yaml +27 -0
- moriarty/assets/modules/social/kitsu.yaml +27 -0
- moriarty/assets/modules/social/letterboxd.yaml +32 -0
- moriarty/assets/modules/social/linktree.yaml +26 -0
- moriarty/assets/modules/social/livejournal.yaml +27 -0
- moriarty/assets/modules/social/mastodon.yaml +30 -0
- moriarty/assets/modules/social/minds.yaml +25 -0
- moriarty/assets/modules/social/myanimelist.yaml +32 -0
- moriarty/assets/modules/social/ravelry.yaml +27 -0
- moriarty/assets/modules/social/snapchat.yaml +25 -0
- moriarty/assets/modules/social/telegram.yaml +35 -0
- moriarty/assets/modules/social/tiktok.yaml +35 -0
- moriarty/assets/modules/social/trakt.yaml +28 -0
- moriarty/assets/modules/social/wattpad.yaml +32 -0
- moriarty/assets/modules/social/wordpress-com.yaml +26 -0
- moriarty/assets/modules/sports/espn.yaml +26 -0
- moriarty/assets/modules/sports/untappd.yaml +32 -0
- moriarty/assets/modules/stackoverflow.yaml +47 -0
- moriarty/assets/modules/steam.yaml +47 -0
- moriarty/assets/modules/streaming/caffeine.yaml +25 -0
- moriarty/assets/modules/streaming/dlive.yaml +27 -0
- moriarty/assets/modules/streaming/trovo.yaml +25 -0
- moriarty/assets/modules/travel/airbnb.yaml +26 -0
- moriarty/assets/modules/travel/booking.yaml +26 -0
- moriarty/assets/modules/travel/couchsurfing.yaml +27 -0
- moriarty/assets/modules/travel/tripadvisor.yaml +32 -0
- moriarty/assets/modules/tumblr.yaml +40 -0
- moriarty/assets/modules/twitch.yaml +48 -0
- moriarty/assets/modules/twitter.yaml +39 -0
- moriarty/assets/modules/youtube.yaml +42 -0
- moriarty/assets/templates/cves/CVE-2017-5638.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2018-7600.yaml +30 -0
- moriarty/assets/templates/cves/CVE-2019-11510.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2019-19781.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2020-14882.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2020-14883.yaml +29 -0
- moriarty/assets/templates/cves/CVE-2020-3452.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2020-5902.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2021-21972.yaml +31 -0
- moriarty/assets/templates/cves/CVE-2021-21985.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2021-26084.yaml +30 -0
- moriarty/assets/templates/cves/CVE-2021-41773.yaml +25 -0
- moriarty/assets/templates/cves/CVE-2021-42013.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2021-44228.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2022-0185.yaml +21 -0
- moriarty/assets/templates/cves/CVE-2022-1388.yaml +36 -0
- moriarty/assets/templates/cves/CVE-2022-22954.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2022-22965.yaml +31 -0
- moriarty/assets/templates/cves/CVE-2022-26134.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2023-22515.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2023-22527.yaml +29 -0
- moriarty/assets/templates/cves/CVE-2023-23752.yaml +33 -0
- moriarty/assets/templates/cves/CVE-2023-27350.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2023-2868.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2023-34362.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2023-3519.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2023-4966.yaml +27 -0
- moriarty/assets/templates/default-logins/admin-weak.yaml +40 -0
- moriarty/assets/templates/default-logins/wordpress-default.yaml +38 -0
- moriarty/assets/templates/exposures/aws-credentials.yaml +35 -0
- moriarty/assets/templates/exposures/backup-files.yaml +36 -0
- moriarty/assets/templates/exposures/database-files.yaml +34 -0
- moriarty/assets/templates/exposures/docker-exposed.yaml +31 -0
- moriarty/assets/templates/exposures/env-exposed.yaml +41 -0
- moriarty/assets/templates/exposures/git-exposed.yaml +41 -0
- moriarty/assets/templates/exposures/phpinfo.yaml +36 -0
- moriarty/assets/templates/exposures/svn-exposed.yaml +28 -0
- moriarty/assets/templates/fuzzing/api-endpoints.yaml +39 -0
- moriarty/assets/templates/fuzzing/common-files.yaml +37 -0
- moriarty/assets/templates/fuzzing/open-redirect-fuzz.yaml +35 -0
- moriarty/assets/templates/fuzzing/xss-search-fuzz.yaml +29 -0
- moriarty/assets/templates/git-config.yaml +18 -0
- moriarty/assets/templates/misconfigurations/cors-misconfiguration.yaml +30 -0
- moriarty/assets/templates/misconfigurations/debug-enabled.yaml +29 -0
- moriarty/assets/templates/misconfigurations/directory-listing.yaml +33 -0
- moriarty/assets/templates/misconfigurations/jwt-none-algo.yaml +30 -0
- moriarty/assets/templates/misconfigurations/ssl-tls-weak.yaml +23 -0
- moriarty/assets/templates/vulnerabilities/lfi-basic.yaml +31 -0
- moriarty/assets/templates/vulnerabilities/open-redirect.yaml +31 -0
- moriarty/assets/templates/vulnerabilities/rce-basic.yaml +34 -0
- moriarty/assets/templates/vulnerabilities/sqli-error.yaml +39 -0
- moriarty/assets/templates/vulnerabilities/ssrf-basic.yaml +31 -0
- moriarty/assets/templates/vulnerabilities/xss-reflected.yaml +38 -0
- moriarty/assets/templates/vulnerabilities/xxe-basic.yaml +30 -0
- moriarty/assets/wordlists/subdomains-1000.txt +1063 -0
- moriarty/cli/__init__.py +3 -0
- moriarty/cli/app.py +120 -0
- moriarty/cli/async_utils.py +19 -0
- moriarty/cli/dns.py +83 -0
- moriarty/cli/domain_cmd.py +572 -0
- moriarty/cli/email.py +383 -0
- moriarty/cli/email_investigate.py +224 -0
- moriarty/cli/intelligence.py +329 -0
- moriarty/cli/output.py +62 -0
- moriarty/cli/rdap.py +94 -0
- moriarty/cli/state.py +38 -0
- moriarty/cli/tls.py +91 -0
- moriarty/cli/user.py +227 -0
- moriarty/core/cache_backend.py +223 -0
- moriarty/core/config_manager.py +303 -0
- moriarty/correlator/__init__.py +0 -0
- moriarty/data/__init__.py +81 -0
- moriarty/data/ioc/__init__.py +142 -0
- moriarty/data/ioc/matcher.py +254 -0
- moriarty/data/ioc/types.py +267 -0
- moriarty/data/local_intelligence.py +507 -0
- moriarty/data/signature_loaders/__init__.py +103 -0
- moriarty/data/signature_loaders/base.py +54 -0
- moriarty/data/signature_loaders/ioc_feed.py +356 -0
- moriarty/data/signature_loaders/wappalyzer.py +112 -0
- moriarty/dsl/__init__.py +0 -0
- moriarty/dsl/loader.py +99 -0
- moriarty/dsl/schema.py +47 -0
- moriarty/export/__init__.py +0 -0
- moriarty/intelligence/__init__.py +27 -0
- moriarty/intelligence/__main__.py +150 -0
- moriarty/intelligence/config.py +395 -0
- moriarty/intelligence/ioc.py +267 -0
- moriarty/intelligence/signatures.py +550 -0
- moriarty/intelligence/storage.py +501 -0
- moriarty/interop/__init__.py +0 -0
- moriarty/logging/__init__.py +0 -0
- moriarty/logging/config.py +47 -0
- moriarty/models/__init__.py +16 -0
- moriarty/models/assertion.py +24 -0
- moriarty/models/entity.py +22 -0
- moriarty/models/evidence.py +37 -0
- moriarty/models/relation.py +24 -0
- moriarty/models/types.py +28 -0
- moriarty/modules/__init__.py +0 -0
- moriarty/modules/avatar_hash.py +184 -0
- moriarty/modules/directory_fuzzer.py +322 -0
- moriarty/modules/dns_scan.py +40 -0
- moriarty/modules/domain_scanner.py +620 -0
- moriarty/modules/email_check.py +98 -0
- moriarty/modules/email_investigate.py +267 -0
- moriarty/modules/email_security.py +274 -0
- moriarty/modules/googlemaps_lookup.py +106 -0
- moriarty/modules/headless_executor.py +201 -0
- moriarty/modules/orchestrator.py +60 -0
- moriarty/modules/passive_recon.py +444 -0
- moriarty/modules/phone_extractor.py +151 -0
- moriarty/modules/pipeline_orchestrator.py +726 -0
- moriarty/modules/port_scanner.py +129 -0
- moriarty/modules/rdap.py +61 -0
- moriarty/modules/rdap_extended.py +188 -0
- moriarty/modules/stealth_mode.py +610 -0
- moriarty/modules/subdomain_discovery.py +595 -0
- moriarty/modules/technology_profiler.py +361 -0
- moriarty/modules/template_executor.py +239 -0
- moriarty/modules/template_scanner.py +1048 -0
- moriarty/modules/tls_scan.py +46 -0
- moriarty/modules/tls_validator.py +188 -0
- moriarty/modules/vuln_scanner.py +483 -0
- moriarty/modules/waf_detector.py +585 -0
- moriarty/modules/wayback_discovery.py +234 -0
- moriarty/modules/web_crawler.py +163 -0
- moriarty/net/__init__.py +0 -0
- moriarty/net/dns_cache.py +175 -0
- moriarty/net/dns_client.py +188 -0
- moriarty/net/rdap_client.py +52 -0
- moriarty/net/smtp_client.py +114 -0
- moriarty/net/tls_client.py +111 -0
- moriarty/parsers/__init__.py +0 -0
- moriarty/parsers/html_parser.py +136 -0
- moriarty/tests/__init__.py +0 -0
- moriarty/tests/test_email_service.py +17 -0
- moriarty/tests/test_models.py +46 -0
- moriarty/tests/test_orchestrator.py +30 -0
- moriarty/tests/test_tls_client.py +18 -0
- moriarty_project-0.1.6.dist-info/METADATA +388 -0
- moriarty_project-0.1.6.dist-info/RECORD +418 -0
- moriarty_project-0.1.6.dist-info/WHEEL +4 -0
- moriarty_project-0.1.6.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from typing import List, Optional
|
5
|
+
|
6
|
+
from email_validator import EmailNotValidError, validate_email
|
7
|
+
|
8
|
+
from ..net.dns_client import DNSClient, DNSLookupResult
|
9
|
+
from ..net.smtp_client import SMTPClient, SMTPProbeResult
|
10
|
+
|
11
|
+
|
12
|
+
@dataclass(slots=True)
|
13
|
+
class EmailCheckOptions:
|
14
|
+
check_dns: bool = True
|
15
|
+
check_smtp: bool = False
|
16
|
+
from_address: str = "postmaster@localhost"
|
17
|
+
retries: int = 1
|
18
|
+
wait: float = 1.0
|
19
|
+
|
20
|
+
|
21
|
+
@dataclass(slots=True)
|
22
|
+
class EmailCheckResult:
|
23
|
+
input_email: str
|
24
|
+
normalized_email: str
|
25
|
+
local_part: str
|
26
|
+
domain: str
|
27
|
+
dns: Optional[DNSLookupResult]
|
28
|
+
smtp: Optional[SMTPProbeResult]
|
29
|
+
warnings: List[str]
|
30
|
+
|
31
|
+
|
32
|
+
class EmailCheckService:
|
33
|
+
"""Run DNS and SMTP probes for an email address."""
|
34
|
+
|
35
|
+
def __init__(self, email: str, options: EmailCheckOptions, timeout: float = 8.0) -> None:
|
36
|
+
self._raw_email = email
|
37
|
+
self._options = options
|
38
|
+
self._timeout = timeout
|
39
|
+
|
40
|
+
async def run(self) -> EmailCheckResult:
|
41
|
+
normalized = self._normalize_email(self._raw_email)
|
42
|
+
domain = normalized["domain"]
|
43
|
+
local_part = normalized["local_part"]
|
44
|
+
warnings: List[str] = []
|
45
|
+
|
46
|
+
dns_result: Optional[DNSLookupResult] = None
|
47
|
+
if self._options.check_dns:
|
48
|
+
dns_client = DNSClient(timeout=self._timeout)
|
49
|
+
dns_result = await dns_client.lookup_domain(domain)
|
50
|
+
if not dns_result.mx:
|
51
|
+
warnings.append("Domain has no MX records; falling back to A/AAAA for SMTP.")
|
52
|
+
|
53
|
+
smtp_result: Optional[SMTPProbeResult] = None
|
54
|
+
if self._options.check_smtp:
|
55
|
+
smtp_client = SMTPClient(timeout=self._timeout, wait=self._options.wait, retries=self._options.retries)
|
56
|
+
hosts: List[str] = []
|
57
|
+
if dns_result and dns_result.mx:
|
58
|
+
hosts = [record.exchange.rstrip('.') for record in dns_result.mx]
|
59
|
+
elif dns_result and (dns_result.a or dns_result.aaaa):
|
60
|
+
hosts = [domain]
|
61
|
+
else:
|
62
|
+
hosts = [domain]
|
63
|
+
warnings.append("No DNS information available; SMTP probe will target the domain directly.")
|
64
|
+
|
65
|
+
smtp_result = await smtp_client.probe(
|
66
|
+
email=normalized["email"],
|
67
|
+
from_address=self._options.from_address,
|
68
|
+
hosts=hosts,
|
69
|
+
)
|
70
|
+
|
71
|
+
return EmailCheckResult(
|
72
|
+
input_email=self._raw_email,
|
73
|
+
normalized_email=normalized["email"],
|
74
|
+
local_part=local_part,
|
75
|
+
domain=domain,
|
76
|
+
dns=dns_result,
|
77
|
+
smtp=smtp_result,
|
78
|
+
warnings=warnings,
|
79
|
+
)
|
80
|
+
|
81
|
+
@staticmethod
|
82
|
+
def _normalize_email(raw_email: str) -> dict[str, str]:
|
83
|
+
try:
|
84
|
+
validated = validate_email(raw_email, check_deliverability=False)
|
85
|
+
except EmailNotValidError as exc:
|
86
|
+
raise ValueError(f"Invalid email address: {exc}") from exc
|
87
|
+
|
88
|
+
normalized_attr = getattr(validated, "normalized", None)
|
89
|
+
normalized_email = (normalized_attr or validated.email).lower()
|
90
|
+
local_part, domain = normalized_email.split("@", 1)
|
91
|
+
return {
|
92
|
+
"email": normalized_email,
|
93
|
+
"local_part": local_part,
|
94
|
+
"domain": domain,
|
95
|
+
}
|
96
|
+
|
97
|
+
|
98
|
+
__all__ = ["EmailCheckOptions", "EmailCheckResult", "EmailCheckService"]
|
@@ -0,0 +1,267 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
import hashlib
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from typing import Any, Dict, List, Optional
|
7
|
+
|
8
|
+
import httpx
|
9
|
+
import structlog
|
10
|
+
|
11
|
+
from ..models.entity import Entity
|
12
|
+
from ..models.types import EntityKind
|
13
|
+
|
14
|
+
logger = structlog.get_logger(__name__)
|
15
|
+
|
16
|
+
|
17
|
+
@dataclass(slots=True)
|
18
|
+
class SocialProfile:
|
19
|
+
"""Perfil encontrado em uma plataforma."""
|
20
|
+
|
21
|
+
platform: str
|
22
|
+
url: str
|
23
|
+
username: Optional[str] = None
|
24
|
+
user_id: Optional[str] = None
|
25
|
+
display_name: Optional[str] = None
|
26
|
+
avatar_url: Optional[str] = None
|
27
|
+
bio: Optional[str] = None
|
28
|
+
verified: bool = False
|
29
|
+
confidence: float = 0.5
|
30
|
+
metadata: Dict[str, Any] = None
|
31
|
+
|
32
|
+
def __post_init__(self) -> None:
|
33
|
+
if self.metadata is None:
|
34
|
+
self.metadata = {}
|
35
|
+
|
36
|
+
|
37
|
+
@dataclass(slots=True)
|
38
|
+
class EmailInvestigationResult:
|
39
|
+
"""Resultado de uma investigação profunda de email."""
|
40
|
+
|
41
|
+
email: str
|
42
|
+
normalized_email: str
|
43
|
+
domain: str
|
44
|
+
local_part: str
|
45
|
+
|
46
|
+
# Gravatar
|
47
|
+
gravatar_hash: str
|
48
|
+
gravatar_profile: Optional[Dict[str, Any]] = None
|
49
|
+
gravatar_avatar: Optional[str] = None
|
50
|
+
|
51
|
+
# Social profiles
|
52
|
+
social_profiles: List[SocialProfile] = None
|
53
|
+
|
54
|
+
# Breaches
|
55
|
+
breached: bool = False
|
56
|
+
breach_count: int = 0
|
57
|
+
breaches: List[Dict[str, Any]] = None
|
58
|
+
|
59
|
+
# Linked data
|
60
|
+
phone_numbers: List[str] = None
|
61
|
+
websites: List[str] = None
|
62
|
+
usernames: List[str] = None
|
63
|
+
|
64
|
+
# Metadata
|
65
|
+
total_platforms_found: int = 0
|
66
|
+
search_timestamp: str = ""
|
67
|
+
|
68
|
+
def __post_init__(self) -> None:
|
69
|
+
if self.social_profiles is None:
|
70
|
+
self.social_profiles = []
|
71
|
+
if self.breaches is None:
|
72
|
+
self.breaches = []
|
73
|
+
if self.phone_numbers is None:
|
74
|
+
self.phone_numbers = []
|
75
|
+
if self.websites is None:
|
76
|
+
self.websites = []
|
77
|
+
if self.usernames is None:
|
78
|
+
self.usernames = []
|
79
|
+
|
80
|
+
|
81
|
+
class EmailInvestigator:
|
82
|
+
"""Investiga email em múltiplas fontes."""
|
83
|
+
|
84
|
+
def __init__(self, timeout: float = 8.0, user_agent: Optional[str] = None) -> None:
|
85
|
+
self._timeout = timeout
|
86
|
+
self._user_agent = user_agent or "Moriarty/0.1.0 (OSINT Client)"
|
87
|
+
|
88
|
+
async def investigate(self, email: str) -> EmailInvestigationResult:
|
89
|
+
"""Executa investigação completa."""
|
90
|
+
from datetime import datetime, timezone
|
91
|
+
from email_validator import validate_email
|
92
|
+
|
93
|
+
# Normaliza
|
94
|
+
validated = validate_email(email, check_deliverability=False)
|
95
|
+
normalized = validated.normalized or validated.email
|
96
|
+
local_part, domain = normalized.lower().split("@", 1)
|
97
|
+
|
98
|
+
# Gera hash Gravatar
|
99
|
+
gravatar_hash = hashlib.md5(normalized.encode("utf-8")).hexdigest()
|
100
|
+
|
101
|
+
# Executa investigações em paralelo
|
102
|
+
tasks = [
|
103
|
+
self._check_gravatar(gravatar_hash),
|
104
|
+
self._check_social_profiles_smart(email, local_part),
|
105
|
+
self._check_breaches_anon(email),
|
106
|
+
]
|
107
|
+
|
108
|
+
gravatar_data, social_profiles, breach_data = await asyncio.gather(*tasks, return_exceptions=True)
|
109
|
+
|
110
|
+
# Processa Gravatar
|
111
|
+
gravatar_profile = None
|
112
|
+
gravatar_avatar = None
|
113
|
+
if isinstance(gravatar_data, dict):
|
114
|
+
gravatar_profile = gravatar_data
|
115
|
+
gravatar_avatar = f"https://www.gravatar.com/avatar/{gravatar_hash}?s=200&d=404"
|
116
|
+
|
117
|
+
# Processa social
|
118
|
+
profiles: List[SocialProfile] = []
|
119
|
+
if isinstance(social_profiles, list):
|
120
|
+
profiles = social_profiles
|
121
|
+
|
122
|
+
# Processa breaches
|
123
|
+
breached = False
|
124
|
+
breach_count = 0
|
125
|
+
breaches = []
|
126
|
+
if isinstance(breach_data, dict):
|
127
|
+
breached = breach_data.get("breached", False)
|
128
|
+
breach_count = breach_data.get("count", 0)
|
129
|
+
breaches = breach_data.get("breaches", [])
|
130
|
+
|
131
|
+
# Extrai dados agregados
|
132
|
+
usernames = list(set(p.username for p in profiles if p.username))
|
133
|
+
websites = list(set(p.metadata.get("website") for p in profiles if p.metadata.get("website")))
|
134
|
+
|
135
|
+
result = EmailInvestigationResult(
|
136
|
+
email=email,
|
137
|
+
normalized_email=normalized,
|
138
|
+
domain=domain,
|
139
|
+
local_part=local_part,
|
140
|
+
gravatar_hash=gravatar_hash,
|
141
|
+
gravatar_profile=gravatar_profile,
|
142
|
+
gravatar_avatar=gravatar_avatar,
|
143
|
+
social_profiles=profiles,
|
144
|
+
breached=breached,
|
145
|
+
breach_count=breach_count,
|
146
|
+
breaches=breaches,
|
147
|
+
phone_numbers=[],
|
148
|
+
websites=websites,
|
149
|
+
usernames=usernames,
|
150
|
+
total_platforms_found=len(profiles),
|
151
|
+
search_timestamp=datetime.now(timezone.utc).isoformat(),
|
152
|
+
)
|
153
|
+
|
154
|
+
return result
|
155
|
+
|
156
|
+
async def _check_gravatar(self, gravatar_hash: str) -> Optional[Dict[str, Any]]:
|
157
|
+
"""Verifica perfil Gravatar."""
|
158
|
+
url = f"https://www.gravatar.com/{gravatar_hash}.json"
|
159
|
+
|
160
|
+
try:
|
161
|
+
async with httpx.AsyncClient(timeout=self._timeout, follow_redirects=False) as client:
|
162
|
+
response = await client.get(url)
|
163
|
+
|
164
|
+
if response.status_code == 200:
|
165
|
+
data = response.json()
|
166
|
+
return data.get("entry", [{}])[0] if data.get("entry") else None
|
167
|
+
except Exception:
|
168
|
+
pass
|
169
|
+
|
170
|
+
return None
|
171
|
+
|
172
|
+
async def _check_social_profiles_smart(
|
173
|
+
self, email: str, local_part: str
|
174
|
+
) -> List[SocialProfile]:
|
175
|
+
"""Busca inteligente em redes sociais usando templates."""
|
176
|
+
from ..dsl.loader import TemplateLoader
|
177
|
+
from ..modules.template_executor import TemplateExecutor
|
178
|
+
|
179
|
+
profiles: List[SocialProfile] = []
|
180
|
+
|
181
|
+
# Carrega templates
|
182
|
+
loader = TemplateLoader()
|
183
|
+
loader.load_builtin()
|
184
|
+
|
185
|
+
# Filtra apenas templates habilitados
|
186
|
+
templates = [t for t in loader.list_templates() if t.enabled and not t.require_headless]
|
187
|
+
|
188
|
+
if not templates:
|
189
|
+
return profiles
|
190
|
+
|
191
|
+
# Executa templates usando local_part como username
|
192
|
+
executor = TemplateExecutor(timeout=self._timeout, user_agent=self._user_agent)
|
193
|
+
|
194
|
+
# Limita a 20 sites mais relevantes para não demorar muito
|
195
|
+
relevant_templates = templates[:20]
|
196
|
+
|
197
|
+
semaphore = asyncio.Semaphore(10) # Max 10 paralelas
|
198
|
+
|
199
|
+
async def check_site(template: Any) -> Optional[SocialProfile]:
|
200
|
+
async with semaphore:
|
201
|
+
try:
|
202
|
+
result = await executor.execute(template, {"username": local_part})
|
203
|
+
|
204
|
+
if result.exists and result.confidence > 0.5:
|
205
|
+
# Extrai dados relevantes
|
206
|
+
profile = SocialProfile(
|
207
|
+
platform=template.site,
|
208
|
+
url=result.url,
|
209
|
+
username=local_part,
|
210
|
+
display_name=result.extracted.get("name"),
|
211
|
+
bio=result.extracted.get("bio"),
|
212
|
+
avatar_url=result.extracted.get("avatar"),
|
213
|
+
confidence=result.confidence,
|
214
|
+
metadata={
|
215
|
+
"website": result.extracted.get("website"),
|
216
|
+
"location": result.extracted.get("location"),
|
217
|
+
"followers": result.extracted.get("followers"),
|
218
|
+
},
|
219
|
+
)
|
220
|
+
return profile
|
221
|
+
except Exception:
|
222
|
+
pass
|
223
|
+
|
224
|
+
return None
|
225
|
+
|
226
|
+
tasks = [check_site(t) for t in relevant_templates]
|
227
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
228
|
+
|
229
|
+
# Filtra resultados válidos
|
230
|
+
for result in results:
|
231
|
+
if isinstance(result, SocialProfile):
|
232
|
+
profiles.append(result)
|
233
|
+
|
234
|
+
return profiles
|
235
|
+
|
236
|
+
async def _check_breaches_anon(self, email: str) -> Dict[str, Any]:
|
237
|
+
"""Verifica breaches usando HIBP k-anonymity."""
|
238
|
+
# HIBP k-anonymity: hash SHA-1, envia apenas primeiros 5 chars
|
239
|
+
sha1_hash = hashlib.sha1(email.encode("utf-8")).hexdigest().upper()
|
240
|
+
prefix = sha1_hash[:5]
|
241
|
+
suffix = sha1_hash[5:]
|
242
|
+
|
243
|
+
url = f"https://api.pwnedpasswords.com/range/{prefix}"
|
244
|
+
|
245
|
+
try:
|
246
|
+
async with httpx.AsyncClient(timeout=self._timeout) as client:
|
247
|
+
response = await client.get(url, headers={"User-Agent": self._user_agent})
|
248
|
+
|
249
|
+
if response.status_code == 200:
|
250
|
+
# Procura pelo suffix na resposta
|
251
|
+
lines = response.text.split("\n")
|
252
|
+
for line in lines:
|
253
|
+
if line.startswith(suffix):
|
254
|
+
count = int(line.split(":")[1])
|
255
|
+
logger.warning("breach.found", email=email, count=count)
|
256
|
+
return {
|
257
|
+
"breached": True,
|
258
|
+
"count": count,
|
259
|
+
"breaches": [{"source": "HIBP", "occurrences": count}],
|
260
|
+
}
|
261
|
+
except Exception as exc:
|
262
|
+
logger.debug("breach.check.error", error=str(exc))
|
263
|
+
|
264
|
+
return {"breached": False, "count": 0, "breaches": []}
|
265
|
+
|
266
|
+
|
267
|
+
__all__ = ["EmailInvestigator", "EmailInvestigationResult", "SocialProfile"]
|
@@ -0,0 +1,274 @@
|
|
1
|
+
"""Validações avançadas de segurança de email."""
|
2
|
+
import asyncio
|
3
|
+
import hashlib
|
4
|
+
import ssl
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from typing import List, Optional
|
7
|
+
|
8
|
+
import aiodns
|
9
|
+
import httpx
|
10
|
+
import structlog
|
11
|
+
|
12
|
+
logger = structlog.get_logger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
@dataclass
|
16
|
+
class TLSARecord:
|
17
|
+
"""Registro TLSA (DANE)."""
|
18
|
+
usage: int
|
19
|
+
selector: int
|
20
|
+
matching_type: int
|
21
|
+
certificate_data: str
|
22
|
+
|
23
|
+
|
24
|
+
@dataclass
|
25
|
+
class ARCResult:
|
26
|
+
"""Resultado da validação ARC."""
|
27
|
+
present: bool
|
28
|
+
chain_valid: bool
|
29
|
+
instances: int
|
30
|
+
sealer: Optional[str] = None
|
31
|
+
|
32
|
+
|
33
|
+
@dataclass
|
34
|
+
class TLSGrade:
|
35
|
+
"""Avaliação TLS do servidor."""
|
36
|
+
protocol: str # TLSv1.2, TLSv1.3
|
37
|
+
cipher: str
|
38
|
+
grade: str # A+, A, B, C, D, F
|
39
|
+
supports_tls13: bool
|
40
|
+
forward_secrecy: bool
|
41
|
+
vulnerabilities: List[str]
|
42
|
+
|
43
|
+
|
44
|
+
@dataclass
|
45
|
+
class GreylistingStatus:
|
46
|
+
"""Status de greylisting."""
|
47
|
+
detected: bool
|
48
|
+
retry_after: Optional[int] = None # segundos
|
49
|
+
temp_fail_code: Optional[int] = None
|
50
|
+
|
51
|
+
|
52
|
+
class EmailSecurityChecker:
|
53
|
+
"""Verifica recursos avançados de segurança de email."""
|
54
|
+
|
55
|
+
def __init__(self, timeout: float = 10.0):
|
56
|
+
self._timeout = timeout
|
57
|
+
self._resolver = aiodns.DNSResolver(timeout=timeout)
|
58
|
+
|
59
|
+
async def check_tlsa(self, domain: str, port: int = 25) -> List[TLSARecord]:
|
60
|
+
"""
|
61
|
+
Verifica registros TLSA (DANE - DNS-based Authentication of Named Entities).
|
62
|
+
|
63
|
+
Ex: _25._tcp.mail.example.com
|
64
|
+
"""
|
65
|
+
tlsa_domain = f"_{port}._tcp.{domain}"
|
66
|
+
|
67
|
+
try:
|
68
|
+
logger.info("email.security.tlsa.check", domain=tlsa_domain)
|
69
|
+
result = await self._resolver.query(tlsa_domain, "TLSA")
|
70
|
+
|
71
|
+
if not isinstance(result, list):
|
72
|
+
return []
|
73
|
+
|
74
|
+
records = []
|
75
|
+
for record in result:
|
76
|
+
# Formato TLSA: usage selector matching_type certificate_data
|
77
|
+
try:
|
78
|
+
usage = getattr(record, 'usage', 0)
|
79
|
+
selector = getattr(record, 'selector', 0)
|
80
|
+
matching_type = getattr(record, 'matching_type', 0)
|
81
|
+
cert_data = getattr(record, 'cert', '')
|
82
|
+
|
83
|
+
records.append(TLSARecord(
|
84
|
+
usage=usage,
|
85
|
+
selector=selector,
|
86
|
+
matching_type=matching_type,
|
87
|
+
certificate_data=cert_data,
|
88
|
+
))
|
89
|
+
except Exception as e:
|
90
|
+
logger.warning("email.security.tlsa.parse_error", error=str(e))
|
91
|
+
|
92
|
+
logger.info("email.security.tlsa.found", domain=tlsa_domain, count=len(records))
|
93
|
+
return records
|
94
|
+
|
95
|
+
except aiodns.error.DNSError:
|
96
|
+
logger.debug("email.security.tlsa.not_found", domain=tlsa_domain)
|
97
|
+
return []
|
98
|
+
|
99
|
+
async def check_arc(self, mx_host: str) -> ARCResult:
|
100
|
+
"""
|
101
|
+
Verifica suporte a ARC (Authenticated Received Chain).
|
102
|
+
|
103
|
+
ARC é usado para preservar autenticação através de intermediários.
|
104
|
+
Checamos via headers SMTP ou via consulta ao servidor.
|
105
|
+
"""
|
106
|
+
# Simulação - em produção, faria uma conexão SMTP real
|
107
|
+
# e verificaria os headers de uma mensagem test
|
108
|
+
logger.info("email.security.arc.check", mx_host=mx_host)
|
109
|
+
|
110
|
+
# Por agora, retorna placeholder
|
111
|
+
# TODO: Implementar verificação real via SMTP
|
112
|
+
return ARCResult(
|
113
|
+
present=False,
|
114
|
+
chain_valid=False,
|
115
|
+
instances=0,
|
116
|
+
sealer=None,
|
117
|
+
)
|
118
|
+
|
119
|
+
async def grade_tls(self, mx_host: str, port: int = 25) -> TLSGrade:
|
120
|
+
"""
|
121
|
+
Avalia a qualidade da configuração TLS do servidor SMTP.
|
122
|
+
|
123
|
+
Verifica:
|
124
|
+
- Versão do protocolo (TLS 1.2, 1.3)
|
125
|
+
- Cipher suites
|
126
|
+
- Forward secrecy
|
127
|
+
- Vulnerabilidades conhecidas
|
128
|
+
"""
|
129
|
+
logger.info("email.security.tls_grade.start", mx_host=mx_host, port=port)
|
130
|
+
|
131
|
+
vulnerabilities = []
|
132
|
+
|
133
|
+
try:
|
134
|
+
# Cria contexto SSL
|
135
|
+
context = ssl.create_default_context()
|
136
|
+
|
137
|
+
# Tenta conexão TLS
|
138
|
+
reader, writer = await asyncio.wait_for(
|
139
|
+
asyncio.open_connection(mx_host, port, ssl=context),
|
140
|
+
timeout=self._timeout
|
141
|
+
)
|
142
|
+
|
143
|
+
# Obtém informações SSL
|
144
|
+
ssl_object = writer.get_extra_info('ssl_object')
|
145
|
+
if ssl_object:
|
146
|
+
protocol = ssl_object.version()
|
147
|
+
cipher = ssl_object.cipher()
|
148
|
+
|
149
|
+
supports_tls13 = protocol == 'TLSv1.3'
|
150
|
+
|
151
|
+
# Verifica forward secrecy (PFS)
|
152
|
+
cipher_name = cipher[0] if cipher else ""
|
153
|
+
forward_secrecy = any(fs in cipher_name for fs in ['ECDHE', 'DHE'])
|
154
|
+
|
155
|
+
# Determina grade
|
156
|
+
if supports_tls13 and forward_secrecy:
|
157
|
+
grade = "A+"
|
158
|
+
elif protocol == 'TLSv1.2' and forward_secrecy:
|
159
|
+
grade = "A"
|
160
|
+
elif protocol == 'TLSv1.2':
|
161
|
+
grade = "B"
|
162
|
+
elif protocol == 'TLSv1.1':
|
163
|
+
grade = "C"
|
164
|
+
vulnerabilities.append("TLS 1.1 deprecated")
|
165
|
+
else:
|
166
|
+
grade = "F"
|
167
|
+
vulnerabilities.append("Insecure TLS version")
|
168
|
+
|
169
|
+
# Verifica cipher fraco
|
170
|
+
if cipher_name and ('RC4' in cipher_name or 'DES' in cipher_name):
|
171
|
+
vulnerabilities.append("Weak cipher detected")
|
172
|
+
grade = "F"
|
173
|
+
|
174
|
+
writer.close()
|
175
|
+
await writer.wait_closed()
|
176
|
+
|
177
|
+
logger.info("email.security.tls_grade.complete", mx_host=mx_host, grade=grade, protocol=protocol)
|
178
|
+
|
179
|
+
return TLSGrade(
|
180
|
+
protocol=protocol,
|
181
|
+
cipher=cipher_name,
|
182
|
+
grade=grade,
|
183
|
+
supports_tls13=supports_tls13,
|
184
|
+
forward_secrecy=forward_secrecy,
|
185
|
+
vulnerabilities=vulnerabilities,
|
186
|
+
)
|
187
|
+
|
188
|
+
writer.close()
|
189
|
+
await writer.wait_closed()
|
190
|
+
|
191
|
+
except Exception as e:
|
192
|
+
logger.warning("email.security.tls_grade.error", mx_host=mx_host, error=str(e))
|
193
|
+
|
194
|
+
# Fallback: sem TLS
|
195
|
+
return TLSGrade(
|
196
|
+
protocol="None",
|
197
|
+
cipher="",
|
198
|
+
grade="F",
|
199
|
+
supports_tls13=False,
|
200
|
+
forward_secrecy=False,
|
201
|
+
vulnerabilities=["No TLS support detected"],
|
202
|
+
)
|
203
|
+
|
204
|
+
async def detect_greylisting(self, mx_host: str, sender: str, recipient: str) -> GreylistingStatus:
|
205
|
+
"""
|
206
|
+
Detecta se o servidor usa greylisting.
|
207
|
+
|
208
|
+
Greylisting temporariamente rejeita emails desconhecidos
|
209
|
+
com código 4xx, esperando retry.
|
210
|
+
"""
|
211
|
+
logger.info("email.security.greylisting.check", mx_host=mx_host)
|
212
|
+
|
213
|
+
try:
|
214
|
+
# Faz tentativa SMTP inicial
|
215
|
+
reader, writer = await asyncio.wait_for(
|
216
|
+
asyncio.open_connection(mx_host, 25),
|
217
|
+
timeout=self._timeout
|
218
|
+
)
|
219
|
+
|
220
|
+
# Lê banner
|
221
|
+
banner = await reader.read(1024)
|
222
|
+
|
223
|
+
# EHLO
|
224
|
+
writer.write(b"EHLO moriarty.local\r\n")
|
225
|
+
await writer.drain()
|
226
|
+
ehlo_response = await reader.read(1024)
|
227
|
+
|
228
|
+
# MAIL FROM
|
229
|
+
writer.write(f"MAIL FROM:<{sender}>\r\n".encode())
|
230
|
+
await writer.drain()
|
231
|
+
mail_response = await reader.read(1024)
|
232
|
+
|
233
|
+
# RCPT TO
|
234
|
+
writer.write(f"RCPT TO:<{recipient}>\r\n".encode())
|
235
|
+
await writer.drain()
|
236
|
+
rcpt_response = await reader.read(1024)
|
237
|
+
|
238
|
+
writer.write(b"QUIT\r\n")
|
239
|
+
await writer.drain()
|
240
|
+
writer.close()
|
241
|
+
await writer.wait_closed()
|
242
|
+
|
243
|
+
# Analisa resposta
|
244
|
+
rcpt_str = rcpt_response.decode('utf-8', errors='ignore')
|
245
|
+
|
246
|
+
# Códigos 4xx indicam erro temporário (greylisting comum)
|
247
|
+
if rcpt_str.startswith('4'):
|
248
|
+
# Extrai código
|
249
|
+
parts = rcpt_str.split()
|
250
|
+
code = int(parts[0]) if parts else 450
|
251
|
+
|
252
|
+
# Greylisting detectado
|
253
|
+
logger.info("email.security.greylisting.detected", mx_host=mx_host, code=code)
|
254
|
+
return GreylistingStatus(
|
255
|
+
detected=True,
|
256
|
+
temp_fail_code=code,
|
257
|
+
retry_after=300, # Típico: 5 minutos
|
258
|
+
)
|
259
|
+
|
260
|
+
logger.info("email.security.greylisting.not_detected", mx_host=mx_host)
|
261
|
+
return GreylistingStatus(detected=False)
|
262
|
+
|
263
|
+
except Exception as e:
|
264
|
+
logger.warning("email.security.greylisting.error", mx_host=mx_host, error=str(e))
|
265
|
+
return GreylistingStatus(detected=False)
|
266
|
+
|
267
|
+
|
268
|
+
__all__ = [
|
269
|
+
"EmailSecurityChecker",
|
270
|
+
"TLSARecord",
|
271
|
+
"ARCResult",
|
272
|
+
"TLSGrade",
|
273
|
+
"GreylistingStatus",
|
274
|
+
]
|