此次TCTF中一道題,給出了一個PHP一句話木馬,設置了open_basedir,disable_functions包含全部執行系統命令的函數,而後目標是運行根目錄下的/readflag,目標很明確,即繞過disable_functions和open_basedir,固然我仍是一如既往的菜,整場比賽就會作個簽到,這題也是賽後看WP才明白。php
LD_PRELOAD是Unix中的一個環境變量,用於定義在程序運行前優先加載的動態連接庫,LD和動態庫有關,PRELOAD表示預加載,結合起來就是預先加載的動態庫。經過這個環境變量,能夠覆蓋正常的函數庫中的函數。html
寫個驗證密碼是否正確的demolinux
若是是不知道密碼的狀況下,就能夠編寫一個動態函數庫來覆蓋掉strcmp函數恆返回0以達到任意密碼都返回Correctgit
-fPIC 做用於編譯階段,告訴編譯器產生與位置無關代碼(Position-Independent Code), 則產生的代碼中,沒有絕對地址,所有使用相對地址,故而代碼能夠被加載器加載到內存的任意 位置,均可以正確的執行。這正是共享庫所要求的,共享庫被加載時,在內存的位置不是固定的github
設置環境變量後,任意密碼都將返回Correctweb
PHP中putenv()函數用於設置服務器環境變量,僅存活於當前請求期間。 在請求結束時環境會恢復到初始狀態。服務器
putenv ( string $setting ) : bool
如網絡
putenv("LD_PRELOAD=/tmp/evil.so");
這種方法已經存在了幾年,它基於這樣的概念:當系統試圖調用某函數時,該函數位於特定的共享庫(xxx.so)。所以,系統在調用以前將加載xxx.so。換句話說,若是我能夠建立一個evil.so有了同名的函數,就能將其覆蓋之。函數
因此須要找到一個php函數,它將運行一個新進程以及觸發上述過程。新進程的緣由就像前面提到的同樣,須要從新啓動服務以更改LD_PRELOAD,這裏用到PHP mail() 函數ui
在Linux中,進程是不能直接去訪問硬件設備的,好比讀取磁盤文件、接收網絡數據等,但能夠將用戶態模式切換到內核模式,經過系統調用來訪問硬件設備。這時strace就能夠跟蹤到一個進程產生的系統調用,包括參數,返回值,執行消耗的時間、調用次數,成功和失敗的次數。
能夠看到 這段PHP代碼(mail()函數)使用 execve 在父進程中 fork 了一個子進程,調用了 /usr/sbin/sendmail
接着看下sendmail中調用了getuid()函數,(能夠用readelf查看sendmail中使用了哪些函數,如readelf -Ws /usr/sbin/sendmail,但我環境彷佛有問題執行不成功,因此仍是用到strace)
因此能夠編寫一個動態庫覆蓋getuid()函數
一開始我是這麼寫的,雖然也能成功執行命令
可是服務器卡頓,一堆報錯,而後關了SSH後就鏈接不上了,最後只得在控制檯重啓,應該是將程序中全部的getuid()都覆蓋形成某些地方嚴重出錯,因此改成以下,在第一次運行到個人寫的getuid()函數時就unsetenv這個環境變量,保證以後的正常調用
在PHP代碼中設置環境變量,並調用mail函數觸發getuid(),最後成功執行命令
__attribute__ 是 GNU C 裏一種特殊的語法,語法格式爲:__attribute__ ((attribute-list)),若函數被設定爲constructor屬性,則該函數會在main()函數執行以前被自動的執行。相似的,若函數被設定爲destructor屬性,則該函數會在main()函數執行以後或者exit()被調用後被自動的執行。
Q
- When exactly does it run?
- Why are there two parentheses?
- Is
__attribute__
a function? A macro? Syntax?- Does this work in C? C++?
- Does the function it works with need to be static?
- When does
__attribute__((destructor))
run?A
- It's run when a shared library is loaded, typically during program startup.
- That's how all GCC attributes are; presumably to distinguish them from function calls.
- GCC-specific syntax.
- Yes, this works in C and C++.
- No, the function does not need to be static.
- The destructor is run when the shared library is unloaded, typically at program exit.
以上回答說的很清楚,__attribute__((constructor)) 在加載共享庫時就會運行。因而只要編寫一個含__attribute__((constructor)) 函數的共享庫,而後在PHP中設置LD_PRELOAD環境變量,而且有一個能 fork 一個子進程並觸發加載共享庫的函數被執行,那麼就能執行任意代碼達到bypass disable_functions。固然mail()函數知足條件。
編寫共享庫
執行PHP
同理,當在linux中設置了環境變量後,那麼使用任意命令都將觸發
evil.c
#include<stdlib.h> __attribute__((constructor)) void l3yx(){ unsetenv("LD_PRELOAD"); system(getenv("_evilcmd")); }
evil.php
<?php putenv("_evilcmd=echo 1>/root/tmp/222222"); putenv("LD_PRELOAD=./evil.so"); mail('a','a','a','a');
gcc -shared -fPIC -o evil.so evil.c
參考:
Bypass disable_functions with LD_PRELOAD