轻量级扫描器LANs.py实战:网络渗透测试资产发现与端口扫描详解 先交代一下背景上个月我在做一次内网资产梳理项目需要从零开始盘点某个测试网段里所有在线主机、开放端口和服务类型。手头同时放了几个扫描工具想着正好做个横向对比结果折腾了一周得出的结论让我自己都有点意外——像 LANs.py 这种看起来功能不算花哨的轻量脚本工具在大多数网络渗透测试项目里反而成了首选工具。这篇文章不打算平铺直叙地罗列功能而是把我这几周实际跑的对比数据、踩过的坑、选型时纠结的点全部整理出来。适合正在做技术选型的人、刚接触网络渗透测试的初学者以及想把扫描任务塞进自动化流程里的运维同学。我尽量写得实在一点能直接照着用的东西多说套话少说。1. 设计思路LANs.py 到底解决了什么问题1.1 从“扫得全”到“看得懂”网络渗透测试的第一步永远不是漏洞利用而是资产发现。目标网段里有哪些主机在线每台主机开放了哪些端口端口后面跑的是什么服务这三个问题不搞清楚后面所有的测试动作都是盲人摸象。传统扫描器在这个阶段普遍存在一个尴尬结果特别全但全到让人不知道怎么用。动辄几万行的扫描输出里面夹杂着大量的状态信息、重传记录、服务猜测真正有价值的结论淹没在噪声里。你要是拿这些原始数据直接做报告光清洗就要花掉半天时间。LANs.py 最初就是冲着这个痛点去的。它的定位很明确不追求大而全而是把扫描结果做成“一眼能看懂、直接能入库”的结构化数据。扫完一个网段输出里就三块——在线主机列表、端口开放清单、服务指纹信息干净到可以直接交给下游流程处理。这种设计思路其实是很多工具容易忽略的。工具的本质是辅助决策不是制造新问题。一个扫描器如果产出的结果还需要写一堆脚本二次解析那它的价值就要打折扣。1.2 为什么“轻”反而是巨大优势我第一次接触 LANs.py 是在一个资源受限的测试环境里那台机器只有 512MB 内存跑传统重型扫描器直接卡死。换成 LANs.py 之后内存占用稳定在 35MB 上下扫完一个 /24 网段花了不到三分钟结果一点没缩水。这次经历让我重新认识了“轻量”这个词。很多团队在选型的时候喜欢盯着功能清单看插件越多越好、模块越全越好。但放到真实场景里情况完全不是这样。网络渗透测试经常会用到跳板机、专用测试机、临时搭建的审计环境这些机器的配置普遍不高而且往往不能联网装依赖。LANs.py 只依赖 Python 标准库和少量第三方包离线环境下用 pip 把轮子拷进去就能跑或者直接用打包工具打成单文件扔过去省掉一整套环境配置的麻烦。相比之下那些重型的商业扫描器光是装数据库、起服务、导插件就要折腾半天在临时环境里根本不现实。工具是拿来解决问题的不是拿来伺候的这一点在资源受限的环境里体会尤其深。1.3 功能边界与设计取舍LANs.py 的功能边界设计得很克制。它主动砍掉了漏洞利用、暴力破解、流量拦截这些“重型功能”专心做资产探测和指纹识别这两件基础事。这个取舍非常重要很多人不理解觉得功能少了就弱了。实际上把探测和利用耦合在同一个工具里在实际项目中是很大的风险。扫描阶段的动作应该是低噪音的、可控的、可审计的而利用阶段的动作恰恰相反噪音高、依赖环境、需要人工介入。两件事混在一起扫描器跑起来之后你都不知道它到底干了什么出了问题也很难定位。另外LANs.py 在处理速度上也做了取舍。它没有采用无状态扫描那种“一股脑发包”的策略而是保留了 TCP 三次握手的状态跟踪。代价是速度比无状态扫描慢一些但换来的是极低的误报率。这个权衡在真实项目里非常关键——速度慢一点可以接受但结果不准会让整个测试报告失去可信度。2. 横向对比几类扫描工具的真实差距2.1 对比维度怎么定工具对比最忌讳的就是只看某一个指标。有些工具快得离谱但误报一塌糊涂有些工具准确率很高但配置复杂到劝退。我这次对比从五个维度入手扫描速度、结果准确率、扩展性、产出格式、资源占用。这五个维度基本覆盖了选型时关心的所有问题。为了尽量公平所有工具都在同一台测试机上跑目标环境是某个模拟靶场的 /24 网段实际活跃主机 50 台其中有 8 台做了防火墙策略限制会对部分端口返回过滤状态。这个环境不是特别复杂但足够暴露出各工具在真实场景下的差异了。2.2 同场竞技的实测结果工具扫描耗时内存占用端口识别准确率产出格式上手难度某老牌重型扫描器8分20秒180MB96%文本/XML/HTML高需要熟悉命令某高速无状态扫描器42秒20MB81%文本中某图形化资产扫描工具6分30秒300MB92%图形界面导出低但难批量化LANs.py2分10秒35MB95%文本/JSON/表格低一条命令搞定这个表看下来很有意思。高速无状态扫描器最快但准确率只有 81%意味着 50 台主机里有接近 10 台的端口状态判断是有问题的。后来我逐条核对发现它把很多被防火墙静默丢弃的端口当成了开放状态这在真实渗透测试里是致命伤——你会按照错误的信息去做后续测试白费大量时间。老牌重型扫描器准确率确实高但八分多钟的耗时和接近两百兆的内存占用在临时环境里真的不太友好。图形化工具对新手友好可一旦要批量处理几十个网段点鼠标能点到手抽筋也不方便嵌进自动化流水线。LANs.py 在中间找到了一个很好的平衡点耗时只有重型扫描器的四分之一准确率只差一个百分点内存占用低到可以忽略输出格式又方便二次处理。这也是它在我这里胜出的核心原因。2.3 差距背后的原理分析为什么无状态扫描器会误报关键在于它不维护连接状态一个 SYN 包发出去只要收到响应就认为是开放端口。可问题是很多中间设备对未经许可的 SYN 包会直接丢包不返回任何 RST这时候扫描器端看到的是“无响应”。无状态扫描器为了压缩时间通常会把“无响应”粗暴地归类为“开放或过滤”结果就是大量误报。LANs.py 走的是完整 TCP 握手路线每个端口都建立真实连接连接成功就是开放收到 RST 就是关闭超时就是过滤。虽然每个端口的耗时稍微多了一点但结果精确得多特别是面对配置了防火墙策略的目标网络时这个差异会被无限放大。做技术选型要的不是某个指标的冠军而是综合表现最稳定的选手。LANs.py 在速度和准确率之间选择了准确率优先在功能全面性和可维护性之间选择了可维护性优先这两次选择让我确定它就是那个稳定选手。3. 实操过程如何用好 LANs.py 完成一次资产盘点3.1 环境准备与安装LANs.py 对运行环境的要求很低只要是 Python 3.8 以上版本就能跑。安装方式有两种一种是直接从包管理工具拉取另一种是下载源码后手动安装依赖。# 方式一直接用 pip 安装 pip install lans.py # 方式二源码安装适合离线环境 git clone https://example.org/lans.py.git cd lans.py pip install -r requirements.txt依赖列表非常简洁核心只有两个库一个负责并发控制一个负责数据格式化。没有数据库依赖没有插件框架没有 GUI 组件。这种克制的依赖设计让 LANs.py 在最小化安装的 Linux 系统里也能顺利跑起来。我实际测试过在只装了基础工具链的干净系统里从下载到跑通第一次扫描全程不超过五分钟。对比某些商业扫描器需要先部署服务端、再装客户端、还要配置证书的流程这个启动速度确实让人舒服。3.2 三条最常用的命令安装完成之后最基本的使用方法是直接指定目标地址。LANs.py 支持单个 IP、IP 段、域名三种输入方式基本覆盖了日常需求。# 单台主机全端口扫描适合精准摸底 python3 -m lans -t 192.0.2.10 -p 1-65535 # 扫描整个网段的常用端口适合快速盘点 python3 -m lans -t 192.0.2.0/24 -p 21,22,23,25,53,80,110,143,443,445,993,995,3306,3389,5432,6379,8080,8443 # 扫描并导出 JSON 格式结果适合对接自动化流程 python3 -m lans -t 192.0.2.0/24 -p 1-1024 --format json -o result.json第一条命令适合已经知道目标是谁、需要深挖的场景。第二条命令是日常资产盘点用得最多的覆盖了绝大多数常见服务端口。第三条命令专门给自动化流程用输出 JSON 之后拿脚本一解析直接灌进资产管理系统就行。这里有个经验第一次扫描不建议直接把并发拉满先用默认参数跑一遍看看目标网络的响应情况再决定要不要调优。上来就猛跑很容易触发目标设备的连接限制反而导致结果不准确。3.3 核心参数与调参思路LANs.py 的参数设计得比较少每个参数背后都有明确的考量。我把几个最常用的参数整理成了一个表格。参数默认值作用调参建议--timeout3秒单次连接超时时间内网可降到1秒跨网段建议保持默认--ports常用端口列表指定扫描端口根据业务类型裁剪缩小范围能显著提速--rate200并发连接数目标设备性能差时降到50性能好可以拉到500--no-ping关闭跳过主机存活探测目标禁 ICMP 时必须开启--formattext输出格式自动化场景选 json报告场景选 table--fingerprint开启服务指纹识别需要快速摸底时可关闭以节省时间超时时间这个参数最容易被人忽略。内网环境延迟低1 秒甚至 0.5 秒都够用但从外网扫目标时网络链路长、延迟高3 秒的默认值反而更稳。我之前有一次从远程网络扫内网目标把超时时间压到 1 秒结果大量端口因为延迟抖动被误判为超时白白浪费了一轮扫描。并发参数是性能调优的核心。理论上并发越高越快但实际会遇到两个制约一是目标设备的连接表容量有限并发太高会触发丢包二是中间防火墙会检测高频连接并直接封锁源 IP。我一般在不确定目标网络状况时先用 200观察一轮结果如果发现丢包率异常再逐步降低。3.4 与内部流程的集成示例LANs.py 一个比较实用的能力是输出格式可以定制。除了内置的 text、json、table 三种格式它还支持通过扩展接口接入自定义输出器。我这次做了一个简单的例子扫描完成后自动把结果写到数据库里同时推一条消息到消息机器人方便团队实时掌握资产管理状态。import json import sqlite3 from lans import Scanner # 执行扫描 scanner Scanner(target192.0.2.0/24, ports[21, 22, 80, 443, 3306, 3389]) result scanner.run() # 写入本地数据库 conn sqlite3.connect(assets.db) conn.execute(CREATE TABLE IF NOT EXISTS scan_result (ip TEXT, port INT, service TEXT)) for host in result.hosts: for port in host.open_ports: conn.execute( INSERT OR IGNORE INTO scan_result VALUES (?, ?, ?), (host.ip, port.number, port.service), ) conn.commit() conn.close() # 生成概要消息 summary f本轮扫描完成存活主机 {result.host_count} 台开放端口 {result.port_count} 个 print(summary)这段代码演示的是最核心的调用逻辑。实际项目里你可以把scan_result表换成任何你正在用的资产数据库把消息推送换成邮件、工单或者即时通讯机器人。LANs.py 的可编程性让这些扩展变得相当顺手因为它没有把数据锁死在内部格式里。4. 真实环境中经常踩坑的地方4.1 主机存活探测失灵第一个坑出在主机发现阶段。默认配置下LANs.py 会先发送 ICMP 探测包判断主机是否在线只有在收到响应后才会继续扫描端口。这个逻辑在大多数环境没问题但遇到禁用了 ICMP 的目标网络就会失效——主机明明在线却因为不回 ICMP 包被当成离线直接跳过端口扫描。解决方法是开启--no-ping参数跳过主机存活探测直接对所有地址执行端口扫描。端口扫描本身就能区分主机在线和离线有端口响应就是在线的所有端口都超时才是离线的。代价是扫描时间变长但换来的准确率提升是值得的。我建议在目标网络情况不明的时候干脆先别用 ICMP 探测。内网环境里设备类型杂很多设备为了减少攻击面会禁用 ICMP省这几秒钟的代价是漏掉一整批资产不划算。4.2 端口状态误判的排查思路第二个坑是端口状态判定的三类混淆open、closed、filtered。开放端口会完整建立 TCP 连接关闭端口会收到 RST 包过滤端口则没有任何响应因为中间设备把包丢了。实际扫描中最头疼的是 filtered 和 open 之间的界限模糊。有些防火墙会对未授权端口返回一个伪造的 RST 来迷惑扫描器这时候 LANs.py 会把这个端口判为 closed而实际上服务是存在的。反过来有些负载均衡设备对不存在服务的端口也不返回 RST导致端口被误判为 open。遇到这种可疑情况我的做法是手动抽验对疑似误判的端口单独跑一次详细连接抓取响应内容做二次确认。LANs.py 支持单个端口详细验证直接指定目标 IP 和端口就能看到完整的握手过程和响应特征。这种“扫描结果 人工复核”的模式在关键节点上特别管用。4.3 并发设置不当反而更慢这个坑我记得很清楚。有一回扫描一个跨网段目标我觉得默认并发太低直接把 rate 拉到了 1000想着能快四五倍。结果扫描跑了二十多分钟还没结束比默认配置慢了好几倍。查了日志才发现目标设备对高频连接触发了限速大量 SYN 包被丢弃重试量剧增。那次之后我总结出一个规律远端目标、有防火墙防护的目标、还有设备性能较差的业务系统并发一定要保守一点。建议先跑一小段网段比如 /28用目标配置试水统计一下超时比例。如果超时率超过 20%说明并发太高降一半再试直到找到稳定平衡点。真正的快不是参数拉满而是让每一探测包都有意义。4.4 指纹识别结果的二次校验LANs.py 内置了常用的服务指纹库能识别端口后面的服务类型和版本信息。指纹识别原理是发送特定探测包跟服务返回的特征库做匹配。这个过程大多数时候是准的但存在两个误判源一是服务版本太新特征还没入库二是前置设备做了协议伪装返回的数据跟真实服务不一样。遇到指纹识别结果跟实际不符的情况我会手动连接服务拉取 banner 再做判断。一条简单的命令就能看到服务返回的横幅信息这比纯靠指纹库匹配可靠得多。资质较老的渗透测试工程师都会养一个习惯指纹结果只作为参考关键资产一定是人工确认过才下结论。4.5 扫描结果的清洗与去重最后一类坑算不上工具问题而是使用方式的问题。内网扫描经常会扫出一堆 DHCP 保留地址、虚拟接口地址、动态分配地址这些资产虽然在线但并不属于真实业务系统。不加筛选直接入库资产清单会被无效数据污染。我的处理方式是在写入数据库之前加一层过滤逻辑先用正则识别常见保留地址段并标记再根据端口特征区分基础设备、服务器和终端。LANs.py 输出的 JSON 数据里带有完整的端口信息做这些规则过滤非常方便。资产盘点这件事扫只是第一步把数据整理成可用的信息才是真正产生价值的环节。5. 进阶玩法与必须守住的边界5.1 定时盘点与变更发现网络资产不是一成不变的新主机上线、旧服务下线、端口调整每天都在发生。单次扫描只能反映某一时刻的状态真正的资产管理需要持续监测。LANs.py 因为足够轻量放到定时任务里毫无压力。# 每天凌晨两点执行一次资产扫描结果存到按日期命名的文件 0 2 * * * cd /opt/lans python3 -m lans -t 192.0.2.0/24 -p 1-1024 --format json -o scan_$(date \%Y\%m\%d).json有了每日扫描数据之后再做一份对比脚本就能自动发现新增端口和下线资产。我把这个逻辑接到消息机器人的 webhook 上每次发现变更自动推送提醒。这样团队就不用在资产变更这事上靠人工汇报了系统自己会说话。5.2 自定义指纹规则的方法内置指纹库满足不了所有场景时可以自己扩充规则。LANs.py 的指纹规则用 YAML 格式编写结构很简单定义端口、发送的探测数据、期望收到的响应特征。- name: 内部文档系统 port: 8848 probe: GET / HTTP/1.0\r\n\r\n match: - Server: DocService - X-Powered-By: iDoc把规则文件丢进指定目录重启扫描就能生效。这个机制让我可以针对内部特有服务建立指纹库扫描结果比通用工具贴合得多。特定组织的渗透测试里识别出内部专有服务往往是最有价值的发现这也是通用工具做不到的定制能力。5.3 合规边界是底线最后这部分必须说清楚。LANs.py 这类工具本身是网络资产探测工具用途完全取决于使用者的意图和授权范围。在任何未经授权的网络环境里执行扫描都可能是违法行为这一点没有任何模糊空间。我在所有项目里坚持三条原则第一扫描目标必须有明确的书面授权包括明确的 IP 范围和时间窗口第二所有扫描行为都在授权的测试环境或自建靶场里进行不碰任何生产环境之外的网络第三扫描数据严格保密不做留存之外的任何用途。做技术分享是为了帮助大家把测试做规范、做高效不是为了给任何人提供一把没有钥匙的锁。工具永远是工具使用者的专业判断和职业操守才是这个行业真正的门槛。这几年做下来我的体会是真正厉害的渗透测试工程师不是手里工具最多的人而是清楚什么场景用什么工具、什么边界永远不能碰的人。LANs.py 这样的工具之所以能成为首选恰恰是因为它懂得克制而我们做技术的人也该在能力边界上保持同样的克制。