关于php的各种比较

md5碰撞

当php对于利用"!="或者"=="对两个哈希字符串进行比较时,它会把每一个以"0E"开头的哈希值解释为0,其实就0*10的n次方,永远解释为0,所以产生了最为经典md5()漏洞。

例如 s878926199as155964671a他们的哈希值都是以0E开头的,分别为0e5459932745177090343288558410200e342768416822451524974117254469当采用"=="比较时,会返回true。

例如下列例子。

<?php
  if(md5("s878926119a")==md5("s155964671a")){
     echo "php is so difficult";
  }
  else{
     echo "php is so strong";
  }
?> 

在阅读php and MySQL 开发一书中的时候,我发现了一个问题,md5()在处理参数的时候,是不能够处理字符串的。

所以在进行比较的时候,传入数组也是能够触发的。

为了观察方便,我写下了如下代码:

<?php
   $a = md5($_GET['a']);
   var_dump($a);
   $b = NULL;
   $b = md5($b);
   echo "$b";
   $c = array("NULL");
   $c = md5($c);
   var_dump($c);
?>

可以清楚的看见,md5在处理数组时,是返回的NULL;也就是返回的0;也就是说,若是md5采用“==”比较两值或者“==“0时,只要变量可控,是可以绕过的。

在php中还有一些特性。

  • 当定义一个变量,如果没有设置值,默认为0。
  • php的一个错误抑制符@,可以将表达式产生的任何错误忽略掉。
  • 例如上述代码,php在采用$_GET方法赋值时,如果$a传入[]=1这样的数组时,php会把这个当做数组传入,然后自动对参数调用 urldecode。$_POST也是同样如此。

总结,在进行md5==0有如下几种方法

  • 0或0e开头的md5值
  • 传入数组这种类型的还有一些php的内置函数,比如strcmp strpos sha1 ereg(低版本php:php4,php5)
  • 注:在高版本php里,ereg被preg取代,且preg可以操作数组。
php7里没有ereg函数

通过传入数组的方式,绕过 strcmp

<?php
   if(isset($_GET['a'])){
      if(strcmp($_GET['a'],$_GET['b'])==0){
         echo "php is so difficult";
      }
   }
   else{
      echo "php is so strong"
   }
?> 

strcmp函数抛出异常,并返回0值。

数字比较

在传入一个变量值为整形时,该整形与一个其他类型进行==(等于运算符)比较的时候,会先把其他类型intval再进行比较。

例如将"123a"与123比较,会返回true。

<?php
   if(isset($_GET['a'])){
      if(!is_numeric($_GET['a'])){
         if($_GET['a']==123){
            echo "php is so difficult";
         }
      }
   }
   else{
      echo "php is so strong";
   }
?> 

但是如果采用===(恒等运算符)比较的话。

会返回false。

switch比较

<?php
   $flag = "flag{k0dosan_4s_s0_hands0me}";
   if (isset($_GET['a'])){
      $a = md5($_GET['a']);
      switch($a){
         case 0:
         case 1:
         case 2:
         case 3:
         case 4:
            echo $flag;
            break;
         default:
            echo "木大木大木大木大木大";
      }
   })
?> 

在这个程序里,首先传入a,然后再switch里对a进行判断。正常思维情况下,应该是如果a不等于1234当中的一个时,会在switch里挨个比较,不等于0进入1,以此类推。若a=3则进入3,若都失败进入default。

但是php里,如果传入了0的话没有写入break的话,这个时候是默认比较成功,下面的case不会再进行比较。直接进入case4输出flag。

若是在比赛中遇到(虽然不太可能)对a加入非数字的限制时,就可以传入数组等绕过。

且如果 switch 是数字类型的 case 的判断时, switch 会将其中的参数转换为 int类型。

将md5删去。

可以绕过 !is_numeric

php文件包含复现

复现前的环境准备:

PHPstudy;

以及

<?php
   echo "come on! just fuking";
   if (isset($_REQUEST['file'])){
      include($_REQUEST['file']);    
   }
?> 

这样简单的一个环境。为了复现的顺利进行,我将allow_url_fopenallow_url_include都打开。

没有任何过滤,我们只需要控制文件内容就可以。

首先是各种各样的伪协议。

file:// — 访问本地文件系统  http:// — 访问 HTTP(s) 网址  ftp:// — 访问 FTP(s) URLs  php:// — 访问各个输入/输出流(I/O streams)  zlib:// — 压缩流  data:// — 数据(RFC 2397)  glob:// — 查找匹配的文件路径模式  phar:// — PHP 归档  ssh2:// — Secure Shell 2  rar:// — RAR压缩流  ogg:// — 音频流  expect:// — 处理交互式的流

比赛常用伪协议

file://

该伪协议用于访问本地文件系统,参数值要绝对路径

且不受allow_url_fopen allow_url_include限制

php://

  • php://用于访问各个输入输出流在比赛中常常有两种用法
  • php://input是个可以访问请求的原始数据的只读流,将读入数据当做php代码读取。根据官方说法,可以读取从来没有处理过的POST数据,但不能用于enctype=multipart/form-data”当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容。且allow_url_include=On。值得注意的是:
    • 只有Coentent-Type不为multipart/form-data的时候,PHP不会将http请求数据包中的相应数据填入php://input,否则其它情况都会。填入的长度,由Coentent-Length指定。
  • 只有Content-Type为application/x-www-data-urlencoded时,php://input数据才跟$_POST数据相一致。
  • php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用,可以用一个表格展示。
名称描述
resource=<要过滤的数据流>该参数是必须的。它指定了你要筛选过滤的数据流。
read=<读链的筛选列表>该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔
write=<写链的筛选列表>同上
<;两个链的筛选列表>任何没有以 read=write= 作前缀 的筛选器列表会视情况应用于读或写链

个人认为,在比赛中php://input常常用于执行php代码,而php://filter用于通过base64编码的方式读取文件源码。

php://input由于我的hackbar出现了一点问题,所以这里我没办法给出截图(嘤嘤嘤)。

这里给出一个涙笑师傅给的filter的各种过滤器的网址:https://www.cnblogs.com/natian-ws/p/7242477.html

zip://

该伪协议的应用条件是php版本大于等于5.3且包含文件要使用绝对路径

将木马弄成压缩包 1.php?filename=zip://D:\1.zip#2.txt

phar://

与zip相似,但可以使用相对路径,前提是包在当前目录下

php版本大于等于5.3 1

利用姿势:

1.php?filename=phar://D:/1.zip/2.txt

这两种的解法在下面。

data://

该协议要求php版本大于等于5.2 1,且allow_url_fopen=Onallow_url_include=On

有几种利用姿势

  • 1.php?filename=data://text/plain, <?php phpinfo(); ?>
  • 执行命令:1.php?filename=data://text/plain, <?php system('ls'); ?>
  • 通过base64编码1.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b

webdav远程包含

其实并不一定需要webdav服务,只要是能够访问那个装有shell的文件,都可以搞。

由于这里我在阿里云机子上开启webdav服务失败了,所以我在我的另一台电脑上,开启了webdav服务。并进行了包含。

包含日志文件

很多时候,web服务器会请求写入到日志文件中,在用户发送请求时,会将请求写入access/log,当发生错误时将错误写入error.log。通常情况下,日志保存路径在/var/log/apache2。

windows采用phpstudy集成的环境下,一般在E:/phpstudy_pro/WWW/Extensions/Apache/log下

这里我通过直接在浏览器访问的方式访问日志文件

查询日志文件

然后文件包含

存在图片上传文件等功能

任意情况下文件包含

在shell.txt里写入小马然后改文件后缀成任意文件,比如png即可实现文件包含

限制后缀的文件

<?php
   echo "come on! just fuking";
   $file=$_GET['file'].".php";  //限制只能上传.php后缀的文件
   if(isset($file)){
      include($file);
   }
?> 

利用zip://或者phar://伪协议.将shell打成一个包。

zip://需要用绝对路径zip://有好几种用法。最常用的就是采用#读取。为了防止浏览器的编码我们可以用%23.

phar://

LFI+phpinfo()

php在处理我们上传的表单时,也会生成一个对应的临时文件。这个临时文件我们可以在$_File查看到

于是我们可以通过构造这样一个表单,仿造官方的表单进行提交,查询我们的$_FILE临时文件名

<html>
<body>

<form action="http://localhost/WWW/test4.php" method="POST" enctype="multipart/form-data">
 <input type="file" name="file1" />
 <input type="submit" />
</form>
</body>

我们随意提交一个含有shell的文件上去。

这个文件在处理完表单就会删除,于是我们就要用到条件竞争。

PS:后面是我在靶机上完成的

运用了GitHub上的开源项目vulhub的exp.py成功getshell。

https://github.com/vulhub/vulhub

包含session

关于session:session内容一般以文件的形式存储于服务器中,而本地浏览器会存储一个与服务器中session文件对应的Cookie值,Cookie存储的是键值为“PHPSESSID”的Seeion_id值,用户在访问web应用时,每次跳转发生http请求时,会自动把这个存储session_id的Cookie值发送过去,因此web应用的所有页面都可以获取到这个SESSION_ID值,也就可以通过session_id获取服务器中存储的session值,当用户关闭浏览器后,cookie存储的session_id自动清除,一般服务器存储的session文件也会在30分钟后自动清除。

我们要包含session,就必须要知道session的位置。

构造如下的上传表单,仿造的别人的师傅的

<html>
<body>

<form action="http://localhost:8080/test3.php" method="POST" enctype="multipart/form-data">
 <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="<?php phpinfo();?>" />
 <input type="file" name="file">
 <input type="submit" />
</form>
</body>

得到了phpinfo页面,同时在该页面知道我们的session存储位置

同时得知了创造了一个名字为什么样的session文件。

下面进行条件竞争达到getshell的目的,这里用其他师傅的脚本,自己改了改。

可以看到成功写入

php崩溃

不知道是因为集成环境的原因还是什么,我始终无法复现成功。原理就是,通过构造http://yourip/test.php?file=php://filter/string.strip_tags=/etc/passwd包含一个存在的文件造成php7的内存爆出,在这个时候如果我们post一个包上去,上文可知,这时会创建一个临时文件,但在这个情况下这个临时文件并不会被删去,然后进行包含 .

php还有许许多多的漏洞,我会随着我的学习继续更新到博客上。


花びらは笑った.