https://www.starduster.me/2020/02/18/get-shell-from-unaccessable-server-by-aria2c/
TL; DR:變更 sshd 這種高危操作三思而後行,最好留個後路謹防失聯。
TL; DR 2:裸奔的 aria2c 可以用於寫入檔案最終導致 getshell,為了安全起見,公網上的 aria2c 建議禁用 rpc-listen-all
或/和設定足夠強的 rpc token,也避免使用 root 執行 aria2c。
目錄
- 起因:從 OpenSSH 8.2 說起
- 意外:SSH 斷線致慘被關伺服器外
- 轉折:從 aria2c 到 getshell
- 總結:珍愛生命,遠離上古系統,小心配置軟體
起因:從 OpenSSH 8.2 說起
如 https://lwn.net/Articles/812537/ 所述,由於 SHA-1 簽名演算法的暴力破解成本已下降到可接受範圍,OpenSSH 8.2 決定不再支援 ssh-rsa 簽名演算法,其結果是,過舊版本的 ssh-server 將無法使用高版本 ssh-client 連線,比如 CentOS6 的 5.3 不支援 ecdsa host key,也不支援 rsa-sha256 或更強的演算法,擺在 CentOS6 使用者面前的只有兩條路,1. 升級 OpenSSH 版本,2. 準備一箇舊版 ssh client 用來登陸老舊伺服器。
很不巧的是我手頭正好有一臺老舊的、沒有接 IPMI 管理網路卡的 CentOS6 物理伺服器,CentOS6 主源中沒有符合要求版本的 OpenSSH(>5.7),於是我需要嘗試自行編譯或找一個別人編譯好的二進位制。由於不清楚 OpenSSH 編譯需要如何處理依賴問題,sftp 等相關子系統能否正常使用,我決定優先搜尋前人留下的預編譯包,於是我找到了 https://github.com/faishal/openssh-portable/releases/download/cent.os.6.7.openssh.7.3p1/openssh-7.3.zip ,事實表明它有坑,Google 搜出來的第一個結果也不一定總是靠譜的。
意外:SSH 斷線致慘被關伺服器外
正常情況下,listen 埠的 daemon 程序和 establish 連線並分配偽終端 pty 的是不同的程序,當我們 reload sshd 時,實際是由 init 系統向 sshd daemon 程序傳送 SIGHUP 訊號,sshd 此時會重新開啟一次配置檔案(systemd 會在 reload 之前檢查配置檔案合法性,而 CentOS6 則不會,reload 不會檢查配置格式合法性甚至仍顯示 reload 成功,實際上此時 sshd daemon 程序已經退出,不再接受新建會話,此時如果關閉當前 ssh 會話伺服器就會失聯,神坑,而且其實我之前就踩過這個坑 但是我忘了),而 restart 則是 SIGTERM,此時 sshd daemon 會退出重啟(可以觀察到 daemon 程序 pid 變化),而不會影響已經啟動的 pty 程序,establish 中的連線也不會中斷,這就是為什麼一般情況下我們可以在 ssh session 中隨意 restart sshd 卻不會掉線。
謹慎起見,我做了如下操作:
- 使用
rpm2cpio /path/to/rpm | cpio -idmv
解包檢視了 rpm 包內容,透過ldd
檢視二進位制檔案沒有缺失的動態連結庫 - 新建了一份 sshd 配置檔案儲存為
sshd_config.new
,透過/path/to/sshd -f /path/to/sshd_config.new
讓解包出來的 sshd 程序讀取獨立的配置檔案並 listen 在一個新的埠上,經過測試可以正確連線(但此時一個錯誤操作是我確認後就 kill 掉了這個獨立程序,理論上應該保留到 service reload 完成) - 備份了原來的
/etc/ssh/sshd_config
和/etc/pam.d/sshd
(但是忘了在 reload 前確認檔案差異,是導致事故的直接原因) - 確認開啟了允許密碼登陸,防止遇到檔案許可權不對導致的無法使用 pubkey auth 等問題
一切似乎都很正常,於是 rpm -U
進行升級,ssh-keygen -A
生成新的 ECDSA host key,service sshd reload
,然而意料之外的遇到 Write Failed: broken pipe
報錯,ssh 會話立刻斷開了(配置過 interval,不應為網路超時導致),嘗試重新登陸,發現埠可以正常建立連線(說明 sshd 正常在執行),然而 pubkey denied,密碼登陸也失敗,問題似乎指向了登陸驗證也就是 PAM。
於是我又找來一臺 CentOS 機器解包,發現 rpm 包內的 PAM 配置缺少 include password-auth
include pam_login
等,在安裝過程中 rpm 未經提示地覆蓋了 /etc/pam.d/sshd
我較少使用紅帽系 Linux,也不知道這是 feature 還是 bug,dpkg 在覆蓋檔案時一般都會提醒使用者選擇。由於這是 PAM 問題而不是 sshd 本身配置問題,sshd -t
並不能測試出 sshd_config
以外的問題,理論上 systemd 發行版也有可能踩到這種坑。
轉折:從 aria2c 到 getshell
人生總是充滿意外,我在多次確認不是我敲錯了密碼之後開始陷入沉思,要跑到上千公里外的某機房去吹空調修機器似乎是一個不現實的事情,於是我開始思考有什麼別的辦法可以讓我不經過 ssh 登回去。很不巧的是這臺伺服器沒有跑著任何業務,也就是沒有 webcgi,透過網站漏洞傳 webshell 是不可能的了,光禿禿的 MySQL 只監聽了本地,另外有一個 aria2c rpc 介面,似乎可作為突破口。
由於機房帶防火牆,6800埠從外部不可訪問,因此我沒有配任何防護,rpc-listen-all=true
且是 root 執行,而我恰好還有同網段的其他機器可以繞過防火牆訪問 rpc 介面,這等同於具有了任意檔案寫入的許可權,結合 https://paper.seebug.org/120/ 和一些經驗,確定思路上是使用 aria2c 透過 rpc 下載包含了反彈 shell 的 cron 配置到 /etc/cron.d
,等待一分鐘即可從外網透過反彈 shell 登陸回伺服器,需要注意預設設定下 aria2c 遇到下載目標已經存在時會自動重新命名,類似 download.file => download.1.file
,aria2c 有一個 allow-overwrite
選項,可以透過 aria2.changeGlobalOption
全域性調整為 true。
所謂反向 shell,簡單講就是在一個公網機器上 nc 監聽一個埠,被控機器執行奇怪的命令或程式後將 shell 的 stdio 透過網路連線轉發到公網機器上,實現從公網機器控制被控端的目的,當前我們的被控機有完整的執行環境、許可權,也不需要考慮隱蔽性、魯棒性等問題,我們可以不用費盡心思尋找真實滲透入侵場景下的全靜態連結二進位制反彈 shell 之類東西,簡單的一句指令碼就行,具體什麼命令 Google 一下有很多,不一一列舉,總之 cron 配置格式形如:
然後找一個類似 YAAW 的前端連上 rpc 介面即可滑鼠點點點下載檔案到指定目錄,到233.233.233.233上 nc -lvp 2333
開啟埠等待一分鐘,我們期待的 shell 就出現了,修改 PAM 配置,reload sshd,ssh 登陸恢復正常。
雖然人生充滿意外,但是人生好像也是有驚喜的,你說我一個運維,怎麼就來打 real world ctf 了呢。
總結:珍愛生命,遠離上古系統,小心配置軟體
事後檢查 authlog,發現如下報錯 PAM unable to dlopen(/lib64/security/pam_stack.so): /lib64/security/pam_stack.so: cannot open shared object file: No such file or directory
,而該 rpm 包自帶的 PAM 配置只有如下幾行:
也就是實際上生效的只剩下了 required pam_nologin.so
,沒有實際有效的登陸認證入口,自然無法登陸。
但我在 Debian 10 with systemd 上進行復盤測試時也發現幾個問題:
- 修改 PAM 後不需要 reload sshd,在下一次登陸時會自動使用新 PAM 配置,使用錯誤的 PAM 配置覆蓋後,下一次登陸會立即失敗
- Debian 10 的 PAM 配置格式似乎太新,CentOS6 不能正確使用,備份原 PAM 配置節省了一部分試錯時間
- 嘗試過在 sshd 二進位制變更的情況下同時修改了 PAM 配置後進行 sshd restart,沒有復現當時的情景,也就是說 reload sshd 在預期內確實是不會斷開的
綜上幾條,似乎說明,嚴謹的流程應該是首先不 reload sshd 嘗試一次登陸,以檢查 PAM 是否存在問題,接著啟動一個獨立的 sshd 或反彈 shell 確保被 init 管理的 sshd 程序掛掉後有辦法連線回來(systemd 管理的發行版稍好一些,不會允許錯誤配置導致 sshd 掛掉起不來的問題),然後 reload sshd,檢查 sshd 程序存在,再嘗試一次登陸,一切正常才能認為更新成功。
事實證明,維護老舊的伺服器發行版是一件很痛苦的事情,包括 ssh 會話意外退出的問題,似乎已經無法確認具體是什麼引起。另一方面,aria2c 作為本次事故的“功臣”,它本不應該有這個作用,反過來說,如果內網有其他想搞事情的鄰居,可以非常簡單的拿下這臺伺服器的許可權,先前的我對 aria2c 的寫檔案能力沒有作出足夠的重視,退一步講,即使需要把這個“功能”留作日後應急,也應該配置 secret 保護 rpc 介面。
本文連結:https://www.starduster.me/2020/02/18/get-shell-from-unaccessable-server-by-aria2c/
本站基於 Creactive Commons BY-NC-SA 4.0 License 允許並歡迎您在註明來源和非商業使用前提下自由地對本文進行復制、分享或基於本文進行創作。
請注意:受限於筆者水平,本站內容可能存在主觀臆斷或事實錯誤,文中資訊也可能因時間推移而不再準確,在此提醒讀者結合自身判斷謹慎地採納。