diff --git a/.gitignore b/.gitignore index dc89ab1..3fc7f05 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ lib/.directory plugins/.directory proxy.txt belati.conf +beta/* diff --git a/Belati.py b/Belati.py index 49ad45b..b205ba2 100644 --- a/Belati.py +++ b/Belati.py @@ -32,6 +32,7 @@ import time import dns.resolver import tldextract +import shlex, subprocess from plugins.about_project import AboutProject from plugins.banner_grab import BannerGrab @@ -43,6 +44,7 @@ from plugins.git_finder import GitFinder from plugins.harvest_email import HarvestEmail from plugins.harvest_public_document import HarvestPublicDocument +from plugins.json_beautifier import JsonBeautifier from plugins.logger import Logger from plugins.robots_scraper import RobotsScraper from plugins.scan_nmap import ScanNmap @@ -94,11 +96,11 @@ def __init__(self): self.show_banner() - conf = Config() + self.conf = Config() self.db = Database() # Setup project - self.project_id = self.db.create_new_project(domain, orgcomp, datetime.datetime.now()) + self.project_id = self.db.create_new_project(domain, orgcomp, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')) log.console_log("{}[+] Creating New Belati Project... {}".format(G, W)) log.console_log("---------------------------------------------------------") log.console_log("Project ID: {}".format(str(self.project_id))) @@ -151,6 +153,7 @@ def __init__(self): log.console_log("This feature will be coming soon. Be patient :)") log.console_log("{}All done sir! All log saved in log directory and dowloaded file saved in belatiFiles {}".format(Y, W)) + self.start_web_server() def show_banner(self): banner = """ @@ -196,7 +199,11 @@ def check_domain(self, domain_name, proxy_address): log.console_log("{}[*] Perfoming Whois... {}".format(G, W)) whois_result = check.whois_domain(domain_name) email = re.findall(r'[a-zA-Z0-9._+-]+@[a-zA-Z0-9._+-]+\s*', str(whois_result)) - self.db.insert_domain_result(self.project_id, self.strip_scheme(domain_name), str(whois_result), str(email) ) + + # JSON Beautifier + json_bf = JsonBeautifier() + json_whois = json_bf.beautifier(str(whois_result)) + self.db.insert_domain_result(self.project_id, self.strip_scheme(domain_name), str(json_whois), str(email).strip("\'.\'[]") ) def banner_grab(self, domain_name, proxy_address): @@ -271,7 +278,7 @@ def scan_DNS_zone(self, domain_name): log.console_log("{}{}{}".format(G, ns.to_text(), W)) mx_record_list.append(ns.to_text()) - self.db.update_dns_zone(self.project_id, domain_name, str(ns_record_list), str(mx_record_list)) + self.db.update_dns_zone(self.project_id, domain_name, str(ns_record_list).strip("\'.\'[]"), str(mx_record_list).strip("\'[]")) except Exception, exc: print("{}[*] No response from server... SKIP!{}".format(R, W)) @@ -286,9 +293,10 @@ def harvest_email_search(self, domain_name, proxy_address): except Exception, exc: log.console_log("{}[-] Not found or Unavailable. {}{}".format(R, str(harvest_result), W )) - self.db.insert_email_result(self.project_id, str(harvest_result)) + self.db.insert_email_result(self.project_id, str(harvest_result).strip("\'.\'[]")) def harvest_email_pgp(self, domain_name, proxy_address): + log.console_log("{}[*] Perfoming Email Harvest from PGP Server...{}".format(G, W) ) harvest = HarvestEmail() harvest_result = harvest.crawl_pgp_mit_edu(domain_name, proxy_address) try: @@ -297,7 +305,7 @@ def harvest_email_pgp(self, domain_name, proxy_address): except Exception, exc: log.console_log("{}[-] Not found or Unavailable. {}{}".format(R, str(harvest_result), W )) - self.db.update_pgp_email(self.project_id, str(harvest_result)) + self.db.update_pgp_email(self.project_id, str(harvest_result).strip("\'.\'[]")) def harvest_document(self, domain_name, proxy_address): log.console_log("{}[*] Perfoming Public Document Harvest from Google... {}".format(G, W)) @@ -371,6 +379,20 @@ def gather_company(self, company_name, proxy_address): gather_company = GatherCompany() gather_company.crawl_company_employee(company_name, proxy_address, self.project_id) + def start_web_server(self): + log.console_log("{}Starting Django Web Server at http://127.0.0.1:8000/{}".format(Y, W)) + py_bin = self.conf.get_config("Environment", "py_bin") + command = "{} web/manage.py runserver".format(py_bin) + process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE) + while True: + output = process.stdout.readline() + if output == '' and process.poll() is not None: + break + if output: + log.console_log(output.strip()) + rc = process.poll() + return rc + def check_update(self, version): log.console_log("{} Checking Version Update for Belati... {}".format(G, W)) # TODO diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e56ce4..0a57c32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,3 +20,11 @@ Update Creating Project Info Update Sublist3r Update Gather Company Plugin Update README + +v0.2.2-dev: + +Add Django Web Management +Update Auto Configuration +Add auto start Django +Update output result +Update Gather Company Info diff --git a/README.md b/README.md index d5c5d5d..2a29483 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,11 @@ Belati is tool for Collecting Public Data & Public Document from Website and oth ## Version -v.0.2.1-dev +v.0.2.2-dev + +## Belati In Action + +[Belati In Action Preview](https://www.youtube.com/watch?v=AGvsIWoaX_k) ## Why I Made this? Just for learning stuff and OSINT purpose. Correct me if i'm wrong @@ -28,6 +32,7 @@ Just for learning stuff and OSINT purpose. Correct me if i'm wrong - Gather Public Company Info & Employee - SQLite3 Database Support for storing Belati Results - Setup Wizard/Configuration for Belati +- Django Web Management ## TODO - Automatic OSINT with Username and Email support @@ -38,7 +43,6 @@ Just for learning stuff and OSINT purpose. Correct me if i'm wrong - Email Harvesting with multiple content(github, linkedin, etc) - Scrapping Public Document with multiple search engine(yahoo, yandex, bing etc) - Metadata Extractor -- Web version with Django - Scanning Report export to PDF - domain or subdomain reputation checker - Reporting Support to JSON, PDF @@ -88,6 +92,35 @@ Don't worry. Belati still running when you are run with normal user ;) - tldextract - fake-useragent +## Missing Dependencies? + +If you are seeing this + +``` +$ python Belati.py + + You are missing a module required for Belati. In order to continue using Belati, please install them with: + + `pip install -r requirements.txt` + + or manually install missing modules with: + + `pip install dnspython requests termcolor colorama future beautifulsoup4 futures` +``` + +and this + +``` +You are using pip version 8.1.2, however version 9.0.1 is available. +You should consider upgrading via the 'pip install --upgrade pip' command. +``` + +Please upgrade pip version: + +``` +pip install --upgrade pip +``` + ## System Dependencies For CentOS/Fedora user, please install this: diff --git a/belati.db b/belati.db deleted file mode 100644 index dada8e7..0000000 Binary files a/belati.db and /dev/null differ diff --git a/beta/belati.db b/beta/belati.db deleted file mode 100644 index 819ac6f..0000000 Binary files a/beta/belati.db and /dev/null differ diff --git a/plugins/.directory b/plugins/.directory deleted file mode 100644 index 50e7968..0000000 --- a/plugins/.directory +++ /dev/null @@ -1,4 +0,0 @@ -[Dolphin] -Timestamp=2017,1,5,14,15,21 -Version=3 -ViewMode=1 diff --git a/plugins/about_project.py b/plugins/about_project.py index e945be8..6af3e9e 100644 --- a/plugins/about_project.py +++ b/plugins/about_project.py @@ -24,7 +24,7 @@ class AboutProject(object): def __init__(self): self.__info__ = 'Collecting Public Data & Public Document for OSINT purpose' self.__author__ = 'Petruknisme' - self.__version__ = 'v0.2.1-dev' + self.__version__ = 'v0.2.2-dev' self.__name__= "Belati" self.__giturl__ = "https://github.com/aancw/Belati" self.__authorurl__ = "https://petruknisme.com" diff --git a/plugins/config.py b/plugins/config.py index fe79b93..f405835 100644 --- a/plugins/config.py +++ b/plugins/config.py @@ -38,7 +38,10 @@ class Config(object): def __init__(self): self.config_file = "belati.conf" if os.path.isfile(self.config_file): - pass + db_string = self.get_config("Database", "db_location") + if db_string == "belati.db": + log.console_log("{}[-] Hm... You are using old Belati configuration{}".format(Y, W)) + self.init_config_file() else: log.console_log("{}[-] No Configuration file found. Setting up...{}".format(Y, W)) self.init_config_file() @@ -56,7 +59,19 @@ def set_config(self, conf_section, conf_key, conf_value): def init_config_file(self): log.console_log("\n{} -----> Initiating Configuration <-----\n{}".format(Y, W)) - db_location = raw_input("Please enter Belati Database Location [belati.db]:") or "belati.db" - config.add_section("Database") - self.set_config("Database", "db_location", db_location) + if config.has_section("Database"): + pass + else: + config.add_section("Database") + + self.set_config("Database", "db_location", "web/db.sqlite3") + log.console_log("\n{} Setting database location to {}\n{}".format(Y,"web/db.sqlite3", W)) + python_binary = raw_input("Please enter Python v2.x Binary name [python]:") or "python" + + if config.has_section("Environment"): + pass + else: + config.add_section("Environment") + + self.set_config("Environment", "py_bin", python_binary) diff --git a/plugins/database.py b/plugins/database.py index 41b72c6..ab8323d 100644 --- a/plugins/database.py +++ b/plugins/database.py @@ -41,6 +41,7 @@ def __init__(self): db_location = conf.get_config("Database", "db_location") try: self.conn = db.connect(db_location) + self.conn.text_factory = str except db.Error, e: print("Error: " + str(e.args[0])) sys.exit() @@ -152,9 +153,9 @@ def insert_linkedin_company_info(self, project_id, company_name, company_linkedi return cur.lastrowid - def insert_company_employees(self, company_id, name, job_title, linkedin_url ): + def insert_company_employees(self, project_id, name, job_title, linkedin_url ): cur = self.conn.cursor() - cur.execute("INSERT INTO linkedin_company_employees(`company_id`, `name`, `job_title`, `linkedin_url`) VALUES(?, ?, ?, ?)", (company_id, name, job_title, linkedin_url)) + cur.execute("INSERT INTO linkedin_company_employees(`project_id`, `name`, `job_title`, `linkedin_url`) VALUES(?, ?, ?, ?)", (project_id, name, job_title, linkedin_url)) self.conn.commit() # def read(table, **kwargs): diff --git a/plugins/gather_company.py b/plugins/gather_company.py index aa2e2aa..fd025d1 100644 --- a/plugins/gather_company.py +++ b/plugins/gather_company.py @@ -61,9 +61,17 @@ def crawl_company_employee(self, company_name, proxy_address, project_id): url_tag = soup2.find_all("a")[0].string # Check if URL is match with one of the string from company name(?) - if company_linkedin_url and any(x in company_name for x in url_tag): - company_linkedin_url_list.append(company_linkedin_url) - self.company_id = self.db.insert_linkedin_company_info(self.project_id, str(company_name), str(company_linkedin_url), "Lorem ipsum") + if company_linkedin_url: + is_contain_name = 0 + for x in company_name.split(): + if x in url_tag: + print(" X" + str(x) + "url tag " + str(url_tag)) + is_contain_name = 1 + break + + if is_contain_name == 1: + company_linkedin_url_list.append(company_linkedin_url) + self.company_id = self.db.insert_linkedin_company_info(self.project_id, str(company_name), str(company_linkedin_url), "Lorem ipsum") # Get data when linkedin url is like this : *.linkedin.com/in if not linkedin_url: @@ -80,7 +88,7 @@ def crawl_company_employee(self, company_name, proxy_address, project_id): log.console_log("Url: {}".format( linkedin_url_fix )) log.console_log("{}[+] --------------------------------------------------- [+]{}\n".format(Y, W)) - self.db.insert_company_employees(self.company_id, name_fix, job_title_fix, linkedin_url_fix) + self.db.insert_company_employees(self.project_id, name_fix, job_title_fix, linkedin_url_fix) log.console_log("\n\n{}[+] --------------------------------------------------- [+]{}".format(Y, W)) log.console_log("{}[+] Found LinkedIn Company URL: {}".format(Y, W)) diff --git a/plugins/harvest_email.py b/plugins/harvest_email.py index 5bcb317..9d69c23 100644 --- a/plugins/harvest_email.py +++ b/plugins/harvest_email.py @@ -37,7 +37,7 @@ def crawl_search(self, domain, proxy_address): dataStrip1 = re.findall(r'[a-zA-Z0-9._+-]+@[a-zA-Z0-9._+-]+' + domain, dataStrip) dataStrip2 = re.findall(r'[a-zA-Z0-9._+-]+@' + domain, dataStrip) dataEmail = set(dataStrip1 + dataStrip2) - dataFix = [x for x in dataEmail if not x.startswith('x22') and not x.startswith('3D') and not x.startswith('x3d')] # ignore email because bad parsing + dataFix = [x for x in dataEmail if not x.startswith('x22') and not x.startswith('3D') and not x.startswith('x3d') and not x.startswith('Cached') and not x.startswith('page')] # ignore email because bad parsing return list(dataFix) except: pass diff --git a/plugins/harvest_public_document.py b/plugins/harvest_public_document.py index 7206a2a..224adc0 100644 --- a/plugins/harvest_public_document.py +++ b/plugins/harvest_public_document.py @@ -67,8 +67,8 @@ def harvest_public_doc(self, domain, extension, proxy_address): data = re.findall(regex, data) list_files_download = list(set(data)) total_files = str(len(list_files_download)) - log.console_log("{}[*] Found {} {} files!".format(G, total_files, extension.upper(), W) ) if total_files != "0": + log.console_log("{}[*] Found {} {} files!".format(G, total_files, extension.upper(), W) ) log.console_log("{}[*] Please wait, lemme download it for you ;) {}[NO PROXY] {}".format(G, Y, W)) for files_download in list_files_download: log.no_console_log(files_download.split('/')[-1]) diff --git a/plugins/json_beautifier.py b/plugins/json_beautifier.py new file mode 100644 index 0000000..24f43e9 --- /dev/null +++ b/plugins/json_beautifier.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Belati is tool for Collecting Public Data & Public Document from Website and other service for OSINT purpose. +# This tools is inspired by Foca and Datasploit for OSINT +# Copyright (C) 2017 cacaddv@gmail.com (Petruknisme a.k.a Aan Wahyu) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This file is part of Belati project + +import json + +class JsonBeautifier(object): + def beautifier(self, json_data): + parsed = json.loads(json_data) + return json.dumps(parsed, indent=4, sort_keys=True) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index d7d00b4..23ca5b4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,3 +16,4 @@ future beautifulsoup4 python-whois futures +django \ No newline at end of file diff --git a/version b/version index 3ea3eab..ce3f072 100644 --- a/version +++ b/version @@ -1 +1 @@ -v0.2.0-dev +v0.2.2-dev diff --git a/web/db.sqlite3 b/web/db.sqlite3 new file mode 100644 index 0000000..9581cb7 Binary files /dev/null and b/web/db.sqlite3 differ diff --git a/web/manage.py b/web/manage.py new file mode 100755 index 0000000..01342ba --- /dev/null +++ b/web/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "web.settings") + try: + from django.core.management import execute_from_command_line + except ImportError: + # The above import may fail for some other reason. Ensure that the + # issue is really that Django is missing to avoid masking other + # exceptions on Python 2. + try: + import django + except ImportError: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) + raise + execute_from_command_line(sys.argv) diff --git a/web/web/__init__.py b/web/web/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/web/web/models.py b/web/web/models.py new file mode 100644 index 0000000..0b4ed48 --- /dev/null +++ b/web/web/models.py @@ -0,0 +1,221 @@ +# This is an auto-generated Django model module. +# You'll have to do the following manually to clean this up: +# * Rearrange models' order +# * Make sure each model has one field with primary_key=True +# * Make sure each ForeignKey has `on_delete` set to the desired behavior. +# * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table +# Feel free to rename the models, but don't rename db_table values or field names. +from __future__ import unicode_literals + +from django.db import models + + +class AuthGroup(models.Model): + id = models.IntegerField(primary_key=True) # AutoField? + name = models.CharField(unique=True, max_length=80) + + class Meta: + managed = False + db_table = 'auth_group' + + +class AuthGroupPermissions(models.Model): + id = models.IntegerField(primary_key=True) # AutoField? + group = models.ForeignKey(AuthGroup, models.DO_NOTHING) + permission = models.ForeignKey('AuthPermission', models.DO_NOTHING) + + class Meta: + managed = False + db_table = 'auth_group_permissions' + unique_together = (('group', 'permission'),) + + +class AuthPermission(models.Model): + id = models.IntegerField(primary_key=True) # AutoField? + content_type = models.ForeignKey('DjangoContentType', models.DO_NOTHING) + codename = models.CharField(max_length=100) + name = models.CharField(max_length=255) + + class Meta: + managed = False + db_table = 'auth_permission' + unique_together = (('content_type', 'codename'),) + + +class AuthUser(models.Model): + id = models.IntegerField(primary_key=True) # AutoField? + password = models.CharField(max_length=128) + last_login = models.DateTimeField(blank=True, null=True) + is_superuser = models.BooleanField() + first_name = models.CharField(max_length=30) + last_name = models.CharField(max_length=30) + email = models.CharField(max_length=254) + is_staff = models.BooleanField() + is_active = models.BooleanField() + date_joined = models.DateTimeField() + username = models.CharField(unique=True, max_length=150) + + class Meta: + managed = False + db_table = 'auth_user' + + +class AuthUserGroups(models.Model): + id = models.IntegerField(primary_key=True) # AutoField? + user = models.ForeignKey(AuthUser, models.DO_NOTHING) + group = models.ForeignKey(AuthGroup, models.DO_NOTHING) + + class Meta: + managed = False + db_table = 'auth_user_groups' + unique_together = (('user', 'group'),) + + +class AuthUserUserPermissions(models.Model): + id = models.IntegerField(primary_key=True) # AutoField? + user = models.ForeignKey(AuthUser, models.DO_NOTHING) + permission = models.ForeignKey(AuthPermission, models.DO_NOTHING) + + class Meta: + managed = False + db_table = 'auth_user_user_permissions' + unique_together = (('user', 'permission'),) + + +class DjangoAdminLog(models.Model): + id = models.IntegerField(primary_key=True) # AutoField? + object_id = models.TextField(blank=True, null=True) + object_repr = models.CharField(max_length=200) + action_flag = models.PositiveSmallIntegerField() + change_message = models.TextField() + content_type = models.ForeignKey('DjangoContentType', models.DO_NOTHING, blank=True, null=True) + user = models.ForeignKey(AuthUser, models.DO_NOTHING) + action_time = models.DateTimeField() + + class Meta: + managed = False + db_table = 'django_admin_log' + + +class DjangoContentType(models.Model): + id = models.IntegerField(primary_key=True) # AutoField? + app_label = models.CharField(max_length=100) + model = models.CharField(max_length=100) + + class Meta: + managed = False + db_table = 'django_content_type' + unique_together = (('app_label', 'model'),) + + +class DjangoMigrations(models.Model): + id = models.IntegerField(primary_key=True) # AutoField? + app = models.CharField(max_length=255) + name = models.CharField(max_length=255) + applied = models.DateTimeField() + + class Meta: + managed = False + db_table = 'django_migrations' + + +class DjangoSession(models.Model): + session_key = models.CharField(primary_key=True, max_length=40) + session_data = models.TextField() + expire_date = models.DateTimeField() + + class Meta: + managed = False + db_table = 'django_session' + + +class DocResults(models.Model): + id = models.IntegerField(blank=True, primary_key=True) + project_id = models.IntegerField(blank=True, null=True) + doc_ext = models.TextField(blank=True, null=True) + doc_url = models.TextField(blank=True, null=True) + doc_location = models.TextField(blank=True, null=True) + + class Meta: + managed = False + db_table = 'doc_results' + + +class LinkedinCompanyEmployees(models.Model): + id = models.IntegerField(blank=True, primary_key=True) + project_id = models.IntegerField(blank=True, null=True) + name = models.TextField(blank=True, null=True) + job_title = models.TextField(blank=True, null=True) + linkedin_url = models.IntegerField(blank=True, null=True) + + class Meta: + managed = False + db_table = 'linkedin_company_employees' + + +class LinkedinCompanyInfo(models.Model): + id = models.IntegerField(blank=True, primary_key=True) + project_id = models.IntegerField(blank=True, null=True) + company_name = models.IntegerField(blank=True, null=True) + company_linkedin_url = models.IntegerField(blank=True, null=True) + company_description = models.IntegerField(blank=True, null=True) + + class Meta: + managed = False + db_table = 'linkedin_company_info' + + +class MailHarvestResults(models.Model): + id = models.IntegerField(blank=True, primary_key=True) + project_id = models.IntegerField(blank=True, null=True) + mail_results = models.TextField(blank=True, null=True) + mail_pgp_results = models.TextField(blank=True, null=True) + + class Meta: + managed = False + db_table = 'mail_harvest_results' + + +class MainDomainResults(models.Model): + id = models.IntegerField(blank=True, primary_key=True) + project_id = models.IntegerField(blank=True, null=True) + domain = models.TextField(blank=True, null=True) + domain_whois = models.TextField(blank=True, null=True) + email = models.TextField(blank=True, null=True) + domain_reputation = models.TextField(blank=True, null=True) + domain_blacklist = models.TextField(blank=True, null=True) + ns_record = models.TextField(db_column='NS_record', blank=True, null=True) # Field name made lowercase. + mx_record = models.TextField(db_column='MX_record', blank=True, null=True) # Field name made lowercase. + + class Meta: + managed = False + db_table = 'main_domain_results' + + +class Projects(models.Model): + project_id = models.IntegerField(blank=True, primary_key=True) + project_domain = models.TextField(blank=True, null=True) + project_org = models.TextField(blank=True, null=True) + started_time = models.TextField(blank=True, null=True) + + class Meta: + managed = False + db_table = 'projects' + + def __unicode__(self): + return self.project_domain + +class SubdomainResults(models.Model): + id = models.IntegerField(blank=True, primary_key=True) + project_id = models.IntegerField(blank=True, null=True) + subdomain = models.TextField(blank=True, null=True) + ip_address = models.TextField(blank=True, null=True) + banner = models.TextField(blank=True, null=True) + wappalyzer = models.TextField(blank=True, null=True) + robots_txt = models.TextField(blank=True, null=True) + is_contain_git = models.IntegerField(blank=True, null=True) + is_contain_svn = models.IntegerField(blank=True, null=True) + + class Meta: + managed = False + db_table = 'subdomain_results' diff --git a/web/web/settings.py b/web/web/settings.py new file mode 100644 index 0000000..dafd5d7 --- /dev/null +++ b/web/web/settings.py @@ -0,0 +1,121 @@ +""" +Django settings for web project. + +Generated by 'django-admin startproject' using Django 1.11.2. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.11/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'i+2-zy6rl=!w+z8zai5un*ta$!+^lmlogkid*a6j0*ssfq&f_@' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'web', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'web.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'web.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.11/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.11/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.11/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/web/web/templates/about.html b/web/web/templates/about.html new file mode 100644 index 0000000..072e039 --- /dev/null +++ b/web/web/templates/about.html @@ -0,0 +1,74 @@ +{% extends "header.html" %} + +{% block content %} + + +

Belati - The Traditional Swiss Army Knife For OSINT

+
+

+Belati is tool for Collecting Public Data & Public Document from Website and other service for OSINT purpose. This tools is inspired by Foca and Datasploit for OSINT :) +

+ +

Why I Made this?

+
+Just for learning stuff and OSINT purpose. + +

What Belati can do?

+
+ + +

Author

+
+Aan Wahyu a.k.a Petruknisme(https://petruknisme.com) + +

Thanks To

+
+Thanks to PyWhois Library, Sublist3r, MailHarvester, Emingoo for being part of my code. Also thanks to Hispagatos, Infosec-ninjas, eCHo, RNDC( Research and development center ) and all other people who are inspiring this project :) +

+Thanks to Echo-Zine Staff for approving my Ezine : http://ezine.echo.or.id/issue31/005.txt - Belati : Collecting Public Data & Public Document for OSINT Purpose - Petruknisme + +

Feedback/Suggestion

+
+Feel free to create Issue in this repository or email me at cacaddv [at] gmail.com . Your feedback and suggestion is useful for Belati development progress :) + +

License

+
+Belati is licensed under GPL V2. You can use, modify, or redistribute this tool under the terms of GNU General Public License (GPLv2). +
+
+ Belati is tool for Collecting Public Data & Public Document from Website and other service for OSINT purpose. + This tools is inspired by Foca and Datasploit for OSINT + Copyright (C) 2017 cacaddv@gmail.com (Petruknisme a.k.a Aan Wahyu) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +
+{% endblock %} diff --git a/web/web/templates/base.html b/web/web/templates/base.html new file mode 100644 index 0000000..46d6f30 --- /dev/null +++ b/web/web/templates/base.html @@ -0,0 +1,4 @@ +{% block header %}{% endblock %} +{% block navbar %}{% endblock %} +{% block content %}{% endblock %} +{% block footer %}{% endblock %} diff --git a/web/web/templates/footer.html b/web/web/templates/footer.html new file mode 100644 index 0000000..b562c68 --- /dev/null +++ b/web/web/templates/footer.html @@ -0,0 +1,5 @@ + diff --git a/web/web/templates/header.html b/web/web/templates/header.html new file mode 100644 index 0000000..eca834a --- /dev/null +++ b/web/web/templates/header.html @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + +
+ {% block content %}{% endblock %} +
+ +{% include "footer.html" %} diff --git a/web/web/templates/index.html b/web/web/templates/index.html new file mode 100644 index 0000000..3538e7b --- /dev/null +++ b/web/web/templates/index.html @@ -0,0 +1,27 @@ +{% extends "header.html" %} + +{% block content %} + + + + + + + + + + + + {% for pro in project_data %} + + + + + + + + {% endfor %} + +
#DomainOrganization/CompanyTimeAction
{{ pro.project_id }}{{ pro.project_domain }}{{ pro.project_org }}{{ pro.started_time }}
+{% endblock %} + diff --git a/web/web/templates/projects.html b/web/web/templates/projects.html new file mode 100644 index 0000000..b1dfcd8 --- /dev/null +++ b/web/web/templates/projects.html @@ -0,0 +1,174 @@ +{% extends "header.html" %} + +{% block content %} + + +
+
+ + + + + + + + + + + {% for pro in project_data %} + + + + + + + {% endfor %} + +
#DomainOrganization/CompanyTime
{{ pro.project_id }}{{ pro.project_domain }}{{ pro.project_org }}{{ pro.started_time }}
+
+
+ + + {% for domain in main_domain %} + + + + + + + + + + + + + + + + + + + + {% endfor %} + +
Domain: + {{ domain.domain }}
Domain Whois: + {{ domain.domain_whois }} +
Emails:{{ domain.email }}
NS Record:{{ domain.ns_record }}
MX Record:{{ domain.ns_record }}
+
+
+ + + + + + + + + + + + + + + {% for subdomain in subdomain_results %} + + + + + + + + + + + {% endfor %} + +
#SubdomainIP AddressBannerWappalyzerRobotsGitSVN
{{ subdomain.id }}{{ subdomain.subdomain }}{{ subdomain.ip_address }}{{ subdomain.banner }}{{ subdomain.wappalyzer }}{{ subdomain.robots_txt }}{{ subdomain.is_contain_git }}{{ subdomain.is_contain_svn }}
+
+
+ + + + + + + + + + {% for mail_data in mail_harvest %} + + + + + + {% endfor %} + +
#Mail ResultsPGP Mail Results
{{ mail_data.id }}{{ mail_data.mail_results }}{{ mail_data.mail_pgp_results }}
+
+
+ + + + + + + + + + + {% for doc in doc_result %} + + + + + + + {% endfor %} + +
#URLFile LocationAction
{{ doc.id }}{{ doc.doc_url }}{{ doc.doc_location }}
+
+
+

Company Info

+
+ {% for info in company_info %} + Company Name: {{ info.company_name }}
+ LinkedIn URL: {{ info.company_linkedin_url }} + {% endfor %} +

Company Employees

+ + + + + + + + + + + {% for employee in company_employee %} + + + + + + + {% endfor %} + +
#NameJob TitleLinkedIn URL
{{ employee.id}}{{ employee.name }}{{ employee.job_title }}{{ employee.linkedin_url }}
+
+
+ +{% endblock %} diff --git a/web/web/urls.py b/web/web/urls.py new file mode 100644 index 0000000..24c5e91 --- /dev/null +++ b/web/web/urls.py @@ -0,0 +1,26 @@ +"""web URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.11/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.conf.urls import url, include + 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) +""" +from django.conf.urls import url +from django.contrib import admin + +from views import index_page, view_projects, about_page + +urlpatterns = [ + url(r'^admin/', admin.site.urls), + url(r'^$', index_page), + url(r'^projects/(?P\d+)/view/$', view_projects), + url(r'^about', about_page), +] diff --git a/web/web/views.py b/web/web/views.py new file mode 100644 index 0000000..050f6ae --- /dev/null +++ b/web/web/views.py @@ -0,0 +1,24 @@ +from django.http import HttpResponse, HttpResponseRedirect, Http404 +from django.shortcuts import render_to_response, get_object_or_404 + +from models import Projects, MainDomainResults, SubdomainResults, MailHarvestResults, DocResults, LinkedinCompanyInfo, LinkedinCompanyEmployees + +def index_page(request): + project_list = Projects.objects.all().order_by('project_id') + return render_to_response('index.html', + {'project_data': project_list}) + + +def view_projects(request, id): + project_list = Projects.objects.filter(project_id=id) + main_domain = MainDomainResults.objects.filter(project_id=id) + subdomain = SubdomainResults.objects.filter(project_id=id) + mail_harvest = MailHarvestResults.objects.filter(project_id=id) + doc_result = DocResults.objects.filter(project_id=id) + company_info = LinkedinCompanyInfo.objects.filter(project_id=id) + company_employee = LinkedinCompanyEmployees.objects.filter(project_id=id) + + return render_to_response('projects.html', {'project_data' : project_list, 'main_domain' : main_domain, 'subdomain_results' : subdomain, 'mail_harvest' : mail_harvest, 'doc_result' : doc_result, 'company_info' : company_info, 'company_employee' : company_employee}) + +def about_page(request): + return render_to_response('about.html') diff --git a/web/web/wsgi.py b/web/web/wsgi.py new file mode 100644 index 0000000..23d62e9 --- /dev/null +++ b/web/web/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for web project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "web.settings") + +application = get_wsgi_application()