장점 간편하고 시간이 적게든다
단점 과/오탐 비율이 크고 솔루션을 잘 이해하고 있는 엔지니어가 필요함
장점 도출한 취약점에 대한 결과가 명확
단점 시간이 오래 걸리고 진단자에 대한 결과물 품질의 차이가 크다
⭐ 1 -Stored XSS
<%@page contentType=”text/html”pageEncoding=”UTF-8”%>
<html>
<head>
<meta http-equiv=”Content-Type”
content=”text/html; charset=UTF-8”>
</head>
<body>
<h1>XSSSample</h1>
<%
String name=request.getParameter(“name”);
String title=null;
…
rs=pstmt.executeQuery(query);
while(rs.next()){
title=rs.getString(“title”))
…}
%>
<p>NAME:<%=name%></p>
<p>TITLE:<%=title%></p>
</body>
</html>
NAME:<%=name%>
<% = %> : 출력을 의미( XSS 공격 취약 )
최선
: 모든 입출력에 대한 특수문자 필터링
: 공격구문으로 동작되지 않게끔 수정
<script>alert(1)</script>
➡️ >'script&It;aaaa(1)>/script&It;
차선
: 키워드 필터링
⭐ 2 - SQL Injection
Class Login{
Public Connection getConnection() throws SQLException{
DriverManager.registerDriver(new com.microsoft.sqlserver.jdbc.SQLServerDriver());
String dbConnection=PropertyManager.getProperty(“db.connection”);
return DriverManager.getConnection(dbConnection);
}
Public void doPrivilegedAction(String username, char[] password) throws SQLException{
Connection connection=getConnection();
if(connection==null){
//handle error
}
try{
String pwd=HashUtil.hashPassword(password);
String sqlString=“SELECT*FROM db_user WHERE username=‘”+username+“’AND
password=‘”+pwd+“’”;
Statement stmt=connection.createStatement();
ResultSetrs=stmt.executeQuery(sqlString);
if(!rs.next()){
throw new SecurityException(“Username or password incorrect”);
} }finally {…}
}}
:
username = ' or 1=1-- 쿼리
SetString(username) = "' or 1=1--" ➡️ 쿼리로서 동작 불가
SetInt(username) = 정수형 처리 (' or 1=1--) ➡️ 쿼리로서 동작 불가( True를 의미하는 1을 명령어로 사용할수 없게 정수형 처리)
'#' : 주로 URL 에서 프래그먼트 식별자를 나타내기 위하여 사용됨 (ex. https://example.com/page#section) 과 같은 URL에서 페이지 내 특정 색션을 가리키는 프래그먼크이므로 프로그래밍 언어나 데이터 베이스 쿼리에서 일반적인 문자로 처리
프로그래밍 언어나 데이터 베이스 쿼리에서는'#' 이 특별한 의미를 갖지 않음
'$' : 쿼리 매개 변수를 나타내기 위하여 URL 에서 사용되며 서버 측에서는 이러한 매개 변수를 해석하여 동적으로 쿼리 처리 가능
⭐ 3 - 파일 업로드
File Upload
- 파일 명에 대한 확장자 검증이 미흡 (블랙리스트)
File Download (Directory Traversal)
- 파일 명에 대한 경로 검증이 부족
✅ Q1
String fileName = request.getParameter ("P");
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
FileInputStream fis = null;
try {
response.setHeader("Content-Disposition",
"attachment;filename="+fileName+";");
...
fis = new FileInputStream ("C:/datas/"+fileName);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(response.getOutputStream());
- Directory Traversal (경로순회)
- 취약
- 사유 : 파일 명에 대한 검증이 없다
1) 디렉토리 이탈
: 변수에 사용자로부터 전달된 값을 사용하여 파일을 읽고 있지만 사용자로부터 제공된 파일 이름에 대한 검증이 없음.
악의적인 사용자 디렉토리 이탈 공격을 수행하여 서버 파일 시스템의 다른 디렉토리에 액세스 가능 (ex. ../../../ect/passwd)
2) 파일 이름으로 인한 보안 문제
: 사용자가 임의의 파일 이름을 지정할 수 있기 때문에
✅ Q2
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
...
<select id="boardSearch" parameterType="map" resultType="BoardDto">
select * from tbl_board where title like '%'||#
{{keyword}||'%' order by pos asc
</select>
- SQL Injection
- 양호
- 사유 : '#'을 사용하고 있기 때문에 (동적쿼리 사용)
select * from tbl_board where title like '%'||# {{keyword}||'%' order by pos asc
✅ Q3
<% String keyword = request.getParameter("keyword"); %>
keyword = keyword.replaceAll("&", "&");
keyword = keyword.replaceAll("<", "<");
keyword = keyword.replaceAll(">", ">");
keyword = keyword.replaceAll("₩"", """);
keyword = keyword.replaceAll("'", "'");
keyword = keyword.replaceAll("/"", "/");
keyword = keyword.replaceAll("(", "(");
keyword = keyword.replaceAll(")", ")");
검색어 : <%=keyword%>
- XSS
- 양호
- 사유 : XSS를 일으키는<>()'"&
를 필터링 해주고 있기 때문에 양호라고 판별하여 XSS 공격이 불가능
하지만 쿼리가 조작되는Select * from board where keyword=(keyword)
, 경로순회도 가능
✅ Q4
if (FileUploadCtr.PostedFile.ContentType == "image/jpeg")
{
if (FileUploadCtr.PostedFile.ContentLength < 102400)
{
string fn = Path.GetFileName(FileUploadCtr.FileName);
FileUploadCtr.SaveAs(Server.MapPath("~/") + fn);
StatusLabel.Text = "Upload status: File uploaed!";
}
else
StatusLabel.Text = "Upload Status: The File has to be
less than 100 kb!";
}
else
StatusLabel.Text = "Upload Status: Only JPEG files are accepted!";
- 파일 업로드(Directory Traversal)
- 양호
- 사유 : 파일처리 (실제로 해석되는 부분은 파일 타입이기 때문에)
- 보완방법 : 확장자 검증 (화이트 리스트 기반)
exploit.exe
-> image/jpeg
text.txt
-> 서버가 이 파일을 받으면 파일명, 파일 타입으로 쪼개고 파일 타입을 검증
✅ Q5
String id = (String)session.getValue("id");
String bn = request.getParameter("gubun");
String rd = request.getParameter("redirect");
if (id.length() > 0) {
String sql = "select level from customer where customer_id = ? ";
conn = db.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, id);
rs = pstmt.executeQuery();
rs.next();
if ("0".equals(rs.getString(1)) && "01AD".equals(bn)) {
response.sendRedirect(rd);
return;
}
- XSS (Reflected XSS) : 검증되지 않은 리다이렉트 공격
- 취약
- 사유 : 특수문자 검증, 도메인 로직이 없다
- SQL Injection
- 양호
- 사유
: PrepareStatement를 안전하게 사용하느냐에 따라 (양호/취약)
:pstmt.setString(1, id);
: 공격구문이 들어와도 쿼리 구문으로 사용하지 않고 문자열 처리가 된다
✅ Q6
String gubun = request.getParameter(“gubun”);
…
String sql = “SELECT * FROM board WHERE b_gubun = ‘”+gubun+ “’”;
Connection con = db.getConnection();
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(sql);
- SQL Injection
- 취약
- 사유
:String sql = “SELECT * FROM board WHERE b_gubun = ‘”+gubun+ “’”
-> 사용자에게서 받은 gubun 파라미터에 대한 검증 없이 그대로 사용
:Statement stmt = con.createStatement();
-> Statement를 안전하게 설정하여 사용하지 않아서
✅ Q7
public static void main(String args[]) throws IOException {
List<String> allowedCommands = new ArrayList<String>();
allowedCommands.add("notepad");
allowedCommands.add("calc");
String cmd = args[0];
if (!allowedCommands.contains(cmd)) {
System.err.println("Error");
return;
}
Process ps = null;
try {
ps = Runtime.getRuntime().exec(cmd);
- Command(OS) Injection
- 취약
- 사유
: 화이트 리스트 기반 배열을 정의하여 command 실행했지만 cal을 직접 정의 했어야했다
:OS exploit.exe
-> 이름 변경 ->notepad.exe
- OS 안에 기본 내장되어 있거나 저장 되어 있어야함
✅ Q8
string file = Request.QueryString["path"];
if (file != null)
{
if (file.IndexOf('\\') > -1 || file.IndexOf('/') > -1)
{
Response.Write("Path Traversal Attack");
}
else
{
File.Delete(file);
}
}
- 경로 순회
- 양호
**- 사유 : 경로순회 문자열을 검증함 (/ , \\
)
✅ Q9
<%
String param = request.getParameter(“param”);
If(param != null) {
param = param.replaceAll(“<script>”,””);
param = param.replaceAll(“</script>”,””);
}
%>
…
<p> 제목 : <%=param%></p>
해당 코드는 사용자로부터 받은 'param' 값을 처리하고 회면에 출력함
- XSS
- 취약
- 사유 : 사용자로부터 받은 'param' 키워드의 필터링이 되지 않음, 사용자가 'param'에<script>
와 같은 HTML 또는JavaScript 코드를 삽입할 경우 이 코드가 그대로 HTML에 랜더링 되어 악성 스크립트가 실행 될 수 있다
<%
String param = request.getParameter("param");
if (param != null) {
// Sanitize user input to prevent XSS
param = param.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll("&", "&")
.replaceAll("\"", """)
.replaceAll("'", "'");
}
%>
<!-- ... -->
<p>제목: <%=param%></p>
위 코드와 같이 특수문자를 바꾸어서 XSS가 발생될 수 있는 취약점을 막는다
✅ Q10
<%@taglibprefix=”c”url=”http://java.sun.com/jsp/jstl/core”%>
<%@tagliburi=”http://java.sun.com/jsp/jstl/functions”prefix=”fn”%>
…
<c:out value=”${param.name}” escapeXml=”false”/>
- SQL Injection
- 양호
- 사유 : '#'을 사용하고 있기 때문에 (동적쿼리 사용)
select * from tbl_board where title like '%'||# {{keyword}||'%' order by pos asc
✅ Q10
<%@taglibprefix=”c”url=”http://java.sun.com/jsp/jstl/core”%>
<%@tagliburi=”http://java.sun.com/jsp/jstl/functions”prefix=”fn”%>
…
<c:out value=”${param.name}” escapeXml=”false”/>
- XSS (Reflected XSS)
- 취약
- 사유
:<c:out value=”${param.name}” escapeXml=”false”/>
-> 출력시 출력값에 대한 필터링을 비활성화 하여 사용
-escapeXml
: 특수문자를 이스케이프 하는 함수 true로 설정하면 변수의 값을 이스케이프 가능
✅ Q11
MultipartRequest multi = new MultipartRequest(request,savePath,sizeLimit,"euc-kr",new
DefaultFileRenamePolicy());
......
String fileName=multi.getFilesystemName("filename");
......
sql="INSERT INTO board(email,r_num,w_date,pwd,content,re_step,re_num,filename)“
+"values(?,0,sysdate(),?,?,?,?,?)";
PreparedStatement pstmt=con.prepareStatement(sql);
pstmt.setString(1,stemail);
pstmt.setString(2,stpwd);
pstmt.setString(3,stcontent);
pstmt.setString(4, stre_step);
pstmt.setString(5, stre_num);
pstmt.setString(6, fileName);
pstmt.executeUpdate();
Thumbnail.create(savePath+"/"+fileName, savePath+"/"+"s_"+fileName, 150);
- 파일 업로드
- 취약
- 사유
sql="INSERT INTO board(email,r_num,w_date,pwd,content,re_step,re_num,filename)“+"values(?,0,sysdate(),?,?,?,?,?)";
: 확장자, 특수문자, 파일 타입 검증 없이 사용
- SQL Injection
- 양호
- 사유
PreparedStatement pstmt=con.prepareStatement(sql);
: PreparedStatement 사용, 안전하게 데이터를 바인딩 하여 사용함
✅ Q12
import java.security.*;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
public class CryptoUtils {
public byte[] encrypt(byte[] msg, Key k) {
byte[] rslt = null;
try {
Cipher c = Cipher.getInstance("DES");
c.init(Cipher.ENCRYPT_MODE, k);
rslt = c.update(msg);
}
- 취약 암호 알고리즘
- 취약
- 사유
Cipher c = Cipher.getInstance("DES");
: DES (대칭키) -> 대칭키 암호 알고리즘 3DES
✅ Q13
import java.util.Random;
...
public Static int getRandomValue(int maxValue) {
Random random = new Random(100);
return random.nextInt(maxValue);
}
public Static String getAuthKey() {
Random random = new Random();
String authKey = Integer.toString(random.nextInt());
- 취약한 난수 사용
- 취약
- 사유
Random random = new Random();
: 초기 시드 값에 의존하여 난수를 생성하는데, 시드값이 예측 가능하다면 생성되는 난수 역시 예측 가능해질 수 있음
SecureRandom
: 보안상 안전한 알고리즘 키 생성
✅ Q14
try {
rd = new BufferedReader(new FileReader(new File(filename)));
} catch(IOException e) {
e.printStackTrace();
}
- 예외 (오류) 처리 미흡
- 취약
- 사유
e.printStackTrace();
: 예외 상황/ 오류 상황 속에서 공격자가 정보를 얻을 수 없도록 최소한의 공격 메시지만 출력 -> "에러가 발생했습니다"
✅ Q15
@RequestMapping(value = "/modify.do", method = RequestMethod.POST)
public ModelAndView memberModifyProcess(@ModelAttribute("MemberModel")
MemberModel memberModel, BindingResult result, HttpServletRequest request,
HttpSession session) {
ModelAndView mav = new ModelAndView();
String userId = (String) session.getAttribute("userId");
String passwd = request.getParameter("oldUserPw");
...
if (service.modifyMember(memberModel)) {
mav.setViewName("redirect:/board/list.do");
session.setAttribute("userName", memberModel.getUserName());
return mav;
} else {
mav.addObject("errCode", 2);
mav.setViewName("/board/member_modify");
return mav;
}}
String requestUser = memberModel.getUserId();
if (userId != null && requestUser != null && !userId.equals(requestUser))
- 부적절한 사용자 검증
- 취약
- 사유
service.modifyMember(memberModel)
: 회원정보 수정 전에 적절한 권한 인증을 거쳐야 함
String userId = (String) session.getAttribute("userId");
: 세션 아이디를 탈취당하지 않도록 해야함