Skip to content

Commit 8885b2a

Browse files
authored
Merge pull request #24 from maxDcb/develop
Develop
2 parents 148edcf + 087f03d commit 8885b2a

File tree

7 files changed

+518
-105
lines changed

7 files changed

+518
-105
lines changed
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
import sys
2+
import os
3+
import logging
4+
import importlib
5+
from datetime import datetime
6+
7+
from threading import Thread, Lock, Semaphore
8+
from PyQt5.QtWidgets import *
9+
from PyQt5.QtGui import *
10+
from PyQt5.QtCore import *
11+
12+
from grpcClient import *
13+
14+
import openai
15+
from openai import OpenAI
16+
17+
#
18+
# Assistant tab implementation
19+
#
20+
class Assistant(QWidget):
21+
tabPressed = pyqtSignal()
22+
logFileName=""
23+
sem = Semaphore()
24+
25+
def __init__(self, parent, grpcClient):
26+
super(QWidget, self).__init__(parent)
27+
self.layout = QVBoxLayout(self)
28+
self.layout.setContentsMargins(0, 0, 0, 0)
29+
30+
self.grpcClient = grpcClient
31+
32+
# self.logFileName=LogFileName
33+
34+
self.editorOutput = QPlainTextEdit()
35+
self.editorOutput.setFont(QFont("Courier"));
36+
self.editorOutput.setReadOnly(True)
37+
self.layout.addWidget(self.editorOutput, 8)
38+
39+
self.commandEditor = CommandEditor()
40+
self.layout.addWidget(self.commandEditor, 2)
41+
self.commandEditor.returnPressed.connect(self.runCommand)
42+
43+
# System prompt defined once
44+
system_prompt = {
45+
"role": "system",
46+
"content": (
47+
"""You are a Red Team Operator Assistant embedded in the "Exploration" C2 framework.
48+
You operate in offensive security engagements and support operators by reasoning over command output from enumeration before getting a foothold and compromised machines and suggesting the next best actions.
49+
You also point out security gaps that could be leveraged. You understand operational security (OPSEC), red teaming tactics, post-exploitation phases, and tradecraft.
50+
51+
## Context:
52+
- Exploration C2 framework run on a kali machine
53+
- You will be fed output from commands previously run (e.g., whoami, ps, ls, Seatbelt, SharpHound, etc.).
54+
- All tools available on a kali machine can use used
55+
56+
## Exploration Framework Capabilities:
57+
- socks start|stop <PORT>: Start/Stop SOCKS5 proxy via beacon
58+
- assemblyExec: Execute shellcode, PE or DLL with donut. Output is returned.
59+
- upload <SRC> <DST>: Upload file to victim.
60+
- download <SRC> <DST>: Download file from victim.
61+
- run <CMD>: Run command or process. Use 'cmd /c' for system commands.
62+
- inject [-r|-e|-d] <file> <pid>: Inject shellcode into a PID or spawn process.
63+
- ls: Directory navigation.
64+
- ps: List processes.
65+
- makeToken <DOMAIN\\User> <Password>: Create and impersonate a token.
66+
- rev2self: Drop current impersonation.
67+
- stealToken <pid>: Steal and impersonate a token.
68+
- coffLoader <FILE> [ARGS]: Load and execute COFF object files.
69+
- powershell [-i|-s] <script>: Run PowerShell code with optional AMSI bypass.
70+
- kerberosUseTicket <FILE>: Inject a .kirbi ticket.
71+
- psExec <target> <file>: Remote service execution via SMB share.
72+
- wmiExec [-u|-k] <target> <cmd>: Execute commands via WMI.
73+
- spawnAs <User> <Pass> <cmd>: Run command as another user.
74+
- chisel <ARGS>: Run Chisel tunnel or proxy.
75+
- tree: View directory tree.
76+
77+
## Enumeration and Linux tools:
78+
impacket, nmap, smbclient, netexec and other
79+
80+
## Post-Ex Tools Available:
81+
These include but are not limited to: ADCollector.exe, Certify.exe, Grouper2.exe, PurpleSharp.exe, scout.exe, SharpChisel.exe, SharpCrashEventLog.exe, SharpExec.exe, SharpLAPS.exe, SharpPrinter.exe, SharpShares.exe, SharpStay.exe, SharpWifiGrabber.exe, StandIn.exe, Watson.exe, ADCSPwn.exe, DeployPrinterNightmare.exe, Inveigh.exe, README.md, SearchOutlook.exe, SharpChrome.exe, SharpDir.exe, SharpGPOAbuse.exe, SharpMapExec.exe, SharpRDP.exe, Sharp-SMBExec.exe, SharpSvc.exe, SharpWMI.exe, StickyNotesExtract.exe, Whisker.exe, ADFSDump.exe, EDD.exe, KrbRelay.exe, Rubeus.exe, Seatbelt.exe, SharpChromium.exe, SharpDPAPI.exe, SharpHandler.exe, SharpMiniDump.exe, SharpReg.exe, SharpSniper.exe, SharpTask.exe, SharpZeroLogon.exe, SweetPotato.exe, winPEAS.exe, ADSearch.exe, ForgeCert.exe, KrbRelayUp.exe, _RunasCs.exe, SharpAllowedToAct.exe, SharpCloud.exe, SharpDump.exe, SharpHose.exe, SharpMove.exe, SharpSCCM.exe, SharpSphere.exe, SharpUp.exe, Shhmon.exe, ThunderFox.exe, WMIReg.exe, AtYourService.exe, GMSAPasswordReader.exe, LockLess.exe, SafetyKatz.exe, SharpAppLocker.exe, SharpCOM.exe, SharpEDRChecker.exe, SharpHound.exe, SharpNamedPipePTH.exe, SharpSearch.exe, SharpSpray.exe, SharpView.exe, Snaffler.exe, TokenStomp.exe, BetterSafetyKatz.exe, Group3r.exe, PassTheCert.exe, SauronEye.exe, SharpBypassUAC.exe, SharpCookieMonster.exe, SharPersist.exe, SharpKatz.exe, SharpNoPSExec.exe, SharpSecDump.exe, SharpSQLPwn.exe, SharpWebServer.exe, SqlClient.exe, TruffleSnout.exe
82+
83+
## Instructions:
84+
- Suggest only what makes tactical sense based on the output provided.
85+
- Prioritize stealth and minimal footprint.
86+
- Chain commands where appropriate to complete an objective (e.g., escalate, pivot, loot).
87+
- When unclear, ask the operator for additional context instead of assuming."""
88+
)
89+
}
90+
91+
# Initialize message history with the system prompt
92+
self.messages = [system_prompt]
93+
94+
# Maximum number of messages to retain
95+
self.MAX_MESSAGES = 20
96+
97+
98+
def nextCompletion(self):
99+
index = self._compl.currentIndex()
100+
self._compl.popup().setCurrentIndex(index)
101+
start = self._compl.currentRow()
102+
if not self._compl.setCurrentRow(start + 1):
103+
self._compl.setCurrentRow(0)
104+
105+
106+
def sessionAssistantMethod(self, action, beaconHash, listenerHash, hostname, username, arch, privilege, os, lastProofOfLife, killed):
107+
if action == "start":
108+
toto = 1
109+
elif action == "stop":
110+
toto = 1
111+
elif action == "update":
112+
toto = 1
113+
114+
115+
def listenerAssistantMethod(self, action, hash, str3, str4):
116+
if action == "start":
117+
toto = 1
118+
elif action == "stop":
119+
toto = 1
120+
121+
122+
def consoleAssistantMethod(self, action, beaconHash, listenerHash, context, cmd, result):
123+
if action == "receive":
124+
# print("consoleAssistantMethod", "-Context:\n" + context + "\n\n-Command sent:\n" + cmd + "\n\n-Response:\n" + result)
125+
self.messages.append({"role": "user", "content": cmd + "\n" + result})
126+
elif action == "send":
127+
toto = 1
128+
129+
130+
def event(self, event):
131+
if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab:
132+
self.tabPressed.emit()
133+
return True
134+
return super().event(event)
135+
136+
137+
def printInTerminal(self, cmd, result):
138+
now = datetime.now()
139+
formater = '<p style="white-space:pre">'+'<span style="color:blue;">['+now.strftime("%Y:%m:%d %H:%M:%S").rstrip()+']</span>'+'<span style="color:red;"> [+] </span>'+'<span style="color:red;">{}</span>'+'</p>'
140+
141+
self.sem.acquire()
142+
if cmd:
143+
self.editorOutput.appendHtml(formater.format(cmd))
144+
self.editorOutput.insertPlainText("\n")
145+
if result:
146+
self.editorOutput.insertPlainText(result)
147+
self.editorOutput.insertPlainText("\n")
148+
self.sem.release()
149+
150+
151+
def runCommand(self):
152+
commandLine = self.commandEditor.displayText()
153+
self.commandEditor.clearLine()
154+
self.setCursorEditorAtEnd()
155+
156+
if commandLine == "":
157+
self.printInTerminal("", "")
158+
159+
else:
160+
161+
api_key = os.environ.get("OPENAI_API_KEY")
162+
163+
if api_key:
164+
client = OpenAI(
165+
# This is the default and can be omitted
166+
api_key=api_key,
167+
)
168+
169+
# Add user command output
170+
self.messages.append({"role": "user", "content": commandLine})
171+
172+
if len(self.messages) > self.MAX_MESSAGES * 2 + 1:
173+
# Always keep the first message (system prompt)
174+
system_prompt = self.messages[0]
175+
recent_messages = self.messages[-(self.MAX_MESSAGES * 2):]
176+
self.messages = [system_prompt] + recent_messages
177+
178+
try:
179+
# Call OpenAI API
180+
response = client.chat.completions.create(
181+
model="gpt-4o",
182+
messages=self.messages,
183+
temperature=0.05
184+
)
185+
186+
assistant_reply = response.choices[0].message.content
187+
188+
self.printInTerminal("User:", commandLine)
189+
self.printInTerminal("Analysis:", assistant_reply)
190+
191+
# Add assistant's response to conversation
192+
self.messages.append({"role": "assistant", "content": assistant_reply})
193+
except openai.APIConnectionError as e:
194+
print("Server connection error: {e.__cause__}")
195+
196+
except openai.RateLimitError as e:
197+
print(f"OpenAI RATE LIMIT error {e.status_code}: (e.response)")
198+
199+
except openai.APIStatusError as e:
200+
print(f"OpenAI STATUS error {e.status_code}: (e.response)")
201+
202+
except openai.BadRequestError as e:
203+
print(f"OpenAI BAD REQUEST error {e.status_code}: (e.response)")
204+
205+
except Exception as e:
206+
print(f"An unexpected error occurred: {e}")
207+
208+
else:
209+
self.printInTerminal("OPENAI_API_KEY is not set, functionality deactivated.", "")
210+
211+
212+
self.setCursorEditorAtEnd()
213+
214+
215+
# setCursorEditorAtEnd
216+
def setCursorEditorAtEnd(self):
217+
cursor = self.editorOutput.textCursor()
218+
cursor.movePosition(QTextCursor.End,)
219+
self.editorOutput.setTextCursor(cursor)
220+
221+
222+
class CommandEditor(QLineEdit):
223+
tabPressed = pyqtSignal()
224+
cmdHistory = []
225+
idx = 0
226+
227+
def __init__(self, parent=None):
228+
super().__init__(parent)
229+
230+
QShortcut(Qt.Key_Up, self, self.historyUp)
231+
QShortcut(Qt.Key_Down, self, self.historyDown)
232+
233+
# self.codeCompleter = CodeCompleter(completerData, self)
234+
# # needed to clear the completer after activation
235+
# self.codeCompleter.activated.connect(self.onActivated)
236+
# self.setCompleter(self.codeCompleter)
237+
# self.tabPressed.connect(self.nextCompletion)
238+
239+
def nextCompletion(self):
240+
index = self.codeCompleter.currentIndex()
241+
self.codeCompleter.popup().setCurrentIndex(index)
242+
start = self.codeCompleter.currentRow()
243+
if not self.codeCompleter.setCurrentRow(start + 1):
244+
self.codeCompleter.setCurrentRow(0)
245+
246+
def event(self, event):
247+
if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab:
248+
self.tabPressed.emit()
249+
return True
250+
return super().event(event)
251+
252+
def historyUp(self):
253+
if(self.idx<len(self.cmdHistory) and self.idx>=0):
254+
cmd = self.cmdHistory[self.idx%len(self.cmdHistory)]
255+
self.idx=max(self.idx-1,0)
256+
self.setText(cmd.strip())
257+
258+
def historyDown(self):
259+
if(self.idx<len(self.cmdHistory) and self.idx>=0):
260+
self.idx=min(self.idx+1,len(self.cmdHistory)-1)
261+
cmd = self.cmdHistory[self.idx%len(self.cmdHistory)]
262+
self.setText(cmd.strip())
263+
264+
def setCmdHistory(self):
265+
cmdHistoryFile = open('.termHistory')
266+
self.cmdHistory = cmdHistoryFile.readlines()
267+
self.idx=len(self.cmdHistory)-1
268+
cmdHistoryFile.close()
269+
270+
def clearLine(self):
271+
self.clear()
272+
273+
def onActivated(self):
274+
QTimer.singleShot(0, self.clear)
275+
276+
277+
class CodeCompleter(QCompleter):
278+
ConcatenationRole = Qt.UserRole + 1
279+
280+
def __init__(self, data, parent=None):
281+
super().__init__(parent)
282+
self.createModel(data)
283+
284+
def splitPath(self, path):
285+
return path.split(' ')
286+
287+
def pathFromIndex(self, ix):
288+
return ix.data(CodeCompleter.ConcatenationRole)
289+
290+
def createModel(self, data):
291+
def addItems(parent, elements, t=""):
292+
for text, children in elements:
293+
item = QStandardItem(text)
294+
data = t + " " + text if t else text
295+
item.setData(data, CodeCompleter.ConcatenationRole)
296+
parent.appendRow(item)
297+
if children:
298+
addItems(item, children, data)
299+
model = QStandardItemModel(self)
300+
addItems(model, data)
301+
self.setModel(model)

0 commit comments

Comments
 (0)