V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
aizya
V2EX  ›  程序员

求助,有什么办法能够控制 cmd/powershell 进行 ssh 自动化登录?

  •  
  •   aizya · 139 天前 · 2111 次点击
    这是一个创建于 139 天前的主题,其中的信息可能已经有所发展或是发生改变。

    遇到一个自动化登录的问题,不是简单的 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 方案。

    第 1 条附言  ·  139 天前

    可能之前问题没有描述的太清楚,以至于大家误以为是为了获取SHA256值,抱歉,下面我再补充一下实际的场景:

    1. 使用本地的Windows登录远程Linux电脑时,输入ssh [email protected].1,会弹出一个动态的Dynamic code:

    2. 需要能够读取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: ???

    由于没法贴图,大致的需求就是如上所示。

    25 条回复    2024-07-24 14:24:29 +08:00
    tool2dx
        1
    tool2dx  
       139 天前
    一般不用管 sha256 ,我用"echo y | ssd ", 可以自动输入 yes
    zhlxsh
        2
    zhlxsh  
       139 天前 via iPhone   ❤️ 1
    paramiko
    Wh0amis
        3
    Wh0amis  
       139 天前
    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())
    caomingjun
        4
    caomingjun  
       139 天前 via Android
    获取 fingerprint 有现成的 ssh-keyscan ,不需要强行读 ssh 输出。sshpass 也支持直接通过选项指定密码登陆。

    你甚至不需要经过 powershell ,我估计 python 有现成的包可以把上面这些事情都干了。
    263
        5
    263  
       139 天前
    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
    aizya
        6
    aizya  
    OP
       139 天前
    @Wh0amis 好像不行,我的脚本要在 windows 上运行,pexpect 对 windows 支持好像不是特别好。

    https://github.com/pexpect/pexpect/issues/567
    virusdefender
        7
    virusdefender  
       139 天前
    fingerprint 我记得不是固定的么,先用一个非交互式的程序获取一次,然后生成密码,再用正常的 ssh 登录的库感觉会比较简单。
    CEBBCAT
        8
    CEBBCAT  
       139 天前
    这是两个问题,第一个是要你确认对方公钥的,另外一个问题是登录时要求提供动态密码的。
    millson
        9
    millson  
       139 天前
    yiyiwa
        10
    yiyiwa  
       139 天前
    import-module posh-ssh

    这个模块可以解决。
    kxg3030
        11
    kxg3030  
       139 天前
    感觉这个不用包都能解决呢,直接拿到 io 对象,输入密码然后输入\r\n 这样不行么
    1rv013c6aiWPGt24
        12
    1rv013c6aiWPGt24  
       139 天前
    不太懂,但是我记得可以配置 ssh 免密登录的,就是用 ssh 生成的 key ,之前用 Windowsterminal 登录服务器的时候就这样弄得
    aizya
        13
    aizya  
    OP
       139 天前
    @caomingjun emm.. 你看我最新补充的需求,并不只是 SSH 免密。
    @263 谢谢回复,这个代码运行起来不太满足我的需求。
    zhangeric
        14
    zhangeric  
       139 天前
    python 不熟,不过可以开 cmd 或 powershell,截图加 ocr 获取 sha,然后获取 key,然后模拟输入.
    .net 下可以用 Process 类将 cmd 或者 powershell 的 StandardInput 和 StandardOutput 重定向进行操作.
    cheng6563
        15
    cheng6563  
       139 天前
    直接读写 stdout ,stdin 就行了吧
    你找下相关库,应该有 API 是启动进程时手动处理输入输出流的
    cheng6563
        16
    cheng6563  
       139 天前
    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 之类的方法控制流程。
    263
        17
    263  
       139 天前
    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
    error451
        18
    error451  
       139 天前
    Python 用 subprocess 下的 Popen 可执行 shell 命令, 可以将 stdout, stdin 重定向,利用 subprocess.PIPE ,就可以实现交互式命令执行。
    然后用 re 模块, 写正则表达式来读取 stdout 中输出的 SHA256 token
    Radiation
        19
    Radiation  
       139 天前
    用 python 的 ssh 库不更好吗,手动实现一个简单的客户端,可操作的范围也大了。
    LonnyWong
        20
    LonnyWong  
       139 天前
    可以自己改一下 tssh 的代码 https://github.com/trzsz/trzsz-ssh/blob/d154d5bba805fa21d36fd0b02a4df6cd4dae374d/tssh/login.go#L593

    将 question 传给配置的 OtpCommand1 命令,这个命令对应的程序再自己实现,从参数 question 解释出 SHA256 码,调用 REST 接口获取到动态密码后,输出到 stdout 即可。
    aizya
        21
    aizya  
    OP
       139 天前
    @LonnyWong 好方法,我试试,感谢!
    LonnyWong
        22
    LonnyWong  
       139 天前
    @aizya #21 如有需要,我可以修改一下 tssh 。欢迎加入 QQ 群详聊,群号在 tssh 的 github 首页最下面。
    chf007
        23
    chf007  
       139 天前
    这种应该是自定义的 PAM 集成了二次校验,看看官方手册有没有非交互式的参数可用,比如 --code=xxx 啥的,或者自已有权限的话,干脆禁用掉二次验证呗
    aizya
        24
    aizya  
    OP
       130 天前
    @LonnyWong #20 结帖了,感谢 LonnyWong , 最终是使用 tszsz-ssh https://github.com/trzsz/trzsz-ssh ,配置了一个#!! OtpCommand1 实现了动态验证码的获取。 🎉
    LonnyWong
        25
    LonnyWong  
       130 天前   ❤️ 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
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2590 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 10:14 · PVG 18:14 · LAX 02:14 · JFK 05:14
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.