当前位置: 首页 > news >正文

对中兴光猫zteOnu.exe项目的简单分析(提供下载地址)

0.前言

在二手市场买了一个光猫,但是通过恢复出厂设置,管理员账号并不是默认密码,是没办法获取到之前运营商设置的随机密码,因为按reast只能恢复用户设置内容,最后通过工具恢复,感谢这位恩山论坛朋友帮我恢复出厂设置。

3550387095  https://www.right.com.cn/forum/?815290

光猫是G7615V2电信江苏地区的(管理员密码是随机的),于是对这个zteOnu工具感兴趣,同时感谢开源作者的贡献。

1.项目地址

GitHub - Septrum101/zteOnu: A tool that can open ZTE onu device factory mode

下载软件:Releases · Septrum101/zteOnu


2.实现原理

猜测项目原始来源:应该有人反编译过中兴某一个专门用来进行出产设置的机器,是用来调试中兴光猫的。

这个工具的实现的具体流程如下(AI画的):

┌──────────────┐     HTTP (TCP 8080)      ┌──────────────┐
│  本机脚本     │ ←——————————————→ │   光猫 Web    │
└────┬─────────┘                       └────┬─────────┘│                                        ││ 1. 激活工厂接口                         ││ 2. 请求 FactoryMode                     ││ 3. 拿随机数→算密钥                      ││ 4. 加密发送账号密码                     ││ 5. 拿到临时 Telnet 账号/密码              │├─────── 以下走 Telnet (TCP 23) ─────────┤│ 6. 登录临时 shell                       ││ 7. 5 条 sendcmd 改写 TelnetCfg 表        ││ 8. reboot                               ││                                        ▼│                              Flash 中的 db 被固化│ 9. 重启完成 → Telnet 23 端口永久开放

中兴光猫内部其实是预留了一个工厂接口用来开启临时的Telnet.

这个接口只能允许特定的网口mac申请访问,也相当一个密码验证,写死在机器里面了,

这个MAC地址是:00:07:29:55:35:57 

不排除还有其他的MAC地址也可以通过验证,希望有能力大佬反编译中兴机器的固件,看看代码怎么写的

这里说明一下,目前大部分光猫是运行的liunx系统,一般系统调试会通过Telnet进行无线设置,比如文件删除移动,调用系统工具等,并且Telnet通过账号,密码访问liunx主机,这个Telnet在liunx是权限大小的,好比Win shell.


这里以中兴默认路由器192.168.1.1地址80端口为例子

首先进入重置工厂模式 (reset),POST方式访问地址http://192.168.1.1:80/webFac

Body内容是 SendSq.gch

然后请求进入工厂模式,POST方式访问地址http://192.168.1.1:80/webFac

Body内容是 RequestFactoryMode.gch

再获取随机数 & 密钥索引,POST方式访问地址http://192.168.1.1:80/webFac

Body内容是SendSq.gch?rand=3

发rand随机数,获取到后,有点复杂,最后通过一个特定解密函数获取到一个密钥

获取交流密钥后,确定接口固件版本,比如version61

中间判断有点多就不写了

然后登录工厂模式

于是访问http://192.168.1.1:80/CheckLoginAuth.gch?&version61&user=telecomadmin&pass=nE7jA%5m

然后会得到相应响应判断是否成功

关键一步获取临时的Telnet账号密码

访问http://192.168.1.1:80/FactoryMode.gch?mode=2&user=notused

然后光猫会返回:

FactoryMode.gch?user=临时的Telnet账号&pass=临时的Telnet密码

后面就可以通过这个账号登录Telnet,然后重新设置,开启Telnet。


3.使用命令

zteOnu.exe --telnet --user 运营商(默认)超级账号 --pass 运营商(默认)超级密码 --ip 192.168.1.1 --port 80


4.Python版本(AI写的没有测试过)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
ZTE ONU Permanent Telnet Enabler – Python Port
Author : github.com/thank243  (Go) → Python by <your-name>
Usage  : python zte_telnet.py -i 192.168.1.1 -u telecomadmin -p nE7jA%5m --telnet
"""import argparse
import base64
import binascii
import socket
import time
import struct
from typing import Tuple, Optionalimport requests
from Crypto.Cipher import AES# ---------- AES-ECB ----------
BS = 16def pad(s: bytes) -> bytes:return s + b'\x00' * (BS - len(s) % BS)def aes_ecb_encrypt(data: bytes, key: bytes) -> bytes:cipher = AES.new(key, AES.MODE_ECB)return cipher.encrypt(pad(data))def aes_ecb_decrypt(data: bytes, key: bytes) -> bytes:cipher = AES.new(key, AES.MODE_ECB)return cipher.decrypt(data).rstrip(b'\x00')# ---------- 密钥池 ----------
AES_KEY_POOL = bytes.fromhex("7B56B0F7DA0E6852C819F32B849079E562F8EAD2649387DF73D7FBCCAAFE7543""1C29DF4C522C6E7B453D1FF1DEBC27858A4591BE3813DE673208541175F4D3B4""A4B312866723994C617FB1D230DF47F17693A38C95D359BF878EF3B3E4764988"
)AES_KEY_POOL_NEW = bytes.fromhex("8C2365D1FC32453711287163072069141473E7D453132436C2B5E1FCCF8A9A41""893C49CF5C728C9EEB750D3FD1FECC57657A35213E68537E9702487471953453""84B4C3E2D6273DE65D729CBC3D03FD76C19C25A89247E4180F243F4F67EC97F4"
)def get_key_pool(version: int, r: int, new_r: int) -> bytes:if version == 1:idx = rpool = AES_KEY_POOL[idx:idx + 24]else:idx = ((0x1000193 * r) & 0x3F ^ new_r) % 60pool = AES_KEY_POOL_NEW[idx:idx + 24]return bytes((b ^ 0xA5) & 0xFF for b in pool)# ---------- HTTP 交互 ----------
class FactoryClient:def __init__(self, ip: str, port: int, user: str, pwd: str):self.ip = ipself.port = portself.user = userself.pwd = pwdself.s = requests.Session()self.s.headers["User-Agent"] = "curl/8.8.0-DEV"self.base = f"http://{ip}:{port}"self.key = b''def reset(self):self.s.get(self.base + "/")resp = self.s.post(self.base + "/webFac", data="SendSq.gch")if resp.status_code == 400:returnresp.raise_for_status()def req_factory_mode(self):try:self.s.post(self.base + "/webFac", data="RequestFactoryMode.gch", timeout=3)except requests.exceptions.RequestException:passdef send_sq(self) -> Tuple[int, int]:r = int(time.time()) % 60resp = self.s.post(self.base + "/webFac",data=f"SendSq.gch?rand={r}\r\n")if resp.status_code != 200:raise RuntimeError(resp.text)if "newrand" in resp.text:version = 2new_r = int(resp.text.split("=")[1])self.key = get_key_pool(version, r, new_r)else:version = 1self.key = get_key_pool(version, r, 0)return version, rdef check_login_auth(self):cmd = f"CheckLoginAuth.gch?&version61&user={self.user}&pass={self.pwd}"payload = aes_ecb_encrypt(cmd.encode(), self.key)resp = self.s.post(self.base + "/webFacEntry", data=payload)if resp.status_code == 401:raise RuntimeError("错误的工厂用户名或密码")if resp.status_code != 200:raise RuntimeError(resp.text)aes_ecb_decrypt(resp.content, self.key)  # 仅校验解密是否成功def send_info(self):magic = base64.b64decode("AAAAAGAIAACTBwAAOggAALoAAACQBwAAxAcAAMoGAACVBAAATggAAM0BAAAnCA==")cmd = b"SendInfo.gch?info=12|" + magicpayload = aes_ecb_encrypt(cmd, self.key)resp = self.s.post(self.base + "/webFacEntry", data=payload)if resp.status_code == 401:raise RuntimeError("SendInfo 校验失败")resp.raise_for_status()def factory_mode(self) -> Tuple[str, str]:cmd = b"FactoryMode.gch?mode=2&user=notused"payload = aes_ecb_encrypt(cmd, self.key)resp = self.s.post(self.base + "/webFacEntry", data=payload)resp.raise_for_status()dec = aes_ecb_decrypt(resp.content, self.key)user, pwd = dec.decode().split("&user=")[1].split("&pass=")return user, pwddef full_flow(self) -> Tuple[str, str]:print("-" * 35)print("step [0] reset factory ... ", end="", flush=True)self.reset()print("ok")print("step [1] request factory mode ... ", end="", flush=True)self.req_factory_mode()print("ok")print("step [2] send sq ... ", end="", flush=True)ver, _ = self.send_sq()print("ok")print("step [3] login auth ... ", end="", flush=True)if ver == 2:self.send_info()self.check_login_auth()print("ok")print("step [4] enter factory mode ... ", end="", flush=True)u, p = self.factory_mode()print("ok")print("-" * 35)return u, p# ---------- Telnet ----------
class ZTETelnet:def __init__(self, ip: str, port: int, user: str, pwd: str):self.ip = ipself.port = portself.user = userself.pwd = pwdself.sock: Optional[socket.socket] = Nonedef open(self):self.sock = socket.create_connection((self.ip, self.port), 10)time.sleep(0.5)self.wait(b"login:")self.send(self.user.encode())self.wait(b"Password:")self.send(self.pwd.encode())self.wait(b"#")def wait(self, pat: bytes):buf = b''while pat not in buf:tmp = self.sock.recv(2048)if not tmp:raise RuntimeError("Telnet 断开")buf += tmpdef send(self, data: bytes):self.sock.sendall(data + b"\n")def perm(self):cmds = ["sendcmd 1 DB set TelnetCfg 0 Lan_Enable 1","sendcmd 1 DB set TelnetCfg 0 TSLan_UName root","sendcmd 1 DB set TelnetCfg 0 TSLan_UPwd Zte521","sendcmd 1 DB set TelnetCfg 0 Max_Con_Num 3","sendcmd 1 DB set TelnetCfg 0 InitSecLvl 3","sendcmd 1 DB save"]for c in cmds:self.send(c.encode())time.sleep(0.2)print("永久 Telnet 已写入")def reboot(self):self.send(b"reboot")print("设备正在重启 ...")def close(self):if self.sock:self.sock.close()# ---------- main ----------
def main():ap = argparse.ArgumentParser(description="ZTE ONU 永久开启 Telnet")ap.add_argument("-i", "--ip", default="192.168.1.1")ap.add_argument("-P", "--http-port", type=int, default=8080)ap.add_argument("-t", "--telnet-port", type=int, default=23)ap.add_argument("-u", "--user", default="telecomadmin")ap.add_argument("-p", "--pass", default="nE7jA%5m")ap.add_argument("--telnet", action="store_true", help="执行永久 Telnet 开启并重启")args = ap.parse_args()fac = FactoryClient(args.ip, args.http_port, args.user, args.pass)for retry in range(10):try:tl_user, tl_pass = fac.full_flow()breakexcept Exception as e:print(e, f" 重试 ({retry + 1}/10)")time.sleep(0.5)else:print("失败 10 次,退出")returnif not args.telnet:print(f"临时 Telnet 账号:\n  user: {tl_user}\n  pass: {tl_pass}")returnprint("登录临时 Telnet ...")tn = ZTETelnet(args.ip, args.telnet_port, tl_user, tl_pass)tn.open()tn.perm()tn.reboot()tn.close()print("永久 Telnet 已启用 →  root / Zte521")if __name__ == "__main__":main()

http://www.dtcms.com/a/532161.html

相关文章:

  • 有没有专门做中考卷子的网站网站建设培训要多久
  • 做网站图片用什么格式最好个人网站备案能做宣传用么
  • JAVA1026 方法;类:抽象类、抽象类继承;接口、接口继承 Linux:Mysql
  • 密码学系列 - 零知识证明(ZKP) - NTT与MSM的总结
  • 《解决界面交互痛点:WaterFlow 瀑布流、双层嵌套滚动、键盘避让与跨 Ability 跳转实践》
  • 网页设计建立站点步骤做网站的广告词
  • Spring Boot3零基础教程,生命周期启动加载机制,笔记64
  • StarGantt在线甘特图最新版本评测
  • 43.渗透-Kali Linux-工具-Ettercap(dns欺骗)
  • 详解C++中的迭代器
  • 区块链论文速读 CCF A--USENIX Security 2025(2)
  • 基于区块链的新能源管理平台的设计与实现(源码+文档)
  • 2025年10月26日 AI大事件
  • 企业建站个人建站源码沧州专业网站建设公司
  • 基于springboot的电影评论网站系统设计与实现
  • 1.2.1.3 大数据方法论与实践指南-一种跨团队业务结算方式探索
  • 西安地产网站建设百度手机管家
  • NoSQL 简介
  • C++ 分治 快速排序优化 三指针快排 力扣 面试题 17.14. 最小K个数 题解 每日一题
  • 网站建设业务员怎么做适合建设网站的国外服务器
  • 【第1章】基于FPGA的图像形态学处理学习教程——目录
  • stm32单片机命名规则,c6t6只有32KB,c8t6有64KB
  • 基于华为设备的 OSPF+MSTP+DHCP+NAT 综合网络架构实现
  • 使用uniapp——实现微信小程序的拖拽排序(vue3+ts)
  • 5.23基于 LabVIEW 的科学计算器设计
  • STM32 异常和中断
  • OWL 简介
  • 元器件网站建设案例网站建设专家联系方式
  • GStreamer实现屏幕录制(视频+麦克风音频)并编码成视频文件
  • asp网站开发教程pdf广安市建设局新网站