Gallopsled / Gallopsled/pwntools
SSH keyboard-interactive handler support for ssh() in pwntools
- Dominant language
- Python
- Stars
- 13.7k
- Forks
- 1.9k
- Avg merge
- 6d 23h
- Merged PRs (30d)
- 3
Description
Currently I am using Paramiko to keyboard-interactive authenticate against an SSH module. The host I am connecting to does not require a password or private key, but purely a 2-factor authentication module. This works fine, but the old interactive shell from Paramiko is not very good.
I looked into solving the same by using pwntools, but it does not seem like pwntools currently support keyboard-interactive handlers for SSH authentication?
`paramiko.Transport` is purely used for this, and it works fine but provides a poor shell and is why I would prefer using pwntools instead:
```python
import paramiko
import interactive # https://raw.githubusercontent.com/paramiko/paramiko/main/demos/interactive.py
HOST = "127.0.0.1"
PORT = 22
USER = "user"
def handler(title, instructions, prompt_list):
print(title, instructions, prompt_list)
return []
transport = paramiko.Transport((HOST, PORT))
transport.connect(username=USER)
transport.auth_interactive(USER, handler)
try:
channel = transport.open_session() # https://github.com/Gallopsled/pwntools/blob/da8bf2667d735a82ab539898e0cc1b36d40bc3d5/pwnlib/tubes/ssh.py#L109
channel.get_pty()
channel.invoke_shell()
interactive.interactive_shell(channel) # poor quality paramiko shell
finally:
channel.close()
transport.close()
```
I could not find this functionality in the pwntools docs and thought it would be a nice addition to the framework.
This can be solved by setting up the transport **without** a `SSHclient.connect()` [ssh.py#L658](https://github.com/Gallopsled/pwntools/blob/da8bf2667d735a82ab539898e0cc1b36d40bc3d5/pwnlib/tubes/ssh.py#L658), and by letting the Transport connect instead, like in the example above.
I have looked into using the paramiko session with pwntools remote and interactive but have not yet succeeded.
---
**Edit:**
Fixed a bit more and the following `posix_shell` works very well with Paramiko Channels instead of `interactive.interactive_shell(channel)`:
```python
def posix_shell(channel):
oldtty = termios.tcgetattr(sys.stdin)
try:
tty.setraw(sys.stdin.fileno())
tty.setcbreak(sys.stdin.fileno())
channel.settimeout(0.05)
while True:
r, w, e = select.select([channel, sys.stdin], [], [])
if channel in r:
try:
data = channel.recv(1024).decode("utf-8")
if len(data) == 0:
#sys.stdout.write("\r\n*** EOF\r\n")
break
sys.stdout.write(data)
sys.stdout.flush()
except socket.timeout:
pass
if sys.stdin in r:
data = sys.stdin.buffer.raw.read(1) # read in binary mode. thanks https://stackoverflow.com/a/51509414/7152474
if len(data) == 0:
break
channel.send(data)
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty) # reset terminal to old terminal settings
```
It would however still be nice to see the equivalent implemented in pwntools.
Contributor guide
Research direction
Start in pwnlib/tubes/ssh.py, especially the SSHclient.connect() path around the referenced line, and compare it with Paramiko Transport.auth_interactive and the transport setup shown in the issue. Done means pwntools' ssh() supports keyboard-interactive authentication and provides the equivalent interactive shell behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- networking
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100