
Footprinting OSINT dogmatique centré sur l'organisation, inspiré de recon-ng et Maltego.
Avis
Ce projet n'est que partiellement terminé et je n'ai pas encore implémenté beaucoup des fonctionnalités décrites dans l'article de blog suivant que j'ai rédigé : https://penafieljlm.com/2017/07/14/inquisitor/.
Inquisitor est un outil simple pour recueillir des informations sur des entreprises et des organisations en utilisant des sources de renseignement en sources ouvertes (OSINT). Il est fortement inspiré du fonctionnement de Maltego et recon-ng, et l'outil réimplémente en grande partie certaines de leurs fonctionnalités tout en ajoutant une couche supplémentaire de sémantique basée sur les types d'actifs afin de créer un flux de travail facile à utiliser.
Les principales fonctionnalités d'Inquisitor incluent :
L'ensemble du concept d'Inquisitor repose sur l'idée d'extraire des informations de sources ouvertes à partir de ce qui est déjà connu sur une organisation cible. Dans le contexte d'Inquisitor, cela s'appelle des « transformations ». Des informations connexes peuvent également être immédiatement récupérées à partir d'un actif connu en se basant sur des métadonnées également accessibles via des sources ouvertes telles que le whois et les registres Internet.
Les concepts sont discutés plus en détail dans cet article de blog : https://penafieljlm.com/2017/07/14/inquisitor/
Pour installer Inquisitor, clonez simplement le dépôt, entrez dedans et exécutez le script d'installation.``` pip install Cython click git clone [email protected]:penafieljlm/inquisitor.git cd inquisitor python setup.py install
## Utilisation
Inquisitor a cinq commandes de base qui incluent `scan`, `status`, `classify`, `dump`, et `visualize`.```
usage: inq [-h] {scan,status,classify,dump,visualize} ...
optional arguments:
-h, --help show this help message and exit
command:
{scan,status,classify,dump,visualize}
The action to perform.
scan Search OSINT sources for intelligence based on known
assets belonging to the target.
status Prints out the current status of the specified
intelligence database.
classify Classifies an existing asset as either belonging or
not belonging to the target. Adds a new asset with the
specified classification if none is present.
dump Dumps the contents of the database into a JSON file
visualize Create a D3.js visualization based on the contents of
the specified intelligence database.
En mode d'analyse, l'outil exécute toutes les transformations disponibles pour tous les actifs que vous avez dans votre Intelligence Database. Assurez-vous de créer des clés API pour les différentes sources OSINT indiquées ci-dessous et de les fournir au script, sinon les transformations utilisant ces sources seront ignorées. Assurez-vous également d'alimenter votre Intelligence Database avec des actifs cibles possédés connus en utilisant la commande classify en premier, car si la base de données ne contient aucun actif possédé, il n'y aura rien à transformer.```
usage: inq scan [-h] [--google-dev-key GOOGLE_DEV_KEY]
[--google-cse-id GOOGLE_CSE_ID]
[--google-limit GOOGLE_LIMIT]
[--shodan-api-key SHODAN_API_KEY]
[--shodan-limit SHODAN_LIMIT]
DATABASE
positional arguments: DATABASE The path to the intelligence database to use. If specified file does not exist, a new one will be created.
optional arguments: -h, --help show this help message and exit --google-dev-key GOOGLE_DEV_KEY Specifies the developer key to use to query Google Custom Search. Visit the Google APIs Console (http://code.google.com/apis/console) to get an API key. If notspecified, the script will simply skip asset transforms that involve Google Search. --google-cse-id GOOGLE_CSE_ID Specifies the custom search engine to query. Visit the Google Custom Search Console (https://cse.google.com/cse/all) to create your own Google Custom Search Engine. If not specified, the script will simply skip asset transforms that involve Google Search. --google-limit GOOGLE_LIMIT The number of pages to limit Google Search to. This is to avoid exhausting your daily quota. --shodan-api-key SHODAN_API_KEY Specifies the API key to use to query Shodan. Log into your Shodan account (https://www.shodan.io/) and look at the top right corner of the page in order to view your API key. If not specified, the script will simply skip asset transforms that involve Shodan. --shodan-limit SHODAN_LIMIT The number of pages to limit Shodan Search to. This is to avoid exhausting your daily quota.
### Statut
En mode statut, l'outil affiche simplement un résumé rapide de l'état de votre base de données de scans.```
usage: inq status [-h] [-s] DATABASE
positional arguments:
DATABASE The path to the intelligence database to use. If specified
file does not exist, a new one will be created.
optional arguments:
-h, --help show this help message and exit
-s, --strong Indicates if the status will be based on the strong ownership
classification.
En mode classify, vous pourrez ajouter manuellement des actifs et reclassifier des actifs déjà existants dans l'Intelligence Database. Vous devriez utiliser cette commande pour alimenter votre Intelligence Database avec des actifs cibles que vous possédez et qui sont connus.``` usage: inq classify [-h] [-ar REGISTRANT [REGISTRANT ...]] [-ur REGISTRANT [REGISTRANT ...]] [-rr REGISTRANT [REGISTRANT ...]] [-ab BLOCK [BLOCK ...]] [-ub BLOCK [BLOCK ...]] [-rb BLOCK [BLOCK ...]] [-ah HOST [HOST ...]] [-uh HOST [HOST ...]] [-rh HOST [HOST ...]] [-ae EMAIL [EMAIL ...]] [-ue EMAIL [EMAIL ...]] [-re EMAIL [EMAIL ...]] [-al LINKEDIN [LINKEDIN ...]] [-ul LINKEDIN [LINKEDIN ...]] [-rl LINKEDIN [LINKEDIN ...]] DATABASE
positional arguments: DATABASE The path to the intelligence database to use. If specified file does not exist, a new one will be created.
optional arguments: -h, --help show this help message and exit -ar REGISTRANT [REGISTRANT ...], --accept-registrant REGISTRANT [REGISTRANT ...] Specifies a registrant to classify as accepted. -ur REGISTRANT [REGISTRANT ...], --unmark-registrant REGISTRANT [REGISTRANT ...] Specifies a registrant to classify as unmarked. -rr REGISTRANT [REGISTRANT ...], --reject-registrant REGISTRANT [REGISTRANT ...] Specifies a registrant to classify as rejected. -ab BLOCK [BLOCK ...], --accept-block BLOCK [BLOCK ...] Specifies a block to classify as accepted. -ub BLOCK [BLOCK ...], --unmark-block BLOCK [BLOCK ...] Specifies a block to classify as unmarked. -rb BLOCK [BLOCK ...], --reject-block BLOCK [BLOCK ...] Specifies a block to classify as rejected. -ah HOST [HOST ...], --accept-host HOST [HOST ...] Specifies a host to classify as accepted. -uh HOST [HOST ...], --unmark-host HOST [HOST ...] Specifies a host to classify as unmarked. -rh HOST [HOST ...], --reject-host HOST [HOST ...] Specifies a host to classify as rejected. -ae EMAIL [EMAIL ...], --accept-email EMAIL [EMAIL ...] Specifies a email to classify as accepted. -ue EMAIL [EMAIL ...], --unmark-email EMAIL [EMAIL ...] Specifies a email to classify as unmarked. -re EMAIL [EMAIL ...], --reject-email EMAIL [EMAIL ...] Specifies a email to classify as rejected. -al LINKEDIN [LINKEDIN ...], --accept-linkedin LINKEDIN [LINKEDIN ...] Specifies a LinkedIn Account to classify as accepted. -ul LINKEDIN [LINKEDIN ...], --unmark-linkedin LINKEDIN [LINKEDIN ...] Specifies a LinkedIn Account to classify as unmarked. -rl LINKEDIN [LINKEDIN ...], --reject-linkedin LINKEDIN [LINKEDIN ...] Specifies a LinkedIn Account to classify as rejected.
### Dump
En mode dump, vous pourrez exporter le contenu de la base de données de renseignements dans un fichier JSON lisible par un humain.```
usage: inq dump [-h] [-j FILE] [-a] DATABASE
positional arguments:
DATABASE The path to the intelligence database to use. If
specified file does not exist, a new one will be
created.
optional arguments:
-h, --help show this help message and exit
-j FILE, --json FILE The path to dump the JSON file to. Overwrites existing
files.
-a, --all Include rejected assets in dump.
En mode visualisation, vous pourrez acquérir une visualisation hiérarchique du Intelligence Repository.``` usage: inq visualize [-h] [-l] DATABASE
positional arguments: DATABASE The path to the intelligence database to use. If specified file does not exist, a new one will be created.
optional arguments: -h, --help show this help message and exit -l, --last Simply open the last visualization generated instead of creating a new one.
## Flux de travail
Maintenant que vous connaissez les fonctionnalités de base d'Inquisitor, il est temps d'apprendre comment l'utiliser *réellement*. Inquisitor a été conçu en tenant compte des étapes suivantes :
### Amorçage
Dans cette étape, votre base de données de renseignement ne contient encore rien. Il va falloir commencer quelque part, alors allez-y et amorcez la base de données avec des actifs que vous savez appartenir à votre organisation cible. Vous pouvez le faire en utilisant la commande `classify`.
### Analyse
Maintenant que la base de données contient des actifs connus pour appartenir à votre organisation cible. Vous pouvez ensuite procéder à l'analyse. Vous pouvez le faire en utilisant la commande `scan`.
Lorsque vous invoquez la commande `scan` sur votre base de données de renseignement, Inquisitor exécute les méthodes `transform` des actifs qui sont classifiés comme `accepted`. Une fois l'analyse terminée, vous vous retrouverez avec plus d'actifs qui pourraient potentiellement appartenir à votre organisation cible.
Si vous ne vous retrouvez avec aucun nouvel actif, vous pouvez soit amorcer votre base de données de renseignement avec de nouvelles informations, soit simplement conclure le processus en passant à l'étape de rapport.
### Classification
Bien qu'Inquisitor effectue une classification automatique des actifs pour vous, il pourrait ne pas détecter certains actifs qui, en réalité, appartiennent à votre organisation cible.
Lorsque cela se produit, vous devrez vérifier le contenu de la base de données et classifier manuellement les actifs. Généralement, vous voudrez prêter attention aux actifs **Registrant** car il n'existe aucun moyen de déterminer automatiquement la propriété pour ce type d'actif. De plus, la plupart des autres types d'actifs dépendent de la classification de propriété des actifs Registrant pour déterminer s'ils appartiennent à votre cible ou non, il est donc préférable de prêter attention à vos actifs Registrant. En outre, vous n'obtenez pas beaucoup d'actifs Registrant en premier lieu, donc ce ne sera pas très difficile de les trier.
### Rapport
Vous pouvez générer une visualisation des actifs appartenant à votre organisation cible en utilisant la commande `visualize` ou la commande `dump`.
## Démonstration
J'ai des démonstrations vidéo de l'outil en fonctionnement sur le lien suivant : https://drive.google.com/open?id=0B_O70BVu38TRclo5dWRBWkdTTWc
Je n'ai pas pu enregistrer entièrement l'exécution de la commande scan car mon enregistreur d'écran gratuit n'enregistre que jusqu'à 10 minutes.
## Développement
Le projet Inquisitor est structuré selon le format suivant :```
.
|-- README.md
|-- inquisitor
| |-- __init__.py
| |-- assets
| | |-- __init__.py
| | |-- block.py
| | |-- email.py
| | |-- host.py
| | |-- linkedin.py
| | `-- registrant.py
| |-- extractors
| | |-- __init__.py
| | `-- emails.py
| `-- sources
| |-- __init__.py
| |-- google_search.py
| `-- shodan_search.py
|-- inq
|-- report
| `-- index.html
|-- setup.py
`-- tests
|-- __init__.py
`-- test_inq.py
It has three main modules named assets, extractors, and sources. The main script is called inq.
En tant que développeur, vous serez surtout intéressé par l'ajout de nouveaux types d'actifs dans le système, donc le guide du développeur se concentrera principalement sur cela.
Avant de passer à l'implémentation des classes d'actifs, nous devons d'abord comprendre comment interagir avec la base de données de renseignement, car nous interagirons avec elle lorsque nous dériverons des actifs connexes à partir de nos classes d'actifs.
Le code source de la base de données de renseignement est stocké dans le fichier inquisitor/__init__.py. Le nom réel de l'encapsulation logique de la base de données de renseignement est IntelligenceRepository.
Vous n'avez besoin d'appeler la fonction IntelligenceRepository.get_asset_string qu'à partir des classes d'actifs, car l'ajout de nouveaux actifs dans la base de données de renseignement est la responsabilité du module scan du script inq. Vous utiliserez principalement cette fonction pour créer des instances d'actifs ou les récupérer depuis la base de données si elles existent. Cette fonction est importante lors du retour d'actifs depuis les fonctions related et transform de vos classes d'actifs, car la création de nouveaux objets d'actifs est coûteuse puisque certains d'entre eux utilisent des ressources réseau lors de l'initialisation.```
Function
IntelligenceRepository.get_asset_string(asset_type, identifier, create=False, store=False)
Description
Retrieves the primary key and asset object for the asset with the provided
type and identifier.
Parameters
asset_type: class, required
The type of the asset to retrieve from the Intelligence Database. You
will actually have to pass the class object of the asset type you want
to retrieve.
identifier: any, required
The identifier of the asset to retrieve. Consider the identifier as the
unique attribute of an asset object. As for which attribute is to be
used to identify an asset, it depends on the contents of the OBJECT_ID
variable in the asset module.
create: bool, optional, default=False
When no matching asset object is found, a new one will be created and
returned if this parameter is set to True. The new asset will not
necessarily be stored in the Intelligence Database unless specified
using the "store" parameter. However, I suggest you do not do this as
adding assets to the Intelligence Database is the responsibility of
another module.
store: bool, optional, default=False
When a new asset is created when none is found, the new one will be
stored in the Intelligence Database. As said previously, I suggest that
you do not do this as adding assets to the Intelligence Database is the
responsibility of another module.
Returns
A two-element tuple where the first element is the database primary key of
the element returned, and the second element is the deserialized asset
object retrieved from the database.
None if the asset was not found.
If the asset was not found and the create flag was set to True, the primary
key member of the tuple will be set to None.
### Actifs
Pour créer un nouveau type d'actif, créez un nouveau fichier dans le répertoire `inquisitor/assets` et collez le code squelette suivant à l'intérieur :```python
import inquisitor.assets
class ASSET_NAMEValidateException(Exception):
pass
def canonicalize(ASSET_IDENTIFIER):
return ASSET_IDENTIFIER
def main_classify_args(parser):
parser.add_argument(
'-aASSET_NAME_LETTER', '--accept-ASSET_NAME',
metavar='ASSET_NAME',
type=canonicalize,
nargs='+',
help='Specifies a ASSET_NAME to classify as accepted.',
dest='ASSET_NAMEs_accepted',
default=list(),
)
parser.add_argument(
'-uASSET_NAME_LETTER', '--unmark-ASSET_NAME',
metavar='ASSET_NAME',
type=canonicalize,
nargs='+',
help='Specifies a ASSET_NAME to classify as unmarked.',
dest='ASSET_NAMEs_unmarked',
default=list(),
)
parser.add_argument(
'-rASSET_NAME_LETTER', '--reject-ASSET_NAME',
metavar='ASSET_NAME',
type=canonicalize,
nargs='+',
help='Specifies a ASSET_NAME to classify as rejected.',
dest='ASSET_NAME_rejected',
default=list(),
)
def main_classify_canonicalize(args):
accepted = set(args.ASSET_NAMEs_accepted)
unmarked = set(args.ASSET_NAMEs_unmarked)
rejected = set(args.ASSET_NAME_rejected)
redundant = set.intersection(accepted, unmarked, rejected)
if redundant:
raise ValueError(
('Conflicting classifications for ASSET_NAMEs '
': {}').format(list(redundant))
)
accepted = set([canonicalize(a) for a in accepted])
unmarked = set([canonicalize(a) for a in unmarked])
rejected = set([canonicalize(a) for a in rejected])
return (accepted, unmarked, rejected)
class ASSET_NAME(inquisitor.assets.Asset):
def __init__(self, ASSET_IDENTIFIER, owned=None):
super(self.__class__, self).__init__(owned=owned)
self.ASSET_IDENTIFIER = canonicalize(ASSET_IDENTIFIER)
# TODO: Perform other initialization actions here
def __eq__(self, other):
if not isinstance(other, self.__class__):
return False
return self.ASSET_IDENTIFIER == other.ASSET_IDENTIFIER
def related(self, repo):
# Prepare the results
results = set()
# TODO: Create related assets here based on the attributes of this asset
# Return the results
return results
def transform(self, repo, sources):
# Prepare the results
assets = set()
# Google Transforms
if sources.get('google'):
subassets = self.cache_transform_get('google', repo)
if not subassets:
# Acquire API
google = sources['google']
# TODO: Perform Google queries here and the results to 'subassets'
# Cache The Transform
self.cache_transform_store('google', subassets)
assets.update(subassets)
# Shodan Transforms
if sources.get('shodan'):
subassets = self.cache_transform_get('shodan', repo)
if not subassets:
# Acquire API
shodan = sources['shodan']
# TODO: Perform Google queries here and the results to 'subassets'
# Cache The Transform
self.cache_transform_store('shodan', subassets)
assets.update(subassets)
# Return the results
return assets
def is_owned(self, repo):
if self.owned:
return True
# TODO: Automatically determine ownership based on repo contents
return False
def parent_asset(self, repo):
# TODO: Return parent asset based on repo contents
return None
REPOSITORY = 'ASSET_REPOSITORY'
ASSET_CLASS = ASSET_NAME
OBJECT_ID = 'ASSET_IDENTIFIER'
Maintenant, remplacez les chaînes suivantes par les valeurs appropriées
ASSET_NAME : Nom propre de votre actif (par exemple, Registrant, Host, etc.)ASSET_IDENTIFIER : Le nom de l'attribut identifiant de votre actifASSET_NAME_LETTER : La première lettre de votre actif en minusculeASSET_REPOSITORY : Le pluriel du nom de votre actif en minusculesEnfin, dans inquisitor/__init__.py, enregistrez votre actif dans la liste ASSET_MODULES. Assurez-vous d'importer votre nouvel actif depuis le fichier en question.
Félicitations ! Vous disposez maintenant d'un nouveau type d'actif fonctionnel !
Cependant, vous allez devoir implémenter les méthodes suivantes pour garantir que vos actifs soient corrélés avec d'autres types d'actifs :``` Function
related
Description
Returns the set of assets directly related to the asset in question (i.e.
those that can be derived without querying a search engine).
When creating asset objects, make sure you use the
IntelligenceRepository.get_asset_string method instead of instatiating a
new one your self so the asset can be returned from the repository if it
exists.
Set the create flag to True when calling the method in question in order
to return a new object when one isn't found.
Set the store flag to False as appending assets is the job of another
module.
Parameters
repo: IntelligenceRepository
The Intelligence Repository that is being used in the current context.
Returns
Set of assets directly related to the asset in question.
ENTRÉE:```
Function
transform
Description
Returns the set of assets potentially related to the asset in question
(i.e. those that can be derived by querying a search engine).
You may access search engine objects through the provided sources
parameter.
Each search engine object has a transform method which automatically
creates asset objects for you. You just need to provide it the repository
and your query string, and then append the objects it returns to the set
of assets to be returned by your asset's transform method.
Parameters
repo: IntelligenceRepository
The Intelligence Repository that is being used in the current context.
sources: dict
The list of search engine objects that are available for use.
Returns
Set of assets potentially related to the asset in question.
| | Optionnel | WMI | | | | Optionnel | SMB | | | | Optionnel | FTP | |``` Function
is_owned
Description
Determines if there is high confidence that this asset does indeed belong
to the target. Usually checks for any "strong" classification tag first by
looking at the contents of the "owned" variable, before performing
automatic evaluation.
Automatic evaluation depends on what type of asset you're writing. For
example, for a Host asset, the secondary sources of determining ownership
would include looking if its registrant is owned by the target, if it's
parent domain is owned by the target. etc.
Parameters
repo: IntelligenceRepository
The Intelligence Repository that is being used in the current context.
Returns
True it is determined with high confidence that this asset does indeed
belong to the target.
Veuillez fournir le contenu Markdown à traduire.```
Function
parent_asset
Description
Returns the asset object that is considered the parent of this asset
object.
Parameters
repo: IntelligenceRepository
Returns
The asset object that this asset falls under (e.g. a Block is under a
Registrant, a Host is under a Block, a Host is under another Host, an Email
is under a Host, etc. This is primarily used for visualization.
Après avoir implémenté les méthodes ci-dessus, assurez-vous de définir les variables REPOSITORY, ASSET_CLASS et OBJECT_ID en bas du code source de votre actif.
Le mode scan n'est pas entièrement testé en raison des quotas concernant les moteurs de recherche impliqués. De plus, ce projet a été réalisé en urgence dans le cadre d'un hackathon d'une semaine, il pourrait donc y avoir beaucoup de problèmes. Veuillez créer un ticket ou me contacter à [email protected] si vous trouvez un bug ou avez des questions.
Ce travail est dérivé des approches mises en œuvre par les outils de renseignement open source Maltego et recon-ng. J'ai complété ces approches avec des idées qui sont soit déjà de notoriété publique (par exemple, whois vous indique qui est le propriétaire d'un domaine, les sous-domaines appartiennent à la même organisation propriétaire de leur parent – comme le suggèrent les attaques par force brute de noms de domaine, les organisations sont autoritaires des noms de domaine qu'elles possèdent, etc.), soit originales et ont été conçues par moi-même sur mon temps personnel dans le cadre de mon hobby (par exemple, les notes d'acceptabilité, diverses transformations, l'héritage de classification, etc.).
Aucun composant de ce travail n'a été dérivé d'un travail que j'ai effectué pour un employeur dans le passé. L'ensemble du projet, y compris la preuve de concept, a été écrit à partir de zéro et a été enrichi d'idées de la communauté de la sécurité de l'information.