遇到一个自动化登录的问题,不是简单的 ssh 免密登录哈,要求如下:
环境:Windows10
需求: 输入 ssh 登录系统后,会自动弹出一个 SHA256 的码,需要能够获取到这个码,再调用一个外部的 REST 接口获取动态密码后,点击 YES,输入密码,进入 Linux 系统。
C:\Users\xxx)>ssh [email protected]
The authenticity of host '192.168.xxx.xxx (192.168.xxx.xxx:)’can't be established
ED25519 key fingerprint is SHA256:edVhpWttoR1W/30HxIn2BiefgyDj6YZuxxxxr2YNo.
This key is not known by any other names
Are you sure you want to continue connecting(yes/no/[fingerprint])?yes
困难:1. 如何获取 cmd/powershell 中展示内容? 2. 如何自动控制 cmd 输入密码?
希望有大哥能提供一点思路,最好是用 Python 方案。
可能之前问题没有描述的太清楚,以至于大家误以为是为了获取SHA256值,抱歉,下面我再补充一下实际的场景:
使用本地的Windows登录远程Linux电脑时,输入ssh [email protected].1,会弹出一个动态的Dynamic code:
需要能够读取CMD的输出,获取到这个code,然后调用一个第三方的REST接口获取OTP Code,将这个OTP Code作为密码输入到cmd中,登录到Linux系统中。整个过程自动化。
C:\Users\demo>ssh [email protected]
([email protected].1)Dynamic code:
XXSAXXXXXXXXPOMBBWNCAA04/rJ80bSm9Vc9hsH99UiB6CACGthZ4Swa+qIMbDSxKXKCIWZ LKSErYRII/PXXXXXtJ04Gv7cI3SCzgtmkXXXXXXXXXAAAAdutUOVPN601/AdFP/cNcGptu+3J/pG9h3UAL3LAUJJrqLLoAi02YdZX6XXXXXXX8TK+nIds8nA57I5/oAb+T
Input OTP Code: ???
由于没法贴图,大致的需求就是如上所示。
1
tool2dx 133 天前
一般不用管 sha256 ,我用"echo y | ssd ", 可以自动输入 yes
|
2
zhlxsh 133 天前 via iPhone 1
paramiko
|
3
Wh0amis 133 天前
import pexpect
# 启动 cmd child = pexpect.spawn("cmd.exe") # 等待 cmd 提示符出现 child.expect("C:\\") # 输入 ssh 命令 child.sendline("ssh [email protected]") # 等待输入密码的提示 child.expect("password:") # 输入密码 child.sendline("mypassword") # 等待登录成功 child.expect("#") # 获取登录后的输出 print(child.before.decode()) |
4
caomingjun 133 天前 via Android
获取 fingerprint 有现成的 ssh-keyscan ,不需要强行读 ssh 输出。sshpass 也支持直接通过选项指定密码登陆。
你甚至不需要经过 powershell ,我估计 python 有现成的包可以把上面这些事情都干了。 |
5
263 133 天前
import paramiko
import hashlib import base64 import getpass def get_host_key_fingerprint(hostname, port=22): client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: client.connect(hostname, port=port, username='dummy', password='dummy') except: pass key = client.get_transport().get_remote_server_key() fingerprint = hashlib.sha256(key.get_fingerprint()).hexdigest() client.close() return ':'.join(a+b for a,b in zip(fingerprint[::2], fingerprint[1::2])) def ssh_login_with_otp(hostname, username, password, otp_func): client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 获取 fingerprint fingerprint = get_host_key_fingerprint(hostname) print(f"Host key fingerprint: {fingerprint}") # 验证 fingerprint if input("Verify fingerprint (y/n): ").lower() != 'y': print("Fingerprint verification failed") return # 获取动态密码 otp = otp_func() try: client.connect(hostname, username=username, password=password+otp) print("Login successful!") # 执行命令 stdin, stdout, stderr = client.exec_command('ls -l') print(stdout.read().decode()) except Exception as e: print(f"Login failed: {str(e)}") finally: client.close() # 模拟获取动态密码的函数 def get_otp(): return input("Enter OTP: ") # 使用示例 hostname = 'example.com' username = 'your_username' password = getpass.getpass("Enter password: ") ssh_login_with_otp(hostname, username, password, get_otp) gpt yyds |
6
aizya OP @Wh0amis 好像不行,我的脚本要在 windows 上运行,pexpect 对 windows 支持好像不是特别好。
https://github.com/pexpect/pexpect/issues/567 |
7
virusdefender 133 天前
fingerprint 我记得不是固定的么,先用一个非交互式的程序获取一次,然后生成密码,再用正常的 ssh 登录的库感觉会比较简单。
|
8
CEBBCAT 133 天前
这是两个问题,第一个是要你确认对方公钥的,另外一个问题是登录时要求提供动态密码的。
|
9
millson 133 天前
|
10
yiyiwa 133 天前
import-module posh-ssh
这个模块可以解决。 |
11
kxg3030 133 天前
感觉这个不用包都能解决呢,直接拿到 io 对象,输入密码然后输入\r\n 这样不行么
|
12
1rv013c6aiWPGt24 133 天前
不太懂,但是我记得可以配置 ssh 免密登录的,就是用 ssh 生成的 key ,之前用 Windowsterminal 登录服务器的时候就这样弄得
|
13
aizya OP |
14
zhangeric 133 天前
python 不熟,不过可以开 cmd 或 powershell,截图加 ocr 获取 sha,然后获取 key,然后模拟输入.
.net 下可以用 Process 类将 cmd 或者 powershell 的 StandardInput 和 StandardOutput 重定向进行操作. |
15
cheng6563 133 天前
直接读写 stdout ,stdin 就行了吧
你找下相关库,应该有 API 是启动进程时手动处理输入输出流的 |
16
cheng6563 133 天前
Process process = Runtime.getRuntime().exec("cmd /c ...");
InputStream inputStream = process.getInputStream(); OutputStream outputStream = process.getOutputStream(); InputStream errorStream = process.getErrorStream(); java 就是这样,可以获得 3 个流,从 outputStream 可以读到 ssh 打出来的文本,之后把密码模拟按键写到 inputStream 里去。要注意用 sleep 之类的方法控制流程。 |
17
263 133 天前
import subprocess
import requests def get_otp(dynamic_code): api_url = 'https://api.example.com/get_otp' response = requests.post(api_url, json={'dynamic_code': dynamic_code}) return response.json()['otp'] ssh_process = subprocess.Popen( ['ssh', '[email protected]'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, ) dynamic_code = '' while True: output = ssh_process.stdout.readline() if 'Dynamic code:' in output: dynamic_code = output.split('Dynamic code:')[1].strip() break otp = get_otp(dynamic_code) ssh_process.stdin.write(f'{otp}\n') ssh_process.stdin.flush() while True: output = ssh_process.stdout.readline() print(output.strip()) if 'Last login:' in output: print("登录成功!") break elif 'Permission denied' in output: print("登录失败!") break ssh_process.stdin.close() ssh_process.stdout.close() ssh_process.stderr.close() CLAUDE 3.5 SONNET |
18
error451 133 天前
Python 用 subprocess 下的 Popen 可执行 shell 命令, 可以将 stdout, stdin 重定向,利用 subprocess.PIPE ,就可以实现交互式命令执行。
然后用 re 模块, 写正则表达式来读取 stdout 中输出的 SHA256 token |
19
Radiation 133 天前
用 python 的 ssh 库不更好吗,手动实现一个简单的客户端,可操作的范围也大了。
|
20
LonnyWong 133 天前
可以自己改一下 tssh 的代码 https://github.com/trzsz/trzsz-ssh/blob/d154d5bba805fa21d36fd0b02a4df6cd4dae374d/tssh/login.go#L593
将 question 传给配置的 OtpCommand1 命令,这个命令对应的程序再自己实现,从参数 question 解释出 SHA256 码,调用 REST 接口获取到动态密码后,输出到 stdout 即可。 |
23
chf007 133 天前
这种应该是自定义的 PAM 集成了二次校验,看看官方手册有没有非交互式的参数可用,比如 --code=xxx 啥的,或者自已有权限的话,干脆禁用掉二次验证呗
|
24
aizya OP @LonnyWong #20 结帖了,感谢 LonnyWong , 最终是使用 tszsz-ssh https://github.com/trzsz/trzsz-ssh ,配置了一个#!! OtpCommand1 实现了动态验证码的获取。 🎉
|
25
LonnyWong 124 天前 1
#24 具体可看 https://github.com/trzsz/trzsz-ssh/blob/main/README.cn.md#%E8%AE%B0%E4%BD%8F%E7%AD%94%E6%A1%88 这个文档,tssh 将在 v0.1.22 正式支持,在此之前可以 go install github.com/trzsz/trzsz-ssh/cmd/tssh@main 这样安装。
自己实现获取动态密码的程序,指定 %q 参数可以得到问题内容,将动态密码输出到 stdout 并正常退出即可,调试信息可以输出到 stderr ( tssh --debug 运行时可以看到 )。配置举例(序号代表第几个问题,一般只有一个问题,只需配置 OtpCommand1 即可): Host custom_otp_command #!! OtpCommand1 /path/to/your_own_program %q #!! OtpCommand2 C:\python3\bin\python C:\your_python_code.py %q |