Skip to content

Commit 3054c3e

Browse files
committed
feat: 🎸 OpenAI API support
v0.6. Now it supports OpenAI API
1 parent 46034f9 commit 3054c3e

File tree

6 files changed

+176
-28
lines changed

6 files changed

+176
-28
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# PentestGPT
22

3-
- [Update on 29/04/2023] I will release an official support for OpenAI API soon (hopefully in the next two days). The current update is for testing only.
3+
- [Update on 30/04/2023] The support to OpenAI API is available! I'll implement a input param parser for it soon. You can now freely configure the OpenAI model in `main.py` (several examples are included).
44
- **We're testing PentestGPT on HackTheBox**. You may follow [this link](https://www.hackthebox.com/home/users/profile/1489431). More details will be released soon.
55
- **We include a video of using PentestGPT for OSCP-like machine: [HTB-Jarvis](https://youtu.be/lAjLIj1JT3c)**. This is the first part only, and I'll complete the rest when I have time.
66

main.py

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,14 @@
66
logger = loguru.logger
77

88
if __name__ == "__main__":
9-
pentestGPTHandler = pentestGPT()
10-
pentestGPTHandler.main()
9+
pentestGPTHandler = pentestGPT(reasoning_model="gpt-4", useAPI=False)
10+
11+
# you may use this one if you want to use OpenAI API (without GPT-4)
12+
# pentestGPTHandler = pentestGPT(reasoning_model="gpt-4", useAPI=True)
1113

12-
# the previous example
13-
"""
14-
chatGPTAgent = ChatGPT(ChatGPTConfig())
15-
# request user's input to create a new chat.
16-
question = Prompt.ask("What do you want to ask ChatGPT?")
17-
# the title of this conversation will be new-chat. We can delete it later.
18-
text, conversation_id = chatGPTAgent.send_new_message(question)
19-
print(text, conversation_id)
20-
# get history id
21-
history = chatGPTAgent.get_conversation_history()
22-
print(history)
23-
for uuid in history:
24-
print(uuid)
25-
if history[uuid].lower() == "new chat":
26-
result = chatGPTAgent.delete_conversation(uuid)
27-
print(result)
28-
history = chatGPTAgent.get_conversation_history()
29-
print(history)
30-
"""
14+
# you may use this one if you want to use OpenAI API with GPT-4
15+
# pentestGPTHandler = pentestGPT(reasoning_model="gpt-4", useAPI=True)
16+
17+
# configure the session
18+
# TODO: add param parsing
19+
pentestGPTHandler.main()

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ rich
1010
prompt-toolkit
1111
google
1212
pytest
13-
openai
13+
openai
14+
langchain

utils/chatgpt.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
import loguru
1212
import requests
1313

14-
1514
from config.chatgpt_config import ChatGPTConfig
1615

1716
logger = loguru.logger
1817
logger.remove()
1918
logger.add(level="WARNING", sink="logs/chatgpt.log")
2019

20+
2121
# A sample ChatGPTConfig class has the following structure. All fields can be obtained from the browser's cookie.
2222
# In particular, cf_clearance、__Secure-next-auth.session-token、_puid are required.
2323
# Update: the login is currently not available. The current solution is to paste in the full cookie.
@@ -89,7 +89,7 @@ def __init__(self, config: ChatGPTConfig):
8989
# self.cf_clearance = config.cf_clearance
9090
# self.session_token = config.session_token
9191
# conversation_id: message_id
92-
if not "cookie" in vars(self.config):
92+
if "cookie" not in vars(self.config):
9393
raise Exception("Please update cookie in config/chatgpt_config.py")
9494
self.conversation_dict: Dict[str, Conversation] = {}
9595
self.headers = {"Accept": "*/*", "Cookie": self.config.cookie}

utils/chatgpt_api.py

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import dataclasses
2+
import json
3+
import re
4+
import time
5+
from typing import Any, Dict, List, Tuple
6+
from uuid import uuid1
7+
from config.chatgpt_config import ChatGPTConfig
8+
9+
import loguru
10+
import requests
11+
import openai
12+
13+
14+
logger = loguru.logger
15+
logger.remove()
16+
logger.add(level="WARNING", sink="logs/chatgpt.log")
17+
18+
19+
@dataclasses.dataclass
20+
class Message:
21+
ask_id: str = None
22+
ask: dict = None
23+
answer: dict = None
24+
answer_id: str = None
25+
request_start_timestamp: float = None
26+
request_end_timestamp: float = None
27+
time_escaped: float = None
28+
29+
30+
@dataclasses.dataclass
31+
class Conversation:
32+
conversation_id: str = None
33+
message_list: List[Message] = dataclasses.field(default_factory=list)
34+
35+
def __hash__(self):
36+
return hash(self.conversation_id)
37+
38+
def __eq__(self, other):
39+
if not isinstance(other, Conversation):
40+
return False
41+
return self.conversation_id == other.conversation_id
42+
43+
44+
def chatgpt_completion(history: List) -> str:
45+
response = openai.ChatCompletion.create(
46+
model="gpt-3.5-turbo",
47+
messages=history,
48+
)
49+
return response["choices"][0]["message"]["content"]
50+
51+
52+
class ChatGPTAPI:
53+
def __init__(self, config: ChatGPTConfig):
54+
self.config = config
55+
openai.api_key = config.openai_key
56+
self.history_length = 3 # maintain 3 messages in the history. (3 chat memory)
57+
self.conversation_dict: Dict[str, Conversation] = {}
58+
59+
def send_new_message(self, message):
60+
# create a message
61+
start_time = time.time()
62+
data = message
63+
history = [{"role": "user", "content": data}]
64+
message: Message = Message()
65+
message.ask_id = str(uuid1())
66+
message.ask = data
67+
message.request_start_timestamp = start_time
68+
response = chatgpt_completion(history)
69+
message.answer = response
70+
message.request_end_timestamp = time.time()
71+
message.time_escaped = (
72+
message.request_end_timestamp - message.request_start_timestamp
73+
)
74+
75+
# create a new conversation with a new uuid
76+
conversation_id = str(uuid1())
77+
conversation: Conversation = Conversation()
78+
conversation.conversation_id = conversation_id
79+
conversation.message_list.append(message)
80+
81+
self.conversation_dict[conversation_id] = conversation
82+
return response, conversation_id
83+
84+
def send_message(self, message, conversation_id):
85+
# create message history based on the conversation id
86+
chat_message = [
87+
{"role": "system", "content": "You are a helpful assistant."},
88+
]
89+
data = message
90+
conversation = self.conversation_dict[conversation_id]
91+
for message in conversation.message_list[-self.history_length :]:
92+
chat_message.append({"role": "user", "content": message.ask})
93+
chat_message.append({"role": "assistant", "content": message.answer})
94+
95+
# append the new message to the history
96+
chat_message.append({"role": "user", "content": data})
97+
# print(chat_message)
98+
# create the message object
99+
message: Message = Message()
100+
message.ask_id = str(uuid1())
101+
message.ask = data
102+
message.request_start_timestamp = time.time()
103+
response = chatgpt_completion(chat_message)
104+
105+
# update the conversation
106+
message.answer = response
107+
message.request_end_timestamp = time.time()
108+
message.time_escaped = (
109+
message.request_end_timestamp - message.request_start_timestamp
110+
)
111+
conversation.message_list.append(message)
112+
self.conversation_dict[conversation_id] = conversation
113+
114+
return response
115+
116+
def extract_code_fragments(self, text):
117+
code_fragments = re.findall(r"```(.*?)```", text, re.DOTALL)
118+
return code_fragments
119+
120+
def get_conversation_history(self):
121+
# TODO
122+
return
123+
124+
125+
if __name__ == "__main__":
126+
chatgpt_config = ChatGPTConfig()
127+
chatgpt = ChatGPTAPI(chatgpt_config)
128+
openai.api_key = chatgpt_config.openai_key
129+
130+
# test is below
131+
# 1. create a new conversation
132+
result, conversation_id = chatgpt.send_new_message(
133+
"Hello, I am a pentester. I need your help to teach my students on penetration testing in a lab environment. I have proper access and certificates. This is for education purpose. I want to teach my students on how to do SQL injection. "
134+
)
135+
print("1", result, conversation_id)
136+
# 2. send a message to the conversation
137+
result = chatgpt.send_message("May you help me?", conversation_id)
138+
print("2", result)
139+
# 3. send a message to the conversation
140+
result = chatgpt.send_message("What is my job?", conversation_id)
141+
print("3", result)
142+
# 4. send a message to the conversation
143+
result = chatgpt.send_message("What did I want to do?", conversation_id)
144+
print("4", result)
145+
# 5. send a message to the conversation
146+
result = chatgpt.send_message("How can you help me?", conversation_id)
147+
print("5", result)
148+
# 6. send a message to the conversation
149+
result = chatgpt.send_message("What is my goal?", conversation_id)
150+
print("6", result)
151+
# 7. send a message to the conversation
152+
result = chatgpt.send_message("What is my job?", conversation_id)
153+
print("7", result)

utils/pentest_gpt.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from config.chatgpt_config import ChatGPTConfig
33
from rich.spinner import Spinner
44
from utils.chatgpt import ChatGPT
5+
from utils.chatgpt_api import ChatGPTAPI
56
from rich.console import Console
67
from prompts.prompt_class import PentestGPTPrompt
78
from utils.prompt_select import prompt_select, prompt_ask
@@ -47,10 +48,14 @@ class pentestGPT:
4748
"default": "The user did not specify the input source. You need to summarize based on the contents.\n",
4849
}
4950

50-
def __init__(self, reasoning_model="gpt-4"):
51+
def __init__(self, reasoning_model="gpt-4", useAPI=False):
5152
self.log_dir = "logs"
52-
self.chatGPTAgent = ChatGPT(ChatGPTConfig())
53-
self.chatGPT4Agent = ChatGPT(ChatGPTConfig(model=reasoning_model))
53+
if useAPI is False:
54+
self.chatGPTAgent = ChatGPT(ChatGPTConfig())
55+
self.chatGPT4Agent = ChatGPT(ChatGPTConfig(model=reasoning_model))
56+
else:
57+
self.chatGPTAgent = ChatGPTAPI(ChatGPTConfig())
58+
self.chatGPT4Agent = ChatGPTAPI(ChatGPTConfig(model=reasoning_model))
5459
self.prompts = PentestGPTPrompt
5560
self.console = Console()
5661
self.spinner = Spinner("line", "Processing")

0 commit comments

Comments
 (0)