在项目中遇到SAML企业应用 想留个后门时候一脸懵
随便的整理记录 记录项目中SAML渗透的知识点。
0x01 前置知识 SAML单点登陆
SAML(Security Assertion Markup Language)是一个XML框架,也就是一组协议,可以用来传输安全声明。比如,两台远程机器之间要通讯,为了保证安全,我们可以采用加密等措施,也可以采用SAML来传输,传输的数据以XML形式,符合SAML规范,这样我们就可以不要求两台机器采用什么样的系统,只要求能理解SAML规范即可,显然比传统的方式更好。SAML 规范是一组Schema 定义。
Web Service 领域,schema就是规范,在Java领域,API就是规范。
SAML 作用
SAML 主要包括三个方面:
1.认证申明。表明用户是否已经认证,通常用于单点登录。
2.属性申明。表明 某个Subject 的属性。
3.授权申明。表明 某个资源的权限。
SAML框架
SAML就是客户向服务器发送SAML 请求,然后服务器返回SAML响应。数据的传输以符合SAML规范的XML格式表示。
SAML 可以建立在SOAP上传输,也可以建立在其他协议上传输。
因为SAML的规范由几个部分构成:SAML Assertion,SAML Prototol,SAML binding等
SAML在单点登录中大有用处:在SAML协议中,一旦用户身份被主网站(身份鉴别服务器,Identity Provider,IDP)认证过后,该用户再去访问其他在主站注册过的应用(服务提供者,Service Providers,SP)时,都可以直接登录,而不用再输入身份和口令。
SAML协议的核心是: IDP和SP通过用户的浏览器的重定向访问来实现交换数据。
过程
SP向IDP发出SAML身份认证请求消息,来请求IDP鉴别用户身份;IDP向用户索要用户名和口令,并验证其是否正确,如果验证无误,则向SP返回SAML身份认证应答,表示该用户已经登录成功了,此外应答中里还包括一些额外的信息,来却确保应答被篡改和伪造。
现在Alice要通过浏览器查阅她的邮件,Alice一般会通过浏览器访问一个网页,比如https://mail.google.com/a/my-university.nl (step1)。因为这是个联合身份域,所以Google不会向用户索要用户名和密码,而是将其从定向到IDP来认证其身份(step3)。用户被重定向的URL类似于这种:
https://idp.uni.nl/sso? SAMLRequest=fVLLTuswEN0j8Q…c%3D
嵌入到HTTP请求中的SAMLRequest就是SAML认证请求消息。因为SAML是基于XML的(通常比较长),完整认证请求消息要经过压缩(为Url节省空间)和编码(防止特殊字符)才能传输。在压缩和编码之前,SAML消息有如下格式:
<AuthnRequest ID="kfcn...lfki" Version="2.0" IssueInstant="2013-02-05T08:28:50Z" ProtocolBinding="urn:oasis:names:tc:SAML: 2.0:bindings:HTTP-POST" ProviderName="google.com" AssertionConsumerServiceURL="https://www.google.com/a/uni.nl/acs"> <Issuer>google.com</Issuer> <NameIDPolicy AllowCreate="true" Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"/>; </AuthnRequest>;
这个来自Google的请求,请验证当前用户的身份,并将结果返回。
当IDP收到消息并确认要接受认证请求之后,就会要求Alice输入用户名和口令来验证其身份(如果Alice已经登录过了,就会跳过该步骤);当验证通过之后,Alice的浏览器将会跳转回Google的特定页面。同样,SAML身份认证响应的内容也是在压缩并编码后以参数的形式传输。在压缩和编码之前,其结构类如下:
<Response Version="2.0" IssueInstant="2013-02-05T08:29:00Z" Destination="https://www.google.com/a/my.uni.nl/acs" InResponseTo="kfcn...lfki"> <Issuer>https://idp.uni.nl/</Issuer> <Status> <StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/> </Status> <Assertion Version="2.0" IssueInstant="2013-02-05T08:29:00Z"> <Issuer>https://idp.uni.nl/</Issuer> <Subject> <NameID>alice</NameID> <SubjectConfirmation ...> <SubjectConfirmationData NotOnOrAfter="2013-02-05T08:34:00Z" Recipient="https://www.google.com/a/my.uni.nl/acs" InResponseTo="kfcn...lfki"/> </SubjectConfirmation> </Subject> <Conditions NotBefore="2013-02-05T08:28:30Z" NotOnOrAfter="2013-02-05T08:34:00Z"> </Conditions> <AuthnStatement AuthnInstant="2013-02-05T08:29:00Z" SessionNotOnOrAfter="2013-02-05T16:29:00Z> </AuthnStatement> </Assertion> </Response>
0x02 XML签名攻击绕过SAML2.0单点登录
在通过SAML2.0登陆到网站时,会涉及到三种角色:服务提供者(SP,我们要访问的Web应用程序)、使用者(正在登录的用户)和身份提供者(IdP,认证管理)。
服务提供者,将使用者重定向到带有SAML请求的身份提供者的系统之中。一旦身份提供者确认使用者的身份,就会将SAML响应发送回服务提供者。对于Web单点登录来说,主要有三种通信方式,官方标准中将其称为“绑定”:
HTTP重定向绑定,是将SAML消息直接包含在URL中。 HTTP POST绑定,将SAML消息包含在POST请求之中。 HTTP工件绑定,我们发送一个随机的token,它将作为标识符,通过一个反向信道来获取文档。
响应包 截取其他博主的图 项目真实不方便截图
响应前面两种 第一种直接url参数中返回 第二种可以在post包中传递 这两种可控。
第三种工件绑定,我们发送一个随机的token,它将作为标识符,通过一个反向信道来获取文档。因为这些token会被解析为原始消息,随后通过一个反向信道获取,除非攻击者有权访问攻击目标的私有网络(可以借助SSL/TLS漏洞),否则这些token对于攻击者来说毫无用处。
从身份提供者获取到文档,将会通过用户的浏览器传递,从而验证身份。而正是在这一消息传输过程中,有可能被篡改。HTTP工件绑定就不存在这一安全问题。
如果这些消息缺少保护,攻击者可能会通过简单地修改返回的响应数据来伪装身份。例如,我以Tim的身份登录到身份提供者,随后我可以对响应文档进行修改,伪装成Emmanuel。更进一步,我们甚至可以伪造整个响应,真正意义上假装自己是Emmanuel。
SMAL标准制定者的解决方案是,为每一条消息附加一个XML签名,以防止消息被篡改。
在通常情况下,使用数字签名时,我们将文档签名,然后运行一个密码散列函数来计算出其哈希值,随后对该哈希值使用数字签名算法。如果收到的文档和原文档完全相同,那么签名有效。否则,即使只有一位,签名都存在改动,签名都会变为无效,并且文档也不会被承认。
XML签名有一个致命的特性:该标准允许我们可以对文档的一部分进行签名,而不是必须对整个文档进行签名,并将这个签名嵌入到应该验证的文档之中,即所谓的内联签名。
XML签名攻击绕过SAML2.0单点登录攻击利用
SAML Raider插件
签名可能会出现在SAML消息中的不同位置,并且覆盖消息的各个部分。如果我们保留原有消息内容,再增加新的内容,同时修改剩余部分的结构,我们就能构造出一个带有合法签名的新消息。
SAML标准要求,通过非安全信道(如用户的浏览器)传输的所有消息都必须进行数字签名。然而,如果消息是通过安全信道(如SSL/TLS反向通道)进行传输,那就可以不进行签名。由此我们发现,SAML的使用者会在签名存在时对其进行验证,并在签名被删除的情况下跳过验证。这一设计,是建立在我们已经检查了来自非安全信道的消息是否签名的前提之下,然而事实并非如此。
验证签名部分:
保存之后对此证书进行自签名,现在我们便有了一个相同证书的自签名副本。
声明的部分进行签名
自动:
手动测试:
1. 对Base64编码后内容进行解码,以获取SAML响应XML。
2. 检查签名的Reference标签是否包含被签名元素的ID。
3. 将签名的内容复制到文档中的其他位置(通常可以放在Response标签的末尾;如需验证XML Schema,则要放在不会破坏XML Schema的位置)。
4. 从副本中删除XML签名,并在原始版本中保留。这是有必要的,因为XML封装签名标准会删除将要被验证的签名。在原始文档中,这就是所包含的签名,所以我们必须将其从副本中删除。
5. 将原始签名元素的ID改为其他值(可以通过更改其中1个字母);
6. 修改原始声明的内容;
7. 对以上内容重新进行Base64编码,将其放回请求中并提交。
如果签名验证指向该副本,那么它将忽略我们所做的更改。在实际中,如果对请求有着严格的时间限制,我们应该可以很快地完成这一过程。
from:
https://www.jianshu.com/p/636c1ee16eba/
https://blog.csdn.net/qq877507054/article/details/88743076
https://www.cnblogs.com/lexiaofei/p/7172214.html
http://www.52bug.cn/hkjs/4173.html