前一阵子遇到了一个NTLM认证的问题,解决问题的同时就顺带了解了一下NTLM的认证过程。
顺便也把我遇到的问题写出来。
正常情况下NTLM的认证过程:
1: C –> S GET/POST …
2: C <– S 401 Unauthorized
WWW-Authenticate: NTLM
3: C –> S GET/POST …
Authorization: NTLM
4: C <– S 401 Unauthorized
WWW-Authenticate: NTLM
5: C –> S GET/POST …
Authorization: NTLM
6: C <—S Ok
以下以一台PC直接登陆,且成功登陆为例:
1: C –> S POST …
客户端发请求访问
2: C <– S 401 Unauthorized
WWW-Authenticate: NTLM
服务器拒绝,返回401.2,提示不可匿名访问,需要认证,并声明是NTLM认证
3: C –> S POST …
Authorization: NTLM
客户端将自己的NTLM代码发给服务器,其中代码中包含加密的用户名和密码
4: C <– S 401 Unauthorized
WWW-Authenticate: NTLM
服务器返回401.1,发起Chanllenge,使用保存在服务器端的用户名密码生成加密的代码发给客户端,请求客户端解析
5: C –> S GET …
Authorization: NTLM
客户端使用服务器发起的Chanllenge代码与自己的认证信息进行回应(如果回应正确则认证通过)
6: C <– S Ok
认证通过,服务器返回302跳转,页面跳转至网站内部,整个认证过程结束。
按照服务器的提示,整个流程为:
1: C –> S POST …
2: C <– S 401.2 Unauthorized
WWW-Authenticate: NTLM
3: C –> S POST…
Authorization: NTLM
4: C <– S 401.1 Unauthorized
WWW-Authenticate: NTLM
5: C –> S POST…
Authorization: NTLM
6: C <—S 302
我在工作中遇到的问题是,过代理之后无法通过服务器的NTLM认证。
也就是说,我在PC上配置了一个代理,通过代理访问一个需要NTLM认证的网站。
首先,按照正常情况下NTLM的认证过程模板
1: C –> S GET …
2: C <– S 401 Unauthorized
WWW-Authenticate: NTLM
3: C –> S GET …
Authorization: NTLM
4: C <– S 401 Unauthorized
WWW-Authenticate: NTLM
5: C –> S GET …
Authorization: NTLM
6: C <– S Ok(出错就在这一步)
从抓包分析(以下仅分析从代理到服务器的数据包,未分析PC到代理的数据包):
1: C –> S GET …
客户端发请求访问
2: C <– S 401 Unauthorized
WWW-Authenticate: NTLM
服务器拒绝,返回401.2,提示不可匿名访问,需要认证,并声明是NTLM认证
3: C –> S GET …
Authorization: NTLM
客户端将自己的NTLM代码发给服务器,其中代码中包含加密的用户名和密码
4: C <– S 401 Unauthorized
WWW-Authenticate: NTLM
服务器返回401.1,发起Chanllenge,使用保存在服务器端的用户名密码生成加密的代码发给客户端,请求客户端解析
5: C –> S GET …
Authorization: NTLM
客户端使用服务器发起的Chanllenge代码与自己的认证信息进行回应(如果回应正确则认证通过)
6: C <– S
认证失败!
整个流程,直通和代理都没有区别。
唯一的几点区别以及NTLM的特性:
1、 网上的资料说,NTLM认证的关键在于“Connection: Keep-Alive”。每次会话生成的认证信息都是不同的。
即:第三步,客户端生成的均为“NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAFASgKAAAADw==”,每次都相同
服务器则是:
“TlRMTVNTUAACAAAAEAAQADgAAAAFgomi2NVi54k850UAAAAAAAAAAIoAigBIAAAABQLODgAAAA9DAE4ATwBPAEMARwBBAFMAAgAQAEMATgBPAE8AQwBHAEEAUwABAAwATQBPAFMAUwAtADEABAAYAGMAbgBvAG8AYwBnAGEAcwAuAGMAbwBtAAMAJgBNAE8AUwBTAC0AMQAuAGMAbgBvAG8AYwBnAGEAcwAuAGMAbwBtAAUAGABjAG4AbwBvAGMAZwBhAHMALgBjAG8AbQAAAAAA”
“TlRMTVNTUAACAAAAEAAQADgAAAAFgomiEMrdcmTWLkwAAAAAAAAAAIoAigBIAAAABQLODgAAAA9DAE4ATwBPAEMARwBBAFMAAgAQAEMATgBPAE8AQwBHAEEAUwABAAwATQBPAFMAUwAtADEABAAYAGMAbgBvAG8AYwBnAGEAcwAuAGMAbwBtAAMAJgBNAE8AUwBTAC0AMQAuAGMAbgBvAG8AYwBnAGEAcwAuAGMAbwBtAAUAGABjAG4AbwBvAGMAZwBhAHMALgBjAG8AbQAAAAAA”
“TlRMTVNTUAACAAAAEAAQADgAAAAFgomieRl6vsRLLukAAAAAAAAAAIoAigBIAAAABQLODgAAAA9DAE4ATwBPAEMARwBBAFMAAgAQAEMATgBPAE8AQwBHAEEAUwABAAwATQBPAFMAUwAtADEABAAYAGMAbgBvAG8AYwBnAGEAcwAuAGMAbwBtAAMAJgBNAE8AUwBTAC0AMQAuAGMAbgBvAG8AYwBnAGEAcwAuAGMAbwBtAAUAGABjAG4AbwBvAGMAZwBhAHMALgBjAG8AbQAAAAAA”
每次都不同,也就是说不同的会话有不同的认证信息。(微软的认证系统靠抓包回放是攻不破了)
2、 不经过代理的时候,整个认证过程只有一条连接:
这条连接一直持续到整个认证过程结束都没有断。
而经过代理时,整个过程被分成了很多条连接:
每一条连接都仅完成一次POST,即12、34、56。整个流程从正常的一段,被分解成了3段。
虽然我们的请求里都包含“keep-alive”(不过是小写的!),但是每条连接之后都被reset了
分析:
首先由于NTLM的认证需要Keep-Alive,所以如果在第4步被reset了,就会导致第四步由服务器生成的WWW-Authenticate失效,这样第五步发出的Authorization就是过期的,进而导致认证失败。
那么,代理的会话被reset就是罪魁祸首,如果代理能和pc直通一样,与服务器保持Keep-Alive,问题就能解决。
检查了代理引擎关于NTLM的代码,发现这个代理引擎在访问需要NTLM认证服务器的时候,会自己把链接断开,也就是说上面的分析是正确的。
改写了代码让代理引擎保证Keep-Alive,问题修复。
参考资料:http://www.blogjava.net/security/archive/2008/11/18/38717.html
与本文不同的是,资料上都是GET,本文都是POST。不影响分析逻辑。
8 comments
Skip to comment form ↓
龙
2016 年 12 月 27 日 在 下午 5:56 (UTC 8) Link to this comment
兄弟,你第3步:3: C –> S POST … 描述的有问题啊,(这一步是不会将域用户密码等信息加密发给服务端的) 这一步发送的只是一个BASE64的字符串,服务端接受到这个字符串后,会回发一个code给客户端,这是第四步,客户端接收到服务端回传的这个code后再用code和用户名密码等加密再次发送给服务端,最后服务端会用接受到的信息进行校验是否正确。(校验的操作是服务端去完成的,你说的在客户端校验也是有问题的)
石樱灯笼
2016 年 12 月 27 日 在 下午 8:43 (UTC 8) Link to this comment
嗯,你说得对,第三步那里描述是有问题,服务端其实给的就是半块盐。另外我没找到你说的“在客户端校验”是哪一段。
龙
2016 年 12 月 27 日 在 下午 9:02 (UTC 8) Link to this comment
额,文章里的这段:“客户端使用服务器发起的Chanllenge代码与自己的认证信息进行回应(如果回应正确则认证通过)”
石樱灯笼
2016 年 12 月 27 日 在 下午 9:04 (UTC 8) Link to this comment
这句哪里有问题了?
龙
2016 年 12 月 27 日 在 下午 9:08 (UTC 8) Link to this comment
应该是客户端接收到服务端回发过来的质询码,客户端接收到该code后,使用该code加密用户信息,发送给服务端,服务端进行校验,并不是在客户端进行校验的,校验通过了返回请求的资源给客户端。
jeffer
2017 年 4 月 13 日 在 上午 11:40 (UTC 8) Link to this comment
你好,请问你用的是什么代理软件?
我在工作中也遇到这个问题。通过nginx代理访问需要ntlm认证的网站,每个请求都要输入用户名和密码。
请问有好的办法解决吗?
石樱灯笼
2017 年 4 月 13 日 在 下午 5:05 (UTC 8) Link to this comment
公司自己开发的
yadsun
2017 年 7 月 26 日 在 下午 1:11 (UTC 8) Link to this comment
同问,兄弟问题解决了吗