소스코드 진단 예제

이전 포스트에 이어서 작성하였습니다.

[보안 취약점 진단 및 대응/취약점] - 소스코드 진단

 

소스코드 진단

소스코드 진단 웹애플리케이션 취약점 진단은 크게 동적진단과 정적진단(소스코드진단)으로 나뉩니다. 두 진단의 가장 큰 차이는 "소스코드(정답지) 보고 진단하느냐?" 입니다. 동적진단 장점:

psjin230.tistory.com


정오탐 판별 #1

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(경로순회)
양호/취약 여부 : 취약
사유 : 파일명에 대한 검증이 없습니다. 외부 입력값에 대한 경로 설정을 할 수 없도록 경로 순회 문자열을 필터링해야합니다.
* 취약점명이 파일업로드가 아닌 이유? 파일 업로드는 업로드 되는 저장 코드가 있어야합니다. 그러나 이 코드는 fileName을 조회하고 출력하는 코드이기에 취약점명이 파일 업로드는 아닙니다.
* XSS도 가능하긴 한데, 출제자의 의도는 경로순회입니다.

 

정오탐 판별 #2

<?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
양호/취약 여부 : 양호
사유 : keyword 부분에 #을 사용하여 SQL 구문이 들어와도 동적쿼리가 불가능합니다.
* 이 코드는 MyBatis를 사용하고 있는 코드입니다. (# 동적쿼리 불가능 / $ 동적쿼리 가능)

 

정오탐 판별 #3

<% String keyword = request.getParameter("keyword"); %>
keyword = keyword.replaceAll("&", "&amp;");
keyword = keyword.replaceAll("<", "&lt;");
keyword = keyword.replaceAll(">", "&gt;");
keyword = keyword.replaceAll("₩", "&quot;");
keyword = keyword.replaceAll("'", "&#x27;");
keyword = keyword.replaceAll("/", "&#x2F;");
keyword = keyword.replaceAll("(", "&#x28;");
keyword = keyword.replaceAll(")", "&#x29;");

검색어 : <%=keyword%>

취약점명 : XSS
양호/취약 여부 : 양호(99%) 취약(1%)
사유 : 특수문자 필터링을 적용하고 있습니다. < > ( ) ' " & 6개 정도만 필터링해도 사실상 스크립트 코드가 동작하기 어렵습니다. 

 

정오탐 판별 #4

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!";
}

취약점명 : 파일 업로드
양호/취약 여부 : 양호(70%) 취약(30%)
사유 :
best는 확장자 검증(화이트리스트 기반)

good은 content type / 파일크기 / 파일의 실행권한 제거 / 특수문자 검증 등
현재 코드에서는 good에서 2개 정도(content type, 파일크기)를 적용하였습니다. 그러므로 양호하다고 볼 수 있습니다. 예를 들어 exploit.exe 파일이 업로드가 되더라도 실행이 되지는 않기 때문에 안전하다고 볼 수 있습니다.

 

정오탐 판별 #5

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) - 검증되지 않은 리다이렉트
양호/취약 여부 : 취약
사유 : 특수문자 필터링, 도메인 검증에 대한 로직이 없습니다.
* DB처리가 되는 것 같아 보이지만 사용자가 넣은 입력이 올바르게 출력되는 부분은 없습니다. 
* redirect 파라미터는 rd 변수를 통해서 최종적으로 response 되는 것을 확인할 수 있습니다. 그래서 reflected xss 가 있다고 판단하였습니다.


취약점명 : SQL Injection
양호/취약 여부 : 양호
사유 : preparestatement 사용하였습니다.
preparestatement를 사용한다고 해서 무조건 안전한 것은 아니고 얼마나 안전하게 사용하느냐에 따라 달라집니다. 이 코드는 customer_id로 id를 받고 있는데, pstmt.setString으로 안전한 처리를 하고 있다. 입력 값으로 들어오는 값을 쿼리로서 동작을 시키지 않고 문자열 처리를 한다는 의미입니다.

 

정오탐 판별 #6

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
양호/취약 여부 : 취약
사유 : statement 클래스를 안전하게 설정하여 사용하지 않았습니다.
* gubun 값을 검증하지 않고 쿼리에 사용하였습니다.
* preparestatement를 사용하는 것이 좋습니다. preparestatement가 무조건 안전한 것은 아니고 이것을 안전하게 사용해야합니다. statement 또한 무조건 안전하지 않은 것은 아닙니다.

 

정오탐 판별 #7

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 injection(os injection)
양호/취약 여부 : 양호
사유 : 화이트리스트 검증을 사용하였습니다. (메모장과 계산기만 허용)
* command injection
=> os에 정의된 command만 실행이 가능합니다.
[질문] exploit.exe 파일을 notepad.exe로 이름 변경하여 공격하면 안될까? exploit exe파일이 os 안에 기본 내장되어 있거나 저장되어 있어야 합니다! 일반적으로 os안에서 이름 변경은 불가능합니다.

 

정오탐 판별 #8

string file = Request.QueryString["path"];
if (file != null){
    if (file.IndexOf('\\') > -1 || file.IndexOf('/') > -1){
    	Response.Write("Path Traversal Attack");
    }else{
    	File.Delete(file);
    }
}

취약점명 : Directory Traversal(경로우회)
양호/취약 여부 : 양호(99%) 취약(1%)
사유 : 경로순회 문자인 \와 /를 포함한 파일경로를 체크하고 있습니다.

 

정오탐 판별 #9

<%
String param = request.getParameter("param");
If(param != null) {
    param = param.replaceAll("<script>","");
    param = param.replaceAll("</script>","");
}
%>
...
<p> 제목 : <%=param%></p>

취약점명 : XSS
양호/취약 여부 : 취약
사유 : <script>를 키워드 필터링하고 있으나, img, alert, iframe등과 같은 키워드는 필터링 되지 않았습니다.
* 키워드 필터링은 안전하지 않습니다.(고작 2개로는 필터링이 된다고 할수없습니다)
* script를 사용하지 않는 xss 공격도 많습니다.

 

정오탐 판별 #10

<%@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)
양호/취약 여부 : 양호
사유 : JSP에서 c:out은 기본적으로 escapeXml이 true로 되어 있기 때문에 <>&'" 문자들이 각각 &lt; &gt; & &#039; &#034;로 출력됩니다. 따라서 악성 스크립트가 실행되는 것을 방지합니다.
*JTSL 자바서버페이지 표준 태그 라이브러리

 

정오탐 판별 #11

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 Injection
양호/취약 여부 : 양호 
사유 : prepareStatement를 사용하고 setString으로 안전한 처리를 하고 있습니다. 입력 값으로 들어오는 값을 쿼리로서 동작을 시키지 않고 문자열로 처리합니다.

 

취약점명 : 파일업로드
양호/취약 여부 : 취약
사유 : 확장자, 특수문자, 파일 타입 검증이 없습니다.

 

정오탐 판별 #12

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);
	}

취약점명 : 취약한 암호화 알고리즘
양호/취약 여부 : 취약
사유 : DES(대칭키)보단 대칭키 암호화 알고리즘 중 안전한 편인 3DES, AES 알고리즘을 사용하는 것이 좋습니다.
* DES는 취약점이 많아서 사용을 지양하는 편입니다.

 

정오탐 판별 #13

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 보다는 SecureRandom을 사용하는 것이 좋습니다. 보안상 안전한 암호화 알고리즘을 사용하여 키를 생성하는 것도 좋습니다.

* random 라이브러리를 사용할 때는 유추하기 어려운 seed 값을 이용하여 난수를 생성해야합니다.

 

정오탐 판별 #14

try {
	rd = new BufferedReader(new FileReader(new File(filename)));
} catch(IOException e) {
	e.printStackTrace();
}

취약점명 : 예외처리 미흡
양호/취약 여부 : 취약
사유 : 예외상황이나 오류상황이 발생했을 때 "에러가 발생했습니다."라는 공통적인 답변을 줘야하데 e.printStackTrace()의 경우 메모리 스택에 대한 정보를 제공하게 됩니다. 그렇다면 공격자는 이 정보를 가지고 또다른 공격을 할 수 있기 때문에 에러 발생 문구만 띄우는 것이 좋습니다.

 

정오탐 판별 #15

@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;
    }
}

취약점명 : 부적절한 사용자 검증
양호/취약 여부 : 취약
사유 : 인증관련 취약점 문제는 좀 어렵고, 추측을 해야합니다. 동적으로 기능을 사용하여 매칭을 시켜야 해당 취약점에 대해 정확하게 찾을 수 있습니다. 코드만으로 알아보기 어렵습니다. 자동화의 경우도 인증취약점은 과/오탐 비율이 높은 편입니다. 
코드를 보면 userid를 받았지만 userid를 사용하는 부분은 없습니다. 그냥 바로 modifymember로 넘어갑니다. 안전하게 코드를 짜려면, 세션으로 받은 id와 글을 작성한 id를 비교하는 검증을 한 후 수정페이지로 넘겨야합니다. memberModifyProcess가 시작이 되고 맨 처음에 사용자 인증을 해주는 것이 좋습니다. 
* 예를 들어, A 게시글의 작성자는 a로,  A게시글에 대한 수정권한은 a만 가지고 있어야합니다. b라는 user는 A게시글을 조회는 가능하지만 수정은 불가해야합니다. A 게시글 수정시 로그인한 사용자와 A 게시글 작성자가 동일한지 사용자 검증을 해야하는데, 그때 세션의 UserID를 비교하는 것입니다.