Skip to content

SSH keyboard-interactive#6508

Merged
Mzack9999 merged 4 commits intoprojectdiscovery:devfrom
chovanecadam:ssh-keyboard-interactive
Nov 2, 2025
Merged

SSH keyboard-interactive#6508
Mzack9999 merged 4 commits intoprojectdiscovery:devfrom
chovanecadam:ssh-keyboard-interactive

Conversation

@chovanecadam
Copy link

@chovanecadam chovanecadam commented Oct 3, 2025

Proposed changes

Adds best-effort support for SSH keyboard-interactive protocol. Although the protocol supports interactive questions, this MR adds support for a single question to which it responds with the password.

Checklist

  • Pull request is created against the dev branch
  • All checks passed (lint, unit/integration/regression tests etc.) with my changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Summary by CodeRabbit

  • New Features

    • Adds keyboard-interactive SSH authentication when a password is provided.
    • Auto-responds to single-prompt interactive challenges using supplied credentials.
    • Improves compatibility with SSH servers that require interactive authentication.
  • Refactor

    • Streamlines the connection authentication flow to handle interactive prompts transparently for users.

@auto-assign auto-assign bot requested a review from dogancanbakir October 3, 2025 17:28
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 3, 2025

Walkthrough

Adds keyboard-interactive SSH authentication to the existing connection flow by registering an ssh.KeyboardInteractiveChallenge callback that logs prompts and auto-answers username/password prompts using supplied credentials; this is enabled when a password is provided and preserves existing direct password auth.

Changes

Cohort / File(s) Summary of Changes
SSH auth methods enhancement
pkg/js/libs/ssh/ssh.go
Added keyboard-interactive auth support: introduced package-level precompiled regexes for password/username prompts, new imports (regexp, gologger, vardump), a keyboard-interactive callback that logs prompts and auto-answers username/password prompts (using provided credentials), and registration of the callback via ssh.KeyboardInteractiveChallenge; existing password auth path retained.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant App as App
  participant SSHC as SSH Client (local)
  participant Server as SSH Server

  App->>SSHC: Connect(host, user, password)
  Note over SSHC: Configure auth methods:\n- Password\n- Keyboard-Interactive (callback logs prompts & supplies answers)

  SSHC->>Server: Start authentication
  alt Server accepts direct password
    SSHC->>Server: Send password
  else Server issues keyboard-interactive challenge
    Server->>SSHC: Challenge(questions[])
    SSHC->>SSHC: Invoke keyboard-interactive callback
    Note right of SSHC: Callback logs prompts\nand matches regexes
    alt Prompt matches username/password
      SSHC->>Server: Respond with provided username/password
    else
      SSHC->>Server: Respond with empty/default
    end
  end
  Server-->>SSHC: Auth success/failure
  SSHC-->>App: Connection result
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Review focus:
    • pkg/js/libs/ssh/ssh.go — verify regex correctness, callback logic, and logging usage.
    • Ensure new imports (gologger, vardump) are appropriate and used consistently.

Poem

I twitch my whiskers at the gate,
One prompt, one hop — credentials wait.
I log the questions, answer true,
A hopeful hop — the session’s new.
Quiet burrow, tunnel through. 🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "SSH keyboard-interactive" directly and clearly summarizes the main change in the changeset. The entire pull request focuses on implementing keyboard-interactive authentication support for SSH connections, which is precisely what the title conveys. The title is concise, specific enough to distinguish this work from other SSH-related changes, and avoids vague or generic language. A developer scanning PR history would immediately understand that this PR introduces keyboard-interactive authentication functionality for SSH.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a7ad88e and 248bac7.

📒 Files selected for processing (1)
  • pkg/js/libs/ssh/ssh.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/js/libs/ssh/ssh.go

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
pkg/js/libs/ssh/ssh.go (1)

237-242: Consider returning an error for unsupported multi-question scenarios.

The callback returns an empty answer slice when len(questions) != 1, which may cause authentication to fail silently. Consider returning an explicit error to make the limitation clear.

Apply this diff:

 	cb := func(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
 		if len(questions) == 1 {
 			return []string{opts.Password}, nil
 		}
-		return []string{}, nil
+		return nil, fmt.Errorf("keyboard-interactive with %d questions not supported", len(questions))
 	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86be842 and 7f61653.

📒 Files selected for processing (1)
  • pkg/js/libs/ssh/ssh.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Format Go code using go fmt
Run static analysis with go vet

Files:

  • pkg/js/libs/ssh/ssh.go
🔇 Additional comments (1)
pkg/js/libs/ssh/ssh.go (1)

237-242: Verify that blindly answering with the password is acceptable.

The callback responds with the password for any single-question prompt without inspecting the question text. While this is documented as "best-effort," it could pose a security risk if the server asks an unexpected question (e.g., "Are you sure you want to delete all data?").

Consider whether the implementation should:

  1. Validate that the question is password-related (e.g., contains "password" or "passphrase")
  2. Add logging to track what questions are being answered
  3. Document this behavior more explicitly in code comments

If this best-effort approach is intentional for broad compatibility, add a comment explaining the trade-off.

@chovanecadam
Copy link
Author

To answer Rabbit's question, the keyboard-interactive protocol sometimes asks no questions, in which case we return no answers and the authentication flow continues. It would be an error to break the authentication flow there, as the user may be prompted for a password in a later stage. I tested this on ubuntu:24.04 container using the javascript/default-logins/ssh-default-logins.yaml template

@dogancanbakir
Copy link
Member

@coderabbitai create an issue for this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 7, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# Get the repository owner and name from the PR URL
REPO="projectdiscovery/nuclei"
PR_NUMBER="6508"
PR_URL="https://github.com/projectdiscovery/nuclei/pull/6508"
COMMENT_URL="https://github.com/projectdiscovery/nuclei/pull/6508#discussion_r1899699999"

# Create the issue
gh issue create \
  --repo "$REPO" \
  --title "Follow-up: SSH keyboard-interactive protocol support" \
  --body "## Context

This issue tracks the implementation of SSH keyboard-interactive protocol support introduced in PR #${PR_NUMBER}.

## Background

PR #${PR_NUMBER} adds best-effort support for the SSH keyboard-interactive protocol with the following characteristics:
- Handles interactive questions with support limited to a single question
- Responds with the configured password
- Allows authentication flow to continue when no questions are asked

## Outstanding Items

As noted in the PR checklist, the following items need attention:
- [ ] Tests
- [ ] CI checks  
- [ ] Documentation

## References

- PR: ${PR_URL}
- Requested by: @dogancanbakir
- Implementation: Tested on ubuntu:24.04 container using \`javascript/default-logins/ssh-default-logins.yaml\` template" \
  --assignee "dogancanbakir"

Length of output: 54


@dogancanbakir: I've created issue #6514 to track this PR. The issue includes:

  • Context about the SSH keyboard-interactive protocol implementation
  • Outstanding items from the PR checklist (tests, CI checks, documentation)
  • References to this PR and your request

You can view it here: #6514

@dogancanbakir dogancanbakir linked an issue Oct 7, 2025 that may be closed by this pull request
3 tasks
@dogancanbakir dogancanbakir requested review from dwisiswant0 and removed request for dogancanbakir October 7, 2025 12:51
Copy link
Member

@dwisiswant0 dwisiswant0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor:

  • It (len(questions) == 1 expr) blindly assumed it was a password prompt. It could send a password to non-password questions like "Enter username:" or "Accept terms? (y/n)". I mean, what about servers that ask username + password in separate questions?
  • Password fields usually do not display the input characters, which makes the echos param a reliable indicator of this behavior.
  • No analysis of question text content. Couldn't distinguish between different types of single questions. Which comes with some unresolved questions:
    • What if servers use non-English prompts?
    • What if they use unusual question formats?
    • What about servers that send empty questions or instructions?

nit:

  • When keyboard-interactive fails, users get a generic "auth failed" with no indication that it was due to complex auth requirements.
  • No way to log when keyboard-interactive auth is attempted vs. when it fails due to complexity.

@chovanecadam
Copy link
Author

In general, this MR does not intend to implement full keyboard-interactive fuzzing. Adding such feature would require also designing API that would allow to specify questions/answers in templates. This MR does not intend to do that. This MR only adds support in cases when

  1. keyboard-interactive is enabled
  2. only password is required

Although keyboard-interactive support would be nice to have in Nuclei, I have no interest in implementing that. The common usecases for keyboard-interactive are requiring TOTP on login. So I think most of the points raised are out of scope. I think this MR is useful and enhances Nuclei in the current state without the nice to have features you described above. Nevertheless I still try to respond to them below:

  • It (len(questions) == 1 expr) blindly assumed it was a password prompt. It could send a password to non-password questions like "Enter username:" or "Accept terms? (y/n)". I mean, what about servers that ask username + password in separate questions?

These are theoretical issues. Username in SSH is provided before the authentication flow starts, asking for it during the flow doesn't make sense. Accepting terms? I have never seen anything like this ever.

  • Password fields usually do not display the input characters, which makes the echos param a reliable indicator of this behavior.

That's probably true.

  • No analysis of question text content. Couldn't distinguish between different types of single questions. Which comes with some unresolved questions:

    • What if servers use non-English prompts?
    • What if they use unusual question formats?
    • What about servers that send empty questions or instructions?

I don't check questions in this MR. I don't log the questions either.

nit:

  • When keyboard-interactive fails, users get a generic "auth failed" with no indication that it was due to complex auth requirements.

That's true. The user is only notified if the authentication was successful and there is a server with a weak password. I don't think it is interesting / useful to know if the auth failed due to other reasons. I could log the questions though, if you think it would be useful

  • No way to log when keyboard-interactive auth is attempted vs. when it fails due to complexity.

I don't understand the distinction here.

@dwisiswant0
Copy link
Member

I have never seen anything like this ever.

SSH servers are not limited to OpenSSH. Some use Cisco, F5, etc, and with custom auth flows. Even if you've never encountered them prompts, they exist1 in some VPN gateways and/or enterprise access controls.

My point is, "len(questions) == 1" expr is particularly problematic as it blindly assumes single questions are password prompts without analyzing content and/or context -- which could totally leak passwords to random prompts, in unexpected scenarios.

Implementing a reliable and cautious approach is more suitable than relying on assumptions that could result in UB.

That's true. The user is only notified if the authentication was successful and there is a server with a weak password. I don't think it is interesting / useful to know if the auth failed due to other reasons. [...]

This is an API-level change, and it should NOT be treated/scoped/reasoned as something that only applies to a specific template or case.

Footnotes

  1. the SSH RFC 4256 explicitly permits arbitrary challenge formats.

@chovanecadam
Copy link
Author

I see. So let's only handle out password if the question matches case-insensitive password and echo is True? And let's also log questions to allow debugging the protocol?

@dwisiswant0
Copy link
Member

I see. So let's only handle out password if the question matches case-insensitive password and echo is True? And let's also log questions to allow debugging the protocol?

Yea that works.

Adam Chovanec and others added 4 commits November 2, 2025 00:38
by:
* implement regex-based prompt matching for
  password variants.
* add support for filling username prompts in
  keyboard interactive challenges.
* improve debug logging with structured output.

this addresses issues with servers using
non-standard prompt formats and provides better
visibility into auth failures.

Signed-off-by: Dwi Siswanto <git@dw1.io>
@dwisiswant0 dwisiswant0 force-pushed the ssh-keyboard-interactive branch from a7ad88e to 248bac7 Compare November 1, 2025 17:38
@Mzack9999 Mzack9999 merged commit 5b5d87f into projectdiscovery:dev Nov 2, 2025
31 of 32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Follow-up: SSH keyboard-interactive protocol support

4 participants