如果我们想熟练进行代码分析和逆向工程,代码反混淆是一项需要学习的重要技能。在红/蓝团队练习中,我们经常遇到想要隐藏某些功能的混淆代码,例如利用混淆的 JavaScript 代码来检索其主要负载的恶意软件。如果不了解这段代码在做什么,我们可能不知道该代码到底在做什么,因此可能无法完成红/蓝队练习。
在本模块中,我们首先学习 HTML 页面的一般结构,然后在其中找到 JavaScript 代码。一旦我们这样做了,我们将了解什么是混淆、它是如何完成的以及它在哪里使用,并通过学习如何对此类代码进行反混淆来遵循这一点。一旦代码被反混淆,我们将尝试了解其一般用法,以复制其功能并揭示其手动执行的操作。
将讨论以下主题:
混淆是一种技术,用于使脚本更难以被人类阅读,但从技术角度来看,它可以发挥相同的功能,尽管性能可能会较慢。这通常是通过使用混淆工具自动实现的,该工具将代码作为输入,并尝试以更难以阅读的方式重写代码,具体取决于其设计
例如,代码混淆器通常将代码转换成代码中使用的所有单词和符号的字典,然后在执行期间尝试通过引用字典中的每个单词和符号来重建原始代码。以下是一个简单的 JavaScript 代码被混淆的示例:
用许多语言编写的代码无需用interpreted
语言(例如Python
、 PHP
和JavaScript
进行编译即可发布和执行。 Python
和PHP
通常驻留在服务器端,因此对最终用户隐藏,而JavaScript
通常在client-side
的浏览器中使用,代码发送给用户并以明文形式执行。这就是JavaScript
经常使用混淆的原因。
开发人员考虑混淆代码的原因有很多。一个常见的原因是隐藏原始代码及其功能,以防止在未经开发人员许可的情况下重复使用或复制它,从而使对代码的原始功能进行逆向工程变得更加困难。另一个原因是在处理身份验证或加密时提供安全层,以防止对代码中可能发现的漏洞进行攻击。
然而,混淆最常见的用途是用于恶意行为。攻击者和恶意行为者通常会混淆其恶意脚本,以阻止入侵检测和防御系统检测其脚本。在下一节中,我们将学习如何混淆简单的 JavaScript 代码,并尝试在混淆之前和之后运行它以记录任何差异。
[!TIP]
必须注意的是,不建议在客户端进行身份验证或加密,因为这样代码更容易受到攻击
代码混淆通常不是手动完成的,因为有许多针对各种语言的工具可以自动进行代码混淆。尽管许多恶意行为者和专业开发人员开发了自己的混淆工具以使反混淆变得更加困难,但许多在线工具都可以这样做。
尝试混淆下面这一段代码:
javascriptconsole.log('HTB JavaScript Deobfuscation Module');
首先我们先测试一下未混淆前代码输出:
在保持 JavaScript 代码完整功能的同时降低 JavaScript 代码片段的可读性的一种常见方法是 JavaScript 缩小。 Code minification
意味着将整个代码放在一行(通常很长)中。 Code minification
对于较长的代码更有用,就好像我们的代码仅由一行组成,缩小后看起来不会有太大不同。
许多工具可以帮助我们缩小 JavaScript 代码,例如javascript-minifier 。我们只需复制代码,然后单击Minify
,我们就可以在右侧获得缩小的输出:
再次,我们可以将缩小后的代码复制到JSConsole并运行它,我们看到它按预期运行。通常,缩小的 JavaScript 代码以扩展名.min.js
保存。
[!CAUTION]
注意:代码压缩并非 JavaScript 所独有,它还可以应用于许多其他语言,如javascript-minifier所示。
我们尝试将上述源代码在BeautifyTools上混淆,然后我们再在jsconsole上执行试试:
发现输出与原代码一致。
混淆后:
javascripteval(function(p,a,c,k,e,d){e=function(c){return c};if(!''.replace(/^/,String)){while(c--){d[c]=k[c]||c}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('1.0(\'2 3 4 5\');',6,6,'log|console|HTB|JavaScript|Deobfuscation|Module'.split('|'),0,{}))
[!CAUTION]
注意:上述类型的混淆称为“打包”,通常可以从初始函数“function(p,a,c,k,e,d)”中使用的六个函数参数来识别。
packer混淆工具通常会将代码中的所有单词和符号转换成列表或者字典,然后使用(p,a,c,k,e,d)函数引用来重新构建原始代码。当然每个打包器的(p,a,c,k,e,d)可以不同。然而,它通常包含一定的顺序。
虽然加壳器在降低代码的可读性方面做得很好,但我们仍然可以看到它的主要字符串以明文形式编写,这可能会揭示它的一些功能。这就是为什么我们可能想要寻找更好的方法来混淆我们的代码。
目前为止,我们已经能够让我们的代码变得难以阅读且功能无缺,但是唯一的缺点就是代码仍然以明文显示,依旧可直接显示其原功能。
让我们访问https://obfuscator.io 。在单击obfuscate
之前,我们将String Array Encoding
更改为Base64
,混淆结果如下所示:
javascriptfunction _0x252c(){var _0x336065=['nJm1mdm0ogrjA1DrDW','mtKXmti3mJHpuwTLufm','sfrciePHDMfty3jPChqGrgvVyMz1...
...
(_0x38d719['shift']());}}}(_0x252c,0xefaae),console[_0x172a9b(0xa7)](_0x172a9b(0xa4)));
这段代码显然更加混乱,我们看不到原始代码的任何残余。我们现在可以尝试在https://jsconsole.com中运行它,以验证它是否仍然执行其原始功能。尝试使用https://obfuscator.io中的混淆设置来生成更多混淆代码,然后尝试在https://jsconsole.com中重新运行它以验证它仍然执行其原始功能。
可以看到输出不变。
现在我们了解了代码混淆的工作原理,让我们开始学习反混淆。正如有自动混淆代码的工具一样,也有自动美化和反混淆代码的工具
我们看到当前的代码全部写在一行中。这称为Minified JavaScript
代码。为了正确格式化代码,我们需要Beautify
我们的代码。最基本的方法是通过我们的Browser Dev Tools
。
例如,如果我们使用的是 Firefox,我们可以使用 [ CTRL+SHIFT+Z
] 打开浏览器调试器,然后单击我们的脚本secret.js
。这将以原始格式显示脚本,但我们可以单击底部的“ { }
”按钮,这会将脚本Pretty Print
为其正确的 JavaScript 格式:
此外,我们可以利用许多在线工具或代码编辑器插件,例如Prettier或Beautifier 。
我们可以找到许多优秀的在线工具来反混淆 JavaScript 代码并将其转换为我们可以理解的东西。 UnPacker是一个很好的工具。让我们尝试复制上面的混淆代码并通过单击UnPack
按钮在 UnPacker 中运行它。
[!NOTE]
提示:确保脚本前不要留下任何空行,因为它可能会影响反混淆过程并给出不准确的结果。
尽管到目前为止,这些工具在将代码清理成我们可以理解的内容方面做得很好,但一旦代码变得更加模糊和编码,自动化工具清理它就会变得更加困难。如果使用自定义混淆工具对代码进行混淆,则尤其如此。
我们需要手动对代码进行逆向工程,以了解它是如何被混淆的以及它在这种情况下的功能。如果您有兴趣了解有关高级 JavaScript 反混淆和逆向工程的更多信息,您可以查看安全编码 101模块,该模块应该全面涵盖该主题
现在我们已经对代码进行了反混淆,我们可以开始检查它:
javascriptfunction generateSerial()
{
var flag="HTB
{
1_4m_7h3_53r14l_g3n3r470r!
}
";
var xhr=new XMLHttpRequest();
var url="/serial.php";
xhr.open("POST",url,true);
xhr.send(null)
}
我们看到, secret.js
文件只包含一个函数, generateSerial
。
该函数首先定义一个变量xhr
,它创建一个XMLHttpRequest
对象。由于我们可能不知道XMLHttpRequest
在 JavaScript 中到底做了什么,让我们 Google XMLHttpRequest
来看看它的用途。
读完之后,我们发现它是一个处理 Web 请求的 JavaScript 函数。
定义的第二个变量是URL
变量,其中包含/serial.php
的 URL,该 URL 应该位于同一域中,因为未指定域。
接下来,我们看到xhr.open
与"POST"
和URL
一起使用。我们可以再次 Google 一下这个函数,我们看到它打开了定义为“ GET
或POST
”的 HTTP 请求到URL
,然后下一行xhr.send
将发送该请求。
因此, generateSerial
所做的只是简单地向/serial.php
发送POST
请求,而不包含任何POST
数据或检索任何返回内容
开发人员可能在需要生成序列号时实现了此功能,例如单击某个Generate Serial
按钮时。不过,由于我们没有看到任何类似的生成连载的 HTML 元素,因此开发人员一定还没有使用过这个功能,并保留它以供将来使用。
在上一节中,我们发现secret.js
主函数正在向/serial.php
发送一个空的POST
请求。在本节中,我们将尝试使用cURL
执行相同的操作,将POST
请求发送到/serial.php
。要了解有关cURL
和 Web 请求的更多信息,您可以查看Web 请求模块
cURL
是一个强大的命令行工具,可用于 Linux 发行版、macOS,甚至最新的 Windows PowerShell 版本。我们可以通过简单地提供 URL 来请求任何网站,并且我们将以文本格式获取它。
要发送POST
请求,我们应该在命令中添加-X POST
标志,并且它应该发送POST
请求
shellcurl -s http://SERVER_IP:PORT/ -X POST
[!NOTE]
提示:我们添加“-s”标志以减少不必要的数据使响应混乱
然而, POST
请求通常包含POST
数据。要发送数据,我们可以使用“ -d "param1=sample"
”标志并包含每个参数的数据,如下所示:
shellcurl -s http://SERVER_IP:PORT/ -X POST -d "param1=sample"
现在我们知道如何使用cURL
发送基本的POST
请求,在下一节中,我们将利用它来复制server.js
正在做的事情,以更好地理解其目的
这是我们在Advanced Obfuscation
部分的More Obfuscation
中提到的混淆的另一个重要方面。许多技术可能会进一步混淆代码,使其难以被人类读取和被系统检测到。因此,您经常会发现包含编码文本块的混淆代码,这些文本块在执行时会被解码。我们将介绍 3 种最常用的文本编码方法
base64
hex
rot13
base64
编码通常用于减少特殊字符的使用,因为除了+
和/
之外,以base64
编码的任何字符都将以字母数字字符表示。无论输入如何,即使是二进制格式,生成的 Base64 编码字符串也只会使用它们。
base64
编码的字符串很容易被发现,因为它们只包含字母数字字符。然而, base64
最显着的特点是它使用=
字符进行填充。 base64
编码字符串的长度必须是 4 的倍数。例如,如果结果输出只有 3 个字符长,则添加额外的=
作为填充,依此类推。
要将任何文本编码为 Linux 中的base64
,我们可以回显它并使用 ' |
进行管道传输。 ' 到base64
shellecho https://www.hackthebox.eu/ | base64 aHR0cHM6Ly93d3cuaGFja3RoZWJveC5ldS8K
shellKriem@htb[/htb]$ echo aHR0cHM6Ly93d3cuaGFja3RoZWJveC5ldS8K | base64 -d
https://www.hackthebox.eu/
另一种常见的编码方法是hex
进制编码,它将每个字符编码为其在ASCII
表中的hex
顺序。例如, a
为十六进制的61
, b
为62
, c
为63
,依此类推。您可以使用man ascii
命令在 Linux 中找到完整的ASCII
表。
任何以hex
进制编码的字符串都仅由十六进制字符组成,即只有 16 个字符:0-9 和 af。这使得识别hex
进制编码的字符串就像识别base64
编码的字符串一样容易。
shellecho xxxx | xxd -p
要解码hex
进制编码的字符串,我们可以使用xxd -p -r
命令:
shell❯ echo 6675636b750a | xxd -p -r fucku
另一种常见且非常古老的编码技术是凯撒密码,它将每个字母移动固定的数字。例如,移位 1 个字符使a
变为b
, b
变为c
,依此类推。凯撒密码的许多变体使用不同数量的移位,其中最常见的是rot13
,它将每个字符向前移位 13 次。
尽管这种编码方法使任何文本看起来都是随机的,但仍然可以发现它,因为每个字符都映射到特定的字符。例如,在rot13
中, http://www
变为uggc://jjj
,它仍然具有一些相似之处并且可能被识别为这样。
Linux 中没有特定的命令来执行rot13
编码。然而,创建我们自己的命令来进行字符转换相当容易:
shellecho https://www.hackthebox.eu/ | tr 'A-Za-z' 'N-ZA-Mn-za-m' uggcf://jjj.unpxgurobk.rh/
shell❯ echo fucku | tr 'A-Za-z' 'N-ZA-Mn-za-m' shpxh ❯ echo shpxh | tr 'A-Za-z' 'N-ZA-Mn-za-m' fucku
我们可以在网上找到数百种其他编码方法。尽管这些是最常见的,但有时我们会遇到其他编码方法,这可能需要一些经验来识别和解码。
有些工具可以帮助我们自动确定编码类型,例如Cipher Identifier 。使用Cipher Identifier尝试上面的编码字符串,看看它是否可以正确识别编码方法
除了编码之外,许多混淆工具还利用加密,即使用密钥对字符串进行编码,这可能会使混淆的代码很难进行逆向工程和反混淆,特别是如果解密密钥未存储在脚本本身中的话。
[!NOTE]
注意区分编码和加密:
加密:
特点
- 需使用密钥:加密和解密过程需要密钥。
- 数据不可读:加密后的数据对人类和计算机是不可理解的。
- 安全性要求高:加密算法设计需要确保即使加密数据被截获,没有密钥也无法解密。
编码:
特点
- 无需密钥:编码和解码不需要密钥。
- 数据可读性好:编码后的数据通常仍然是可读的或可轻松解码。
- 强调兼容性:用于确保数据在不同环境中能正确表示。
Congratulations on reaching the end of JavaScript Deobfuscation
. We hope you can now recognize obfuscated scripts and deobfuscate them, decode their output, and replicate their functions.
The following is a summary of what we learned:
HTML
source code of the webpage and located the JavaScript code.本文作者:Hyrink
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!