├── .gitignore ├── README.md ├── gui.py ├── pppoe-intercept.spec └── pppoe_intercept.py /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | *.spec 4 | Pipfile* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## PPPoE-Intercept 2 | 3 | ## 喷 4 | 5 | 我开源的东西,这么快就有人商用了,我在群里看到一个 Crazy 的东西,很明显就是我这个东西,一模一样,加了个激活码机制开始卖了, 6 | 多的我不说了,只是希望大家广为流传,以后如果有时候我会更新的,但请你不要吵我,不能用了说一声即可,有时候有精力我自然会更新。 7 | 8 | 多说的,暂定一个交流群吧,462693866,就像我之前说的,请你不要一直吵着更新,不能用了说一声即可,有时候有精力我自然会更新。 9 | 10 | ## exe和用法 11 | 12 | https://github.com/akkuman/pppoe-intercept/releases 13 | 14 | 15 | 电脑上先安装 winpcap 16 | 下载 pppoe-intercept.exe 后直接双击运行,选择你要捕获的网卡,点击开始捕获,然后打开电脑客户端拨号即可。 17 | 18 | ## 原理 19 | 20 | 自己实现了从 PPPoE 发现阶段到 PPPoE 会话阶段(LCP 和 IPCP) 21 | 22 | ### 效果 23 | 24 | 拦截 PPPoE 拨号过程中的账号密码 25 | 26 | 并欺骗客户端登录成功(拿湖北客户端做的测试,其他地区可能不太行,但是拦截应该通用) 27 | 28 | ### 使用 29 | 30 | 1. Python,我的本地环境是 3.x 31 | 32 | 2. 安装 winpcap,注意不要使用 npcap,存在不知名问题 33 | 34 | 3. 然后安装 scapy,目前采用 pip 安装的是 2.4.2,只支持 npcap,我们需要改为手动安装 35 | 36 | ``` 37 | git clone https://github.com/secdev/scapy.git 38 | cd scapy 39 | python setup.py install 40 | ``` 41 | 42 | 然后执行本项目中的 pppoe-intercept.py 即可 -------------------------------------------------------------------------------- /gui.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | ''' 4 | @author Akkuman 5 | @date 2019.6.12 6 | @update 2019.6.12 7 | ''' 8 | 9 | import tkinter as tk 10 | from tkinter import ttk 11 | import scapy.all as scapy 12 | import re 13 | import threading 14 | import pppoe_intercept 15 | 16 | def start_sniff(): 17 | def intercept_callback(username, password): 18 | ui_edtusername.set(username) 19 | ui_edtpassword.set(password) 20 | pppoe_intercept.CALLBACK = intercept_callback 21 | pppoe_intercept.MAC_ADDRESS = pppoe_intercept.get_mac_address() 22 | n = pppoe_intercept.PPPoEServer() 23 | n.start(iface=comboIface.get()) 24 | 25 | def fn_btnStartSniff(): 26 | start_sniff_thread = threading.Thread(target=start_sniff) 27 | start_sniff_thread.start() 28 | 29 | pattern = re.compile(r'\[(.+?)\]') 30 | 31 | app = tk.Tk() 32 | #app.geometry('500x300+500+200') 33 | # 标题 34 | app.title("PPPoE intercept----by:Akkuman") 35 | 36 | # 标签 37 | labelTop = tk.Label(app, text = "选择捕获网卡") 38 | labelTop.grid(column=0, row=0, columnspan=2) 39 | 40 | # 下拉框 41 | if_list = [pattern.search(str(scapy.ifaces.data[i])).group(1) for i in scapy.ifaces.data] 42 | 43 | comboIface = ttk.Combobox(app, width = 60, values = if_list) 44 | 45 | comboIface.grid(column=0, row=1, columnspan=2) 46 | for i in range(len(if_list)): 47 | if 'Realtek' in if_list[i]: 48 | comboIface.current(i) 49 | else: 50 | comboIface.current(0) 51 | 52 | # 按钮 53 | btnStartSniff = ttk.Button(app, text = "开始捕获", command = fn_btnStartSniff) 54 | btnStartSniff.grid(column=0, row=2, columnspan=2) 55 | 56 | # 文本输入框 57 | lbl_username = tk.Label(app, text = "账号") 58 | lbl_username.grid(column=0, row=3) 59 | 60 | ui_edtusername = tk.StringVar() 61 | edt_username = ttk.Entry(app, width=60, textvariable=ui_edtusername) 62 | edt_username.grid(column=1, row=3) 63 | 64 | lbl_password = tk.Label(app, text = "密码") 65 | lbl_password.grid(column=0, row=4) 66 | 67 | ui_edtpassword = tk.StringVar() 68 | edt_password = ttk.Entry(app, width=60, textvariable=ui_edtpassword) 69 | edt_password.grid(column=1, row=4) 70 | 71 | print(comboIface.current(), comboIface.get()) 72 | 73 | app.mainloop() -------------------------------------------------------------------------------- /pppoe-intercept.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python -*- 2 | 3 | block_cipher = None 4 | 5 | 6 | a = Analysis(['pppoe-intercept.py'], 7 | pathex=['C:\\Users\\Akkuman\\gitrepo\\pppoe-intercept'], 8 | binaries=[], 9 | datas=[], 10 | hiddenimports=[], 11 | hookspath=[], 12 | runtime_hooks=[], 13 | excludes=[], 14 | win_no_prefer_redirects=False, 15 | win_private_assemblies=False, 16 | cipher=block_cipher, 17 | noarchive=False) 18 | pyz = PYZ(a.pure, a.zipped_data, 19 | cipher=block_cipher) 20 | exe = EXE(pyz, 21 | a.scripts, 22 | a.binaries, 23 | a.zipfiles, 24 | a.datas, 25 | [], 26 | name='pppoe-intercept', 27 | debug=False, 28 | bootloader_ignore_signals=False, 29 | strip=False, 30 | upx=False, 31 | runtime_tmpdir=None, 32 | console=True ) 33 | -------------------------------------------------------------------------------- /pppoe_intercept.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | ''' 4 | @author Akkuman 5 | @date 2019.5.11 6 | @update 2019.6.12 7 | ''' 8 | 9 | import struct 10 | import random 11 | import copy 12 | import binascii 13 | import socket 14 | 15 | from scapy.layers.ppp import * 16 | import scapy.all as scapy 17 | 18 | # 获取到账号密码后的回调函数 19 | CALLBACK = None 20 | 21 | MAC_ADDRESS = "0a:0a:0a:0a:0a:0a" 22 | # 会话 id,可自定义 23 | SESSION_ID = 0x0005 24 | 25 | # LCP Options Type 26 | TYPE_MAX_RECEIVE_UNIT = 0x01 27 | TYPE_MAGIC_NUM = 0x05 28 | 29 | 30 | # 获取一个随机的 mac 地址 31 | def get_mac_address(): 32 | maclist = [] 33 | for i in range(6): 34 | randstr = "".join(random.sample("0123456789abcdef",2)) 35 | maclist.append(randstr) 36 | return ":".join(maclist) 37 | 38 | # 获取本机物理网卡 ip 地址的 bytes 值 39 | def get_host_ip_bytes(): 40 | try: 41 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 42 | s.connect(('8.8.8.8', 80)) 43 | ip = s.getsockname()[0] 44 | finally: 45 | s.close() 46 | 47 | bytes_ip = b'' 48 | for i in ip.split('.'): 49 | bytes_ip += struct.pack('!B', int(i)) 50 | 51 | return bytes_ip 52 | 53 | class PPPoEServer: 54 | filter = "pppoed or pppoes" 55 | 56 | def __init__(self): 57 | self.ipaddr_bytes = get_host_ip_bytes() 58 | self.magic_num = b'\x25\x5f\xc5\xcb' 59 | self.username = None 60 | self.password = None 61 | 62 | # 开始监听 63 | def start(self, iface=None): 64 | if iface: 65 | scapy.sniff(iface=iface, filter=self.filter, prn=self.filterData) 66 | else: 67 | scapy.sniff(filter=self.filter, prn=self.filterData) 68 | 69 | # 过滤pppoe数据 70 | def filterData(self, pkt): 71 | if hasattr(pkt, "type"): 72 | _type2Method = { 73 | #发现阶段 74 | 0x8863: { 75 | "code": { 76 | # PADI 77 | 0x09: self.send_pado_packet, 78 | # PADR 79 | 0x19: self.send_pads_packet 80 | } 81 | }, 82 | #会话阶段 83 | 0x8864:{ 84 | "proto":{ 85 | # LCP 链路处理 86 | 0xc021: self.handle_lcp, 87 | # PAP 协议处理 88 | 0xc023: self.handle_pap, 89 | # IPCP 协议处理 90 | 0x8021: self.handle_ipcp 91 | } 92 | } 93 | } 94 | if pkt.type in _type2Method: 95 | _nMethod = _type2Method[pkt.type] 96 | for k, v in _nMethod.items(): 97 | _nVal = getattr(pkt, k) 98 | if _nVal in _nMethod[k]: 99 | handle_func = _nMethod[k][_nVal] 100 | handle_func(pkt) 101 | 102 | #处理 PPP LCP 请求 103 | def handle_lcp(self, pkt): 104 | # 处理 LCP-Configuration-Req 请求 105 | if bytes(pkt.payload)[8] == 0x01: 106 | print("收到 LCP-Config-Req") 107 | # 当 Req 请求里面包含除 MRU 和魔术数以及 AuthProto 之外的字段时,返回 Rej 108 | if len(pkt.payload.options) > 3: 109 | print("发送 LCP-Config-Rej 包") 110 | self.send_lcp_reject_packet(pkt) 111 | print("发送 LCP-Config-Req 包") 112 | self.send_lcp_req_packet(pkt) 113 | else: 114 | print("发送 LCP-Config-Ack 包") 115 | self.send_lcp_ack_packet(pkt) 116 | # 处理 LCP-Configuration-Rej 请求 117 | elif bytes(pkt.payload)[8] == 0x04: 118 | print("收到 LCP-Config-Rej") 119 | print("发送 LCP-Config-Req 包") 120 | self.send_lcp_req_packet(pkt) 121 | # 处理 LCP-Configuration-Ack 请求 122 | elif bytes(pkt.payload)[8] == 0x02: 123 | print("收到 LCP-Config-Ack") 124 | else: 125 | pass 126 | 127 | # IPCP 协议处理 128 | def handle_ipcp(self, pkt): 129 | payload = bytes(pkt.payload) 130 | # 处理 IPCP-Configuration-Req 请求 131 | if payload[8] == 0x01: 132 | # 当 Req 请求的 options 有 ip dns 之外的字段,发送 rej 133 | if len(pkt.payload.options) > 3: 134 | self.send_ipcp_rej_packet(pkt) 135 | # 当 Req 请求的 ip 以 0 开头时(0.0.0.0),发送 nak 开始分配 ip 136 | elif payload[14] == 0x00: 137 | print('IPCP Nak 开始分配 ip') 138 | self.send_ipcp_nak_packet(pkt) 139 | else: 140 | print('IPCP Ack 确认分配 ip') 141 | self.send_ipcp_ack_packet(pkt) 142 | 143 | # 发送 IPCP-Configuration-Rej 144 | def send_ipcp_rej_packet(self, pkt): 145 | code = 0x04 146 | identifier = bytes(pkt.payload)[9] 147 | options = b'\x82\x06\x00\x00\x00\x00\x84\x06\x00\x00\x00\x00' 148 | length = len(options) + 4 149 | _payload = struct.pack('!BBH', code, identifier, length) + options 150 | _pkt = Ether(src=pkt.dst, dst=pkt.src, type=0x8864) / PPPoED(version=1, type=1, code=0x00, sessionid=SESSION_ID) / PPP(proto=0x8021) / _payload 151 | scapy.sendp(_pkt) 152 | 153 | # 发送 IPCP-Configuration-Nak 准备分配 ip 154 | def send_ipcp_nak_packet(self, pkt): 155 | code = 0x03 156 | identifier = bytes(pkt.payload)[9] 157 | options = b'\x03\x06' + self.ipaddr_bytes + b'\x81\x06\xca\x67\x2c\x96' + b'\x83\x06\xca\x67\x18\x44' 158 | length = len(options) + 4 159 | _payload = struct.pack('!BBH', code, identifier, length) + options 160 | _pkt = Ether(src=pkt.dst, dst=pkt.src, type=0x8864) / PPPoED(version=1, type=1, code=0x00, sessionid=SESSION_ID) / PPP(proto=0x8021) / _payload 161 | scapy.sendp(_pkt) 162 | 163 | # 发送 IPCP-Configuration-Ack 确认分配 ip 164 | def send_ipcp_ack_packet(self, pkt): 165 | code = 0x02 166 | identifier = bytes(pkt.payload)[9] 167 | options = b'\x03\x06' + self.ipaddr_bytes + b'\x81\x06\xca\x67\x2c\x96' + b'\x83\x06\xca\x67\x18\x44' 168 | ipcp_len = len(options) + 4 169 | _payload = struct.pack('!BBH', code, identifier, ipcp_len) + options 170 | _pkt = Ether(src=pkt.dst, dst=pkt.src, type=0x8864) / PPPoED(version=1, type=1, code=0x00, sessionid=SESSION_ID) / PPP(proto=0x8021) / _payload 171 | scapy.sendp(_pkt) 172 | 173 | # 解析pap账号密码 174 | def handle_pap(self, pkt): 175 | # pap-req 176 | _payLoad = bytes(pkt.payload) 177 | if _payLoad[8] == 0x01: 178 | print("获取账号信息...") 179 | _nUserLen = int(_payLoad[12]) 180 | _nPassLen = int(_payLoad[13 + _nUserLen]) 181 | _userName = _payLoad[13:13 + _nUserLen] 182 | _passWord = _payLoad[14 + _nUserLen:14 + _nUserLen + _nPassLen] 183 | self.username = _userName.decode('utf-8') 184 | self.password = _passWord.decode('utf-8') 185 | if CALLBACK: 186 | print("回调") 187 | CALLBACK(self.username, self.password) 188 | print("账户: %s\n密码: %s" % (self.username, self.password)) 189 | # 拒绝认证 190 | #self.send_pap_authreject(pkt) 191 | #self.send_lcp_end_packet(pkt) 192 | # 通过认证 193 | self.send_pap_authack(pkt) 194 | 195 | print("欺骗完毕....") 196 | 197 | # 发送 PAP 通过认证 198 | def send_pap_authack(self, pkt): 199 | code = 0x02 200 | identifier = bytes(pkt.payload)[9] 201 | message = '0;User(%s) Authenticate OK, Request Accept by hb.cn' % self.username[7:-5] 202 | message_len = len(message) 203 | pap_len = message_len + 5 204 | _payload = struct.pack('!BBHB', code, identifier, pap_len, message_len) + message.encode() 205 | _pkt = Ether(src=pkt.dst, dst=pkt.src, type=0x8864) / PPPoED(version=1, type=1, code=0x00, sessionid=SESSION_ID) / PPP(proto=0xc023) / _payload 206 | scapy.sendp(_pkt) 207 | 208 | # 发送 PAP 拒绝验证 209 | def send_pap_authreject(self, pkt): 210 | _payload = b'\x03' + bytes(pkt.payload)[9:10] + b'\x00\x06\x01\x00' 211 | _pkt = Ether(src=pkt.dst, dst=pkt.src, type=0x8864) / PPPoED(version=1, type=1, code=0x00, sessionid=SESSION_ID) / PPP(proto=0xc023) / _payload 212 | scapy.sendp(_pkt) 213 | 214 | # 发送lcp-config-ack回执包 215 | def send_lcp_ack_packet(self, pkt): 216 | _payload = bytes(pkt.payload)[:8] + b'\x02' + bytes(pkt.payload)[9:] 217 | _pkt = Ether(src=pkt.dst, dst=pkt.src, type=0x8864) / _payload 218 | scapy.sendp(_pkt) 219 | 220 | #发送lcp-config-reject回执包 221 | def send_lcp_reject_packet(self, pkt): 222 | code = 0x04 223 | identifier = bytes(pkt.payload)[9] 224 | options = bytes(pkt.payload)[22:] 225 | length = len(options) + 4 226 | _payload = struct.pack('!BBH', code, identifier, length) + options 227 | _pkt = Ether(src=pkt.dst, dst=pkt.src, type=0x8864) / PPPoED(version=1, type=1, code=0x00, sessionid=SESSION_ID) / PPP(proto=0xc021) / _payload 228 | scapy.sendp(_pkt) 229 | 230 | #发送 lcp-config-req 请求 231 | def send_lcp_req_packet(self, pkt): 232 | code = 0x01 233 | identifier = 0x01 234 | _opt_MRU = b'\x01\x04\x05\xd4' 235 | _opt_auth_proto = b'\x03\x04\xc0\x23' # 服务端声明使用PAP认证 236 | _opt_magic_num = b'\x05\x06\x5e\x63\x0a\xb8' 237 | options = _opt_MRU + _opt_auth_proto + _opt_magic_num 238 | length = len(options) + 4 239 | _payload = struct.pack('!BBH', code, identifier, length) + options 240 | _pkt = Ether(src=pkt.dst, dst=pkt.src, type=0x8864) / PPPoED(version=1, type=1, code=0x00, sessionid=SESSION_ID) / PPP(proto=0xc021) / _payload 241 | scapy.sendp(_pkt) 242 | 243 | # 发送 lcp-echo-req包 244 | def send_lcp_echo_request(self, pkt): 245 | _payload = b'\x09\x00\x00\x08' + b'\x25\x5f\xc5\xcb' 246 | lcp_req = Ether(src=pkt.dst, dst=pkt.src, type=0x8864) / PPPoED(version=1, type=1, code=0x00, sessionid=SESSION_ID) / PPP(proto=0xc021) / _payload 247 | scapy.sendp(lcp_req) 248 | 249 | #发送lcp-termination会话终止包 250 | def send_lcp_end_packet(self, pkt): 251 | _payload = b'\x05\x02\x00\x04' 252 | _pkt = Ether(src=pkt.dst, dst=pkt.src, type=0x8864) / PPPoE(version=0x1, type=0x1, code=0x00, sessionid=SESSION_ID) / PPP(proto=0xc021) / _payload 253 | scapy.sendp(_pkt) 254 | 255 | #发送PADS回执包 256 | def send_pads_packet(self, pkt): 257 | print("PADR阶段开始,发送PADS...") 258 | #寻找客户端的Host_Uniq 259 | _host_Uniq = self.padi_find_hostuniq(pkt.payload) 260 | _payload = b'\x01\x01\x00\x00' 261 | if _host_Uniq: 262 | _payload += _host_Uniq 263 | 264 | pkt.sessionid = SESSION_ID 265 | sendpkt = Ether(src=MAC_ADDRESS, dst=pkt.src, type=0x8863) / PPPoED(version=1, type=1, code=0x65, sessionid=pkt.sessionid, len=len(_payload)) / _payload 266 | scapy.sendp(sendpkt) 267 | 268 | #发送PADO回执包 269 | def send_pado_packet(self, pkt): 270 | print("PADI阶段开始,发送PADO...") 271 | # 寻找客户端的Host_Uniq 272 | _host_Uniq = self.padi_find_hostuniq(pkt.payload) 273 | _payload = b'\x01\x02\x00\x07akkuman\x01\x01\x00\x00' 274 | if _host_Uniq: 275 | _payload += _host_Uniq 276 | # PADO 回执包的 sessoinid 为 0x0000 277 | pkt.sessionid = getattr(pkt, 'sessionid', 0x0000) 278 | sendpkt = Ether(src=MAC_ADDRESS, dst=pkt.src, type=0x8863) / PPPoED(version=1, type=1, code=0x07, sessionid=pkt.sessionid, len=len(_payload)) / _payload 279 | scapy.sendp(sendpkt) 280 | 281 | 282 | #寻找客户端发送的Host-Uniq 283 | def padi_find_hostuniq(self, payload): 284 | _key = b'\x01\x03' 285 | payload = bytes(payload) 286 | if _key in payload: 287 | _nIdx = payload.index(_key) 288 | _nLen = struct.unpack("!H", payload[_nIdx + 2:_nIdx + 4])[0] 289 | _nData = payload[_nIdx + 2:_nIdx + 4 + _nLen] 290 | return _key + _nData 291 | return 292 | 293 | 294 | if __name__ == "__main__": 295 | MAC_ADDRESS = get_mac_address() 296 | n = PPPoEServer() 297 | n.start() 298 | --------------------------------------------------------------------------------