2016년 11월 14일 월요일

클라이언트 측 통제 우회

클라이언트 측 통제 우회

클라이언트가 웹 애플리케이션에게 임의의 값을 전달 할 수 있기 때문에 서버는 클라이언트 측 통제를 위해 사용자 입력값을 제한한다.

1. 클라이언트를 통한 데이터 전송


client로부터 server 로 전송되는 모든 데이터는 사용자가 완전히 통제할 수 있기 때문에 클라이언트를 통해 전송되는 데이터가 변조되지 않을 것이라는 것은 잘못된 가정이다.
서버가 특정 데이터 아이템을 알고 있고 지정할 수 있다면 애플리케이션이 왜 이런 값을 client에게 전송했다가 다시 읽어 들이는가에 대한 의문을 가질 수 있는데 이에 대한 장점은 다음과 같다.
  • 사용자 세션에 사용되는 모든 종류의 데이터를 일일이 관리할 필요가 없다. 세션당 서버에 저장되는 데이터 양을 줄이면 애플리케이션 성능이 향상된다.
  • 동일한 사용자의 request을 처리하기 위해 서버 간에 서버 측 데이터를 공유하는 것은 어려운 문제이다.
  • third party component 사용시 이를 수정하기에 어렵기 때문에 client를 통한 데이터 전송은 해결하기 위한 가장 쉬운 방법이다.
Tip
(thrid party component란? 
  -오픈소스이거나 외부 소프트웨어 개발 회사로부터 사온 쇼핑 카트,로그인 메카니즘, 게시판 기능과 같은 component를 칭한다)

그러나 보통 cliet 측에서 민감한 데이터를 전송하는 것은 안전하지 않으므로 애플리케이션 상에서 취약점의 원인이 되기도 한다.

1.1 숨겨진 폼 필드


클라이언트를 통해 외관상 변조 할 수없는 방식으로 데이터를 전송하는 데 사용되는 메카니즘이다. 어떤 필드가 hidden  으로 설정되면 그 필드는 화면에 나타나지 않는다. 하지만 submit 할 때 이름과 값이 서버에 보내진다.

<html>
<body>
<form method="post" >
Price : 449
Quantity : <input type= "text" name="quantity" >
<input type="hidden" name="price " value="449">
           <input type="submit" value="buy!"  >
</form>
</body>
</html>

가장 기본적인 POST request 방식이다 . 그런데 hidden type의 input element는 프록시 서버를 통해 간단히 변경 가능하다. 실제로 Burp suite를 프록시 서버로 설정하여 request를 가로채 쉽게 hidden 값을 바꿀수 있다.  즉,  위와 같은 코드로 되어있는 상품을 살때 더 싸게 살수 있다는 뜻이다.

그래서 위 사진에서 가로채기를 통해 price값을 1로 바꾼것을 볼수 잇다.  이런 공격에 대한 간단한 해결방법은 price값을 암호화해서 보내면 된다.
예를 들어 price=499 에 대한 암호화 값이 price=sdKj12j39djskdk 와 같이 암호화 돼서 보낸다.
허나 이 방법도 높은 취약성을 보인다. 한 예로 다른 싼 제품의 암호화 문자열을 위의 암호화 문자열에 대치시키는 방법이다.
이 공격도 막으려면 예를 들어 제품코드와 가격을 함께 암호화하는 방법이 있다.

1.2 HTTP 쿠키


client를 통해 데이터를 전송하는 다른 방식은 HTTP 쿠키를 통하는 것이다.  가로채기 프록시를 사용하면 쿠키를 설정하는 서버 응답을 조작하거나 , 쿠키를 전달하는 클라이언트 요청을 조작해 쿠키를 전달하는 client request를 조작해 수정하는 것이 가능하다.

HTTP/1.1 200 OK
Set-Cookie : DiscountAgreed=25
Content-Length= 1050
....

DiscountAgreed 쿠키는 client를 통해 전송되는 데이터를 보호하고자 하는 클라이언트 측 통제에 기초한 전형적인 사례를 보여준다. 애플리케이션이 서버로 돌아온 DiscountAgreed 쿠키의 값을 믿는 경우 이를 조작해 얼마든지 할인 받을 수 있다.

POST /shop/shop.aspx?prod=3 HTTP/1.1
Host: example.net
Cookie: DiscountAgreed=25
Content-Length: 10

quantity=1


1.3 Referer 헤더


브라우저들은 대부분의 HTTP request에 Referer 헤더를 포함하고 있다.  사용자가 특정 hyperlink를 click 하거나 form을 submit하거나 그 page가 이미지와 같은 다른 resource를 불러오는 경우에 Referer 헤더를 포함한다.

Referer 헤더 조작으로 얻을수 있는 것은 다음과 같다
블로거들이 자신의 블로그를 찾아온 경로에 관심이 많단 사실을 알게된 스패머들은 블로그를 방문하면서 홍보할 사이트를 리퍼러로 남겨 놓는다. 블로거는 호기심에 그 사이트에 들어가 보면 스패머들은 소기의 목적을 달성하게 된다.

또는 Referer Header를 이용해서 뒤로가기 버튼을 사용하는 웹 애플리케이션의 경우 Referer Header를 조작해서 공격자가 원하는 사이트로 이동시킬 수 있다.

1.4 ASP.net ViewState


ASP.net ViewState는 client를 통해 변형된 데이터를 전송하는 데 자주 쓰이는 방식이다. 이것은 기본적으로 생성되는 Hidden필드인데, 현재 페이지의 상태에 대한 정보를 contain한다. ViewState는 사용자 interface내의 element들을 client 측에 저장해서 서버에서는 관련 state information을 유지할 필요가 없어지는 효과를 볼 수 있다.


예를 들어 설명하자면 

string price=getPrice(499);
ViewState.Add("price", price);

는 사용자에게 밑과 같은 폼으로 나타난다.


<html>
<body>
<form method="post" >
Price : 449
Quantity : <input type= "text" name="quantity" >
<input type="hidden" name="__VIEWSTATE id="__VIEWSTATE" value="/wdMRKDMSELFK
DLSmsmxkdksdSDKFKFSDF==" />
<input type="submit" value="buy!"  >
</form>
</body>
</html>

위와 같이 알수 없는 값으로 Encoding되어 보내진다. 하지만 ViewState 매개변수는 실제로 Base64 Encdoing 된 문자열이므로 Base64 Decoding하면 쉽게 실제 매개변수 값을 찾을 수 있다.
이 문제점을 보완하기 위해 기본적으로 ASP.net 은 ViewState을 보호하려고 Hash value을 더해서(MAC 보호) ViewState값을 변경시킨다.

2.사용자 데이터의 획득 : HTML 폼


2.1 길이제한


<input type="text" name="quantity" maxlength="1" >

와 같은 HTML input element는 html내에서 입력길이가 최대 1이라고 설정해놓았지만 proxy server로 request 를 가로채셔 maxlength값을 바꾸면 임의의 길이로 전송 가능하다.




이런 보안상 문제점을 해결하기 위해 user의 input값에 대한 검증 및 제한은 php(server-side script)에서 서 해야한다.

3. 사용자 데이터의 획득 : 브라우저 확장

HTML form 이외에 사용자 데이터를 받아들이는 방식이  browser 확장에서 동작하는 자바나 플래시 같은 client 측 컴포넌트를 사용하는 것이다.

크롬에서는 오른쪽 상단에    와 같은 아이콘들이 브라우저 확장 컴포넌트의 예이다.

브라우저 확장 기능은 여러가지 다양한 방식으로 데이터를 받아들이는데 input form 과 어떤 경우에는 client의 OS 내에 있는 파일 시스템이나 레지스트리와 상호 작용하기도 한다. 브라우저 확장 기술은 대게 서버에서 처리하던 기능을 클라이언트가 수행하도록 하므로 client 컴퓨터가 느려지는 경우가 발생하기도 한다. 그러나 애플리케이션 처리에 있어서 유연성이나 피드백을 제공하는 강점을 가진다.

3.1 일반적인 브라우저 확장 기술


쉽게 사용하는 브라우저 확장 기술로는 java applet, Flash , Silverlight가 있다. 각 브라우저 확장 기술 추구하는 목적이 비슷하므로 다음과 같은 속성을 가진다.
  • 중간 바이트코드로 컴파일된다.
  • SandBox 환경이 제공되는 virtual machine내에서 실행된다.
  • 복잡한 data 구조나 HTTP를 통한 객체를 전송하기 위해 Serializable를 적용한 원격 프레임워크 사용

(Serializable란? 객체의 직렬화는 객체의 내용을 바이트 단위로 변환하여 파일 또는 네트워크를 통해서 스트림(송수신)이 가능하게 하는것을 의미한다.)

3.2 브라우저 확장에 대한 접근


브라우저 확장에 접근하려면 2가지 기술을 이용한다.

첫번째로, Component에서 생성하는 요청과 Server로부터 전달받은 응답을 가로채서 수정할 수 있다. 대부분의 경우 이런 접근 방식은 컴포넌트를 검사할 때 많이 사용되는 가장 빠르면서 쉬운 방법이지만 제한이 있다. 전달되는 데이터가 암호화되거나 사용된 기술에 따라 어렵게 변형될수 있다는 것이 제한이다. 보통 modified된 형태라도 주의깊게 보면 형태를 추측하거나 원래 형태로 바꿀수 있는데 이에 대해서는 뒤에서 자세히 알아보겠다.
두번째로, Component 자체를 직접적인 공격 대상으로 삼을 수 있다. 원래 source code를 보기 위해 byte code로 디컴파일을 시도하거나, 디버거를 통해 Component를 동적으로 분석할 수도 있다.

3.3 브라우저 확장 트래픽 가로채기


프록시를 통해 Component가 서버로 전달하는 데이터를 볼 수도있고 데이터의 값을 바꿀수 도 있을것이다. 그러나 분석하기 어려운 변형된 문자를 볼 수도 있을 것이다.

직렬화 데이터 처리

애플리케이션은 데이터를  HTTP  request에 담기 전에 직렬화(serializable)할수도 있다. 애플리케이션의 처리와 상호작용하기위해 데이터를 수정하려면 다음과 같은 과정을 거친다.
그래서 단순히 변형된 원본(m)을 수정하려고 한다면 애플리케이션 처리에서 에러가 날것이다. 
각 브라우저 확장 기술은 HTTP 내에 데이터를 직렬화 하는 고유 체계를 가진다.

자바 직렬화

자바 언어는 객체 직렬화를 위한 고유 지원을 가지고 java applet은 이런 고유지원을 이용해서 client와 server application component 간 serializable된 데이터 구조를 delivery한다. 직렬화된 자바 객체에 포함돼 있는 메시지는 보통 헤더에 다음과 같이 나온다.

Content-Type: application/x-java-serialized-object

프록시를 통해 원본 직렬화 데이터를 획득하고 자바를 이용해서 역직렬화하면 원본 데이터를 얻을 수 있다.

플래시 직렬화

플래시는 server와 client 컴포넌트 간의 복잡한 데이터 구조를 전달하는데 사용되는 자체 직렬화 포맷을 사용한다. 액션 메시지 포맷(AMF)은 보통 Content-Type헤더로 구분가능하다.

Content-Type: application/x-amf

실버라이트 직렬화

실버라이트는 MS 사의 웹브라우저 플러그인으로 닷넷 플랫폼에서 구동되는 WCF(윈도우 통신 기반) 원격 프레임워크르 사용할 수 있게 한다. 보통 WCF를 사용하는 sliverlight는 SOAP를 위한 MS 닷넷 바이너리 포맷(NBFS)을 채택하며 이것도 Content-Type 헤더로 구분가능하다.

Content-Type: application/soap+msbin1

(Soap 이란? 분산 환경에서 어플리케이션 간에 정보를 교환하기 위한 XML 기반의 메시지 프로토콜 )

3.4브라우저 확장의 가로채기 트래픽 장애물


프록시를 통해 브라우저 확장 컴포넌트가 생성한 request를 수집 못하는 경우도 있는데 이런 문제는 주로 컴포넌트가 HTTP 프록시나 SSL 기반으로 데이터를 처리할 때 발생한다.

첫번째 문제(HTTP Proxy Problem)는 client Component가 사용자의 컴퓨터 설정이나 브라우저에서 지정한 프록시 설정을 신뢰하지 않을 수 있다. 원인은 Component가 직접 HTTP Request를 만들거나, 확장 프레임워크 또는 브라우저 자체에 제공되는 API의 외부에서 발생하는 문제다. 
이런 문제점에도 request를 가로챌 방법은 있다. Computer host file 을 수정하거나 , 자동으로 원하는 목적지 host로 데이터를 redirect하도록 프록시를 설정한다. 

두번째 문제(SSL based Problem)는 client Component가 프록시에 의해 나타나지 않는 SSL인증을 받아 들일수 없어서 이다. 프록시로 자체 서명된 인증서 즉, 임시로 신뢰하는 인증서에 대해 컴포넌트 자체가 프로그래밍적으로 받아들이지 않는다.
이 해결법은 나중에 알아본다.....

3.5 브라우저 확장 디컴파일


가장 좋은 공격으로 객체를 디컴파일해서 소스코드을 전체 검토하고 필요한 경우 객체의 행위를 변경하기 위해 코드를 수정한 후 그것을 다시 재컴파일하는 것이다. 브라우저 확장은 바이트 코드로 컴파일된다.
바이트 코드는 연관된 interpreter로 실행될 수 있는 high-level 플랫폼 독립 바이너리이고 각 브라우저 확장 기술은 자체 바이트코드 포맷을 갖고 있다. 그래서 애플리케이션은 interpreter가 실행되는 어떤 플랫폼에서든 동작한다.
위에서 말한 공격을 막기위해 다양한 방어 기술을 통해 디컴파일 하지 못하게 할수도 있고 디컴파일된 결과 코드가 해석하기 어렵게 만들어 놓을 수도 있다.

바이트코드 다운로드

실행 가능한 바이트코드를 다운로드한다. 바이트 코드는 애플리케이션 page의  HTML 소스코드내에 브라우저 확장을 구동하게 명시돼있는 URL로부터 파일을 로드한다. java applet은 일반적으로 <applet>  태크를 통해 로드되고 다른 컴포넌트는 일반적으로 <object> 태를 통해 로드 된다.

<applet code="example.class" codebase= "/scripts" ></applet>
(codebases 는 경로를 뜻함)
일부 경우 컴포넌트가 다른 브라우저 확장 프레임워크에 의해 제공되는 다양한 보호 스크립트를 이용해서 로드 되므로 바이트코드를 로드하는 URL 이 명확히 보이지 않을 수도 있다. 바이트코드에 대한 URL을 식별하는 방법은 브라우저 확장 컴포넌트가 로드되고 난 이후에 프록시 히스토리를 살펴보는것이다.
왜냐하면 프록시 히스토리 필터가 작동 중일수도 있었고 확장 컴포넌트에 대한 캐시값을 지워지지 않았었기 때문이다.

바이트코드 디컴파일

바이트코드는 보통 단일 파일 패키지 형태로 형태로 배포된다. 소스코드로 디컴파일을 하기전에 필요한 개별적인 바이트코드 파일들을 얻기 위해 단일 패키지를 언패킹할 필요가 있다.
java applet은 .jap(자바 아카이브) 파일로 패키징돼 있고 silverlight 객체는 .xap 파일로 패키지 돼있다.
두 개의 파일은 .zip 아카이브 포맷을 사용할수도 있다. 자바 바이트 코드는 .class 파일을 갖고있고 silverlight는 .dll 파일들을 갖고있다. Flash 객체는 .swf 파일로 패키징 돼있다.


  • 자바 디컴파일러 도구 - Jad
  • 플래시 도구 - Flare, Flasm , SWFScan
  • 실버라이트 도구 - 닷넷 Reflector
(플래시 바이트코드는 ActionScript 소스코드로 디컴파일 가능)

소스코드상에서 작업

디컴파일하고 나면 컴포넌트가 어떻게 동작하는지, 어떤 기능을 갖고 있는지를 이해하기 위해 소스코드를 살펴본다.  다음은 중요하게 볼 것들이다.
  • client 측에서 발생하는 이벤트와 입력 값 검증 또는 다른 보안 관련된 로직
  • 사용자가 제공한 데이터를 서버에 전달하기 전에 보호하는데 사용되는 난독화 또는 암호화
  • client 측  Hidden 기능
  • 애플리케이션 매핑을 통해서는 식별되지 않는 서버 측 기능에 대한 참조 부분
다음은 다양한 방법으로 컴포넌트의 행동을 수정할 수있다.

재컴파일과 브라우저 내에서의 실행 

디컴파일후 소스코드를 수정한다. 그리고 컴포넌트가 사용하는 기술과 관련된 개발 도구를 사용해서 재컴파일한다.

  • 자바 - javac프로그램을 통해 재컴파일가능
  • 플래시 - Adobe에서 제공하는 플래시 개발 스튜디오 사용
  • 실버라이트 - visual studio
재컴파일후 분산된 파일을 재패킹해야한다. 자바와 실버라이트의 경우 언패킹한곳에 있는 원래 파일을 수정된 바이트코드 파일로 대체하고 zip 압축 프로그램을 통해 .jap(.xap) 확장자로 변경시킨다.
그리고 마지막으로 수정한 컴포넌트를 브라우저에 로드하는 것이며 이에 대한 방법은 다음과 같이 다양하다.
  • 브라우저의 디스크 캐시에서 실행 할 수 있는 원본을 찾았다면 수정한 버전의 파일로 바꾸고 브라우저를 재 실행시킨다. 브라우저가 각 캐시된 리소스에 대해 각기 다른 개별 파일을 사용하지 않거나 브라우저 확장 컴포넌트의 캐시가 오직 메모리에서만 구동된다면 이 접근 방법은 성공하기 어렵다.
  • 가로채기 프록시를 사용해서 컴포넌트가 로드되는 페이지의 소스코드를 수정해서 다른 URL로 지정할 수 있으며 또한 로컬 파일 시스템을 지정하거나 통제할 수 있는 웹서버를 가리키게 할 수 있다.
  • 프록시를 이용해서 실행되는 코드가 포함된 응답을 가로채서 메시지 바디를 수정한 버전으로 교체할수 있다. 위의 2가지 방법보다 문제가 적은 방식
브라우저 외부에서 재컴파일과 실행

어떤 브라우저 확장 컴포넌트는 사용자가 제공한 입력값을 검증한 후에 서버에게 전달하기 전에 결과 데이터를 암호화하거나 변형시킨다. 이런 상황에서 임의의 유효하지 않은 입력을 통해 요구하는 변형이나 암호화를 수행해 결과를 로컬에서 보기 위해 컴포넌트를 수정 할 수도 있다. 그리고나서 컴포넌트가 유효한 입력 값을 전달할 때 프록시를 통해 수정한 컴포넌트에서 나온 결과 값으로 바꿔치기 할 수 있다.
이와 같은 공격을 수행하려면 관련 브라우저 확장 내에서 동작하고 command line에서 독립적으로 실행될 수 있게 설계된 실행 가능한 원본을 수정할 필요가 있다. 이 방법은 사용된 프로그래밍 언어에 의존한다.

자바스크립트를 통한 원본 컴포넌트 조작

HTML 페이지 안에 있는 javaScript를 수정해서 컴포넌트 조작가능하다. 가로채기 프록시를 이용해서 컴포넌트가 로드되는 HTML 페이지를 수정할 수 있고 숨김으로 대있는 인터페이스의 중요한 부분에 일부 JS를 더하거나 수정할 수 있다.

바이트코드 변형 기법

자주 쓰이는 변형 기법은 다음과 같다

  • 의미있는 클래스,메소드,변수명을 의미없는 표현(a,b,c)로 치환한다.
  • 아이템의 이름을 int , long, System 같은 자바 키워드로 치환한다.
  • 코드가 실행되는 경로를 jump명령을 통해 변형시켜 로직의 진행 경로를 이해하기 힘들게 함
  • 재컴파일 못하도록 접근할 수 없는 문장이나 return 문이 없는 코드 경로 등 허용되지 않는 프로그래밍 방식을 사용
  • 필요없는 디버깅 정보나 메타 정보를 바이트코드에서 제거시킨다. 이런 정보에는 소스 파일명, 라인번호, 지역변수명 등등에 해당한다.


0 개의 댓글:

댓글 쓰기