Skip to content

Commit

Permalink
PyShodan Script added, PythonScript importer added, Label fixes, Bug …
Browse files Browse the repository at this point in the history
…fixes
  • Loading branch information
root authored and root committed May 6, 2019
1 parent e9602da commit 6f6fc94
Show file tree
Hide file tree
Showing 16 changed files with 174 additions and 53 deletions.
4 changes: 3 additions & 1 deletion app/hostmodels.py
Expand Up @@ -41,7 +41,7 @@ def headerData(self, section, orientation, role):
if section < len(self.__headers):
return self.__headers[section]
else:
return "!not implemented"
return "not implemented in view model"

def data(self, index, role): # this method takes care of how the information is displayed
if role == QtCore.Qt.DecorationRole: # to show the operating system icon instead of text
Expand Down Expand Up @@ -108,6 +108,8 @@ def data(self, index, role): # this metho
value = self.__hosts[row]['state']
elif column == 15:
value = self.__hosts[row]['count']
else:
value = 'Not set in view model'
return value

if role == QtCore.Qt.FontRole:
Expand Down
33 changes: 17 additions & 16 deletions app/logic.py
Expand Up @@ -19,6 +19,8 @@
from app.auxiliary import *
from ui.ancillaryDialog import *
from six import u as unicode
from pyShodan import PyShodan
from scripts.python import pyShodan

class Logic():
def __init__(self):
Expand Down Expand Up @@ -562,7 +564,7 @@ def isCanceledProcess(self, procId):
return True
return False

class ShodanImporter(QtCore.QThread):
class PythonImporter(QtCore.QThread):
tick = QtCore.pyqtSignal(int, name="changed") # New style signal
done = QtCore.pyqtSignal(name="done") # New style signal
schedule = QtCore.pyqtSignal(object, bool, name="schedule") # New style signal
Expand All @@ -571,6 +573,9 @@ class ShodanImporter(QtCore.QThread):
def __init__(self):
QtCore.QThread.__init__(self, parent=None)
self.output = ''
self.hostIp = ''
self.pythonScriptDispatch = {'pyShodan': pyShodan.PyShodanScript()}
self.pythonScriptObj = None
self.importProgressWidget = ProgressWidget('Importing shodan data..')

def tsLog(self, msg):
Expand All @@ -579,8 +584,11 @@ def tsLog(self, msg):
def setDB(self, db):
self.db = db

def setFilename(self, filename):
self.filename = filename
def setHostIp(self, hostIp):
self.hostIp = hostIp

def setPythonScript(self, pythonScript):
self.pythonScriptObj = self.pythonScriptDispatch[pythonScript]

def setOutput(self, output):
self.output = output
Expand All @@ -590,22 +598,15 @@ def run(self): # it is nece
session = self.db.session()
startTime = time()
self.db.dbsemaphore.acquire() # ensure that while this thread is running, no one else can write to the DB

for h in parser.getAllHosts(): # create all the hosts that need to be created
db_host = session.query(hostObj).filter_by(ip=h.ip).first()

if not db_host: # if host doesn't exist in DB, create it first
hid = hostObj(osMatch='', osAccuracy='', ip=h.ip, ipv4=h.ipv4, ipv6=h.ipv6, macaddr=h.macaddr, status=h.status, hostname=h.hostname, vendor=h.vendor, uptime=h.uptime, \
lastboot=h.lastboot, distance=h.distance, state=h.state, count=h.count)
self.tsLog("Adding db_host")
session.add(hid)
else:
self.tsLog("Found db_host already in db")
#self.setPythonScript(self.pythonScript)
db_host = session.query(hostObj).filter_by(ip = self.hostIp).first()
self.pythonScriptObj.setDbHost(db_host)
self.pythonScriptObj.setSession(session)
self.pythonScriptObj.run()
session.commit()
self.db.dbsemaphore.release() # we are done with the DB
self.tsLog('Finished in '+ str(time()-startTime) + ' seconds.')
self.tsLog('Finished in ' + str(time() - startTime) + ' seconds.')
self.done.emit()
self.schedule.emit(parser, self.output == '') # call the scheduler (if there is no terminal output it means we imported nmap)

except Exception as e:
self.tsLog(e)
Expand Down
38 changes: 27 additions & 11 deletions controller/controller.py
Expand Up @@ -28,7 +28,7 @@ class Controller():
def __init__(self, view, logic):
self.name = "LEGION"
self.version = '0.3.5'
self.build = '1557146732'
self.build = '1557176518'
self.author = 'GoVanguard'
self.copyright = '2019'
self.links = ['http://github.com/GoVanguard/legion/issues', 'https://GoVanguard.io/legion']
Expand All @@ -49,7 +49,7 @@ def __init__(self, view, logic):

self.loadSettings() # creation of context menu actions from settings file and set up of various settings
self.initNmapImporter()
self.initShodanImporter()
self.initPythonImporter()
self.initScreenshooter()
self.initBrowserOpener()
self.start() # initialisations (globals, etc)
Expand All @@ -64,7 +64,7 @@ def start(self, title='*untitled'):
self.fastProcessesRunning = 0 # counts the number of fast processes currently running
self.slowProcessesRunning = 0 # counts the number of slow processes currently running
self.nmapImporter.setDB(self.logic.db) # tell nmap importer which db to use
self.shodanImporter.setDB(self.logic.db)
self.pythonImporter.setDB(self.logic.db)
self.updateOutputFolder() # tell screenshooter where the output folder is
self.view.start(title)

Expand All @@ -74,11 +74,11 @@ def initNmapImporter(self):
self.nmapImporter.schedule.connect(self.scheduler) # run automated attacks
self.nmapImporter.log.connect(self.view.ui.LogOutputTextView.append)

def initShodanImporter(self):
self.shodanImporter = ShodanImporter()
self.shodanImporter.done.connect(self.importFinished)
self.shodanImporter.schedule.connect(self.scheduler) # run automated attacks
self.shodanImporter.log.connect(self.view.ui.LogOutputTextView.append)
def initPythonImporter(self):
self.pythonImporter = PythonImporter()
self.pythonImporter.done.connect(self.importFinished)
self.pythonImporter.schedule.connect(self.scheduler) # run automated attacks
self.pythonImporter.log.connect(self.view.ui.LogOutputTextView.append)

def initScreenshooter(self):
self.screenshooter = Screenshooter(self.settings.general_screenshooter_timeout) # screenshot taker object (different thread)
Expand Down Expand Up @@ -316,6 +316,8 @@ def handleHostAction(self, ip, hostid, actions, action):
if 'nmap' in name: # to make sure different nmap scans appear under the same tool name
name = 'nmap'
invisibleTab = True
elif 'python-script' in name:
invisibleTab = True
# remove all chars that are not alphanumeric from tool name (used in the outputfile's name)
outputfile = self.logic.runningfolder+"/"+re.sub("[^0-9a-zA-Z]", "", str(name))+"/"+getTimestamp()+"-"+re.sub("[^0-9a-zA-Z]", "", str(self.settings.hostActions[i][1]))+"-"+ip
command = str(self.settings.hostActions[i][2])
Expand Down Expand Up @@ -388,6 +390,8 @@ def handleServiceNameAction(self, targets, actions, action, restoring=True):

if 'nmap' in tabTitle: # we don't want to show nmap tabs
restoring = True
elif 'python-script' in tabTitle: # we don't want to show nmap tabs
restoring = True

self.runCommand(tool, tabTitle, ip[0], ip[1], ip[2], command, getTimestamp(True), outputfile, self.view.createNewTabForHost(ip[0], tabTitle, restoring))
break
Expand Down Expand Up @@ -466,7 +470,7 @@ def handleProcessAction(self, selectedProcesses, action): # selectedPr
self.view.updateProcessesTableView()
return

if action.text() == 'Clear': # hide all the processes that are not running
if action.text() == 'Clear': # h.ide all the processes that are not running
self.logic.toggleProcessDisplayStatus()
self.view.updateProcessesTableView()

Expand Down Expand Up @@ -723,6 +727,8 @@ def processCrashed(self, proc):
self.logic.storeProcessCrashStatusInDB(str(proc.id))
log.info('Process {qProcessId} Crashed!'.format(qProcessId=str(proc.id)))
qProcessOutput = "\n\t" + str(proc.display.toPlainText()).replace('\n','').replace("b'","")
#self.view.closeHostToolTab(self, index))
self.view.findFinishedServiceTab(str(self.logic.getPidForProcess(str(proc.id))))
log.info('Process {qProcessId} Output: {qProcessOutput}'.format(qProcessId=str(proc.id), qProcessOutput=qProcessOutput))

# this function handles everything after a process ends
Expand All @@ -732,13 +738,21 @@ def processFinished(self, qProcess):
if not self.logic.isKilledProcess(str(qProcess.id)): # if process was not killed
if not qProcess.outputfile == '':
self.logic.moveToolOutput(qProcess.outputfile) # move tool output from runningfolder to output folder if there was an output file

if 'nmap' in qProcess.name: # if the process was nmap, use the parser to store it
print(qProcess.command)
if 'nmap' in qProcess.command : # if the process was nmap, use the parser to store it
if qProcess.exitCode() == 0: # if the process finished successfully
newoutputfile = qProcess.outputfile.replace(self.logic.runningfolder, self.logic.outputfolder)
self.nmapImporter.setFilename(str(newoutputfile)+'.xml')
self.nmapImporter.setOutput(str(qProcess.display.toPlainText()))
self.nmapImporter.start()
elif 'PythonScript' in qProcess.command:
pythonScript = str(qProcess.command).split(' ')[2]
print('PythonImporter running for script: {0}'.format(pythonScript))
if qProcess.exitCode() == 0: # if the process finished successfully
self.pythonImporter.setOutput(str(qProcess.display.toPlainText()))
self.pythonImporter.setHostIp(str(qProcess.hostIp))
self.pythonImporter.setPythonScript(pythonScript)
self.pythonImporter.start()
exitCode = qProcess.exitCode()
if exitCode != 0 and exitCode != 255:
log.info("Process {qProcessId} exited with code {qProcessExitCode}".format(qProcessId=qProcess.id, qProcessExitCode=qProcess.exitCode()))
Expand Down Expand Up @@ -814,6 +828,8 @@ def runToolsFor(self, service, ip, port, protocol='tcp'):

if 'nmap' in tabTitle: # we don't want to show nmap tabs
restoring = True
elif 'python-script' in tabTitle:
restoring = True

tab = self.view.ui.HostsTabWidget.tabText(self.view.ui.HostsTabWidget.currentIndex())
self.runCommand(tool[0], tabTitle, ip, port, protocol, command, getTimestamp(True), outputfile, self.view.createNewTabForHost(ip, tabTitle, not (tab == 'Hosts')))
Expand Down
8 changes: 8 additions & 0 deletions db/database.py
Expand Up @@ -284,6 +284,14 @@ def __init__(self, **kwargs):
self.distance = kwargs.get('distance') or 'unknown'
self.state = kwargs.get('state') or 'unknown'
self.count = kwargs.get('count') or 'unknown'
self.city = kwargs.get('city') or 'unknown'
self.countryCode = kwargs.get('countryCode') or 'unknown'
self.postalCode = kwargs.get('postalCode') or 'unknown'
self.longitude = kwargs.get('longitude') or 'unknown'
self.latitude = kwargs.get('latitude') or 'unknown'
self.isp = kwargs.get('isp') or 'unknown'
self.asn = kwargs.get('asn') or 'unknown'



class note(Base):
Expand Down
8 changes: 7 additions & 1 deletion deps/installDeps.sh
Expand Up @@ -9,4 +9,10 @@ source ./deps/apt.sh
apt-get update -m

echo "Installing deps..."
DEBIAN_FRONTEND="noninteractive" apt-get -yqqqm --allow-unauthenticated --force-yes -o DPkg::Options::="--force-overwrite" -o DPkg::Options::="--force-confdef" install nmap finger hydra nikto whatweb nbtscan nfs-common rpcbind smbclient sra-toolkit ldap-utils sslscan rwho medusa x11-apps cutycapt leafpad xvfb imagemagick eog hping3 sqlmap wapiti libqt5core5a python-pip python-impacket ruby perl dnsmap urlscan git xsltproc
DEBIAN_FRONTEND="noninteractive" apt-get -yqqqm --allow-unauthenticated --force-yes -o DPkg::Options::="--force-overwrite" -o DPkg::Options::="--force-confdef" install nmap finger hydra nikto nbtscan nfs-common rpcbind smbclient sra-toolkit ldap-utils sslscan rwho x11-apps cutycapt leafpad xvfb imagemagick eog hping3 sqlmap wapiti libqt5core5a python-pip ruby perl urlscan git xsltproc

DEBIAN_FRONTEND="noninteractive" apt-get -yqqqm --allow-unauthenticated --force-yes -o DPkg::Options::="--force-overwrite" -o DPkg::Options::="--force-confdef" dnsmap
DEBIAN_FRONTEND="noninteractive" apt-get -yqqqm --allow-unauthenticated --force-yes -o DPkg::Options::="--force-overwrite" -o DPkg::Options::="--force-confdef" wapiti
DEBIAN_FRONTEND="noninteractive" apt-get -yqqqm --allow-unauthenticated --force-yes -o DPkg::Options::="--force-overwrite" -o DPkg::Options::="--force-confdef" python-impacket
DEBIAN_FRONTEND="noninteractive" apt-get -yqqqm --allow-unauthenticated --force-yes -o DPkg::Options::="--force-overwrite" -o DPkg::Options::="--force-confdef" whatweb
DEBIAN_FRONTEND="noninteractive" apt-get -yqqqm --allow-unauthenticated --force-yes -o DPkg::Options::="--force-overwrite" -o DPkg::Options::="--force-confdef" medusa
2 changes: 2 additions & 0 deletions deps/installPythonLibs.sh
Expand Up @@ -5,3 +5,5 @@ source ./deps/detectPython.sh
# Setup Python deps
${PIP3BIN} install -r requirements.txt --upgrade
${PIP3BIN} install service-identity --upgrade

${PIP3BIN} ./deps/primeExploitDb.py
9 changes: 9 additions & 0 deletions deps/primeExploitDb.py
@@ -0,0 +1,9 @@
from pyExploitDb import PyExploitDb

def prime():
pEdb = PyExploitDb()
pEdb.debug = False
pEdb.openFile()

if __name__ == "__main__":
prime()
17 changes: 14 additions & 3 deletions deps/setupWsl.sh
@@ -1,13 +1,24 @@
#!/bin/bash

# Setup linked Windows NMAP
if [ ! -f "/sbin/nmap" ]
if [ -f "/usr/bin/nmap" ]
then
nmapBinCheck=$(cat /usr/bin/nmap | grep -c "nmap.exe")
else
nmapBinCheck=1
fi

if [ ! -f "/sbin/nmap" ] | [ ${nmapBinCheck} -eq 0 ]
then
echo "Installing Link to Windows NMAP..."
mv /usr/bin/nmap /usr/bin/nmap_lin
today=$(date +%s)
mv /usr/bin/nmap /usr/bin/nmap_lin_${today}
cp ./deps/nmap-wsl.sh /sbin/nmap
chmod a+x /sbin/nmap
ln -s /sbin/nmap /usr/bin/nmap
if [ ! -f "/sbin/nmap" ]
then
ln -s /sbin/nmap /usr/bin/nmap
fi
else
echo "Link to Windows NMAP already exists; skipping."
fi
4 changes: 2 additions & 2 deletions legion.conf
Expand Up @@ -29,8 +29,8 @@ nmap-fast-tcp=Run nmap (fast TCP), nmap -Pn -sV -sC -F -T4 -vvvv [IP] -oA \"[OUT
nmap-fast-udp=Run nmap (fast UDP), "nmap -n -Pn -sU -F --min-rate=1000 -vvvvv [IP] -oA \"[OUTPUT]\""
nmap-full-tcp=Run nmap (full TCP), nmap -Pn -sV -sC -O -p- -T4 -vvvvv [IP] -oA \"[OUTPUT]\"
nmap-full-udp=Run nmap (full UDP), nmap -n -Pn -sU -p- -T4 -vvvvv [IP] -oA \"[OUTPUT]\"
nmap-script-Shodan=Run nmap script - Shodan, "nmap -sn -Pn -n --script=./scripts/nmap/shodan-api.nse --script-args shodan-api.apikey=SNYEkE0gdwNu9BRURVDjWPXePCquXqht [IP] -vvvv -oA [OUTPUT]"
nmap-script-Shodan-HQ=Run nmap script - Shodan HQ, "nmap -sn -Pn -n --script=./scripts/nmap/shodan-hq.nse --script-args apikey=SNYEkE0gdwNu9BRURVDjWPXePCquXqht [IP] -vvvv -oA [OUTPUT]"
python-script-PyShodan=Run PyShodan python script, /bin/echo PythonScript pyShodan
python-script-CrashMe=Run CrashMe python scripy, python3 ./scripts/python/dummy.py
nmap-script-Vulners=Run nmap script - Vulners, "nmap -sV --script=./scripts/nmap/vulners.nse -vvvv [IP] -oA \"[OUTPUT]\""
nmap-udp-1000=Run nmap (top 1000 quick UDP), "nmap -n -Pn -sU --min-rate=1000 -vvvvv [IP] -oA \"[OUTPUT]\""
unicornscan-full-udp=Run unicornscan (full UDP), unicornscan -mU -Ir 1000 [IP]:a -v
Expand Down
18 changes: 9 additions & 9 deletions parsers/CVE.py
Expand Up @@ -15,12 +15,12 @@ class CVE:
exploitUrl = ''

def __init__(self, cveData):
self.name = cveData['id']
self.product = cveData['product']
self.version = cveData['version']
self.url = cveData['url']
self.source = cveData['source']
self.severity = cveData['severity']
self.exploitId = cveData['exploitId']
self.exploit = cveData['exploit']
self.exploitUrl = cveData['exploitUrl']
self.name = cveData.get('id', 'unknown')
self.product = cveData.get('product', 'unknown')
self.version = cveData.get('version', 'unknown')
self.url = cveData.get('url', 'unknown')
self.source = cveData.get('source', 'unknown')
self.severity = cveData.get('severity', 'unknown')
self.exploitId = cveData.get('exploitId', 'unknown')
self.exploit = cveData.get('exploit', 'unknown')
self.exploitUrl = cveData.get('exploitUrl', 'unknown')
2 changes: 1 addition & 1 deletion parsers/Script.py
Expand Up @@ -81,7 +81,7 @@ def processVulnersScriptOutput(self, vulnersOutput):
if exploitResults:
resultCveDict['exploitId'] = exploitResults['edbid']
resultCveDict['exploit'] = exploitResults['exploit']
resultCveDict['exploitUrl'] = "https://www.exploit-db.com/exploits/{0}".format(resultCveDict['exploit'])
resultCveDict['exploitUrl'] = "https://www.exploit-db.com/exploits/{0}".format(resultCveDict['exploitId'])
resultCvesProcessed.append(resultCveDict)
resultCpeDetails['cves'] = resultCvesProcessed
resultsDict[resultCpeData[3]] = resultCpeDetails
Expand Down
Empty file added scripts/python/__init__.py
Empty file.
5 changes: 5 additions & 0 deletions scripts/python/dummy.py
@@ -0,0 +1,5 @@
#!/usr/bin/python3

import sys
print('Dummy!')
sys.exit(1)
31 changes: 31 additions & 0 deletions scripts/python/pyShodan.py
@@ -0,0 +1,31 @@
from pyShodan import PyShodan

class PyShodanScript():
def __init__(self):
self.dbHost = None
self.session = None

def setDbHost(self, dbHost):
self.dbHost = dbHost

def setSession(self, session):
self.session = session

def run(self):
print('Running PyShodan Class')
if self.dbHost:
pyShodanObj = PyShodan()
pyShodanObj.apiKey = "SNYEkE0gdwNu9BRURVDjWPXePCquXqht"
pyShodanObj.createSession()
pyShodanResults = pyShodanObj.searchIp(self.dbHost.ipv4, allData = True)
if pyShodanResults:
self.dbHost.latitude = pyShodanResults.get('latitude', 'unknown')
self.dbHost.longitude = pyShodanResults.get('longitude', 'unknown')
self.dbHost.asn = pyShodanResults.get('asn', 'unknown')
self.dbHost.ips = pyShodanResults.get('isp', 'unknown')
self.dbHost.city = pyShodanResults.get('city', 'unknown')
self.dbHost.countryCode = pyShodanResults.get('country_code', 'unknown')
self.session.add(self.dbHost)

if __name__ == "__main__":
pass

0 comments on commit 6f6fc94

Please sign in to comment.