Fix the problem

This commit is contained in:
haby0
2021-03-04 19:54:49 +08:00
parent f795d5e0d3
commit c5577cb09a
32 changed files with 1084 additions and 428 deletions

View File

@@ -0,0 +1,42 @@
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSources
import DataFlow::PathGraph
/** Json string type data */
abstract class JsonpStringSource extends DataFlow::Node { }
/** Convert to String using Gson library. */
private class GsonString extends JsonpStringSource {
GsonString() {
exists(MethodAccess ma, Method m | ma.getMethod() = m |
m.hasName("toJson") and
m.getDeclaringType().getASupertype*().hasQualifiedName("com.google.gson", "Gson") and
this.asExpr() = ma
)
}
}
/** Convert to String using Fastjson library. */
private class FastjsonString extends JsonpStringSource {
FastjsonString() {
exists(MethodAccess ma, Method m | ma.getMethod() = m |
m.hasName("toJSONString") and
m.getDeclaringType().getASupertype*().hasQualifiedName("com.alibaba.fastjson", "JSON") and
this.asExpr() = ma
)
}
}
/** Convert to String using Jackson library. */
private class JacksonString extends JsonpStringSource {
JacksonString() {
exists(MethodAccess ma, Method m | ma.getMethod() = m |
m.hasName("writeValueAsString") and
m.getDeclaringType()
.getASupertype*()
.hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper") and
this.asExpr() = ma
)
}
}

View File

@@ -0,0 +1,128 @@
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import java.io.PrintWriter;
import java.util.HashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class JsonpController {
private static HashMap hashMap = new HashMap();
static {
hashMap.put("username","admin");
hashMap.put("password","123456");
}
@GetMapping(value = "jsonp1", produces="text/javascript")
@ResponseBody
public String bad1(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
Gson gson = new Gson();
String result = gson.toJson(hashMap);
resultStr = jsonpCallback + "(" + result + ")";
return resultStr;
}
@GetMapping(value = "jsonp2")
@ResponseBody
public String bad2(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
return resultStr;
}
@GetMapping(value = "jsonp3")
@ResponseBody
public String bad3(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
String jsonStr = getJsonStr(hashMap);
resultStr = jsonpCallback + "(" + jsonStr + ")";
return resultStr;
}
@GetMapping(value = "jsonp4")
@ResponseBody
public String bad4(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
String restr = JSONObject.toJSONString(hashMap);
resultStr = jsonpCallback + "(" + restr + ");";
return resultStr;
}
@GetMapping(value = "jsonp5")
@ResponseBody
public void bad5(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String jsonpCallback = request.getParameter("jsonpCallback");
PrintWriter pw = null;
Gson gson = new Gson();
String result = gson.toJson(hashMap);
String resultStr = null;
pw = response.getWriter();
resultStr = jsonpCallback + "(" + result + ")";
pw.println(resultStr);
}
@GetMapping(value = "jsonp6")
@ResponseBody
public void bad6(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String jsonpCallback = request.getParameter("jsonpCallback");
PrintWriter pw = null;
ObjectMapper mapper = new ObjectMapper();
String result = mapper.writeValueAsString(hashMap);
String resultStr = null;
pw = response.getWriter();
resultStr = jsonpCallback + "(" + result + ")";
pw.println(resultStr);
}
@GetMapping(value = "jsonp7")
@ResponseBody
public String good1(HttpServletRequest request) {
String resultStr = null;
String token = request.getParameter("token");
if (verifToken(token)){
String jsonpCallback = request.getParameter("jsonpCallback");
String jsonStr = getJsonStr(hashMap);
resultStr = jsonpCallback + "(" + jsonStr + ")";
return resultStr;
}
return "error";
}
public static String getJsonStr(Object result) {
return JSONObject.toJSONString(result);
}
public static boolean verifToken(String token){
if (token != "xxxx"){
return false;
}
return true;
}
public static boolean verifReferer(String referer){
if (!referer.startsWith("http://test.com/")){
return false;
}
return true;
}
}

View File

@@ -0,0 +1,170 @@
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class JsonpInjection {
private static HashMap hashMap = new HashMap();
static {
hashMap.put("username","admin");
hashMap.put("password","123456");
}
@GetMapping(value = "jsonp1")
@ResponseBody
public String bad1(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
Gson gson = new Gson();
String result = gson.toJson(hashMap);
resultStr = jsonpCallback + "(" + result + ")";
return resultStr;
}
@GetMapping(value = "jsonp2")
@ResponseBody
public String bad2(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
return resultStr;
}
@GetMapping(value = "jsonp3")
@ResponseBody
public String bad3(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
String jsonStr = getJsonStr(hashMap);
resultStr = jsonpCallback + "(" + jsonStr + ")";
return resultStr;
}
@GetMapping(value = "jsonp4")
@ResponseBody
public String bad4(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
String restr = JSONObject.toJSONString(hashMap);
resultStr = jsonpCallback + "(" + restr + ");";
return resultStr;
}
@GetMapping(value = "jsonp5")
@ResponseBody
public void bad5(HttpServletRequest request,
HttpServletResponse response) throws Exception {
response.setContentType("application/json");
String jsonpCallback = request.getParameter("jsonpCallback");
PrintWriter pw = null;
Gson gson = new Gson();
String result = gson.toJson(hashMap);
String resultStr = null;
pw = response.getWriter();
resultStr = jsonpCallback + "(" + result + ")";
pw.println(resultStr);
}
@GetMapping(value = "jsonp6")
@ResponseBody
public void bad6(HttpServletRequest request,
HttpServletResponse response) throws Exception {
response.setContentType("application/json");
String jsonpCallback = request.getParameter("jsonpCallback");
PrintWriter pw = null;
ObjectMapper mapper = new ObjectMapper();
String result = mapper.writeValueAsString(hashMap);
String resultStr = null;
pw = response.getWriter();
resultStr = jsonpCallback + "(" + result + ")";
pw.println(resultStr);
}
@GetMapping(value = "jsonp7")
@ResponseBody
public String good(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
String val = "";
Random random = new Random();
for (int i = 0; i < 10; i++) {
val += String.valueOf(random.nextInt(10));
}
// good
jsonpCallback = jsonpCallback + "_" + val;
String jsonStr = getJsonStr(hashMap);
resultStr = jsonpCallback + "(" + jsonStr + ")";
return resultStr;
}
@GetMapping(value = "jsonp8")
@ResponseBody
public String good1(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
String token = request.getParameter("token");
// good
if (verifToken(token)){
System.out.println(token);
String jsonStr = getJsonStr(hashMap);
resultStr = jsonpCallback + "(" + jsonStr + ")";
return resultStr;
}
return "error";
}
@GetMapping(value = "jsonp9")
@ResponseBody
public String good2(HttpServletRequest request) {
String resultStr = null;
String jsonpCallback = request.getParameter("jsonpCallback");
String referer = request.getHeader("Referer");
boolean result = verifReferer(referer);
// good
if (result){
String jsonStr = getJsonStr(hashMap);
resultStr = jsonpCallback + "(" + jsonStr + ")";
return resultStr;
}
return "error";
}
public static String getJsonStr(Object result) {
return JSONObject.toJSONString(result);
}
public static boolean verifToken(String token){
if (token != "xxxx"){
return false;
}
return true;
}
public static boolean verifReferer(String referer){
if (!referer.startsWith("http://test.com/")){
return false;
}
return true;
}
}

View File

@@ -0,0 +1,35 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The software uses external input as the function name to wrap JSON data and return it to the client as a request response. When there is a cross-domain problem,
there is a problem of sensitive information leakage.</p>
</overview>
<recommendation>
<p>Adding `Referer` or random `token` verification processing can effectively prevent the leakage of sensitive information.</p>
</recommendation>
<example>
<p>The following example shows the case of no verification processing and verification processing for the external input function name.</p>
<sample src="JsonpInjection.java" />
</example>
<references>
<li>
OWASPLondon20161124_JSON_Hijacking_Gareth_Heyes:
<a href="https://owasp.org/www-chapter-london/assets/slides/OWASPLondon20161124_JSON_Hijacking_Gareth_Heyes.pdf">JSON hijacking</a>.
</li>
<li>
Practical JSONP Injection:
<a href="https://securitycafe.ro/2017/01/18/practical-jsonp-injection">
Completely controllable from the URL (GET variable)
</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,64 @@
/**
* @name JSONP Injection
* @description User-controlled callback function names that are not verified are vulnerable
* to jsonp injection attacks.
* @kind path-problem
* @problem.severity error
* @precision high
* @id java/JSONP-Injection
* @tags security
* external/cwe/cwe-352
*/
import java
import JsonpInjectionLib
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.deadcode.WebEntryPoints
import semmle.code.java.security.XSS
import DataFlow::PathGraph
/** Determine whether there is a verification method for the remote streaming source data flow path method. */
predicate existsFilterVerificationMethod() {
exists(MethodAccess ma,Node existsNode, Method m|
ma.getMethod() instanceof VerificationMethodClass and
existsNode.asExpr() = ma and
m = getAnMethod(existsNode.getEnclosingCallable()) and
isDoFilterMethod(m)
)
}
/** Determine whether there is a verification method for the remote streaming source data flow path method. */
predicate existsServletVerificationMethod(Node checkNode) {
exists(MethodAccess ma,Node existsNode|
ma.getMethod() instanceof VerificationMethodClass and
existsNode.asExpr() = ma and
getAnMethod(existsNode.getEnclosingCallable()) = getAnMethod(checkNode.getEnclosingCallable())
)
}
/** Taint-tracking configuration tracing flow from get method request sources to output jsonp data. */
class RequestResponseFlowConfig extends TaintTracking::Configuration {
RequestResponseFlowConfig() { this = "RequestResponseFlowConfig" }
override predicate isSource(DataFlow::Node source) {
source instanceof RemoteFlowSource and
getAnMethod(source.getEnclosingCallable()) instanceof RequestGetMethod
}
override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(MethodAccess ma |
isRequestGetParamMethod(ma) and pred.asExpr() = ma.getQualifier() and succ.asExpr() = ma
)
}
}
from DataFlow::PathNode source, DataFlow::PathNode sink, RequestResponseFlowConfig conf
where
not existsServletVerificationMethod(source.getNode()) and
not existsFilterVerificationMethod() and
conf.hasFlowPath(source, sink) and
exists(JsonpInjectionFlowConfig jhfc | jhfc.hasFlowTo(sink.getNode()))
select sink.getNode(), source, sink, "Jsonp Injection query might include code from $@.",
source.getNode(), "this user input"

View File

@@ -0,0 +1,130 @@
import java
import DataFlow
import JsonStringLib
import semmle.code.java.security.XSS
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.frameworks.spring.SpringController
/** Taint-tracking configuration tracing flow from user-controllable function name jsonp data to output jsonp data. */
class VerificationMethodFlowConfig extends TaintTracking::Configuration {
VerificationMethodFlowConfig() { this = "VerificationMethodFlowConfig" }
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma, BarrierGuard bg |
ma.getMethod().getAParameter().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
bg = ma and
sink.asExpr() = ma.getAnArgument()
)
}
}
/** The parameter name of the method is `token`, `auth`, `referer`, `origin`. */
class VerificationMethodClass extends Method {
VerificationMethodClass() {
exists(MethodAccess ma, BarrierGuard bg, VerificationMethodFlowConfig vmfc, Node node |
this = ma.getMethod() and
this.getAParameter().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
bg = ma and
node.asExpr() = ma.getAnArgument() and
vmfc.hasFlowTo(node)
)
}
}
/** Get Callable by recursive method. */
Callable getAnMethod(Callable call) {
result = call
or
result = getAnMethod(call.getAReference().getEnclosingCallable())
}
abstract class RequestGetMethod extends Method { }
/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
private class ServletGetMethod extends RequestGetMethod {
ServletGetMethod() {
exists(Method m |
m = this and
isServletRequestMethod(m) and
m.getName() = "doGet"
)
}
}
/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
private class SpringControllerGetMethod extends RequestGetMethod {
SpringControllerGetMethod() {
exists(Annotation a |
a = this.getAnAnnotation() and
a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "GetMapping")
)
or
exists(Annotation a |
a = this.getAnAnnotation() and
a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping") and
a.getValue("method").toString().regexpMatch("RequestMethod.GET|\\{...\\}")
)
}
}
/** A concatenate expression using `(` and `)` or `);`. */
class JsonpInjectionExpr extends AddExpr {
JsonpInjectionExpr() {
getRightOperand().toString().regexpMatch("\"\\)\"|\"\\);\"") and
getLeftOperand()
.(AddExpr)
.getLeftOperand()
.(AddExpr)
.getRightOperand()
.toString()
.regexpMatch("\"\\(\"")
}
/** Get the jsonp function name of this expression */
Expr getFunctionName() {
result = getLeftOperand().(AddExpr).getLeftOperand().(AddExpr).getLeftOperand()
}
/** Get the json data of this expression */
Expr getJsonExpr() { result = getLeftOperand().(AddExpr).getRightOperand() }
}
/** A data flow configuration tracing flow from remote sources to jsonp function name. */
class RemoteFlowConfig extends DataFlow2::Configuration {
RemoteFlowConfig() { this = "RemoteFlowConfig" }
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
exists(JsonpInjectionExpr jhe | jhe.getFunctionName() = sink.asExpr())
}
}
/** A data flow configuration tracing flow from json data to splicing jsonp data. */
class JsonDataFlowConfig extends DataFlow2::Configuration {
JsonDataFlowConfig() { this = "JsonDataFlowConfig" }
override predicate isSource(DataFlow::Node src) { src instanceof JsonpStringSource }
override predicate isSink(DataFlow::Node sink) {
exists(JsonpInjectionExpr jhe | jhe.getJsonExpr() = sink.asExpr())
}
}
/** Taint-tracking configuration tracing flow from user-controllable function name jsonp data to output jsonp data. */
class JsonpInjectionFlowConfig extends TaintTracking::Configuration {
JsonpInjectionFlowConfig() { this = "JsonpInjectionFlowConfig" }
override predicate isSource(DataFlow::Node src) {
exists(JsonpInjectionExpr jhe, JsonDataFlowConfig jdfc, RemoteFlowConfig rfc |
jhe = src.asExpr() and
jdfc.hasFlowTo(DataFlow::exprNode(jhe.getJsonExpr())) and
rfc.hasFlowTo(DataFlow::exprNode(jhe.getFunctionName()))
)
}
override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
}

View File

@@ -0,0 +1,64 @@
import com.google.gson.Gson;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class JsonpInjectionServlet1 extends HttpServlet {
private static HashMap hashMap = new HashMap();
static {
hashMap.put("username","admin");
hashMap.put("password","123456");
}
private static final long serialVersionUID = 1L;
private String key = "test";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json");
String jsonpCallback = req.getParameter("jsonpCallback");
PrintWriter pw = null;
Gson gson = new Gson();
String jsonResult = gson.toJson(hashMap);
String referer = req.getHeader("Referer");
boolean result = verifReferer(referer);
// good
if (result){
String resultStr = null;
pw = resp.getWriter();
resultStr = jsonpCallback + "(" + jsonResult + ")";
pw.println(resultStr);
pw.flush();
}
}
public static boolean verifReferer(String referer){
if (!referer.startsWith("http://test.com/")){
return false;
}
return true;
}
@Override
public void init(ServletConfig config) throws ServletException {
this.key = config.getInitParameter("key");
System.out.println("初始化" + this.key);
super.init(config);
}
}

View File

@@ -0,0 +1,50 @@
import com.google.gson.Gson;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class JsonpInjectionServlet2 extends HttpServlet {
private static HashMap hashMap = new HashMap();
static {
hashMap.put("username","admin");
hashMap.put("password","123456");
}
private static final long serialVersionUID = 1L;
private String key = "test";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json");
String jsonpCallback = req.getParameter("jsonpCallback");
PrintWriter pw = null;
Gson gson = new Gson();
String result = gson.toJson(hashMap);
String resultStr = null;
pw = resp.getWriter();
resultStr = jsonpCallback + "(" + result + ")";
pw.println(resultStr);
pw.flush();
}
@Override
public void init(ServletConfig config) throws ServletException {
this.key = config.getInitParameter("key");
System.out.println("初始化" + this.key);
super.init(config);
}
}

View File

@@ -0,0 +1,43 @@
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.util.StringUtils;
public class RefererFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String refefer = request.getHeader("Referer");
boolean result = verifReferer(refefer);
if (result){
filterChain.doFilter(servletRequest, servletResponse);
}
response.sendError(444, "Referer xxx.");
}
@Override
public void destroy() {
}
public static boolean verifReferer(String referer){
if (StringUtils.isEmpty(referer)){
return false;
}
if (referer.startsWith("http://www.baidu.com/")){
return true;
}
return false;
}
}