|
| 1 | +# Java常见漏洞 |
| 2 | + |
| 3 | +## 命令执行 |
| 4 | + |
| 5 | +Java中实现命令执行的方式: |
| 6 | + |
| 7 | +* 反射 |
| 8 | +* `Runtime.getRuntime.exec` |
| 9 | +* `ProcessBuilder` |
| 10 | +* `groovy_shell` |
| 11 | + |
| 12 | +代码审计时,查找可用于命令执行的相关关键字如`groovy`、`Runtime.getRuntime.exec`、`ProcessBuilder `、`Class.forName`等,找到对应的地址后跟踪方法调用栈,最后找他的入口点即客户端传参获取地点进行分析 |
| 13 | + |
| 14 | +**示例代码1:使用 `Runtime.getRuntime().exec()`** |
| 15 | + |
| 16 | +```java |
| 17 | +import java.io.BufferedReader; |
| 18 | +import java.io.InputStreamReader; |
| 19 | + |
| 20 | +public class CommandExecutor { |
| 21 | + public static void main(String[] args) { |
| 22 | + try { |
| 23 | + Process process = Runtime.getRuntime().exec("calc"); |
| 24 | + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); |
| 25 | + String line; |
| 26 | + while ((line = reader.readLine()) != null) { |
| 27 | + System.out.println(line); |
| 28 | + } |
| 29 | + } catch (Exception e) { |
| 30 | + e.printStackTrace(); |
| 31 | + } |
| 32 | + } |
| 33 | +} |
| 34 | +``` |
| 35 | + |
| 36 | +**示例代码2:使用 `ProcessBuilder`** |
| 37 | + |
| 38 | +```java |
| 39 | +import java.io.BufferedReader; |
| 40 | +import java.io.InputStreamReader; |
| 41 | + |
| 42 | +public class ProcessBuilderDemo { |
| 43 | + public static void main(String[] args) { |
| 44 | + try { |
| 45 | + ProcessBuilder builder = new ProcessBuilder("ipconfig"); |
| 46 | + Process process = builder.start(); |
| 47 | + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); |
| 48 | + String line; |
| 49 | + while ((line = reader.readLine()) != null) { |
| 50 | + System.out.println(line); |
| 51 | + } |
| 52 | + } catch (Exception e) { |
| 53 | + e.printStackTrace(); |
| 54 | + } |
| 55 | + } |
| 56 | +} |
| 57 | + |
| 58 | +``` |
| 59 | + |
| 60 | +**示例代码3:使用`GroovyShell`** |
| 61 | + |
| 62 | +```java |
| 63 | +import groovy.lang.GroovyShell; |
| 64 | + |
| 65 | +public class groovyShellDemo { |
| 66 | + public static void main(String[] args) { |
| 67 | + GroovyShell shell = new GroovyShell(); |
| 68 | + String cmd = "\"whoami\".execute().text"; |
| 69 | + System.out.println(shell.evaluate(cmd)); |
| 70 | + } |
| 71 | +} |
| 72 | + |
| 73 | +``` |
| 74 | + |
| 75 | +类似的还有很多,还可以远程加载脚本,参考[Groovy命令执行指南 - Atomovo - 博客园 (cnblogs.com)](https://www.cnblogs.com/yyhuni/p/18012041) |
| 76 | + |
| 77 | +**示例代码4:反射调用之一** |
| 78 | + |
| 79 | +```java |
| 80 | +import java.io.BufferedReader; |
| 81 | +import java.io.IOException; |
| 82 | +import java.io.InputStreamReader; |
| 83 | +import java.lang.Class; |
| 84 | +import java.lang.reflect.*; |
| 85 | +public class reflectDemo { |
| 86 | + public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { |
| 87 | + Class<?> c = Class.forName("java.lang.Runtime");//获取类 |
| 88 | + Method m1 = c.getMethod("getRuntime");//获取getRuntime方法,用于创建对象 |
| 89 | + Method m2 = c.getMethod("exec",String.class);//获取exec方法,用于执行命令 |
| 90 | + Object obj = m1.invoke(null,null);//创建对象 |
| 91 | + Process process = (Process) m2.invoke(obj,"whoami");//反射调用 |
| 92 | + // 下面可以不要,直接m2.invoke(obj,"whoami"); 只是没回显 |
| 93 | + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { |
| 94 | + String line; |
| 95 | + while ((line = reader.readLine()) != null) { |
| 96 | + System.out.println(line); |
| 97 | + } |
| 98 | + } catch (IOException e) { |
| 99 | + throw new RuntimeException(e); |
| 100 | + } |
| 101 | + } |
| 102 | +} |
| 103 | + |
| 104 | +``` |
| 105 | + |
| 106 | +## 反序列化 |
| 107 | + |
| 108 | +在Java中反序列化漏洞之所以比较严重的原因之一是:Java存在大量的公用库,例如Apache Commons Collections。WebLogic、WebSphere、JBoss、Jenkins、OpenNMS这些应用的反序列化漏洞能够得以利用,便是依靠了Apache Commons Collections。当然反序列漏洞的根源并不在于公共库,而是在于Java程序没有对反序列化生成的对象的类型做限制。 |
| 109 | + |
| 110 | +代码审计时,首先查找用于解析的类库(xml、yml、json等),追踪方法调用栈然后考虑参数是否可控: |
| 111 | + |
| 112 | +* `XMLDecoder.readObject` |
| 113 | + |
| 114 | +* `Yaml.load` |
| 115 | +* `XStream.fromXML ` |
| 116 | +* `ObjectMapper.readValue` |
| 117 | +* `JSON.parseObject` |
| 118 | + |
| 119 | +当参数可控时,查找应用的Class Path中是否包含Apache Commons Collections等危险库(ysoserial所支持的其他库亦可)。同时满足了这些条件后,我们便可直接通过ysoserial生成所需的命令执行的反序列化语句。 |
| 120 | + |
| 121 | +利用链通常分为三部分,触发点、中继点、执行点。 |
| 122 | + |
| 123 | +* 触发点 |
| 124 | + |
| 125 | + * 触发点比较简单,主要是`readObj` |
| 126 | + |
| 127 | +* 中继点,这里不太懂,管他呢 我也挖不出来~ |
| 128 | + |
| 129 | + * 动态代理,相关知识可参考[Java动态代理](https://juejin.im/post/5ad3e6b36fb9a028ba1fee6a)。要实现动态代理需要有三个类: |
| 130 | + |
| 131 | + * 委托类,委托类就是处理业务逻辑的类,动态代理的目的就是在委托类中的代码运行时插入其他的操作,如日志打印。此外,委托类必须实现某个接口。 |
| 132 | + |
| 133 | + * 中介类,中介类是对`InvocationHandler`接口的实现,它持有一个委托类对象的引用,在代理类调用相关方法时,会劫持到中介类的`invoke`方法中,在插入操作后,通过反射调用委托类的方法。 |
| 134 | + |
| 135 | + * 代理类,代理类通过`Proxy.newProxyInstance`来创建,返回类型是委托类所实现的接口的类型。其他类会调用代理类来获取相应的功能,委托类是透明的。 |
| 136 | + |
| 137 | +* **执行点** |
| 138 | + |
| 139 | + 反序列化利用链的挖掘比较困难的点是反序列化执行点,有了反序列化执行点,一般情况下都可以挖掘出不止一条的利用连。常见执行命令的方式: |
| 140 | + |
| 141 | + * 反射利用`Runtime.getRuntime().exec`或`java.lang.ProcessBuilder`执行 |
| 142 | + * JNDI远程调用 |
| 143 | + * Templates执行字节码 |
| 144 | + * EL表达式 |
| 145 | + * 其他可执行命令的接口 |
| 146 | + |
| 147 | +## 文件相关 |
| 148 | + |
| 149 | +文件上传、下载、删除 |
| 150 | + |
| 151 | +关键字: |
| 152 | + |
| 153 | +* JDK原始的`java.io.FileInputStream`类 |
| 154 | +* JDK原始的`java.io.RandomAccessFile`类 |
| 155 | +* Apache Commons IO提供的`org.apache.commons.io.FileUtils`类 |
| 156 | +* JDK1.7新增的基于NIO非阻塞异步读取文件的`java.nio.channels.AsynchronousFileChannel`类。 |
| 157 | +* JDK1.7新增的基于NIO读取文件的`java.nio.file.Files`类。常用方法如:`Files.readAllBytes`、`Files.readAllLines` |
| 158 | +* `FileInputStream` |
| 159 | +* `FileOutputStream` |
| 160 | +* `File` |
| 161 | +* `FileUtils` |
| 162 | +* `IOUtils` |
| 163 | +* `BufferedReader` |
| 164 | +* `ServletFileUpload` |
| 165 | +* `MultipartFile` |
| 166 | +* `CommonsMultipartFile` |
| 167 | +* `PrintWriter` |
| 168 | +* `ZipInputStream` |
| 169 | +* `ZipEntry.getSize` |
| 170 | +* `Delete` |
| 171 | +* `deleteFile` |
| 172 | +* `fileName` |
| 173 | +* `filePath` |
| 174 | + |
| 175 | +找到对应的地址后跟踪方法调用栈,最后找他的入口点即客户端传参获取地点进行分析 |
| 176 | + |
| 177 | +## 表达式注入 |
| 178 | + |
| 179 | +Spring为解析SpEL提供了两套不同的接口,分别是`SimpleEvaluationContext`及`StandardEvaluationContext`。`SimpleEvaluationContext`仅支持SpEL语法的子集,抛弃了Java类型引用、构造函数及beam引用相对较为安全。而`StandardEvaluationContext`则包含了SpEL的所有功能,并且在不指定 |
| 180 | +`EvaluationContext`的情况下,将默认采用`StandardEvaluationContext`。 |
| 181 | +漏洞成因:很大一部分开发人员未对用户输入进行处理就直接通过解析引擎对SpEL继续解析。一旦用户能够控制解析的SpEL语句,便可通过反射的方式构造代码执行的SpEL语句,从而达到RCE的目的。 |
| 182 | + |
| 183 | +**SpEL表达式的用法** |
| 184 | + |
| 185 | +1. 注解(无法外部传入) |
| 186 | + |
| 187 | + ```java |
| 188 | + @value("#{表达式}") |
| 189 | + public String arg; |
| 190 | + ``` |
| 191 | + |
| 192 | +2. xml |
| 193 | + |
| 194 | + ```xml |
| 195 | + <bean id="Bean1" class="com.test.xxx"> |
| 196 | + <property name="arg" value="#{表达式}"> |
| 197 | + </bean> |
| 198 | + ``` |
| 199 | + |
| 200 | + 前面两种情况通常也是写死在代码中的,但是也有已知的利用场景,就是利用反序列化让程序加载我们事先构造好的恶意xml文件,如jackson的CVE-2017-17485、weblogic的CVE-2019-2725等。 |
| 201 | + |
| 202 | +3. 在代码中处理外部传入的表达式 |
| 203 | + |
| 204 | + 这部分是关注的重点。 |
| 205 | + |
| 206 | + ```java |
| 207 | + @RequestMapping("/spel") |
| 208 | + public String spel(@RequestParam(name = "spel") String spel) { |
| 209 | + ExpressionParser expressionParser = new SpelExpressionParser(); |
| 210 | + Expression expression = expressionParser.parseExpression(spel); |
| 211 | + Object object = expression.getValue(); |
| 212 | + return object.toString(); |
| 213 | + } |
| 214 | + ``` |
| 215 | + |
| 216 | +**漏洞可以利用的前置条件有三个:** |
| 217 | + |
| 218 | +1. 传入的表达式没过滤 |
| 219 | +2. 表达式解析之后调用了`getValue/setValue`方法 |
| 220 | +3. 使用`StandardEvaluationContext`(默认)作为上下文对象 |
| 221 | + |
| 222 | +**想要执行命令,spel表达式有如下两种:** |
| 223 | + |
| 224 | +* 使用`T(Type)`表示`Type`类的实例,`Type`为全限定名称,如`T(com.test.Bean1)`。但是`java.lang`例外,该包下的类可以不指定包名。得到类实例后会访问类静态方法与字段。 |
| 225 | + |
| 226 | + ```java |
| 227 | + T(java.lang.Runtime).getRuntime().exec("whoami") |
| 228 | + ``` |
| 229 | + |
| 230 | +* 直接通过java语法实例化对象、调用方法 |
| 231 | + |
| 232 | + ```java |
| 233 | + new ProcessBuilder("whoami").start() |
| 234 | + |
| 235 | + //可以利用反射来绕过一些过滤 |
| 236 | + #{''.getClass().forName('java.la'+'ng.Ru'+'ntime').getMethod('ex'+'ec',''.getClass()).invoke(''.getClass().forName('java.la'+'ng.Ru'+'ntime').getMethod('getRu'+'ntime').invoke(null),'calc')} |
| 237 | + ``` |
| 238 | + |
| 239 | +**审计技巧:** |
| 240 | + |
| 241 | +全局查找关键字 |
| 242 | + |
| 243 | +* `org.springframework.expression` |
| 244 | +* `parseExpression` |
| 245 | +* `getValue` |
| 246 | +* `getValueType` |
| 247 | +* `value="#{*}` |
| 248 | + |
| 249 | +## SQL注入 |
| 250 | + |
| 251 | +**执行sql语句的几种方式** |
| 252 | + |
| 253 | +1. JDBC |
| 254 | +2. Hibernate |
| 255 | +3. Mybatis |
| 256 | + |
| 257 | +**审计技巧** |
| 258 | + |
| 259 | +* 使用`statement`对象带入数据库中查询 |
| 260 | +* `+`、`append`直接拼接(**没有预编译**) |
| 261 | +* `like`、`order by`等无法使用**预编译**的语句 |
| 262 | + |
| 263 | +* `$()`拼接参数 |
| 264 | +* 常用的sql查询关键字,如`Select`,`insert`,`update`,`delete` |
| 265 | +* `%`、`in`等 |
| 266 | + |
| 267 | +找到对应的地址后跟踪方法调用栈,最后找客户端传参获取地点进行分析 |
| 268 | + |
| 269 | +## SSRF |
| 270 | + |
| 271 | +SSRF漏洞形成的原因大部分是因为服务端提供了可以从其他服务器获取资源的功能,然而并没有对用户的输入以及发起请求的url进行过滤&限制,从而导致了ssrf的漏洞。 |
| 272 | + |
| 273 | +**常见漏洞情况** |
| 274 | + |
| 275 | +* 抓取用户输入图片的地址并且本地化存储 |
| 276 | +* 从远程服务器请求资源 |
| 277 | +* 对外发起网络请求 |
| 278 | + |
| 279 | +**利用方式** |
| 280 | + |
| 281 | +* 利用file协议读取文件内容(仅限使用URLConnection或URL发起的请求) |
| 282 | +* 利用http 进行内网web服务端口探测 |
| 283 | +* 利用http 进行内网非web服务端口探测(如果将异常抛出来的情况下) |
| 284 | +* 利用http进行NTLM-relay攻击(仅限 HttpURLConnection 或者二次包装 HttpURLConnection 并未复写AuthenticationInfo方法的对象) **不懂** |
| 285 | + |
| 286 | +**审计技巧** |
| 287 | + |
| 288 | +* 全局查找`URLConnection`、`HttpURLConnection`、`HttpClient`、`Request`、`okhttp`、`OkHttpClient`、`Request.Get`、`Request.post`、`URL.openStream`、`ImageIO`等能够发起远程请求的类及函数,找到对应地址后打断点跟踪引用其的方法调用栈,从客户端传参开始,判断是否可控,及可控情况 |
| 289 | + |
| 290 | +* SSRF漏洞**URL**中常出现url、f、file、page等**参数**。 |
| 291 | + |
| 292 | +## XXE |
| 293 | + |
| 294 | +**解析XML的几种方式** |
| 295 | + |
| 296 | +* XMLReader |
| 297 | +* SAXBuilder |
| 298 | +* SAXReader |
| 299 | +* SAXParserFactory |
| 300 | +* Digester |
| 301 | +* DocumentBuilderFactory |
| 302 | + |
| 303 | +**审计技巧** |
| 304 | + |
| 305 | +- `Documentbuilder` |
| 306 | +- `DocumentBuilderFactory` |
| 307 | + |
| 308 | +- `SAXReader` |
| 309 | + |
| 310 | +- `SAXParser` |
| 311 | + |
| 312 | +- `SAXParserFactory` |
| 313 | + |
| 314 | +- `SAXBuilder` |
| 315 | + |
| 316 | +- `TransformerFactory` |
| 317 | + |
| 318 | +- `reqXml` |
| 319 | + |
| 320 | +- `getInputStream` |
| 321 | + |
| 322 | +- `XMLReaderFactory` |
| 323 | + |
| 324 | +- `.newInstance` |
| 325 | + |
| 326 | +- `SchemaFactory` |
| 327 | +- `SAXTransformerFactory` |
| 328 | + |
| 329 | +- `javax.xml.bind` |
| 330 | +- `XMLReader` |
| 331 | + |
| 332 | +- `XmlUtils.get` |
| 333 | + |
| 334 | +- `Validator` |
| 335 | + |
| 336 | +找到对应的地址后跟踪方法调用栈,最后找他的入口点即客户端传参获取地点进行分析 |
| 337 | + |
| 338 | +xxe的防御比较简单,禁用外部实体即可。 |
| 339 | + |
| 340 | +## XSS |
| 341 | + |
| 342 | +不想写了 |
0 commit comments