Skip to content

Commit

Permalink
Kamerka GUI - initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
root committed Nov 6, 2019
0 parents commit 3d0b46f
Show file tree
Hide file tree
Showing 479 changed files with 106,220 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions .idea/Kamerka-GUI.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

146 changes: 146 additions & 0 deletions README.md
@@ -0,0 +1,146 @@
# ꓘamerka GUI

## Ultimate Internet of Things/Industrial Control Systems reconnaissance tool.

### Powered by Shodan - Supported by Binary Edge & WhoisXMLAPI

writeup -
Demo - https://woj-ciech.github.io/kamerka-demo/kamerka.html

## Requirements
- python3
- django
- celery
- redis
- Shodan
- BinaryEdge
- WHOISXMLAPI
- Flickr
- Google Maps API

```pip3 install -r requirements.txt```

**Make sure your API keys are correct and put them in keys.json in main directory.**

## Run
```
python3 manage.py makemigrations
python3 manage.py migrate
python3 manage.py runserver
```
In a new window run celery worker
```celery -A kamerka --loglevel=info```

In a new window fire up redis
```redis-server```

And server should be available on ```https://localhost:8000/```


# Search
## Search for Industrial Control Devices in specific country
![](https://i.imgur.com/8qx5X3l.jpg)

- "All results" checkbox means get all results from Shodan, if it's turned off - only first page (100) results will be downloaded.
- "Own database" checkbox does not work but shows that is possible to integrate your own geolocation database. Let me know if you have access to better than Shodan's default one.

## Search for Internet of things in specific coordinates
Type your coordinates in format "lat,lon", hardcoded radius is 20km.
![](https://i.imgur.com/dSo4Kg0.jpg)


# Dashboard
![](https://i.imgur.com/H0cQJVY.jpg)

# Maps
## Los Angeles map
![](https://i.imgur.com/Oq9ZTBn.jpg)

## Industrial Control Systems in Canada
![](https://i.imgur.com/Z8xfHkB.jpg)

# Device map & details
![](https://i.imgur.com/M7V4IAq.jpg)

## Full list of supported devices with corresponding queries
```
"webcam": "device:webcam",
'printer': "device:printer",
'mqtt': 'product:mqtt',
'rtsp': "port:'554'",
'dicom': "dicom",
"ipcamera": "IPCamera_Logo",
"yawcam": "yawcam",
"blueiris": "http.favicon.hash:-520888198",
'ubnt': "UBNT Streaming Server",
"go1984": "go1984",
"dlink": "Server: Camera Web Server",
"avtech": "linux upnp avtech",
"adh": "ADH-web",
"niagara": "port:1911,4911 product:Niagara",
'bacnet': "port:47808",
'modbus': "port:502",
'siemens': 'Original Siemens Equipment Basic Firmware:',
'dnp3': "port:20000 source address",
"ethernetip": "port:44818",
"gestrip": 'port:18245,18246 product:"general electric"',
'hart': "port:5094 hart-ip",
'pcworx': "port:1962 PLC",
"mitsubishi": "port:5006,5007 product:mitsubishi",
"omron": "port:9600 response code",
"redlion": 'port:789 product:"Red Lion Controls"',
'codesys': "port:2455 operating system",
"iec": "port:2404 asdu address",
'proconos': "port:20547 PLC",
"plantvisor": "Server: CarelDataServer",
"iologik": "iologik",
"moxa": "Moxa",
"akcp": "Server: AKCP Embedded Web Server",
"spidercontrol": "powered by SpiderControl TM",
"tank": "port:10001 tank",
"iq3": "Server: IQ3",
"is2": "IS2 Web Server",
"vtscada": "Server: VTScada",
'zworld': "Z-World Rabbit 200 OK",
"nordex": "Jetty 3.1.8 (Windows 2000 5.0 x86) \"200 OK\" "
```

# Used components
- Background IoT photo by https://unsplash.com/@pawel_czerwinski
- Background ICS photo by https://unsplash.com/@wimvanteinde
- Joli admin template - https://github.com/sbilly/joli-admin
- Search form - Colorlib Search Form v15
- country picker - https://github.com/mojoaxel/bootstrap-select-country
- Multiselect - https://github.com/varundewan/multiselect/
- Arsen Zbidniakov Flat UI Checkbox https://codepen.io/ARS/pen/aeDHE/

# Known bugs:
- It's version 1.0 so please raise an issue if you think you found any bug or have an idea to make it better.
- Sometimes search page keeps the last values, so please use ctrl+shift+R to refresh the main search page
- Debug info is left on purpose for raising an issues
- still some problems with getting cves from shodan search results
- Flickr infowindow size

# Contribution
I really care about feedback from you. If you have any idea how to make tool better, I'm more than happy to hear it.
It's also possible to upload and host the tool online, if you want to help, dm me.

# TODO
- Offensive capabilities
- More devices
- More sources (Instagram?, Youtube?)
- Integration with Nmap and plcscan
- Extensive error checking/debugging
- Cleanup code, delete legacy/unused dependencies js, css files
- Keeping keys in db
- Your ideas

# Remarks
- It uses default sqlite Django database
- Buttons in Intel tab for device do not show the progress bars, you have a results in max couple of seconds.
- Own database button does not work, it shows that it's possible to load your own geolocation database. I haven't found better than Shodan's but let me know if you have access to one.
- Looking for nearby Tweets works but I wasn't able to find any tweets. It may be a problem with Twitter API. Let me know if you can find anything.
- Don't blame me for unintentional bug that might exhaust your Shodan/BinaryEdge/WHOISXMLAPI credits.
- I'm not responsible for any damage caused by use this tool.
Empty file added app_kamerka/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions app_kamerka/admin.py
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
5 changes: 5 additions & 0 deletions app_kamerka/apps.py
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class AppKamerkaConfig(AppConfig):
name = 'app_kamerka'
16 changes: 16 additions & 0 deletions app_kamerka/forms.py
@@ -0,0 +1,16 @@
from django import forms


#
class CoordinatesForm(forms.Form):
coordinates = forms.CharField(max_length=100)


class CountryForm(forms.Form):
country = forms.CharField(max_length=100)
all = forms.BooleanField(required=False)
own_database = forms.BooleanField(required=False)


class DevicesNearbyForm(forms.Form):
id = forms.CharField(max_length=100)
Empty file.
85 changes: 85 additions & 0 deletions app_kamerka/models.py
@@ -0,0 +1,85 @@
from django.db import models
from jsonfield import JSONField


# Create your models here.


class Search(models.Model):
coordinates = models.CharField(max_length=100)
country = models.CharField(max_length=100)
ics = models.CharField(max_length=100)
coordinates_search = models.CharField(max_length=1000)


class Device(models.Model):
search = models.ForeignKey(Search, on_delete=models.CASCADE)
ip = models.CharField(max_length=100, default="")
product = models.CharField(max_length=500, default="")
org = models.CharField(max_length=100, default="", null=True)
data = models.CharField(max_length=1000, default="")
port = models.CharField(max_length=10, default="")
type = models.CharField(max_length=100, default="")
city = models.CharField(max_length=100, default="", null=True)
lon = models.CharField(max_length=100, default="")
lat = models.CharField(max_length=100, default="")
country_code = models.CharField(max_length=100, default="")
query = models.CharField(max_length=100, default="")
category = models.CharField(max_length=100, default="")
vulns = models.CharField(max_length=100, default="")
indicator = models.CharField(max_length=100, default="")
hostnames = models.CharField(max_length=100, default="")


class DeviceNearby(models.Model):
device = models.ForeignKey(Device, on_delete=models.CASCADE)
lat = models.CharField(max_length=100)
lon = models.CharField(max_length=100)
ip = models.CharField(max_length=100)
product = models.CharField(max_length=100)
port = models.CharField(max_length=100)
org = models.CharField(max_length=100)


class TwitterNearby(models.Model):
device = models.ForeignKey(Device, on_delete=models.CASCADE)
lat = models.CharField(max_length=100)
lon = models.CharField(max_length=100)
link = models.CharField(max_length=100)
tweet = models.CharField(max_length=100)


class FlickrNearby(models.Model):
device = models.ForeignKey(Device, on_delete=models.CASCADE)
lat = models.CharField(max_length=100)
lon = models.CharField(max_length=100)
url = models.CharField(max_length=100)
title = models.CharField(max_length=100)


class ShodanScan(models.Model):
device = models.ForeignKey(Device, on_delete=models.CASCADE)
ports = models.CharField(max_length=100)
tags = models.CharField(max_length=100)
products = models.CharField(max_length=100)
module = models.CharField(max_length=100)


class BinaryEdgeScore(models.Model):
device = models.ForeignKey(Device, on_delete=models.CASCADE)
grades = JSONField()
cve = JSONField()
score = models.CharField(max_length=3)


class Whois(models.Model):
device = models.ForeignKey(Device, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
org = models.CharField(max_length=100)
street = models.CharField(max_length=100)
city = models.CharField(max_length=100)
netrange = models.CharField(max_length=100)
admin_org = models.CharField(max_length=100)
admin_email = models.CharField(max_length=100)
admin_phone = models.CharField(max_length=100)
email = models.CharField(max_length=100)
67 changes: 67 additions & 0 deletions app_kamerka/static/celery_progress/celery_progress.js
@@ -0,0 +1,67 @@
var CeleryProgressBar = (function () {
function onSuccessDefault(progressBarElement, progressBarMessageElement) {
progressBarElement.style.backgroundColor = '#1caf9a ';
progressBarMessageElement.innerHTML = "Success!";
}

function onResultDefault(resultElement, result) {
if (resultElement) {
resultElement.innerHTML = result;
}
}

function onErrorDefault(progressBarElement, progressBarMessageElement) {
progressBarElement.style.backgroundColor = '#dc4f63';
progressBarMessageElement.innerHTML = "Uh-Oh, something went wrong!";
}

function onProgressDefault(progressBarElement, progressBarMessageElement, progress) {
progressBarElement.style.backgroundColor = '#68a9ef';
progressBarElement.style.width = progress.percent + "%";
progressBarMessageElement.innerHTML = progress.current + '/' + progress.total //aaaaaaaaaaaaaaaaaaaa
}

function updateProgress (progressUrl, options) {
options = options || {};
var progressBarId = options.progressBarId || 'progress-bar';
var progressBarMessage = options.progressBarMessageId || 'progress-bar-message';
var progressBarElement = options.progressBarElement || document.getElementById(progressBarId);
var progressBarMessageElement = options.progressBarMessageElement || document.getElementById(progressBarMessage);
var onProgress = options.onProgress || onProgressDefault;
var onSuccess = options.onSuccess || onSuccessDefault;
var onError = options.onError || onErrorDefault;
var pollInterval = options.pollInterval || 500;
var resultElementId = options.resultElementId || 'celery-result';
var resultElement = options.resultElement || document.getElementById(resultElementId);
var onResult = options.onResult || onResultDefault;


fetch(progressUrl).then(function(response) {
response.json().then(function(data) {
if (data.progress) {
onProgress(progressBarElement, progressBarMessageElement, data.progress);
}
if (!data.complete) {
setTimeout(updateProgress, pollInterval, progressUrl, options);
} else {
if (data.success) {
onSuccess(progressBarElement, progressBarMessageElement);
} else {
onError(progressBarElement, progressBarMessageElement);
}
if (data.result) {
onResult(resultElement, data.result);
}
}
});
});
}
return {
onSuccessDefault: onSuccessDefault,
onResultDefault: onResultDefault,
onErrorDefault: onErrorDefault,
onProgressDefault: onProgressDefault,
updateProgress: updateProgress,
initProgressBar: updateProgress
};
})();

0 comments on commit 3d0b46f

Please sign in to comment.