Repository: Gallopsled/pwntools

Issue:
SSHException("No authentication methods available")
I'm trying to use pwn in the following CTF:
`ssh -v lucky@misc.challenges.qctf.ca`

From ssh@bash I can connect without any issues.
Server do not asks for any password and runs challange straight away.

This line is interesting
`Authenticated to misc.challenges.qctf.ca ([45.63.2.192]:22) using "none".`

But in python:
`ssh_conn = pwn.tubes.ssh.ssh(user = 'lucky', host = 'misc.challenges.qctf.ca')`

I'm getting following error message:
`    raise SSHException("No authentication methods available")`
`paramiko.ssh_exception.SSHException: No authentication methods available`

Additional read:
[https://stackoverflow.com/a/71750537](https://stackoverflow.com/a/71750537)

I've dig through pwnlib documentation and didn't find any solution.
If there is a way to interact with such service then please point me to the right direction.


Discussion:
This is most likely a bug in paramiko, not pwntools. Please see if updating paramiko helps, if not, please file your report there.
This seems to work
```diff
diff --git a/pwnlib/tubes/ssh.py b/pwnlib/tubes/ssh.py
index 53b8be0d..ab481650 100644
--- a/pwnlib/tubes/ssh.py
+++ b/pwnlib/tubes/ssh.py
@@ -674,6 +674,11 @@ class ssh(Timeout, Logger):
                            "    To remove the existing entry from your known_hosts and trust the new key, run the following co>
                            "        $ ssh-keygen -R %(host)s\n"
                            "        $ ssh-keygen -R [%(host)s]:%(port)s" % locals())
+            except paramiko.SSHException as e:
+                if str(e) == "No authentication methods available":
+                    self.client.get_transport().auth_none(user)
+                else:
+                    raise

             self.transport = self.client.get_transport()
             self.transport.use_compression(True)

```

with 

```python
from pwn import *
io = ssh('lucky', 'misc.challenges.qctf.ca', raw=True)
io.interactive()
```

I don't think this is a commonly used "authentication" service which gets added to paramiko's SSHClient, so I'd be fine with adding this along with a new parameter like `auth_none=False` to `tubes.ssh`?
I can confirm that above patch works fine under Python/3.11.8 and pwntools/4.12.0.
Thank you!

Resolving patch:
diff --git a/pwnlib/tubes/ssh.py b/pwnlib/tubes/ssh.py
--- a/pwnlib/tubes/ssh.py
+++ b/pwnlib/tubes/ssh.py
@@ -573,7 +573,8 @@ class ssh(Timeout, Logger):
 
     def __init__(self, user=None, host=None, port=22, password=None, key=None,
                  keyfile=None, proxy_command=None, proxy_sock=None, level=None,
-                 cache=True, ssh_agent=False, ignore_config=False, raw=False, *a, **kw):
+                 cache=True, ssh_agent=False, ignore_config=False, raw=False, 
+                 auth_none=False, *a, **kw):
         """Creates a new ssh connection.
 
         Arguments:
@@ -587,10 +588,11 @@ def __init__(self, user=None, host=None, port=22, password=None, key=None,
             proxy_sock(str): Use this socket instead of connecting to the host.
             timeout: Timeout, in seconds
             level: Log level
-            cache: Cache downloaded files (by hash/size/timestamp)
-            ssh_agent: If :const:`True`, enable usage of keys via ssh-agent
-            ignore_config: If :const:`True`, disable usage of ~/.ssh/config and ~/.ssh/authorized_keys
-            raw: If :const:`True`, assume a non-standard shell and don't probe the environment
+            cache(bool): Cache downloaded files (by hash/size/timestamp)
+            ssh_agent(bool): If :const:`True`, enable usage of keys via ssh-agent
+            ignore_config(bool): If :const:`True`, disable usage of ~/.ssh/config and ~/.ssh/authorized_keys
+            raw(bool): If :const:`True`, assume a non-standard shell and don't probe the environment
+            auth_none(bool): If :const:`True`, try to authenticate with no authentication methods
 
         NOTE: The proxy_command and proxy_sock arguments is only available if a
         fairly new version of paramiko is used.
@@ -686,6 +688,11 @@ def __init__(self, user=None, host=None, port=22, password=None, key=None,
                            "    To remove the existing entry from your known_hosts and trust the new key, run the following commands:\n"
                            "        $ ssh-keygen -R %(host)s\n"
                            "        $ ssh-keygen -R [%(host)s]:%(port)s" % locals())
+            except paramiko.SSHException as e:
+                if user and auth_none and str(e) == "No authentication methods available":
+                    self.client.get_transport().auth_none(user)
+                else:
+                    raise
 
             self.transport = self.client.get_transport()
             self.transport.use_compression(True)


Test patch: