NTLM认证协议学习笔记(一)

拥有回忆 提交于 2020-04-09 20:32:32

NTLM认证协议学习笔记(一)

zhao_rong 2011-06-15  
这个文章放草稿箱里已经太多天了,工作太忙,没时间继续写,先发出来,有空点就写点吧。。。希望大家给意见   

   NTLM是一种保护在认证过程中保护用户名和密码的认证协议,一般用在Microsoft的产品中,比如IE,IIS等等,以前Windows登陆认证也是用这种认证协议,现在已经被Kerberos协议取代。现在其他的浏览器也支持NTLM认证,Firefox需要输入about:config,把network.auth.force-generic-ntlm设置为true,或者将信任网站加入到network.automatic-ntlm-auth-trusted-uris中, Firefox的相关设置可以google一下,一搜一大把;现在的chrome自己就支持ntlm,不需要额外设置,至少chrome11是这样。
   关于IE,需要看设置,工具>Internet选项>安全>自动定义级别,滚动框拉到最下面,用户登录有四个选项,一般都是选择在intranet中自动登录,这是正常的,一般只有内网站点的认证信息才会和你的用户名密码有关,我们现在为了学习实验,可以它设置为自动使用当前用户名和密码登录。 
  
   当使用浏览器访问一个URL时,获得401 response code的时候,会弹出对话框让用户输入用户名和密码,类似于下图所示:

  
    用JSP来做一个简单的,弹框认证的server来研究一下NTLM认证协议。首先看看不用NTLM认证协议的危害。

  我用netbeans(当然也可以用eclipse)新建一个web项目,项目名称为NTLM,新建一个过滤器,url pattern为\*,取名为AuthFilter,在 chain.doFilter(request, response); 语句之前,加入下面的代码:
HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpSession session = httpRequest.getSession();

        boolean unauthenticated = true;

        if (!httpRequest.getRequestURI().endsWith("login.jsp")) {
            if (session.getAttribute("username") == null || "".equals(session.getAttribute("username"))) {
                String authStr = httpRequest.getHeader("authorization");
                if (authStr != null) {
                    authStr = (authStr.split("\\s"))[1];
                    authStr = new String(new BASE64Decoder().decodeBuffer(authStr));
                    System.out.println(authStr);
                    String[] params = authStr.split(":");
                    if (params != null && params.length == 2) {
                        if ("rong_zhao".equals(params[0]) && "123456".equals(params[1])) {
                            System.out.println("pass, username=[" + params[0] + "], password=[" + params[1] + "]");
                            unauthenticated = false;
                        }
                    }
                }
            }
        }

        if (unauthenticated) {
            httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            httpResponse.setHeader("www-Authenticate", "Basic realm=\"rong.com\"");
            return;
        }
        else{
            session.setAttribute("username", "rong_zhao");
        }

   

这段待码是用Basic方式认证,加入这段代码以后,

访问http://localhost:8084/NTLM, 可以看到浏览器弹框,如果输入的用户名和密码是“rong_zhao” 和 "123456",那么就会通过认证,否则就会不停的弹框让你输入用户名和密码。

Basic认证方式是由代码:

httpResponse.setHeader("www-Authenticate", "Basic realm=\"rong.com\"");
代码确定的,后面realm指定域名,这个牵扯到LDAP相关知识,这里就不再罗嗦其他话题,我们可以直接忽略他,也可以不写它。

ok,这种方式的缺点就是用户名和密码等同于是明文传输的,因为他是Base64编码的,任何人抓包可以获得用户名和密码。如下图所示:


只要知道了这个base64编码的字符串就能直接得到用户名和密码,很明显,这是极度不安全的。

那么如果使用NTLM认证协议会怎么样呢?

httpResponse.setHeader("www-Authenticate", "Basic realm=\"rong.com\"");
写成
httpResponse.setHeader("www-Authenticate", "NTLM");
然后再运行,用IE访问http://localhost:8084/NTLM,抓包可以看到下面的数据包:
GET /NTLM/ HTTP/1.1

Host: localhost:8084

Connection: keep-alive

Cache-Control: max-age=0

User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.68 

Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5

Accept-Encoding: gzip,deflate,sdch

Accept-Language: en-US,en;q=0.8

Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

Cookie: JSESSIONID=154FDACF04E51C847E8B80AFD41E0841



HTTP/1.1 401 Unauthorized

Server: Apache-Coyote/1.1

www-Authenticate: NTLM

Content-Type: text/html;charset=utf-8

Content-Length: 954

Date: Fri, 10 Jun 2011 12:12:19 GMT

.......
.......
.......

Host: localhost:8084

Connection: keep-alive

Cache-Control: max-age=0

Authorization: NTLM NTLM TlRMTVNTUAABAAAAB7IIogkACQAzAAAACwALACgAAAAFAs4OAAAAD1JPTkctV0lOMkszV09SS0dST1VQ

User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.68 

Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5

Accept-Encoding: gzip,deflate,sdch

Accept-Language: en-US,en;q=0.8

Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

Cookie: JSESSIONID=154FDACF04E51C847E8B80AFD41E0841

HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
www-Authenticate: NTLM
Content-Type: text/html;charset=utf-8
Content-Length: 954
Date: Fri, 10 Jun 2011 12:12:19 GMT
    很容易看到第一次浏览器发送请求,server返回401并指出可以用NTLM认证协议,这时请注意,我们没有输入任何信息就看到了第二次请求,并且有如下一行:

Authorization: NTLM NTLM TlRMTVNTUAABAAAAB7IIogkACQAzAAAACwALACgAAAAFAs4OAAAAD1JPTkctV0lOMkszV09SS0dST1VQ
    这是Chrome看到server支持NTLM认证自动发送的,与Server进行NTLM协商,可以通过Base64解码来查看具体信息是什么,很幸运的是wireshark帮我们解析了,见下图:

    Base64解码对照着看看:
4E,54,4C,4D,53,53,50,00,01,00,00,00,07,B2,08,A2,09,00,09,00,33,00,00,00,0B,00,0B,00,28,00,00,00,
05,02,CE,0E,00,00,00,0F,52,4F,4E,47,2D,57,49,4E,32,4B,33,57,4F,52,4B,47,52,4F,55,50
    开始的八个字节表示“NTLMSSP”的ASCII码加上‘\0’字符,接着01,00,00,00表示第1次NTLM协商握手,为什么wireshark写的是0x00000001呢?因为这里NTLM规定使用小端表示整数(little-endian),这个应该是为了防止不同的系统,整数表示方法不同而带来不兼容的问题。
    接下来的'07B208A2',表示NTLM协商的flag设置,一共32个,首先它还是little-endian表示方法,应该像wireshark中那样看,是0xa208b207, 这32位每一位表示的意思如下:

1

Unicode协商

是否支持unicode字符串

2

OEM协商

是否支持OEM字符串,这里的OEM不是贴牌生产,是一种字符串表示形式,即字符串中的每一个字符用一个8bit的值表示,该值表示该机器code page上的一个字符,比如一般简体中文的windows的code page是936

3

Request Target

告诉服务器在第二次NTLM协商的时候要将认证域名发过来

4

未定义

未定义

5

Negotiate Sign

在已经通过认证的通信过程中,信息应该带上一个数字签名,以便验证信息的完整性

6

Negotiate Seal

在已经通过认证的通信过程中,信息应该被加密,以保证信息的保密性

7

Negotiate Datagram Style

是否使用Datagram认证,Datagram是一种NTLM协议使用的认证方式,主要意思是NTLM三次握手的过程中不使用一个连接,从而增加攻击难度

8

Negotiate Lan Manager Key

LAN Manager session key 是不是用在上面第56位规定的通信方式,就是说这个key是不是用做签名和加密的密钥

9

Negotiate Netware

未定义

10

Negotiate NTLM

是否正在用NTLM认证,这一位当然是1了,不然还算是NTLM认证吗?

11

未定义

未定义

12

未定义

未定义

13

Negotiate Domain Supplied

第一次NTLM协商握手时是不是带上client机器所在的范围(这个范围有可能是workgroup,有可能是域名)

14

Negotiate Workstation Supplied

第一次NTLM协商握手时是不是带上client机器的机器名

15

Negotiate Local Call

服务器向client说,你和我是一台机器,直接用本地的凭据吧,不要再协商了

16

Negotiate Always Sign

服务器和通过认证的通信是不是总是签名的,好吧,这个我也不知道具体啥意思。。。虽然字面意思很简单

17

Target Type Domain

服务器告诉clientclient发过来的范围(workgroup或者域名)是不是个域名

18

Target Type Server

第二次NTLM握手的时候,server告诉client,认证服务器是一台机器

19

Target Type Share

第二次NTLM握手的时候,server告诉client,认证服务器是一个共享服务,用途不明。。。一般都是用第17位,这两个基本可以忽略了

20

Negotiate NTLM2 Key

是否用NTLM2签名和加密scheme来保护认证通信

21

Request Init Response

未定义

22

Request Accept Response

未定义

23

Request Non-NT Session Key

未定义

24

Negotiate Target Info

第二次NTLM握手时,server告诉client,这次的消息体中是否包含target information, 这个target information包括域名,服务器名,DNS中域名和服务器名信息

25

未定义

未定义

26

未定义

未定义

27

未定义

未定义

28

未定义

未定义

29

未定义

未定义

30

Negotiate 128

是否支持128bit密钥的加密强度

31

Negotiate Key Exchange

Client在第三次握手时会提供一个加密的session key,这个密钥会用来做通过认证的通信签名和加密

32

Negotiate 56

是否支持56bit密钥的加密强度(其实也就是DES啦,一般都支持的)

    有兴趣的人可以把0xa208b207展开成二进制来对应上面说的每一位来看是什么意思,至少通过wireshark可以看到client提供了机器所在的工作组和机器名,所以第13,14位应该设为1。
    
    现在第2次握手,该我们写server端的代码,做第二次NTLM握手了。
    因为只是学习用,所以可以写个假的,响应,

    开始8个字节当然还是(16进制):4E,54,4C,4D,53,53,50,00

接着标识这是第二次握手(16进制):02,00,00,00



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!