观海听涛
Waiting for you

对ppp的认证方式进行回溯攻击笔记

14 Oct 2012

我电脑上相关软件包的版本分别是:

pylibnet:3.0-beta

libnet:1.1.2.1

通过多次对ppp认证的抓包分析后,暂时得出了以下的结论:

要想成功欺骗到服务器,在伪造包的时候需要几个关键域,第一个就是ip头部中的Identification,在链中控制协议进行配置的过程中客户端每发一个包,此域就会加1。第二个就是客户端和服务器端的ip地址。

第三个是gre中标识一个客户端会话的callid值,这个值在整个会话过程中会一直保持,这样服务器在同时有多个客户端的时候就可以区分出来。这里有一点问题,通过抓包可以发现服务器的callid值经常会是0,但是有时候又不是。而目前在攻击程序中恰好是通过看抓到的gre包中callid是不是0来区分客户端和服务器端的,显然这里是需要改进的。

第四个是gre中的Sequence number,在整个连接过程中,客户端每发送一个包这个值就会增加1

以上就是需要抓包提取出的信息。

接下来就是构造一个LCP NAK的包发送给服务器了。

针对我们要把认证协议从MSCHAPV2回溯到MSCHAP,构造出来的ppp包应该如下图所示:

根据多次抓包发现这些值似乎都是固定的,所以构造的时候直接给定值就行了。

我们采用pylibnet中 的函数来构造包(它也是通过libnet来构造包的),pylibnet只提供了构造gre包的函数,并没有提供ppplcp包的构造函数。

这里很有必要记录一下在构造gre包的时候遇到的问题。

实际上微软用的gre协议并不是原生的,而是经过它自己改动过的,改动后的协议格式如图所示:

因此不应该调用buile_gre这个函数,经过查阅后发现应该调用的是build_egre这个函数,也即是构造加强版的gre

通过查看pylibnet的文档可以看到build_egre函数调用方法:

有趣的事情就在这里。通过查看pylibnet的源码可以看到这个函数的实现,里面有一句很关键的代码:

ptag = libnet_build_egre (fv, type, sum, offset, key, seq, len, payload, payload_s, self->l, ptag);

可以看出它是通过调用libnet里面的函数来进行真正的构造,pylibnet只不过做了一些类似包装的工作。

沿着这条踪迹找到libnet_build_egre()函数,以下是它的定义:

libnet_ptag_t

libnet_build_egre(u_int16_t fv, u_int16_t type, u_int16_t sum,

u_int16_t offset, u_int32_t key, u_int32_t seq, u_int16_t len,

u_int8_t *payload, u_int32_t payload_s, libnet_t *l, libnet_ptag_t ptag)

{

return (libnet_build_gre(fv, type, sum, offset, key, seq, len,

payload, payload_s, l, ptag));

}

这个函数似乎并没有做任何的工作,只是把收到的参数又给了 libnet_build_gre,继续看一下 libnet_build_gre的定义。里面有很多类似这样的判断:

注释v0一栏的意思是如果flaggre的版本是0sum位被置为1或者routing位被置为1则执行。

注释v1一栏的意思是如果flaggre的版本是1则执行。

在上面这段代码之前已经构造完成了fvtype这两个域。根据微软给出的GRE包的格式可以知道实际上这里是把sum写进了payload length这个地方,以此类推,offset被当成了call id看待,key被当

seqnum,seqnum当做ack看待。但pylibnet的文档却没有说明这些问题,带来了很多麻烦。个人认为不仅pylibnet文档有错,libnet也有过错。可以找到libnetgre的格式是这样定义的:

struct libnet_gre_hdr 

{

u_int16_t flags_ver;

#define GRE_CSUM 0x8000

#define GRE_ROUTING 0x4000

#define GRE_KEY 0x2000

#define GRE_SEQ 0x1000

#define GRE_STRICT 0x0800

#define GRE_REC 0x0700

#define GRE_ACK 0x0080

#define GRE_FLAGS_MASK 0x00F8

#define GRE_VERSION_MASK 0x0007

#define GRE_VERSION_0 0x0000

#define GRE_VERSION_1 0x0001

u_int16_t type;

#define GRE_SNA 0x0004

#define GRE_OSI_NETWORK_LAYER 0x00FE

#define GRE_PUP 0x0200

#define GRE_XNS 0x0600

#define GRE_IP 0x0800

#define GRE_CHAOS 0x0804

#define GRE_RFC_826_ARP 0x0806

#define GRE_FRAME_RELAY_ARP 0x0808

#define GRE_VINES 0x0BAD

#define GRE_VINES_ECHO 0x0BAE

#define GRE_VINES_LOOPBACK 0x0BAF

#define GRE_DECNET 0x6003

#define GRE_TRANSPARENT_ETHERNET_BRIDGING 0x6558

#define GRE_RAW_FRAME_RELAY 0x6559

#define GRE_APOLLO_DOMAIN 0x8019

#define GRE_ETHERTALK 0x809B

#define GRE_NOVELL_IPX 0x8137

#define GRE_RFC_1144_TCP_IP_COMPRESSION 0x876B

#define GRE_IP_AUTONOMOUS_SYSTEMS 0x876C

#define GRE_SECURE_DATA 0x876D

#define GRE_PPP 0x880b /* taken from RFC 2637 */

union {

struct {

u_int16_t sum; /* optional */

u_int16_t offset; /* optional */

u_int32_t key; /* optional */

u_int32_t seq; /* optional */

} _gre;

struct {

u_int16_t payload_s; /* optional */

u_int16_t callID; /* optional */

u_int32_t seq; /* optional */

u_int32_t ack; /* optional */

} _egre;

}_data;

#define gre_sum _data._gre.sum

#define gre_offset _data._gre.offset

#define gre_key _data._gre.key

#define gre_seq _data._gre.seq

#define egre_payload_s _data._egre.payload_s

#define egre_callID _data._egre.callID

#define egre_seq _data._egre.seq

#define egre_ack _data._egre.ack

};

可看到针对egre实际上它定义了一个直观的数据结构,但在libnet_build_egre中并没有使用。libnet_build_gre中也只涉及到了对_greV0)进行赋值,没有用到_egre(V1)。目前看来调用这个函数就相当于调用build_gre这个函数,build_egre没有任何作用。应该说libnetlibnet_build_egre这个函数中应该做一个改进的。

再看攻击的结果,根据测试,只要伪造的包比真正客户端回应的包早到达服务器,攻击一般都是成功的。这应该也可以算做它的一个漏洞,对于服务器的反悔,客户端并不做检查。