[Cloud] 소스 코드 취약점

1=1·2023년 12월 14일
0

SK shieldus 16

목록 보기
1/10

소스 코드 진단

💥 자동화 솔루션

장점 간편하고 시간이 적게든다
단점 과/오탐 비율이 크고 솔루션을 잘 이해하고 있는 엔지니어가 필요함

💥 수동진단

장점 도출한 취약점에 대한 결과가 명확
단점 시간이 오래 걸리고 진단자에 대한 결과물 품질의 차이가 크다



취약점 예시

⭐ 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> ➡️ &gt'script&It;aaaa(1)&gt;/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("&", "&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
- 양호
- 사유 : 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("<", "&lt;")
                 .replaceAll(">", "&gt;")
                 .replaceAll("&", "&amp;")
                 .replaceAll("\"", "&quot;")
                 .replaceAll("'", "&#39;");
}
%>
<!-- ... -->
<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");: 세션 아이디를 탈취당하지 않도록 해야함












0개의 댓글