
Reconocimiento OSINT centrado en la organización y opinado, inspirado en recon-ng y Maltego
Aviso
Este proyecto está solo parcialmente completo y aún tengo que implementar muchas de las características descritas en la siguiente publicación de blog que hice: https://penafieljlm.com/2017/07/14/inquisitor/.
Inquisitor es una herramienta simple para recolectar información sobre empresas y organizaciones mediante el uso de fuentes de Inteligencia de Fuentes Abiertas (OSINT). Está fuertemente inspirada en cómo operan Maltego y recon-ng, y la herramienta básicamente reimplementa algunas de las características de esas herramientas pero agrega una capa adicional de semántica basada en opiniones sobre los tipos de activos para crear un flujo de trabajo fácil de usar.
Las características clave de Inquisitor incluyen:
Todo el concepto de Inquisitor gira en torno a la idea de extraer información de fuentes abiertas basándose en lo que ya se conoce sobre una organización objetivo. En el contexto de Inquisitor, esto se llama "transformaciones". También se puede recuperar información relacionada inmediatamente de un activo conocido basándose en metadatos también recuperables de fuentes abiertas como whois y registros de internet.
Los conceptos se discuten en mayor detalle en este artículo del blog: https://penafieljlm.com/2017/07/14/inquisitor/
Para instalar Inquisitor, simplemente clone el repositorio, ingrese a él y ejecute el script de instalación.``` pip install Cython click git clone [email protected]:penafieljlm/inquisitor.git cd inquisitor python setup.py install
## Uso
Inquisitor tiene cinco comandos básicos que incluyen `scan`, `status`, `classify`, `dump`, y `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 el modo de escaneo, la herramienta ejecuta todas las transformaciones disponibles para todos los activos que tengas en tu Base de Datos de Inteligencia. Asegúrate de crear Claves API para las diversas fuentes OSINT indicadas a continuación y proporcionarlas al script para que no se omitan las transformaciones que utilizan esas fuentes. Además, asegúrate de sembrar tu Base de Datos de Inteligencia con algunos activos objetivo conocidos y propios usando el comando classify primero, porque si la base de datos no contiene ningún activo propio, no habrá nada que transformar.```
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.
### Estado
En el modo de estado, la herramienta simplemente imprime un resumen rápido del estado de su base de datos de escaneo.```
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 el modo classify, podrás añadir manualmente assets y reclasificar assets ya existentes en la Intelligence Database. Debes usar este comando para poblar tu Intelligence Database con activos objetivo conocidos y poseídos.``` 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 el modo de volcado, podrá volcar el contenido de la base de datos de inteligencia en un archivo JSON legible por humanos.```
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 el modo de visualización, podrá obtener una visualización jerárquica del Repositorio de Inteligencia.``` 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.
## Flujo de trabajo
Ahora que conoces las características básicas de Inquisitor, es momento de que aprendas a usarlo *de verdad*. Inquisitor ha sido escrito pensando en los siguientes pasos:
### Siembra
En este paso, tu Base de datos de inteligencia aún no contiene nada. Tendremos que empezar por algún lado, así que procede a sembrar la base de datos con activos que sabes que pertenecen a tu organización objetivo. Puedes hacerlo usando el comando `classify`.
### Escaneo
Ahora la base de datos tiene activos que se sabe que pertenecen a tu organización objetivo. Luego puedes proceder con el escaneo. Puedes hacerlo usando el comando `scan`.
Cuando invocas el comando `scan` en tu Base de datos de inteligencia, Inquisitor ejecuta los métodos `transform` de los activos clasificados como `accepted`. Una vez finalizado el escaneo, obtendrás más activos que potencialmente podrían pertenecer a tu organización objetivo.
Si no obtienes ningún activo nuevo, puedes sembrar tu Base de datos de inteligencia con nueva información, o simplemente pasar a finalizar el proceso y proceder al paso de Informes.
### Clasificación
Si bien Inquisitor realiza una clasificación automática de activos por ti, es posible que no detecte algunos activos que, de hecho, pertenecen a tu organización objetivo.
Cuando esto sucede, tendrás que revisar el contenido de la base de datos y clasificar manualmente los activos. Por lo general, querrás prestar atención a los activos **Registrant**, ya que no hay forma de determinar automáticamente la propiedad para ese tipo de activo. Además, la mayoría de los otros tipos de activos dependen de la clasificación de propiedad de los activos Registrant para determinar si pertenecen a tu objetivo o no, por lo que definitivamente es mejor prestar atención a tus activos Registrant. Adicionalmente, no obtienes muchos activos Registrant en primer lugar, así que no será muy difícil revisarlos.
### Informes
Puedes generar una visualización de los activos que pertenecen a tu organización objetivo usando el comando `visualize` o el comando `dump`.
## Demo
Tengo demostraciones en video de la herramienta en funcionamiento en el siguiente enlace: https://drive.google.com/open?id=0B_O70BVu38TRclo5dWRBWkdTTWc
Sin embargo, no pude grabar completamente la ejecución del comando `scan` ya que mi grabador de pantalla gratuito solo graba hasta 10 minutos.
## Desarrollo
El proyecto Inquisitor está organizado en el siguiente formato:```
.
|-- 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
Tiene tres módulos principales llamados assets, extractors y sources. El script principal se llama inq.
Como desarrollador, estarías principalmente interesado en agregar nuevos tipos de activos al sistema, por lo que la guía para desarrolladores se centraría principalmente en eso.
Antes de pasar a implementar realmente las clases de activos, primero necesitamos entender cómo interactuar con la Intelligence Database, ya que interactuaremos con ella al derivar activos relacionados de nuestras clases de activos.
El código fuente de la Intelligence Database está almacenado en el archivo inquisitor/__init__.py. El nombre real del envoltorio lógico de la Intelligence Database se llama IntelligenceRepository.
Solo necesitas llamar a la función IntelligenceRepository.get_asset_string desde las clases de activos, ya que añadir nuevos activos a la Intelligence Database es responsabilidad del módulo scan en el script inq. Usarías principalmente esta función para crear instancias de activos o recuperarlos de la base de datos si existen. Esta función es importante al devolver activos desde las funciones related y transform de tus clases de activos, ya que crear nuevos objetos de activos es costoso porque algunos de ellos usan recursos de red durante la inicialización.```
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.
### Activos
Para crear un nuevo tipo de activo, crea un nuevo archivo dentro del directorio `inquisitor/assets` y pega el siguiente código esqueleto dentro:```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'
Ahora reemplace las siguientes cadenas con los valores apropiados
ASSET_NAME : Nombre propio de su activo (por ejemplo, Registrant, Host, etc.)ASSET_IDENTIFIER : El nombre del atributo identificador de su activoASSET_NAME_LETTER : La primera letra de su activo en minúsculaASSET_REPOSITORY : Minúscula de la forma plural del nombre de su activoFinalmente, en inquisitor/__init__.py, registre su activo en la lista ASSET_MODULES. Asegúrese de importar su nuevo activo desde el archivo en cuestión.
¡Felicidades! En este punto, ¡ahora tiene un nuevo tipo de activo funcional!
Sin embargo, necesitará implementar los siguientes métodos para asegurarse de que sus activos se correlacionen con otros tipos de activos:``` 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.
## Instalación
**Elige una de las siguientes opciones:**
- **Directo:** `cargo install --git https://github.com/hakaioffsec/navi`
- **Fuente:** Clona y compila: `git clone https://github.com/hakaioffsec/navi && cd navi && cargo build --release >/dev/null 2>&1 && cargo install --path .`
- **Precompilado:** Descarga desde la [página de lanzamientos](https://github.com/hakaioffsec/navi/releases)```
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.
Para obtener más información sobre cómo reportar problemas de seguridad con la comunidad o el proyecto, te animamos a que revises nuestra política de seguridad a través del siguiente enlace.
Política de seguridad``` 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.
Traduce el siguiente contenido de la herramienta Kitploit.
Este es el fragmento 27 de 29 de un documento Markdown más largo que se está traduciendo en secuencia.
El idioma de origen es en.
Idioma de destino: es.
Tipo de contenido: fragmento README 27/29.
REGLAS ESPECÍFICAS DEL FRAGMENTO:
1. Traduce SOLO texto en lenguaje natural. NUNCA traduzcas: bloques de código, comandos de terminal, rutas de archivos, URL, nombres de paquetes, identificadores técnicos, IDs de CVE, nombres de variables de entorno.
2. Preserva TODA la sintaxis Markdown EXACTAMENTE como está.
3. NO agregues encabezados introductorios como "## Fragmento N", "## Parte N", "## Continuación de..." o "## Traducción del fragmento...". NO agregues marcadores "Fin del fragmento N" o "El contenido continúa...".
4. NO agregues marcadores de puntos suspensivos "..." para indicar omisión. Traduce SOLO el texto exacto proporcionado, carácter por carácter en estructura.
5. Los límites de los fragmentos son intencionales. Preserva la estructura para que los fragmentos se puedan concatenar sin problemas sin artefactos visuales.
6. Devuelve SOLO el texto traducido. Sin preámbulos, sin comentarios, sin envolver en bloques de código, sin JSON/YAML/XML, sin arrays, sin objetos, sin esquemas, sin envoltorios clave/valor.
7. Si el fragmento comienza a mitad de un párrafo, continúa traduciendo desde ese punto. No agregues un salto de línea ni sangría al inicio a menos que exista en el origen.
ENTRADA:```
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.
Después de implementar los métodos anteriores, asegúrate de establecer las variables REPOSITORY, ASSET_CLASS y OBJECT_ID al final del código fuente de tu activo.
El modo de escaneo no está completamente probado debido a las cuotas relacionadas con los motores de búsqueda involucrados. Además, este proyecto se realizó con prisa como parte de un hackatón de una semana, por lo que podría haber muchos problemas por ahí. Por favor, crea un ticket de incidencia o contáctame en [email protected] si encuentras un error o tienes alguna pregunta.
Este trabajo se deriva de los enfoques implementados por las herramientas de inteligencia de código abierto Maltego y recon-ng. Complementé estos enfoques con ideas que ya son de conocimiento común (por ejemplo, whois te dice quién es el propietario de un dominio, los subdominios son propiedad de la misma organización que posee su padre - como lo implican los ataques de fuerza bruta de nombres de dominio, las organizaciones son autoritativas sobre los nombres de dominio que poseen, etc.), o son originales y fueron concebidos por mí en mi tiempo personal como parte de mi hobby (por ejemplo, calificaciones de aceptabilidad, diversas transformaciones, herencia de clasificación, etc.).
Ningún componente de este trabajo fue derivado de ningún trabajo que haya realizado para algún empleador en el pasado. Todo el proyecto, incluida la prueba de concepto, fue escrito desde cero y se complementó con ideas de la comunidad de seguridad de la información.