这种 JavaScript 压缩技术是如何工作的?

3
我正在查看一个涉及XSS的安全比赛(link)的结果,并发现了一些精彩而可怕的JS XSS负载。获胜者(@kinugawamasato)使用了一种JavaScript压缩技术,这对我来说完全是另一个世界:
压缩负载:
https://cure53.de/xmas2013/?xss=<scriPt>document.write(unescape(escape(location)
.replace(/u(..)/g,'$1%')))<\/scriPt>㱯扪散琠楤㵥⁣污獳楤㵣汳楤㨳㌳䌷䉃㐭㐶うⴱㅄ〭
䉃〴ⴰ〸ぃ㜰㔵䄸㌠潮牯睥湴敲㵡汥牴⠯繷⸪ℱ⼮數散⡲散潲摳整⠰⤩⤾㱳癧湬潡搽攮摡瑡畲氽慬汛攮
牯睤敬業㴳㍝⬧㽳慮瑡㵀Ⅱ汬潷彤潭慩湳㴧⭤潭慩渻攮捨慲獥琽❵瑦ⴷ✾

真正发生的事情:
<object id=e classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83 onrowenter=alert(/~w.*!1/.exec(recordset(0)))><svg onload=e.dataurl=all[e.rowdelim=33]+'?santa=@!allow_domains='+domain;e.charset='utf-7'>

这个技术已经有文档记录了吗?我能否学习一下它的工作原理?是否已经有自动化的javascript压缩器可以实现这一点?WAF会如何对这样的有效负载做出反应?

您可以在这里看到更多示例。

1个回答

3
我在将任何数据放入localStorage时都使用lz-string库进行JS压缩。我只是该库的用户,而不是压缩专家。但这是可以在该工具周围找到的信息... lz-string的目标:

lz-string旨在满足在移动设备上存储大量数据的需求,特别是在localStorage中。localStorage通常限制为5MB,您可以压缩的数据量就越多。

... 我(注:“我”指lz-string的作者Pieroxy)从LZW实现开始(该实现不再受专利保护),它非常简单...

因此,这种实现的基础是LZW,这在Javascript client-data compression中提到了,由Andy E提出。让我指出

维基百科-算法摘录:

韦尔奇(Welch)1984年描述的场景将8位数据序列编码为固定长度的12位代码。从0到255的代码表示由相应的8位字符组成的1个字符序列,而256到4095的代码则在字典中创建,以记录在编码数据时遇到的序列。在压缩的每个阶段,输入字节被收集成一个序列,直到下一个字符会使序列中没有代码。该序列的代码(不包括该字符)被添加到输出中,并且一个新代码(用于该字符的序列)被添加到字典中。

维基百科 - 编码:

编码算法的高级视图如下:

  1. 初始化字典以包含所有长度为1的字符串。
  2. 在字典中查找与当前输入相匹配的最长字符串W。
  3. 将W的字典索引发射到输出并将W从输入中删除。
  4. 将W跟随输入中的下一个符号添加到字典中。
  5. 转到第2步。

在lz-string的情况下,它的工作原理可以在这里观察到:

让我引用一些已经提到的lz-string源中的步骤:

我所做的是:
  • localStorage只能包含JavaScript字符串。JavaScript中的字符串在内部以UTF-16存储,这意味着每个字符的权重为16位。我修改了实现方式,使其适用于16位宽的标记空间。
  • 我必须删除默认字典初始化,在16位宽的标记空间上完全没有用处。
  • 我使用三个标记初始化字典:
    • 产生一个16位标记的条目。
    • 产生一个8位标记的条目,因为我将存储的大多数内容都在ISO-Latin-1空间中,这意味着标记低于256。
    • 标记流的结束的条目。
  • 输出由比特流处理,每个字符在输出字符串中有效地存储16位。
  • 每个标记根据字典大小需要的位数进行存储。因此,第一个标记需要2位,第二到第七个需要3位,以此类推...
  • 现在我们知道,通过这些压缩技术,我们可以获得16位信息。我们可以在这个演示中测试它:http://pieroxy.net/blog/pages/lz-string/demo.html (或者/和另一个 链接)

    它将Hello, world.转换成:

    85 04 36 30 f6 60 40 03 0e 04 01 e9 80 39 03 26
    00 a2
    

    我们需要进行最后一步,让我再引用一下:
    这个库生成的内容并不是真正的字符串。通过使用 UTF-16 位空间的所有 16 位,这些字符串并不完全是有效的 UTF-16。在版本 1.3.0 中,我添加了两个帮助编码器来生成我们可以处理的内容:
    - `compress` 生成无效的 UTF-16 字符串。这些字符串只能在 Webkit 浏览器中的 `localStorage` 中存储(已在 Android、Chrome 和 Safari 上测试)。可以使用 `decompress` 进行解压缩。 继续我们的例子,"Hello, world." 将被转换为:
    ҅〶惶̀Ў㦀☃ꈀ
    

    最后就是这样了。我们可以看到,所有的 ...除了拉丁字符之外的其他字符... 都来自于最终转换为UTF-16。希望这能给一些提示...


    1
    如果这有所帮助,那就太好了! :) - Radim Köhler

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接