☰
IIS短文件名枚举实战:8.3规则、404/400差异与短名还原
2026/11/6 5:32:22 网站建设 项目流程

简介:围绕 IIS 短文件名泄露检测整理的实战工具包,面向从事 Web 安全测试、渗透测试学习及服务器配置核查的人员,可用于验证目标站点是否存在 IIS 8.3 短文件名枚举风险,并给出可复现的环境准备线索。压缩包共 32 个文件,约 87KB,包含 Python 与 Java 两套实现:Java 侧有 class、jar、java 源码及 build.xml 等构建配置,Python 侧提供扫描脚本,另附 md 说明、bat/cmd 启动脚本和配置类 xml,目录按语言与构建模块划分,便于对照阅读和二次改造。已有 250 人学习下载,说明该工具在短文件名检测场景中具有一定参考价值。借助其中的扫描器、配置样例和运行脚本,读者可快速搭建校验环境,理解短文件名猜测的请求构造、字符集遍历与结果判定思路,也能参考 Java 工程结构完成编译调试;说明文档与多语言版本并存,适合做工具对比、排错复盘和检测流程梳理。

1. 一台看起来很干净的 IIS,往往栽在一个 ~ 号上

先说结论:iis短文件名枚举这件事,扫描器十分钟能跑完,但真正决定成败的不是工具,而是你能不能读懂 404 和 400 之间那点差异。我做过一次内部评估,某公司的业务站该关的都关了——目录遍历没有、备份文件改名了、编辑器漏洞打了补丁,最后是从一个带~1的响应里,把整条上传目录的命名规律摸了出来。不是靠爆破,是靠状态码。

这篇讲三件事:短名到底泄了什么、扫描器怎么用才不白跑、以及那些扫不出结果时该往哪儿看。刚接触的人可以照着复现,做过几轮的人可以对照后半段的判断依据,看看自己的结论是不是站得住。

2. 短名到底泄了什么:8.3 命名规则与 ~ 号请求的底层逻辑

在动手点扫描器之前,得先搞清楚这条路为什么走得通。很多人把它当成一个“有洞就出结果”的黑匣子,实际它是一个有明确因果的机制:系统为了兼容老程序留了一份短名字,IIS 在处理 URL 时又会去查这份短名字,两件事叠在一起,就变成了一条信息泄露通道。

2.1 短名生成规则:6 个字符 + ~ + 序号

NTFS 会给每个不符合 8.3 格式的文件额外挂一个兼容名,形如XXXXXX~1.EXT。生成时有几条固定规则:先剔除空格和一批非法字符,取主名前 6 个字符,统一转成大写,追加~和从 1 开始的序号,扩展名只保留前 3 位。如果序号排到两位数,主名会再截短一位腾位置,变成XXXXX~10.EXT。

几个例子对照着看更清楚:

长文件名可能的短名说明
UploadCenter.aspxUPLOADC~1.ASP主名取前 6 位,扩展名取前 3 位
backup_2024.zipBACKUP~1.ZIP下划线属于合法字符,保留
web.configWEB~1.CON只有 9 个字符,照样不符合 8.3
Report-Final-v2.xlsxREPORT~1.XLS连字符保留,其余截断
a.aspx无本身已符合 8.3,不生成短名

最后一行是整件事里最容易被忽略的一句:短名只给“不符合 8.3 格式”的文件生成。反过来说,只要一个短名存在,就说明这个对象的真实名字一定长于 8.3 格式,或者含有需要被转写的字符。所以你扫出来的不是“某个文件存在”,而是“这里有一个名字比较长的东西”——这个区别,决定了后面拼名字的思路。

2.2 一次请求里发生了什么:通配符和 ~ 的组合

探测请求的形态通常是这样的:

GET /a*~1*/a.aspx HTTP/1.1 Host: target.example

拆开看,a是待验证的前缀,*是通配符,~1是短名后缀,后面那截/a.aspx是一个“必然不存在”的尾巴。IIS 收到后会走短名兼容路径:先拿a*~1*去匹配已有短名,匹配上了就把短名展开,展开之后的路径里带着通配符残留,没法继续解析,于是返回 400;匹配不上,直接 404。

所以判断依据不是“响应内容”,而是“同一个尾巴上,状态码从 404 变成了 400”。这个差异很小,但在 HTTP 语义上是明确的:服务器认得这个前缀,只是处理不下去了。

提示:不同 IIS 版本、不同补丁级别下,这条路径的表现不完全一致,有的返回 400,有的返回 500,有的干脆统一 404。所以任何一次扫描之前,都要先做基线校准,别拿着别人文章里的结论直接套。

2.3 我关了 8.3,为什么还能扫出来

这是踩坑最多的一条。很多运维看到评估报告第一反应是“我们早就关了短名生成”,但报告里的名字确实是真的。原因通常有三层:

第一层,NtfsDisable8dot3NameCreation这个开关只管“新创建”的文件。策略改掉之前就躺在盘上的文件,短名一个都不会少。想清理存量,得单独跑剥离命令,而这一步在很多人眼里属于“影响面不明确,先不动”。

第二层,系统组件和安装程序写入的文件,往往是在策略生效前或者以特殊方式创建的,短名依然留着。站点的上传目录、.NET 的临时编译目录、某些更新缓存目录,是这类残留的重灾区。

第三层,就算短名真的没了,~这个字符在 URL 里的处理逻辑本身也可能带来别的行为差异,值得单独测一轮。换句话说,短名是主因,但不是唯一变量,别把所有异常都归到它头上。

3. 复现环境与扫描器用法:从搭靶机到跑出第一条结果

看别人写的东西,最容易翻车的地方就是“照抄命令”。所以我一般会先在本地或授权环境里搭一个最小闭环,把“有短名”和“没短名”两台机器摆在一起,先确认自己看得懂差异,再去碰真实目标。

3.1 靶机搭建:从 fsutil 到 dir /x 的自检

一台 Windows Server 加 IIS 就够。把站点建起来,往 wwwroot 里放几个长文件名的对象,然后确认短名真的生成了。

# 查询卷的 8.3 生成策略:0=启用,1=禁用,2=按卷配置,3=除系统卷外禁用 fsutil 8dot3name query C: # 把 C 盘设为启用,让后续新建的文件重新带上短名 fsutil 8dot3name set C: 0 # 造几个长文件名对象(内容无所谓) fsutil file createnew C:\inetpub\wwwroot\UploadCenter.aspx 1024 fsutil file createnew C:\inetpub\wwwroot\backup_2024.zip 1024

命令里的query和set是两件事:前者只看不动,后者会改策略。set那一步改完,只对之后新建的文件生效,之前建的不会追溯——这一点和前面说的第二层原因是一回事。

建完之后用dir /x看短名列:

C:\inetpub\wwwroot>dir /x 2024/05/12 10:21 1,024 UPLOADC~1.ASP UploadCenter.aspx 2024/05/12 10:21 1,024 BACKUP~1.ZIP backup_2024.zip

看到~1就说明靶子造好了。再准备一台把策略设成1的对照机,同样放文件,dir /x里短名列应该是空的。两台摆在一起,后面所有结论都有参照。

3.2 基线校准:用 curl 手工确认 404 与 400 的分界

扫描器之前先手工发三条请求,这一步花不了两分钟,但能省掉后面半小时的困惑。

# 基线一:确定不存在的普通路径,期望 404 curl -s -o /dev/null -w "%{http_code}\n" "http://10.10.10.20/zzzznothing.aspx" # 基线二:确定存在的普通路径,期望 200 或 302 或 403 curl -s -o /dev/null -w "%{http_code}\n" "http://10.10.10.20/UploadCenter.aspx" # 基线三:带通配符和 ~ 的探测路径,命中期望 400,不命中期望 404 curl -s -o /dev/null -w "%{http_code}\n" "http://10.10.10.20/u*~1*/a.aspx"

三条请求各有用处。第一条确认“404 长什么样”,第二条确认“存在的资源返回什么”(有些站点对静态文件直接 403,那你后面的判断基准就变了),第三条确认这台机器会不会给出~特征。如果第三条换几个不同前缀试下来全是 404,那这台机器基本不出这个特征,后面再跑什么都没意义——早点收手比硬跑省时间。

用脚本批量做基线更省事:

import urllib3, requests urllib3.disable_warnings() def probe(base, path): """返回状态码和响应体长度,重定向不跟随,避免 302 掩盖真实结果""" r = requests.get(base.rstrip("/") + path, verify=False, allow_redirects=False, timeout=8) return r.status_code, len(r.content) base = "http://10.10.10.20" print("基线-不存在:", probe(base, "/zzzznothing.aspx")) print("基线-存在 :", probe(base, "/UploadCenter.aspx")) print("基线-tilde :", probe(base, "/u*~1*/a.aspx"))

allow_redirects=False是关键参数。很多站点会 302 到登录页,一跟随,所有请求都变成 200,状态码差异被彻底吃掉,你会得到一片“全部命中”的假结果——这种翻车我见过不止一次。

3.3 扫描器怎么跑:并发、超时、字典三个旋钮

这类扫描器的参数基本跑不出三类:目标地址、并发与超时、前缀字典。

目标地址只写到站点根,别带具体页面路径,否则拼出来的探测路径全是错的。并发别贪,IIS 的请求队列深度有限,而且默认的请求筛选对高频异常 URL 是有反应的,我一般从 5 起步,稳定了再往上加。超时给 5 到 8 秒,太短会把慢响应误判成不存在。

字典这块的取舍很实际。第一轮只跑单字符 a 到 z 加 0 到 9,目的是确认特征存在;第二轮再上常见目录名和业务词根,比如 upload、backup、static、files、temp、log 这类。不要在没确认特征的情况下直接上大字典,那是纯浪费。

跑完第一轮之后,先别急着看结果列表,回头对一下基线:如果字典里明明有u,而/u*~1*/a.aspx返回的还是 404,说明靶子上那个UploadCenter.aspx的短名没生成,问题出在环境而不是工具。

4. 手工验证与字典补全:把 ~1 变成真实文件名

扫描器给出的结果通常是“存在一个以某字符开头的长文件名”,仅此而已。真正有价值的是把它拼成人能看懂的名字,这一步工具帮不上太多忙,得手工收敛。

4.1 前缀逐位收敛:一个字符一个字符往下试

思路很朴素:已知短名以u开头,那就把u后面每一位当成未知,逐字符试。发/ua*~1*/a.aspx,命中就说明第二位是 a;不命中换下一个字符。命中之后把这个字符固定进前缀,继续试下一位。

import string, requests CHARS = string.ascii_lowercase + string.digits + "_-" def hit(base, prefix, ext="aspx"): """prefix 为已确认前缀,命中返回 True(状态码跃迁到 400/500)""" path = "/%s*~1*.%s?=a" % (prefix, ext) # 加个无意义查询串,规避缓存 r = requests.get(base.rstrip("/") + path, verify=False, allow_redirects=False, timeout=6) return r.status_code in (400, 500) def expand(base, seed): prefix = seed # 短名主名最多 6 位,超过就没必要再试 while len(prefix) < 6: p = [] for c in CHARS: if hit(base, prefix + c): p.append(c) if len(p) != 1: # 0 个说明前缀已到尽头;多个说明有歧义,需要人工介入 print("分支点:", prefix, "候选:", p) break prefix += p[0] print("已确认前缀:", prefix) return prefix print(expand("http://10.10.10.20", "u"))

几个参数值得说明。ext默认给aspx,换成别的后缀要同步改;?=a这个无意义查询串是为了绕开某些中间层的 URL 缓存;len(prefix) < 6是硬上限,因为短名主名就 6 位,试到第 7 位永远是空的。返回 400 和 500 都算命中,前面说过不同版本表现不同,宁可放宽一点。

这个循环最怕的不是慢,是“候选多个”的分支点。一旦某一位同时命中两个字符,说明同前缀下存在多个短名对象,机械往下走会串味,得退回去换扩展名维度重新切分。

4.2 扩展名枚举:为什么 .aspx 比 .txt 更值得先试

短名里的扩展名只保留 3 位,.aspx变成.ASP,.config变成.CON,.zip原样。枚举时把扩展名塞进探测路径做对照,命中说明这个短名的后缀对得上。

优先级的判断依据是站点类型,不是字典热度:

优先级扩展名判断依据
高aspx / ashx / asmxIIS 站点的业务主体,短名密度最高
高configweb.config 这类名字天然超长,必带短名
中zip / rar / bak / sql运维备份习惯,一旦命中价值极高
中xlsx / docx / pdf上传目录里的常见产物
低txt / log名字短,很多本身就符合 8.3,不出短名

web.config这一条值得单独说。它只有 9 个字符,但依然不符合 8.3,所以几乎每个 IIS 站点上都躺着一个WEB~1.CON之类的短名。这意味着“扫到 config 类短名”几乎不能作为漏洞证据,它更接近默认状态——判断的时候得把这条排除掉,别自己吓自己也别吓客户。

4.3 从短名到实际路径:目录、文件与二次组合

短名分两类,处理方式完全不同。文件名短名(比如UPLOADC~1.ASP)给出的是“前缀 + 后缀”,可以直接进字典做二次验证;目录名短名给出的是路径层级,价值更大,因为它能帮你拼出原本猜不到的目录结构。

有的环境里短名可以直接访问,/UPLOADC~1.ASP返回 200;有的会被 URL 重写规则或者请求筛选拦掉,返回 404。这一点必须在靶机上先试,别假设。

拿到目录短名之后,正确做法是把它当前缀继续往下探。比如确认了/upload~1/这一层,就发/upload~1/b*~1*/a.aspx,看这一层下面还有什么。一层一层往下推,比在根目录上死磕大字典有效得多。

再往后就是组合:目录短名 + 扩展名枚举 → 猜备份文件名;文件短名 + 站点错误页回显 → 补全真实名字;短名结果 + robots.txt / sitemap 里出现的路径 → 交叉验证。这一步没什么技巧,就是把零散信息拼起来。

5. 排查清单:扫不出结果的 5 种真相

这条路上最常见的失败不是“靶子没有漏洞”,而是“你以为你在测 A,实际测的是 B”。下面五条是出现频率最高的,每条都按现象、原因、解决写。

5.1 现象:全站 404,连确定存在的目录也是 404

原因:站点绑定了 Host 头,或者强制 HTTPS,或者前面挂了一层 CDN 回源。你请求的 URL 根本没过到真正的 IIS 上,返回的是边缘节点或默认站点的 404。

解决:先把 Host 头带上,用curl -H "Host: 业务域名"对照一次;再看 80 和 443 分别返回什么;如果确认是边缘层,在授权范围内直连源站地址复测。这一步不做,后面所有结果都是噪音。

5.2 现象:扫出一堆短名,但名字永远卡在 6 个字符

原因:这是机制本身的上限,不是工具的问题。8.3 主名就是截断到 6 位,第 7 位之后的真实字符根本不会体现在短名里,你再怎么试也试不出来。

解决:接受“短名只给前缀”这个事实,剩下的靠别的信息源补:上传接口的响应回显、错误页里的物理路径、下载链接的文件名规律、备份命名的团队习惯。指望扫描器直接吐出完整文件名,是把工具当成了后悔药。

5.3 现象:本地靶机一测就中,线上一个都没有

原因:两种可能。一是线上 IIS 版本较新,~的处理逻辑已经改过,不再返回 400;二是前面有 WAF 或反向代理,把带~、*的 URL 统一拦掉,导致所有请求都返回同一个状态码,看起来“每个前缀都有反应”。

解决:先看响应体差异,WAF 拦截一般有特征页面或固定长度;再用不同路径层级、不同 HTTP 方法(OPTIONS、HEAD)做对照组。如果所有组合返回完全一致,基本可以判定是设备在统一拦截,此时扫描结论不可信,得换手法。

5.4 现象:跑到一半开始 500、连接被重置、源 IP 被拉黑

原因:并发太高触发请求筛选或动态 IP 限制;也有可能是目标站点本身应用池不稳定,被高频异常 URL 拖垮了。

解决:并发降到 3 到 5,请求之间加 200 到 500 毫秒间隔,分批跑。更重要的是做断点记录——把已确认的字符位写进文件,被断了从断点续跑,别每次从头来。这条是血泪经验,我第一次跑大字典没做记录,被打断之后重跑了三遍。

5.5 现象:短名命中,但访问返回 403.14 或目录列表被拒

原因:命中的是目录短名,不是文件;IIS 默认不开目录浏览,所以直接访问就是 403。

解决:把目录短名当路径前缀往下探,而不是当文件访问。同时留意 403 和 404 的区别——403 说明路径存在只是不让看,这个信息本身就有价值,可以作为目录结构的确认点。

6. 拿到结果之后:三个判断依据和一条修复验证闭环

判断一份扫描结果可不可信,我一般看三件事,缺一件就不下结论。

对照组(普通不存在路径)含 ~ 通配路径判断
404404无匹配短名,或该环境不出此特征
404400存在匹配前缀的短名,结果可信
404500短名存在但后续处理异常,值得人工复核
403403前面有设备统一拦截,结果不可信
302302登录跳转吃掉了状态码,带 Cookie 重跑

表格第一列是基准,第二列才是信号。少了第一列,第二列的任何数字都没有意义——这是我见过最多的误判来源。

修复侧的动作,按影响面从小到大排:先在请求筛选里加一条 URL 序列规则,把含~的请求挡掉,这个改动最小、见效最快;再处理存量短名,用剥离命令清理站点目录,注意这一步会影响依赖短名的老程序,得先确认没有这类依赖;最后才是把卷级别的短名生成策略关掉,管住新增。三步做完,拿同一个扫描器再跑一遍,理想结果是全部落回 404——这个回归验证一定要做,改完不验证等于没改。

日志侧也别浪费。请求日志里 grep 一下带~的 URL,能看出历史上有没有人试过,也能反过来验证你的拦截规则有没有生效。

回过头看,我早年最大的毛病是拿到工具就想直接对着目标跑,跑完拿一堆名字去交差。后来吃过一次亏:整份报告里最“漂亮”的那几个文件名,其实是登录跳转造成的假阳性,客户那边一验证全对不上,后面所有结论的可信度都被拖下水。从那以后我每次碰 IIS 站点,都强制先走一遍三条基线、再开扫描器,哪怕多花五分钟。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询