18Linux之服务基础SSH

BamB00 2019-10-15 00:00:00
Categories: Tags:

0x01 什么是SSH

SSH就是一种网络协议,用与在网络主机之间进行加密的一种协议。如从我的电脑使用SSH协议登陆了服务器,我们就认为这一登陆时安全的,即使我们的登陆信息在中间被人截获了,我们的密码也不会被泄露。

0x02 原理解析

先通过一幅时序图看看SSH密码登陆原理

ssh原理图

从上图可以看出SSH登陆时,主要分为一下几步

  1. 用户使用ssh user@host命令对远程主机发起登陆
  2. 远程登录将自己公钥返回给请求主机
  3. 请求主机使用公钥对用户输入的密码进行加密
  4. 请求主机将加密后的密码发送给远程主机
  5. 远程主机使用私钥对密码进行解密
  6. 最后,远程主机判断解密后的密码是否与用户密码一致,一致就登陆,否则反之。

这个过程看上去没有问题,但时存在风险的漏洞。由于SSH不想https协议那样,SSH协议的公钥时没有证书中心(CA)公证的,也就是说,都是自己签发的。这就导致了如果有人截获登陆请求,然后冒充远程主机,将伪造的公钥发给用户,那么用户很难辨别真伪,用户再通过伪造的公钥加密密码,再发送给冒充主机,此时冒充的主机就可以获取用户的登陆密码,那么SSH的安全机制就荡然无存了,也就是我们常说的中间人攻击。

既然存在这种问题,SSH就想了一个办法来绕开这个问题,也就是下面的known_hosts文件作用。

0x03 known_hosts的作用

ssh远程登陆的时候我们会看到这样的提示

The authenticity of host '192.168.1.4 (192.168.1.4)' can't be established.
ECDSA key fingerprint is SHA256:YMLLk0vyfeoY4rbRTnkMSxY11arS2S4qgVgvnwWpBFw.
ECDSA key fingerprint is MD5:82:75:be:8e:e7:26:ea:18:73:aa:fc:10:44:c8:4f:c3.
Are you sure you want to continue connecting (yes/no)? 

上面这段话的意思是,无法确认192.168.1.4主机的真实性,只知道它的公钥指纹,问你还想继续连接吗?这样我们就可以看到,SSH是将这个问题抛给了SSH使用者,让SSH使用者自己来确定是否相信远程主机。但是这样对于用户来说,就存在一个难题,用户怎么知道远程主机的公钥指纹是多少;这的确是一个问题,此时就需要远程主机必须公开自己的公钥指纹,以便用户自行核对。

在经过用户的风险衡量以后,用户只需要输入yes来决定接受这个远程主机的公钥。紧接着,系统会出现以下这样的一句提示,表示远程主机已经得到认可:

Warning: Permanently added '192.168.1.4' (ECDSA) to the list of known hosts.

接下来,用户输入远程主机密码即可完成整个登陆。那这个和我这里要总结的known_hosts文件有什么关系呢?请听我慢慢道来。

当远程主机的公钥被接受以后,它就会被保存在文件~/.ssh/known_hosts之中。下次再连接这台主机,系统就会认出它的公钥已经保存在本地了,从而跳过警告部分,直接提示输入密码。

但是由于known_hosts这个机制的存在,也会引起一些问题,比如远程主机的重新装操作系统了,远程主机就会重新生成公钥,如果我们再登陆远程主机时,由于我们本地的known_hosts文件中记录了原来的公钥,此时就会提示指纹认证失败的错误,这个时候我们只需要删除本地的known_hosts文件即可。又比如,我们经常会写一些自动化的脚本,会自动的登陆到远程主机上去,但是这个known_hosts机制却必须要我们手动输入yes才能完成远程登陆,这样整个自动化登陆就无法完成了,但是我们可以通过修改/etc/ssh/ssh_config配置文件,跳过这个known_hosts的询问机制,将# StrictHostKeyChecking ask修改为StrictHostKeyChecking no即可。

0x04 公钥免密登陆

SSH免密登陆的时序图

ssh免密登陆原理图

上图就是SSh免密登陆原理图,从上图可以看出,SSH免密登陆的前提是使用ssh-keygen -t RSA生成公私秘钥对,然后通过ssh-copy-id -i ~/.ssh/id_rsa.pub user@host命令将公钥分发至远程主机。接下来的每次免密登陆步骤如下:

  1. 用户使用ssh user@host 命令对远程主机发起登录
  2. 远程主机对用户返回一个随机串
  3. 用户所在主机使用私钥对这个随机串加密,并将加密的随机串返回至远程主机
  4. 远程主机使用分发过来的公钥对加密随机串进行解密
  5. 如果解密成功,就证明用户登录信息是正确的,则允许登录

通过ssh-copy-id -i ~/.ssh/id_rsa.pub user@host命令分发的公钥都会被保存至远程主机的~/.ssh/authorized_keys文件中。

他总共提供

加密方式

0x041 完整的链接文字描述

0x042 密钥验证登录过程

  1. 客户端生成密钥对文件

    #ssh-keygen -t rsa -b 2048
        -t 指定加密类型(rsa/dsa等)
        -b 指定密钥对加密长度
        #建议加密长度>=256 124密码已经能被快速爆破出来了
    
    [root@centos7 ~]# ssh-keygen -t rsa -b 1024
    Generating public/private rsa key pair.
    Enter file in which to save the key (/root/.ssh/id_rsa): 
    

    问题一: 是否指定安放密钥路径

    ​ 一般默认保存在当前用户家目录下的.ssh/

    问题二: 是否钥对密钥文件进行加密

    • 加密: 若加密,则在调用密钥文件时需要先验证密钥的秘密,密码正确才能是否使用密码
    • 不加密: 若不加密,则密钥文件可以直接被调用,整个登录验证过程无需输入任何密码,即免密登录
  2. 将公钥文件上传到服务器

    ssh-copy-id 用户名@IP

  3. 客户端尝试登录服务器

    ssh 用户名@IP

    #密钥对验证优先级大于账户密码验证

0x043 设置服务器禁止密码登录

/etc/ssh/sshd_config里面

# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes
#PermitEmptyPasswords no
PasswordAuthentication yes
#唯一生效的密码验证改为no即可

0x044 禁止使用root远程登录

​ 在运维管理中尽量不要允许root远程登录,在需要root权限的时候再使用普通用户切换即可

​ 将/etc/ssh/sshd_config里面

#PermitRootLogin yes 
将开启并且改为No 即
PermitRootLogin no

0x045 修改默认端口,限制ssh监听IP

修改默认端口: ssh作为一个来远程管理服务器的工具,需要特别的安全,默认情况下使用TCP的22端口,若不进行修改很容易进行遭受到,所以我们一般都会修改端口,尽量使用一个高位端口(端口范围1-65535)

​ 将/etc/ssh/sshd_config里面

#Port 22
Port 33333
登录方法:
ssh -p 端口号 用户名@IP

限制ssh监听IP: 只监听某一个IP的传输。有些服务则安全级别更高一些,不允许使用外网直接登录, 只有通过局域网才能登录,我们可以在机房里设置其中一台能够被外网远程连接,其他主机都通过这个主机远程来接到外网即可

同样是配置文件

ListenAddress 192.168.205.130

0x05 SSH 端口转发

SSH 再渗透中有时候作为一个端口转发工具也是一个不错的选择

0x051 本地端口转发

本地端口转发指的是在本机上发起请求,由 ssh client 转发到远程的机器上。它的命令如下:

ssh -L <port_a>:<remote host>:<port_b> user_b@ip_b

下图中,远程的机器上起了一个服务 python3 -m http.server,它监听端口 8000,现在我们想在本机访问这个服务,但由于防火墙的存在,8000 端口无法直接访问,于是我们使用 ssh 端口转发。

img

首先在 A 上执行 ssh -L 1234:localhost:8000 user_b@ip_b 建立 ssh 隧道,它表示:所有对 A:1234 端口的请求,相当于在 B 机器上对 localhost:8000 的请求。因此在 A 上执行 curl localhost:1234 就相当于访问 B 机器上的 python 服务。

上面的情况是服务部署在 ssh server 所在的机器(B)上,那么如果服务部署在其它的机器上呢? img

可以看到我们只是把上例中的 localhost 换成了 C 机器的 hostname/IP 就可以了。此时,发送到 A:1234 的请求相当于从 B 机器上对 remote:8000 的请求,图中在 /etc/hosts 中设置了 hostname 和 IP 的对应关系,直接用机器 C 的 IP 也是可以的。可以看到,机器 A 由于防火墙无法访问内网的服务,但是由于

通过本地端口转发可以实现从 A 访问内网的服务。

0x052 远程端口转发

上节中,本地机器 A 可以 ssh 到内网机器 B,但如果防火墙不允许呢(或者 B 没有公网的 IP)?如果机器 A 有公网的 IP,且机器 B 允许联网,则可以通过远程端口转发完成。远程端口转发的命令如下:

ssh -R <port_a>:<remote host>:<port_a> user_a@ip_a

与之前不同,此时 ssh server 是运行在本地机器 A 上,在内网机器 B 上执行上述命令。

img

为什么称作“远程转发”呢?把最终请求的发起方(机器 A)为“本地”,服务所在的机器 B/C 为“远程”,在本地机器上执行 ssh 命令就称为“本地转发”,在远程机器上执行命令就称为“远程转发”。

0x053 路由转发

上面的例子中,机器 A 配置好本地转发后,A:1234 只能被机器 A 访问,如果本地还有其它机器想访问这个服务怎么办?可以使用 -g 开启路由模式,命令如下:

ssh -g -L <port_a>:<remote host>:<port_b> user_b@ip_b

但要注意的是本地机器间的连接(下图中 X->AB->C)不是安全连接,所以要谨慎使用

img

要注意的是上述方法只对“本地转发”有效,如果想要在远程转发上使用路由功能,需要在“本地”机器 A 中的 /etc/sshd_config 中加入 GatewayPorts yes 并重启 sshd 服务:

img

0x054 动态转发

上面的所有方法都只针对一个端口,想要转发所有端口怎么做?

ssh -D <port> user@remote_ip

这个模式对本地和远程转发都有效,它的工作模式和 Shadow Socks 很像,会在本地创建一个 Socks5 代理服务,监听端口 ``,并将所有请求转发到远程机器上。如下图:

img

图中我们通过 curl -x socks5h://... 来指定使用 Socks5 代理,在机器 A 上请求 ip_c:8000 时,相当于在 B 机器上发起对 ip_c:8000 的请求。

0x06 SSH服务相关命令

0x061 scp: 安全的远程文件复制命令

scp时secure copy的简写,用于在Linux下进行远程拷贝的命令,类似的命令有cp,scp传输是加密的,所以回影响一点速度。另外,scp还非常不占资源,不会提高多少系统负荷

#scp [选项] 本地文件 用户名@IP
选项:
    -P 端口

0x062 stfp: 安全的文件传输

sftp是Secure File Transfer Protocol的缩写,安全文件传输协议。sftp与ftp有着几乎一样的语法和功能。由于这中传输方式使用了加密/解密计算,所以sftp比ftp更安全一些,但传输小于回低很多。

#sftp [选项] 用户名@服务器
选项:
 -oPort=端口
交互命令:
 help:
 pwd/lpwd: pwd是查看服务器所在路径: lpwd是查看客户端所在路径
 ls/lls: ls是查看服务器当前目录下的文件列表: lls是查看客户端当前目录下的文件列表
 put: 将客户端中的指定文件上传到服务器端
 get: 将服务器端的指定文件下载到客户端当前目录
 rm: 删除掉服务端的指定目录
 quit: 退出交互模式且断开连接