文件上还有一层你看不见的属性:三个平台各叫什么
发布时间:2026/10/25 21:12:38 锦皓数字建站

授权与合规声明本文全部操作对象均为自建隔离靶场本机容器或隔离虚拟机涉及安全测试的环节必须以取得合法授权为前提。未经授权的渗透测试违反《中华人民共和国网络安全法》与《刑法》相关条款须承担相应法律责任。本文只讲环境配置、版本对照与靶场隔离不含任何攻击步骤、利用载荷与绕过手法请勿将文中环境指向任何非自有系统。一、ls -l之后还有一层没显示1.1 权限位后面那个在 macOS 上随手对一个文件执行ls -l输出长这样-rw-r--r-- 1 wzk staff 5 Oct 25 06:09 note.txt。权限位那一段末尾多了一个。同一份文件传到一台 Linux 机器上再ls -l不见了剩下的是-rw-r--r-- 1 ...。文件没坏字节数一样校验值也一样。变的只有那个字符。第一次看见它的人常有两种猜法都不对以为它是权限的一部分以为它表示文件被锁。它确实跟权限位并排显示但不属于权限位——那一段已有专文讲过这里只处理跟在它后面的这个字符。的含义很窄就一句这个文件上还挂着扩展属性。它不告诉你挂了几条、叫什么、值是什么。想知道得换命令。1.2stat给得出的和它给不出的stat能吐出大小、块数、inode 号、链接数、三个时间、属主属组、权限位。这是文件系统为每个文件维护的那一组POSIX 意义上的stat结构。扩展属性不在这一组里。macOS 的man xattr把两者的关系写得很干脆Extended attributes are arbitrary metadata stored with a file, but separate from the filesystem attributes (such as modification time or file size).这句话里有两个词要分开读。stored with a file它是跟文件绑在一起的文件在它就在不是外面另建的一张表。separate from the filesystem attributes它不属于stat报的那一组所以你用ls -l、stat、du都看不见它。同一段手册还交代了值的形态通常是以空字符结尾的 UTF-8 字符串也可以是任意二进制。后半句后面会反复用到——打印出来是空白不代表它是空的可能是一段不可打印的字节长度还在。1.3 一个文件其实有三层信息把这几件事摊开一个文件身上能读到的信息至少分三层层记录在哪用什么看换平台还认不认内容层文件的数据块cat、读文件、算校验值认字节就是字节文件系统属性层inode 与目录项ls -l、stat大体认字段名和精度有差异扩展属性层文件系统单独为它留的那块地方macOSxattr、Linuxgetfattr、Windowsdir /r概念在名字和规则全不一样第三列最容易被记混前两层看哪条命令基本是常识第三层不是。也正因为它不参与ls -l的默认输出在一次普通的复制里被丢掉时没有任何提示。本章可以带走的一句ls -l末尾那个只说明「有」不说明「有什么」扩展属性跟stat报的那一组是两套记录不是一套的补充字段。二、macOSxattr的五个动作和一次本机实测2.1 名字长什么样macOS 上这一层的命令行入口是xattr。属性的名字是一串用点分隔的 ASCII 字符实测里见到的两个是com.apple.provenance和com.example.demo都是反向域名的写法。这个写法是习惯本文只描述实测到的名字不推断命名约束——没核到能下定论的程度就不写。值是另一回事。默认按 UTF-8 打印遇到不可打印的字节就打不出来屏幕上表现为冒号后面空空一片。这不是「属性没有值」是「值不是文本」。要确认用十六进制看或者看长度。2.2 五个动作三个修饰man xattr给的是一组选项。按用途归一下真正算「动作」的是五个动作选项输出什么列名字不带选项一行一个属性名不给值列名字和值-l名字加冒号加值不可打印处留空看单个值-p加属性名只给那一条的值写-w加名字和值不输出成功即返回删一条-d加属性名不输出清全部-c不输出一条不剩严格说表里是六行-l是「列」的变体所以习惯上说五个动作。另有三个修饰选项作用在动作上而不是自成动作-x把值按十六进制显示用来对付上面那种打不出来的值-r递归到目录里的每一项-s让命令作用于符号链接本身而不是它指向的目标。最后这个容易被忽略——默认情况下你对一个软链接执行xattr动的是链接指向的那个文件。-d和-c的区别就一句一个是点名删一个是全删。后者会把这个文件上的属性清空包括系统自己写的那条。2.3 本机实测两行属性一行不是我写的下面这段是 2026-10-25 在 macOS 上真跑的命令和输出都原样贴。printftest\n/tmp/_xa.txt xattr-wcom.example.demohello/tmp/_xa.txt xattr-l/tmp/_xa.txt输出是两行不是我写进去的一条com.apple.provenance: com.example.demo: hello第一行是系统自己加的我只写了一条。第二行是我写的那条值就是hello打印得出来。第一行的冒号后面是空的——按 2.1 说的它的值不是可打印文本。再换ls -l的扩展选项看一次ls-l /tmp/_xa.txt xattr /tmp/_xa.txt-rw-r--r-- 1 wzk wheel 5 Oct 25 06:09 /tmp/_xa.txt com.apple.provenance 11 com.example.demo 5 com.apple.provenance com.example.demo这里能读出两件事。第一的位置在权限位之后、链接数之前它是个标记位不是权限字符。顺带说一句边界-rw-r--r--那九位怎么解读本文不涉及那是另一篇文章的事这里只看。第二ls -l在下面列出两行格式是名字加制表符加长度。com.example.demo是 5跟我写进去的hello对得上com.apple.provenance是 11说明它确实有值只是打印不出来。xattr不带选项则只列名字两行干净——想知道有没有用它最快。一个能带走的观察新建一个普通文件你一条属性都不写它上面也已经有一条了——系统会写自己的东西这部分不在你的控制范围内。所以「有扩展属性」这件事本身说明不了任何问题得看是哪一条。本章可以带走的一句只提示存在xattr列名字-l给值长度比内容更可靠——值打不出来不等于没有值。三、Linux名字必须带前缀ACL 就住在这一层3.1 定义几乎一样规则完全不同Linux 侧的手册xattr(7)开头是这么写的Extended attributes are name:value pairs associated permanently with files and directories, similar to the environment strings associated with a process.跟 macOS 那句对照着看前半段讲的是同一件事名字值对跟文件长期绑定。后半段那个比喻值得停一下——像进程的环境变量。像的是形态不像的是生命周期进程退出环境变量就没了文件还在属性就还在。这句permanently是理解后面所有限制的前提。真正的差异在名字上。Linux 要求名字写成全限定形式namespace.attribute前面的命名空间不是装饰它决定了这条属性能不能被看见、被谁写。3.2 四个命名空间命名空间谁在用谁能看见典型名字security内核安全模块如 SELinux、文件能力由那套机制自己决定本文不推断security.selinuxsystem内核自己用如 ACL跟内核功能绑定system.posix_acl_accesstrusted给特权程序留的只有具备CAP_SYS_ADMIN的进程可见trusted.md5sumuser普通属性用户自己写普通用户user.mime_type最该记住的是第三列。trusted那一行是个硬边界没有那个能力你连它存在都看不到。这不是权限位那种「看得见但改不了」是列表里压根不出现。所以在某台机器上看不到一条属性不能推出「这台机器上没有它」——先确认自己是拿什么能力集看的。user那一行也有边界这类属性只能加在普通文件和目录上对某些目录另有额外限制。后半句在xattr(7)里是单独一段本文不展开。3.3 两个上限、整体替换和住在这里的 ACL上限有两个都是 VFS 层的跟具体文件系统无关属性名最长 255 字节属性值最长 64 kB。还有第三个列出名字的那个调用返回的名字列表总共也限 64 kB。三个数字的实际含义是扩展属性不是给你存大块数据的地方。64 kB 要装下所有属性值名字列表那份还要装下所有名字属性一多列名字这个动作本身就会撞上限。读写语义是另一条getxattr(2)一次取回整个值setxattr(2)整体替换旧值。没有「改中间一个字节」的接口要改一个字符也得把整个值写回去。这条语义推下去有两个后果。一是并发写不安全两个进程同时setxattr同一条落盘的是后完成的那次修改不会合并。二是它适合「标记」不适合「状态」。ACL 也是这一层里的一条值得单独说。很多人把 ACL 当成跟扩展属性并列的另一套东西xattr(7)里有一句把这个误会直接打消additional security features such as Access Control Lists (ACLs) may be implemented using extended attributes也就是说ACL 是用扩展属性实现的。落到名字上就是system.posix_acl_access这一条。价值在操作层面「清空扩展属性」和「清掉 ACL」在某些命令下是同一个动作。你把一个目录上的属性全删掉顺手也删了访问控制列表屏幕上一句提示都没有。所以清理前先列一遍名字看清有哪些条、属于哪个命名空间。所以我建议用-d逐条删而不是-c一把清干净前者你至少知道自己删了什么。⚠️代码待验证Linux 侧命令按xattr(7)与 attr 工具手册写本机是 macOS未在 Linux 上执行getfattr-d-m- /tmp/example.txt setfattr-nuser.mime_type-vtext/plain/tmp/example.txt getfattr-nuser.mime_type --only-values /tmp/example.txt第一条把文件上所有可读的属性连名字带值列出来第二条写一条user.*第三条只取值。跑之前先确认这台机器上装了 attr 这套工具——不是所有发行版都预装这一点本文不给具体包名。本章可以带走的一句Linux 的扩展属性名字必须带命名空间命名空间决定可见性ACL 就住在system.*里所以「清属性」和「清 ACL」可能是同一次操作。四、Windows一个文件至少有一个流4.1 默认数据流与那个双冒号Windows 那边的对应机制不叫扩展属性叫流。[MS-FSCC] 对 NTFS 的描述第一句就把范围说清了All files on an NTFS volume consist of at least one stream - the main stream – this is the normal, viewable file in which data is stored.注意at least one一个文件不是「一块数据加若干附加信息」而是「一个或多个流」其中必有一个是主数据流——你在资源管理器里双击打开的那个。完整的名字是三段式filename:stream name:stream type。主数据流没有名字所以一个sample.txt的主数据流全名写出来是sample.txt::$DATA——中间那段是空的双冒号就是这么来的。写法指的是什么默认看得见吗sample.txt主数据流等价于sample.txt::$DATA看得见就是那个文件sample.txt:note:$DATA一条备用数据流类型是$DATA不显示目录的$I30段类型$INDEX_ALLOCATION目录自己也有流不显示第三行容易被漏掉目录也有流。目录项的索引本身就以$INDEX_ALLOCATION类型的一条流存在默认流名是$I30。所以「流」不是文件专有的机制它是这个文件系统描述一切东西的方式。4.2 怎么看见/R这个开关[MS-FSCC] 里写得很直接These alternate data streams are not normally visible, but can be observed from a command line using the /R option of the DIR command.这句话里两个词要分开看not normally visible说的是资源管理器can be observed说的是命令行加一个开关。⚠️代码待验证Windows 侧命令按 [MS-FSCC] 写未在本机执行rem 在 cmd.exe 里对一个目录执行 dir /r输出里会在文件名之外多出以冒号分隔的那一段具体排版随版本不同本文不贴样例也不猜格式。边界划在这里本文只讲它存在、怎么看见不讲怎么用它放东西。看见和利用是两件事很多讲这一层的文章就是在这一句上滑过去的。4.3 三平台并排看把前面三章压成一张表平台这一层叫什么怎么列出来谁能看见macOS扩展属性extended attributesxattr列名字xattr -l连值ls -l看与长度属主可读写2.3 实测系统另写一批Linux扩展属性名字必须是namespace.attributegetfattr或直接调listxattr取决于命名空间trusted需CAP_SYS_ADMINWindows NTFS流stream主数据流加备用数据流dir /r或按文件名:流名:$DATA拼完整名主数据流就是文件本身备用流默认不显示表里没有明说、但最该记住的一点只有 macOS 会在ls -l里留一个字符提示你有东西。Linux 和 Windows 什么都不提示。所以「看不见」在三个平台上一个意思不等于没有只等于「你用的这条命令不看这一层」。本章可以带走的一句NTFS 里文件是流的集合主数据流没名字、全名带::$DATA三个平台都不会在默认输出里提醒你这一层存在。五、搬家的时候这一层跟不跟着走5.1 结论只有一句默认不搬这一段最容易被说过头。能确定的只有这一层打包、复制这类动作默认不会把扩展属性带过去要带就得显式打开某个开关。至于每个工具默认开不开、开关叫什么、开了之后跨文件系统还成不成立各工具不一样本文不下结论也不给参数写法。原因可以从定义推出来不需要查任何一个工具这一层是stored with a file但separate from the filesystem attributes2.1 引的那句。一个「复制文件」的动作常规做法是读数据、写数据、再按需要补几个stat字段。属性那一层不在数据里也不在stat那一组里所以它天然落在复制的默认路径之外。5.2 四种搬家场景要确认的东西不一样场景这一层跟不跟着走需要确认什么同一块盘上改名或移动位置变了没有写出新文件仍建议前后各列一次别假设跨盘、跨文件系统复制新文件被写出来属性不自动跟工具是否提供开关开关在手册哪一节打包再解开中间产物是归档属性要单独带打包与解包两端都得支持缺一端就丢跨操作系统传递目标平台未必有同名概念名字空间规则不同开关打开也可能无处落脚最后一行最麻烦。macOS 是扁平的点分名字Linux 要求namespace.attribute全限定NTFS 那边是流、名字里带冒号。三种模型之间没有共同的表示所以跨平台搬这一层问题不在「能不能复制」在「目标那边有没有这个概念可以接收」。⚠️ 这一段按待验证处理各工具的具体默认值本文不给只保留「默认不搬、需要显式开关」这一层。要写进自己的操作手册得拿目标版本的工具亲手试一遍。5.3 为什么它丢得悄无声息因为它丢的时候所有你能想到的检查都是通过的。文件大小一样——属性不在文件大小里。修改时间一样——那是stat那一组跟这一层不相干。校验值一样——属性不在数据块里校验算的是内容。打开也正常——读内容的程序根本不看这一层。唯一的检查方式是列属性不是比内容。搬完之后用目标平台那条命令列一次名字跟搬之前那份对一遍名字少了就是少了名字在而值变了得单独看。⚠️代码待验证流程示意本机未对真实跨机复制执行printf%s\n第 1 步源端列一次属性名字存成清单printf%s\n第 2 步执行复制记录用了哪些开关printf%s\n第 3 步目标端再列一次属性名字存成清单printf%s\n第 4 步两份清单对比只比名字先不比值printf%s\n第 5 步名字缺失的回到第 2 步看开关不要先怀疑文件损坏第 5 步是我最想强调的属性缺失和文件损坏是两件不同的事表现却很像。先怀疑开关比先怀疑磁盘划算。完整版对照资料这一章对照的三平台「怎么列属性、搬家前后怎么比对」的操作卡我整理成了一页跟靶场环境对照表放在同一个资料包里扫码即可获取本章可以带走的一句复制工具复制的是文件不是文件在原来那台机器上的全部状态属性丢没丢只能靠搬家前后各列一次名字来判。六、安全上为什么在意6.1 有些判定读的是这一层不是内容把前面几章散落的点收一下会发现这一层里装的并不都是「备注」访问控制列表在system.posix_acl_access3.3 那句原文。SELinux 的标签在security.selinux3.2 的表。文件能力也挂在security命名空间下同一处。Windows 上「文件来源」这类信息是按流记录的[MS-FSCC] 的流模型具体流名本文不展开。这些信息的共同点是做访问判断时系统读的是它们不是内容。判断发生在你读到文件之前——能不能访问、以什么身份执行、这个文件从哪来。第五章的结论到这里就变得更要紧了属性默认不跟着走意味着这些判定在新机器上读到的是「没有」。而「没有标签」和「标签是对的」是两种状态前者通常落到那套机制的默认处理上后者才是你在原来那台机器上验证过的。默认处理具体是什么取决于那套机制自己的策略本文不给结论——不同策略答案不一样写个通用答案就是在编。6.2 两个方向丢了和被带过来方向一该在的不在了。一台机器上配好的访问控制、安全标签经过一次打包和一次解包落到新机器上。文件能用但那些判定读到的是空的。这种降级不报错也不会有人察觉——除非有人专门去列一次属性。方向二不该在的来了。反过来一个从别处来的文件属性如果被完整带过来了记录的就是它原来那台机器上的状态。1.3 那张表里我把它叫第三层现在可以补一句这一层记录的是过程不是内容。最典型的是系统自己写的那条。2.3 里新建一个空文件com.apple.provenance就已经在了我什么都没做。它的名字指向「来源」但具体含义本文不推断——没人为它给出语义我也不替它编。能确定的只有两点它由系统写入它的值不是给人读的文本。这就够支撑一条建议看到com.apple.*开头的属性先假设它不该被你手动删。6.3 名字本身就是线索判断一条属性该不该动第一眼看的不是值是名字。前缀基本就交代了它是谁写的com.apple.*系统写的2.3 实测。删了文件内容不变但那条记录就没了。system.*内核用ACL 就在这一类里。删它等于改访问控制不是清理垃圾。security.*内核安全模块用。删了可能让判定落到默认策略上后果由那套策略决定。trusted.*特权侧普通身份连看都看不到别把它算进「没有」。user.*普通用户这一类才是可以随手清的。只有最后一行让人放心。前四行都属于「不是你的东西」其中system.*和security.*尤其——看上去像备注实际是判定输入。这也回到了 2.2 提到的那个差别-d点名删-c全删。如果你连这些名字是谁写的都还没分清就不该用全删。本章可以带走的一句安全判定读的是这一层而「属性不在」不等于「属性和原来一样」动它之前先看名字前缀com.apple.*、system.*、security.*都不是清理对象。七、三分平台自查清单7.1 macOS三条命令按顺序来ls -l看末尾有没有。有才值得往下走。xattr 文件名列名字。这一步回答「有几条、叫什么」。xattr -l 文件名连值一起看。值打不出来的回ls -l看长度或加-x看十六进制。顺序不能反先看值容易陷进「这一串是什么」而多数时候你只需要知道有几条。三个判读要点只表示存在冒号后空白不代表没有值-s不写动的是链接指向的目标而不是链接本身。7.2 Linux 与 Windows各一条Linux 侧用getfattr -d -m - 文件名把可读的都列出来重点看名字前缀落在哪个命名空间。看不到某条先确认自己的能力集——trusted.*在没有CAP_SYS_ADMIN时列表里就是不出现。Windows 侧在 cmd 里dir /r看文件名后面有没有多出一段以冒号分隔的名字。有就是备用数据流没有也不代表这个文件「干净」只代表这一层没有额外的流。两边共同的注意点这两个命令的输出格式在不同版本上不完全一样别把某一次的排版当成标准答案。7.3 三条判读纪律第一看不见不等于没有。三个平台默认都不提醒你这一层。macOS 好歹留了个Linux 和 Windows 连这个都没有。第二读不出来不等于空的。属性值可以是任意二进制打印成空白是正常现象长度才是它存在的证据。第三复制完内容一致不等于这一层一致。大小、时间、校验值全对属性可能一条没跟过来。要确认只能列名单对比。这三条其实是一句话的三个面这一层的存在感和它的重要性成反比。越是不显示的东西越容易在搬家时被丢掉而丢掉之后又越难发现。本章可以带走的一句先确认有没有看标记、列名字再确认是什么看值、看长度最后才谈搬不搬得走——顺序对了这一层就不神秘。完整版对照资料第七章这份三平台自查清单macOS 三条、Linux 与 Windows 各一条、三条判读纪律和第五章的搬家对照是同一套材料的两面我都收在资料包里了扫码即可获取附表 A本文引用事实与官方出处对照表序号事实出处核验日期本文位置1扩展属性是随文件存储、但与文件系统属性修改时间、文件大小等分离的任意元数据macOSman xattrXA-022026-10-25第一章 1.22属性值通常是以空字符结尾的 UTF-8 字符串也可以是任意二进制macOSman xattrXA-022026-10-25第一章 1.2、第七章 7.33本机实测用xattr -w把 hello 写进com.example.demo后xattr -l输出com.apple.provenance:与com.example.demo: hello两行本机命令输出XA-012026-10-25第二章 2.34本机实测新建文件会自动带com.apple.provenance由系统写入本机命令输出XA-012026-10-25第二章 2.3、第六章 6.25本机实测ls -l在权限位后显示下方按名字与值长度列出两条11 与 5本机命令输出XA-012026-10-25第二章 2.36各动作对应的选项不带选项列名字、-l连名字带值、-p打印单值、-w写、-d删单条、-c清全部macOSman xattrXA-032026-10-25第二章 2.2、第六章 6.37三个修饰-x十六进制、-r递归、-s作用于符号链接本身macOSman xattrXA-032026-10-25第二章 2.2、第七章 7.18「Extended attributes are name:value pairs associated permanently with files and directories, similar to the environment strings associated with a process.」xattr(7)XA-042026-10-25第三章 3.19四个命名空间security、system、trusted、user的用途名字一律写成namespace.attributexattr(7)XA-052026-10-25第三章 3.210trusted命名空间只有具备CAP_SYS_ADMIN的进程可见xattr(7)XA-052026-10-25第三章 3.2、第七章 7.211user.*只允许加在普通文件和目录上对某些目录另有额外限制xattr(7)XA-062026-10-25第三章 3.212属性名最长 255 字节、属性值最长 64 kB、名字列表也限 64 kBxattr(7)XA-072026-10-25第三章 3.313「additional security features such as Access Control Lists (ACLs) may be implemented using extended attributes」xattr(7)XA-082026-10-25第三章 3.3、第六章 6.114getxattr(2)一次取回整个值setxattr(2)整体替换旧值xattr(7)XA-092026-10-25第三章 3.315「All files on an NTFS volume consist of at least one stream - the main stream…」[MS-FSCC] 5.1XA-102026-10-25第四章 4.116流全名形如filename:stream name:stream type主数据流无名字sample.txt的默认流全名是sample.txt::$DATA[MS-FSCC] 5.1XA-102026-10-25第四章 4.117「These alternate data streams are not normally visible, but can be observed from a command line using the /R option of the DIR command.」[MS-FSCC] 5.1XA-112026-10-25第四章 4.218目录也有流类型$INDEX_ALLOCATION默认流名$I30[MS-FSCC] 5.1XA-112026-10-25第四章 4.119Windows 上「文件来源」这类信息按备用数据流记录本文不展开流名与取值[MS-FSCC]XA-122026-10-25第六章 6.120待验证打包与复制工具是否搬运扩展属性各工具默认值不同本文只写「默认不搬、需要显式开关」不给参数写法本批事实文件XA-132026-10-25第五章 5.1、5.221本文范围只用 ADS 说明「它存在、怎么看见」不写任何用它放置数据的手法本批纪律XA-142026-10-25第四章 4.222本文范围不涉及权限位的rwx、特殊位与目录的那套规则仅在 2.3 作一句边界说明本批纪律XA-152026-10-25第二章 2.3附表 B术语速查表术语一句话解释扩展属性跟文件绑在一起、但不在stat那一组里的名字值对值可以是文本也可以是二进制标记macOSls -l在权限位后面加的提示只表示「有扩展属性」不表示内容xattrmacOS 上操作这一层的命令行入口列名字、看值、写、删、清都靠它的选项命名空间Linux 名字的前缀user/system/trusted/security决定可见性与用途CAP_SYS_ADMINLinux 的一个特权能力没有它trusted.*属性在列表里根本不出现ACL访问控制列表在 Linux 上就是用扩展属性实现的system.posix_acl_access主数据流NTFS 里那个「没有名字」的流就是你在资源管理器里看到的文件本身全名以::$DATA收尾备用数据流NTFS 里主数据流之外的那些流默认不显示跨平台复制时也没有对应物$I30目录自身那条流的默认名字类型是$INDEX_ALLOCATION整体替换setxattr的语义一次写完整个值没有「改一半」的接口写在最后这篇用到的资料写这篇文章时我把三个平台的这一层并排摊在一张纸上最费劲的是接受「它们压根不是同一个东西」——只在三个平台上都靠ls -l那点信息就永远对不上顺手也整理了几份配套的东西靶场环境对照表DVWA、upload-labs 在 Windows / macOS / Linux 三平台的可行性与推荐路径Web 安全学习路线图从基础打牢到安全管理四个阶段各学什么常用靶场清单每个靶场练什么、适合哪个阶段资料是我自己整理的放在下面这个码上扫码即可获取添加时备注「靶场」优先通过。拿到之后建议先看环境对照表那一份先把手上这台机器的「看不见的那一层」列一遍再谈环境怎么搭。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。