背景
使用者回報使用密碼無法正常登入,提示“Permission Denied”,需要重置密碼後才能正常,而相同的密碼在一個月前能正常登入,一共有兩台執行個體有這樣的現象。
分析
一般來說,類似問題可以從幾個方面來考慮可能性:
- SSH server配置問題
- 伺服器密碼被改動過或者密碼相關配置檔案被改動過
- 通過遠端或者VNC的方式鍵入密碼傳遞到系統内時候發生了變動
- 記錯密碼
以上幾點我們一一進行排查,由于要保留現場,無法重置密碼後再登入檢視,我們可以通過将系統盤快照做成雲盤來繞過登入的環節,進而進入系統。
挂載快照盤,我們通過chroot進入系統,要定位是否密碼有改動或者配置檔案有變動,我們首先通過stat檢視/etc/ssh/目錄下各檔案,從mtime/ctime時間戳來看,最近的改動在一年多前,說明sshd如果是讀取的正常路徑配置檔案,那麼使用者名和密碼以及sshd相關配置都沒有被改動過。

單方面看MAC時間可能說服力不夠,我們通過檢視配置檔案和對比測試進一步說明sshd正常工作且伺服器端沒有改動:
- 實際檢視/etc/ssh/sshd_conf配置檔案,發現是保持的預設配置,沒有異常改動。
- 有快照的前提下,嘗試修改密碼/重置密碼,之後再登入發現可以正常登入。
以上排查驗證了伺服器端沒有改動且sshd工作正常,為了驗證是否輸入密碼到sshd讀取過程是否存在異常,我們挂載快照盤并chroot進入系統,通過strace來跟蹤sshd程序以及其fork出的程序,之後通過ssh自己系統來規避網絡上的影響。我們可以看到輸入的密碼在被讀取時候并沒有發生改變。也能從strace輸出中了解到去讀取的配置檔案是正常路徑的配置檔案。說明輸入的密碼在驗證之前也沒有被修改過。
基于以上資訊,我們基本可以肯定是使用者密碼記錄存在問題。但使用者也表示從資料庫讀取的,期間沒動過資料庫,應該不會出現問題,那麼為了更進一步說服使用者,我們可以考慮實際驗證一下用一樣的辦法加密密碼明文後擷取的資訊是否和/etc/shadow裡記錄的一緻。
我們知道密碼加密後的密碼存儲在/etc/shadow檔案裡,那麼我們來看一下/etc/shadow檔案裡的密碼存儲形式,以CentOS 7.2為例:
root:$6$KqCVFlfd$vAARGaFcI5mNWGoOSJeE87ZrI5anStEWZYRz6EkrvXRSuD7X1teJwrWvC2a3YQRSeKuDaqmuN5ScinV/fvOyy0:17497:0:99999:7:::
這裡引用wiki的注解:
User login name
salt and hashed password OR a status exception value e.g.:
"$id$salt$hashed", the printable form of a password hash as produced by crypt (C), where "$id" is the algorithm used. (On GNU/Linux, "$1$" stands for MD5, "$2a$" is Blowfish, "$2y$" is Blowfish (correct handling of 8-bit chars), "$5$" is SHA-256 and "$6$" is SHA-512, other Unix may have different values, like NetBSD. Key stretching is used to increase password cracking difficulty, using by default 1000 rounds of modified MD5, 64 rounds of Blowfish, 5000 rounds of SHA-256 or SHA-512. The number of rounds may be varied for Blowfish, or for SHA-256 and SHA-512 by using e.g. "$6$rounds=50000$".
Empty string – No password, the account has no password (reported by passwd on Solaris with "NP").
"!" – the account is password locked, user will be unable to log in via password authentication but other methods (e.g. ssh key) may be still allowed.
"LK" or "*" – the account is locked, user will be unable to log in via password authentication but other methods (e.g. ssh key) may be still allowed.
"!!" – the password has never been set (RedHat)
Days since epoch of last password change
Days until change allowed
Days before change required
Days warning for expiration
Days before account inactive
Days since epoch when account expires
這裡我們主要需要關注的部分是$id$salt$hashed,其中$id是加密算法,$salt是混入加密算法的鹽,$hashed是經過加密算法hash對應密碼和鹽的結果。
ID | Method
───────────────────────────
1 | MD5
2a | Blowfish (not in mainline glibc; added in some Linux distributions)
5 | SHA-256 (since glibc 2.7)
6 | SHA-512 (since glibc 2.7)
這種場景加密過程就是将密碼通過加密算法計算得到一個hash串,從hash串是無法反推出密碼的。而加入鹽的目的是增強加密複雜性,規避一些強行破解的辦法。
具體加密算法介紹可以參考:
https://en.wikipedia.org/wiki/SHA-2在實際設定密碼過程中,salt是随機生成的,shadow檔案記錄加密算法、salt以及加密後hash串,而當有使用者登入時候,會将輸入的密碼與shadow檔案中salt基于加密算法重新計算一次然後跟hash串比對,如果相同,則說明密碼正确。
我們有了算法和salt後,那麼就可以實際驗證使用者提供的密碼加密後的hash串是否跟shadow檔案中對應使用者的hash串相符。
RHEL 6 and newer - python
• Execute the following one-liner:
$ python -c 'import crypt,getpass; print crypt.crypt(getpass.getpass())'
Password:
$6$Q3dbIWgPMCVBpRZK$QSw1cG41FImM0E8B.Hpx1G8eZGqHALzGg75LLAt.MkFZtVma3MHRGBpFSrXEEdVHwySr8B0JfXAgLHgmpSViI0
In short, the above will prompt for input and then interface with python's implementation of the standard crypt() function, generating a shadow-compatible hash by choosing a random salt and the strongest hash method available (SHA-512)
• For documentation, do as the following demonstrates
$ python
Python 2.6.6 (r266:84292, May 1 2012, 13:52:17)
[GCC 4.4.6 20110731 (Red Hat 4.4.6-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
import crypt
help(crypt)
RHEL 5 and newer - perl
$ perl -le 'print "Password:"; stty -echo; chomp($passphrase=); stty echo; @chars = ("a".."z", "A".."Z", 0..9, ".", "/"); $salt .= $chars[rand @chars] for 1..16; print crypt($passphrase, "$6$$salt");'
$6$RDGTu1VHQPH2NLpw$no4LMowmxaJogzYoIUdsfi7pfTz2EtzN//oRmEe12AEWc2h6NPoQwrWA4KYe4W0OSAUaBLOG8K59kzENV2bvY0
• Notes on the above perl code:
○ This code was tested by the author on RHEL 5 - RHEL 7; however, it is offered without any guarantees or warranty (to use it, ensure the quotation marks and backslashes are kept intact)
○ This code generates a SHA512-hashed password string; to use SHA256 instead, change the maroon-colored $6 to a $5
*以上為RedHat給出的模拟方式
為了讓資料更有說服力,我們先要驗證加密辦法是對的,通過類似方法加密的資訊能正确跟shadow檔案裡存儲的資訊對的上。
驗證符合後,我們進一步驗證使用者提供的密碼資訊。發現從使用者提供的密碼來正向擷取hash串後跟shadow裡root的不比對,由此進一步說明了是使用者提供的密碼存在問題。
cat /mnt/etc/shadow
root:$6$D9.mVsWm$W9XlxzRHWU6B6KFNaPZQPCkXF0GrL.Cq.zzx2H24qaDC4bphDFZqZtD5G.t1Rz1cegl2tyQAdx9W6iBoJTANc.:17013:0:99999:7:::
python -c 'import crypt; printcrypt.crypt("Spidermanwsx123","$6$D9.mVsWm")'
$6$D9.mVsWm$Y8rVe1915SlIkdwOloHhFedpW2MZ7xYOYHy69jXOoZqyodDm5WJOBP4P/EeLP1BjJlibHwovaHqawTWD05c6o1