-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Vulnerability ID: 028L-1EJ6-1MC7-PAMZ
Application Name: CargoCats-contrast-cargo-cats-dataservice
Vulnerability Link: https://eval.contrastsecurity.com/Contrast/static/ng/index.html#/545a3bce-97c5-4732-af38-1ac459087b0a/applications/2aaf9ddd-d652-40d0-ade2-b1bc01389e93/vulns/028L-1EJ6-1MC7-PAMZ
What Happened?
We tracked the following data from "creditCard" Parameter, "shipmentId" Parameter:
GET /payments?creditCard=contrast-redacted-financial-info&shipmentId=1
...which was accessed within the following code:
com.contrast.dataservice.PaymentController#executeRawQuery(), line 55
...and ended up in this database query:
INSERT INTO credit_card (card_number, shipment_id) VALUES ('9999999999999999 ' AND SLEEP(5) OR 'a'='a', 1)
What's the risk?
SQL injection is possible when developers hand-build SQL statements containing untrusted data without validation or encoding. The goal of such attacks is to force the database to retrieve and output data to which the user would not otherwise have access. For example, an attacker could use SQL Injection on a vulnerable application in order to query the database for customer credit card numbers and other data, even if it wasn't part of the query the developer created. SQL injection also allows privilege escalation, account hijacking, and in some cases, it may be possible for an attacker to gain shell access to the database server.
Recommendation
The most effective method of stopping SQL injection attacks is to only use an
https://en.wikipedia.org/wiki/Object-relational_mapping
like
(https://www.hibernate.org)
that safely handles database interaction. If you must execute queries manually, use
(https://docs.oracle.com/javase/6/docs/api/java/sql/CallableStatement.html)
(for stored procedures) and
(http://docs.oracle.com/javase/6/docs/api/java/sql/PreparedStatement.html)
(for normal queries). Both of these APIs utilize bind variables. Both techniques completely stop the injection of code if used properly.
You must still avoid concatenating untrusted supplied input to queries and use the binding pattern to keep untrusted input from being
misinterpreted as SQL code.
Here's an example of an unsafe query:
String user = request.getParameter("user");
String pass = request.getParameter("pass");
String query = "SELECT user_id FROM user_data WHERE user_name = '" + user + "' and user_password = '" + pass +"'";
try {
Statement statement = connection.createStatement( );
ResultSet results = statement.executeQuery( query ); // Unsafe!
}
Here's an example of the same query, made safe with PreparedStatement:
String user = request.getParameter("user");
String pass = request.getParameter("pass");
String query = "SELECT user_id FROM user_data WHERE user_name = ? and user_password = ?";
try {
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, user );
pstmt.setString( 2, pass );
pstmt.execute(); // Safe!
}
There are some scenarios, like dynamic search, that make it difficult to use parameterized queries because the order and quantity
of variables is not predetermined. If you are unable to avoid building such a SQL call on the fly, then validation and escaping all
untrusted data is necessary. Deciding which characters to escape depends on the database in use and the context into which the untrusted
data is being placed.
This is difficult to do by hand, but luckily the (https://www.owasp.org/index.php/ESAPI) offers such functionality. Here's an example of safely encoding a dynamically built statement for an Oracle database using untrusted data:
Codec ORACLE_CODEC = new OracleCodec();
String user = req.getParameter("user");
String pass = req.getParameter("pass");
String query = "SELECT user_id FROM user_data WHERE user_name = '" +
ESAPI.encoder().encodeForSQL( ORACLE_CODEC, **user**) + "' and user_password = '" +
ESAPI.encoder().encodeForSQL( ORACLE_CODEC, **pass**) + "'";
If your user data needs to enter the logic of the query, rather than the data of the query. For example controlling the order the data is returned in a ORDER BY Field.
String userType = request.getParameter("userType");
String orderBy = request.getParameter("orderBy");
String query = "+;
try {
PreparedStatement preparedStatement = con.prepareStatement("select email,first_name,last_name from tbl_employees where user_type = ? ORDER BY "+ orderBy+";");
preparedStatement.setString(userType);
ResultSet results = preparedStatement.executeQuery();
}
This cannot be set in a parameterised field and SQL Sanitisers like ESAPI.encoder().encodeForSQL() are not sufficient. The safest way is to create a white list of allowed column names. For example :
String userType = request.getParameter("userType");
String orderBy = request.getParameter("orderBy");
String query = "+;
try {
PreparedStatement preparedStatement = con.prepareStatement("select email,first_name,last_name from tbl_employees where user_type = ? ORDER BY "+ ColumnMapping.valueOf(orderBy).getColumnName()+";");
preparedStatement.setString(userType);
ResultSet results = preparedStatement.executeQuery();
}
...
enum ColumnMapping {
FIRSTNAME("first_name"),
LASTNAME("last_name"),
AGE("age");
private String columnName;
ColumnMapping(String columnName) {
this.columnName = columnName;
}
public String getColumnName() {
return columnName;
}
}
It's also helpful to ensure that the application is granted only the minimum database privileges necessary to perform its function. This may help reduce the impact of a successful SQL injection attack. At a minimum, access to powerful database APIs that interact with the operating or file systems should be revoked.
Check out our AI-generated Intelligent Remediation Guidance! https://eval.contrastsecurity.com/Contrast/static/ng/index.html#/545a3bce-97c5-4732-af38-1ac459087b0a/vulns/028L-1EJ6-1MC7-PAMZ/overview/recommendation
First Event
Stack:
org.apache.catalina.connector.RequestFacade.getParameterValues(RequestFacade.java:345)
org.springframework.web.context.request.ServletWebRequest.getParameterValues(ServletWebRequest.java:147)
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.resolveName(RequestParamMethodArgumentResolver.java:181)
org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:119)
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)
org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:227)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:181)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)
jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:732)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398)
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903)
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1740)
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189)
org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
java.base/java.lang.Thread.run()
Last Event
Stack:
com.zaxxer.hikari.pool.HikariProxyStatement.execute()
org.springframework.jdbc.core.JdbcTemplate$1ExecuteStatementCallback.doInStatement(JdbcTemplate.java:435)
org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:393)
org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:444)
com.contrast.dataservice.PaymentController.executeRawQuery(PaymentController.java:55)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0()
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke()
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke()
java.base/java.lang.reflect.Method.invoke()
org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)
jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:732)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398)
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903)
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1740)
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189)
org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
java.base/java.lang.Thread.run()
HTTP Request
GET http://dataservice:8080/payments?creditCard=contrast-redacted-financial-info&shipmentId=1 HTTP/1.1
Accept: application/json
Connection: keep-alive
Contrasttraceparent: 00-b7705173d749047ad5e9acc001a97ba5-8f64ed514c8a3c63-01
Host: dataservice:8080
User-Agent: Java/11.0.30
References
https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
Session ID: 1d2f360643d2ac6f719834d251a47f93
artifactHash: 4acf3d0b