Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Improved modeling of Apache HttpClient `execute` method sinks for `java/ssrf` and `java/non-https-url`.
7 changes: 6 additions & 1 deletion java/ql/lib/ext/org.apache.http.client.model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ extensions:
pack: codeql/java-all
extensible: sinkModel
data:
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpHost,HttpRequest)", "", "Argument[0]", "request-forgery", "ai-manual"]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this right? Based on the type names and the models below, which target the HttpUriRequest argument, I'd expect the sink to be the HttpRequest argument (possibly in addition to HttpHost).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The models look good, but I'm not sure if maybe Argument[1] is also a sink here.

https://codeql.github.com/codeql-query-help/javascript/js-host-header-forgery-in-email-generation/

The request here will have the headers of the outgoing request so if they are user controlled, they could for example cause a malicious redirect or something like that depending on the server that reads it.

I will approve it, but do check this out too

- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpHost,HttpRequest,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpHost,HttpRequest,ResponseHandler)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpHost,HttpRequest,ResponseHandler,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest,ResponseHandler)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest,ResponseHandler,HttpContext)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.client", "HttpClient", True, "execute", "(HttpUriRequest)", "", "Argument[0]", "request-forgery", "ai-manual"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import java.io.IOException;
import java.net.URI;

import org.apache.http.Header;
import org.apache.http.HeaderIterator;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.RequestLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ApacheHttpClientExecuteSSRF extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {

String sink = request.getParameter("host"); // $ Source

HttpHost host = new HttpHost(sink);
HttpRequest req = new BasicHttpRequest("GET", "/");
HttpUriRequest uriReq = new HttpUriRequest() {
@Override
public String getMethod() {
return "GET";
}

@Override
public URI getURI() {
return URI.create("https://" + sink);
}

@Override
public void abort() throws UnsupportedOperationException {
}

@Override
public boolean isAborted() {
return false;
}

@Override
public RequestLine getRequestLine() {
return null;
}

@Override
public ProtocolVersion getProtocolVersion() {
return null;
}

@Override
public boolean containsHeader(String name) {
return false;
}

@Override
public Header[] getHeaders(String name) {
return null;
}

@Override
public Header getFirstHeader(String name) {
return null;
}

@Override
public Header getLastHeader(String name) {
return null;
}

@Override
public Header[] getAllHeaders() {
return null;
}

@Override
public void addHeader(Header header) {
}

@Override
public void addHeader(String name, String value) {
}

@Override
public void setHeader(Header header) {
}

@Override
public void setHeader(String name, String value) {
}

@Override
public void setHeaders(Header[] headers) {
}

@Override
public void removeHeader(Header header) {
}

@Override
public void removeHeaders(String name) {
}

@Override
public HeaderIterator headerIterator() {
return null;
}

@Override
public HeaderIterator headerIterator(String name) {
return null;
}

@Override
public HttpParams getParams() {
return null;
}

@Override
public void setParams(HttpParams params) {
}
};
HttpContext context = null;
HttpClient client = new HttpClient() {
@Override
public HttpResponse execute(HttpHost target, HttpRequest request) throws IOException {
return null;
}

@Override
public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws IOException {
return null;
}

@Override
public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler)
throws IOException {
return null;
}

@Override
public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler,
HttpContext context) throws IOException {
return null;
}

@Override
public HttpResponse execute(HttpUriRequest request) throws IOException {
return null;
}

@Override
public HttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException {
return null;
}

@Override
public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler)
throws IOException {
return null;
}

@Override
public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler,
HttpContext context) throws IOException {
return null;
}
};
ResponseHandler<Object> handler = null;
Comment on lines +30 to +180
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this is confusing. Also, the source is named sink, which is confusing as well.


client.execute(host, req); // $ Alert
client.execute(host, req, context); // $ Alert
client.execute(host, req, handler); // $ Alert
client.execute(host, req, handler, context); // $ Alert
client.execute(uriReq); // $ Alert
client.execute(uriReq, context); // $ Alert
client.execute(uriReq, handler); // $ Alert
client.execute(uriReq, handler, context); // $ Alert

} catch (Exception e) {
// TODO: handle exception
}
}
}
Loading