代码审计 | Wavsep靶场审计防御
2023-6-13 14:47:55 Author: www.secpulse.com(查看原文) 阅读量:18 收藏


声明:文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!

你能收获的

1

编程基础 | PHP代码审计(上)

2

编程基础 | PHP代码审计(下)

3

代码审计 | zzcms8.2

1.1 基于wavsep靶场的jsp代码审计防御

本次实验是基于wavsep进行设计,因此我们主要做了sql注入及xss跨站请求

漏洞的实验结束。

1.1.1 wavsep靶场搭建

1、java环境搭建 因为本地已搭建好java环境,因此不进行演示了,网上也有很多java环境搭建教程。

2、wavsep靶场搭建 在网上下载wavsep.war包,将war包放在tomcat下的webapps下则会自动解压为wavsep文件夹;在浏览器中打开

http://127.0.0.1:8080/wavsep/:

通过页面信息我们可知当我们需要打开sql注入或者xss跨站脚本请求漏洞进行

实验时只需在url:http://127.0.0.1:8080/wavsep/ 后面跟上  index-xss.jsp

等练习页面即可。

1.1.2 SQL注入代码审计防御

1.字符型(String)sql注入 打开本次sql注入页面(wavsep中GET500中的

case1):

http://127.0.0.1:8080/wavsep/SInjection-Detection-Evaluation-GET-500E

rror/Case1-InjectionInLogin-String-LoginBypass-WithErrors.jsp?username=textvalue&password=textvalue2

在用户名和密码处插入注入语句进行尝试绕过登录('or'7'='7):

服务器回显"hello user1",登陆成功,该点存在注入,查看一下源码:
<%@ page language="java" contentType="text/html; charset=windows-1255"
pageEncoding="windows-1255"%>
<%@page import="java.sql.*" %><%@page import="com.sectooladdict.constants.DatabaseConstants" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=windows-1255"
>

<title>Case1-Injectionintostringvaluesinaloginpagewitherroneous
responses</title>
</head>
<body>
<%
if (request.getParameter("username") == null
&& request.getParameter("password") == null    ) {
%>

Login Page:<br><br>
<form name="frmInput" id="frmInput"
action="Case1-InjectionInLogin-String-LoginBypass-WithErrors.jsp"
method="POST">

<input type="text" name="username" id="username"><br>
<input type="password" name="password" id="password"><br>
<input type=submit value="submit">
</form>
<%
}
else {
try {
String username = request.getParameter("username"); String password =
request.getParameter("password");
/* Test loading driver */
String driver = DatabaseConstants.DATABASE_DRIVER;
Class.forName(driver).newInstance();
/* Test the connection */
String url = DatabaseConstants.CONNECTION_STRING;
Connection conn=DriverManager.getConnection(url);
System.out.print("Connection Opened Successfully");
String SqlString =
"SELECT username, password " +
"FROM users " + "WHERE username='" + username + "'" + " AND password='"
+ password + "'"; Statement stmt = conn.createStatement(); ResultSet rs
= stmt.executeQuery(SqlString); if(rs.next()) { out.println("hello " +
rs.getString(1)); } else { out.println("login failed"); }
out.flush();
} catch (Exception e) {
response.sendError(500,"Exception details: " + e);
}
} //end of if/else block
%>

</body>
</html>
以上便是这个页面的所有源码,看一下其中的查询语句:
String SqlString =
"SELECT username, password " +
"
FROM users " + "WHERE username='" + username + "'" + " AND password='"
+ password + "'
"; Statement stmt = conn.createStatement(); ResultSet rs
= stmt.executeQuery(SqlString);

通过上述登录成功('or'7'='7')的事例可以

发现其sql查询语句为:select  username,password from users where

username=''or'7'='7' and password=''or'7'='7''当username或username

和password为真的时候则查询成功,为否则查询失败;这个就是利用相关语句组合的查询语句造成注入成功的注入语句;对于这类sql注入漏洞该如何预防呢?"WHERE username='" + username + "'" + " AND password='" + password + "'";发现传入值为单引号进行包含起来了,该注入为字符型查询,针对字符型查询直接可以将其单引号替换成双引号,数据为库oracle、mssql:则函数为String.replace("'",""");数据库为mysql:则函数为

String.replace("'","'");具体在源码中进行替换如下:

String username = request.getParameter("username").replace("'","'");

String password = request.getParameter("password").replace("'","'");

在对该源码进行替换防御后我们再次实验,在用户名和密码处插入注入语句进行

尝试绕过登录('or'7'='7):

登录失败,说明此方法对单引号进行转译,起到了对SQL注入的防护作用。 上面通过对单引号的转译,防止了sql注入漏洞,同时,还可以利用参数化查询的方式防止字符型sql注入漏洞,对于参数化查询我们可以先看看owasp官网上的

文档:

https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet

实例如下:

String custname = request.getParameter("customerName"); // This should
REALLY be validated too
// perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name
= ? "
;
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );

参考上述实例,我们对源码中sql查询语句进行修改:

<%@ page language="java" contentType="text/html; charset=windows-1255"
pageEncoding="windows-1255"%>
<%@page import="java.sql.*" %><%@page import="com.sectooladdict.constants.DatabaseConstants" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=windows-1255"
>

<title>Case1-Injectionintostringvaluesinaloginpagewitherroneous
responses</title>
</head>
<body>
<%
if (request.getParameter("username") == null
&& request.getParameter("password") == null    ) {
%>

Login Page:<br><br>
<form name="frmInput" id="frmInput"
action="Case1-InjectionInLogin-String-LoginBypass-WithErrors.jsp"
method="POST">

<input type="text" name="username" id="username"><br>
<input type="password" name="password" id="password"><br>
<input type=submit value="submit">
</form>
<%
}
else {
try {
String username = request.getParameter("username"); String password =
request.getParameter("password");
/* Test loading driver */
String driver = DatabaseConstants.DATABASE_DRIVER;
Class.forName(driver).newInstance();
/* Test the connection */
String url = DatabaseConstants.CONNECTION_STRING;
Connection conn=DriverManager.getConnection(url);
System.out.print("Connection Opened Successfully");
String SqlString1 =
"SELECT username, password " +
"FROM users " +
"WHERE username=?" + " AND password=?"; //用?代替参数  PreparedStatement
prepstmt = conn.prepareStatement(SqlString1);
prepstmt.setString( 1, username); //利用set给一个值
prepstmt.setString(2,password);ResultSetrs=prepstmt.executeQuery();
//进行查询 if(rs.next()) { out.println("hello " + rs.getString(1)); }
else { out.println("login failed"); }
out.flush();
} catch (Exception e) {
response.sendError(500,"Exception details: " + e);
}
} //end of if/else block
%>

</body>
</html>

在查询语句中,利用?代替我们传入的username、password,然后再分别给其

set一个值进行查询,进行实验一下:

登录失败,参数化查询防护成功。以上就是本次字符型sql注入的测试及防护。

2.数字型sql注入  针对数字型sql注入,利用函数进行防护,把常见参数转换

成数字类型进行查询,该函数如下:

Integer.parseInt(s)

3.非字符非数字型对于非字符型又非数字型的sql注入该如何应对,先访问该页

面(wavsep中GET500中的

case5):http://127.0.0.1:8080/wavsep/SInjection-Detection-Evaluation-G

ET-500Error/Case5-InjectionInSearchOrderBy-String-BinaryDeliberateRun

timeError-WithErrors.jsp?orderby=msgid

在参数后边加上单引号,页面出现sql报错,存在sql注入:

查询一些该页面源码:

<%@ page language="java" contentType="text/html; charset=windows-1255"
pageEncoding="windows-1255"%>
<%@page import="java.sql.*" %><%@page import="com.sectooladdict.constants.DatabaseConstants" %><%@ page import="com.sectooladdict.validators.InputValidator" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=windows-1255"
>

<title>Case 5 - Injection into an order by clause (any type) in a search
page with erroneous responses</title>
</head>
<body>
<%
if (request.getParameter("orderby") == null) {
%>

View Messages and Organize Results According to the Field:<br><br>
<form name="frmInput" id="frmInput"
action="Case5-InjectionInSearchOrderBy-String-BinaryDeliberateRuntim
eError-WithErrors.jsp"
 method="POST">

<input type="text" name="orderby" id="orderby"
value="msgid" >
<br>
<input type=submit value="submit">
</form>
<%
}
else {
try {
String order = request.getParameter("orderby"); if
(InputValidator.validateSemicolon(order)) { throw new
Exception("Invalid Characters in Input: Semicolon (;)"); }
/* Test loading driver */
String driver = DatabaseConstants.DATABASE_DRIVER;
Class.forName(driver).newInstance();
/* Test the connection */
String url = DatabaseConstants.CONNECTION_STRING;
Connection conn=DriverManager.getConnection(url);
System.out.print("Connection Opened Successfully");
String SqlString =
"SELECT msgid, title, message " +
"FROM messages " + "ORDER BY " + order; Statement stmt =
conn.createStatement(); ResultSet rs = stmt.executeQuery(SqlString);
out.println("The list of messages:"); out.println("<TABLE>");
out.println("<TR>");
out.println("<TD>");
out.println("<B>");
out.println("MsgId");
out.println("</B>");
out.println("</TD>");
out.println("<TD>");
out.println("<B>");
out.println("Title");
out.println("</B>");
out.println("</TD>");
out.println("<TD>");
out.println("<B>");
out.println("Message");
out.println("</B>");
out.println("</TD>");
out.println("</TR>");
while(rs.next()) { out.println("<TR>"); out.println("<TD>");
out.println(rs.getString(1)); out.println("</TD>");
out.println("<TD>"); out.println(rs.getString(2));
out.println("</TD>"); out.println("<TD>");
out.println(rs.getString(3)); out.println("</TD>");
out.println("</TR>"); } out.println("</TABLE>");
out.flush();
} catch (Exception e) {
response.sendError(500,"Exception details: " + e);
}
} //end of if/else block
%>

</body>
</html>

通过查阅源码,可以发现在查询时即不是字符型也不是数字型,是通过orderby  进行传参查询,因为一般表名的长度是有一定的长度限制的,所以这个时候可以利用限制查询时的字符长度来进行防护;在源码上进行防护修改,在 String order = request.getParameter("orderby");下面加入长度判断条件:

String order = request.getParameter("orderby");
if(order.length()>10){
return;
}

首先在未进行限制参数长度的情况下插入超过   10  个以上字符的参数:

出现sql报错;

接下来对站点传入参数长度进行限制,并插入超过   10  个以上的字符参数:

未出现sql报错,该防护成功。以上就是在wavsep上相对具有代表性的三类注

入,对其进行了测试及防护。

1.1.3 XSS跨站请求漏洞代码审计防御

本次xss跨站请求漏洞代码审计防御实验选取GET型中的反射性xss漏洞做

实例进行查看及防御;首先访问存在反射型xss漏洞地址(xssGET中的case1):

http://127.0.0.1:8080/wavsep/RXSS-Detection-Evaluation-GET/Case1-Tag2

HtmlPageScope.jsp?userinput=textvalue

1.1.4 jsp代码审计防御

在userinput后面填写xss脚本代码进行提交:

查看该页面源码:

<%@ page language="java" contentType="text/html; charset=windows-1255"

pageEncoding="windows-1255"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=windows-1255"
>

<title>Case 1 - RXSS via tag injection into the scope of an HTML
page</title>
</head>
<body>
<%
if (request.getParameter("userinput") == null) {
%>
Enter your input:<br><br>
<form name="frmInput" id="frmInput"
action="Case1-Tag2HtmlPageScope.jsp" method="POST">

<input type="text" name="userinput" id="userinput"><br>
<input type=submit value="submit">
</form>
<%
}
else {
try {
String userinput = request.getParameter("userinput");
out.println("The reflected value: " + userinput);
out.flush();
} catch (Exception e) {
out.println("Exception details: " + e);
}
} //end of if/else block
%>
</body>
</html>

我们发现在插入的userinput参数未做任何防御,直接进行插入,导致xss反射脚本执行成功。对于 xss跨站请求漏洞在代码层的防御简单介绍以下一种,

如下是我们需要插入的防御代码:

<%!
String csHTML(String text){
if(text == null)return "";
StringBuffer results = null;
char[] orig = null;
int beg = 0len = text.length();
for(int i = 0;i < len;++i){
char c = text.charAt(i);
switch(c){
case 0:
case '&':
case '<':
case '>':
case '"':
if(results == null){
orig = text.toCharArray();
results = new StringBuffer(len + 10);
}
if(i>beg)results.append(orig,beg,i-beg);
beg = i + 1;
switch(c){
default://case 0:
continue;
case '&':
results.append("&amp;");
break;
case '<':
results.append("&lt;");
break;
case '>':
results.append("&gt;");
break;
case '"':
results.append(""");
break;
}
break;
}
}
if(results == null) return text;
results.append(orig,beg,len - beg);
return results.toString();
}
%>

我们将此防御代码插入到页面源码中:

<%@ page language="java" contentType="text/html; charset=windows-1255"
pageEncoding="windows-1255"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=windows-1255"
>

<title>Case 1 - RXSS via tag injection into the scope of an HTML
page</title>
</head>
<body>
<%!
String HTML(String text){
if(text == null)return " ";
StringBuffer results = null;
char[] orig = null;
int beg = 0len = text.length();
for(int i = 0;i < len;++i){
char c = text.charAt(i);
switch(c){
case 0:
case '&':
case '<':
case '>':
case '"':
if(results == null){
orig = text.toCharArray();
results = new StringBuffer(len + 10);
}
if(i>beg)results.append(orig,beg,i-beg);
beg = i + 1;
switch(c){
default://case 0:
continue;
case '&':
results.append("&amp;");
break;
case '<':
results.append("&lt;");
break;
case '>':
results.append("&gt;");
break;
case '"':
results.append(""");
break;
}
break;
}
}
if(results == null) return text;
results.append(orig,beg,len - beg);
return results.toString();
}
%>

<%
if (request.getParameter("
userinput") == null) {
%>

Enter your input:<br><br>
<form name="frmInput" id="frmInput"
action="Case1-Tag2HtmlPageScope.jsp" method="POST">

<input type="text" name="userinput" id="userinput"><br>
<input type=submit value="submit">
</form>
<%
}
else {
try {
Stringuserinput=HTML(request.getParameter("
userinput"));
out.println("
The reflected value: " + userinput);
out.flush();
} catch (Exception e) {
out.println("
Exception details: " + e);
}
} //end of if/else block
%>

</body>
</html>

访  问   该   页  面   ,  提   交   xss    脚   本  代   码  :

插入 xss  脚本代码解析失败,防御代码生效。一次修复方法可以对靶机内所有

的xss跨站请求漏洞进行实验修复。

本文作者:红日安全团队

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/201916.html


文章来源: https://www.secpulse.com/archives/201916.html
如有侵权请联系:admin#unsafe.sh