0x01 代码审计-app

访问靶场地址,我们可以先下载这道题的代码。经过观察可以发现这道题使用的后端平台基于Node.js,这类题目中所蕴含的漏洞一般会分为两种情况:一种是基于二进制分析(Pwn)的漏洞,这种漏洞一般会对Node.js所运行的平台(Google V8引擎)进行攻击,从而篡改引擎中运行的数据实现攻击所产生的效果;另一种是基于Web方面的攻击,比如对于低版本NPM包中现存的漏洞实行攻击,或是类似原型链污染从而对JS语言本身进行攻击。从实际情况来看,这种攻击方式应该更倾向于是后一种。

我们先来分析源码结构。

image.png

根据源码结构进行分析,首先我们可以得到以下信息:

config.js,配置参数文件,对于密钥的攻击一般体现在这个文件之中;

setup.sh和start.sh,这两个文件是环境启动的文件,对于环境错误配置的攻击一般体现在这两个文件之中;

app.js,这个文件是程序主要代码,对于原型链的攻击以及代码本身漏洞的攻击一般体现在这个文件之中;

package.json和package-lock.json,这个是Node.js的依赖包的版本代码,对于包的攻击一般体现在里面;

routes文件夹,一般包含了路由及方法执行相关的代码,有些漏洞点会选择放在路由中。
现在我们来逐个对文件进行分析。

config.js

image.png

给的代码告诉我们这个session_keys是每次启动时随机生成的,我们无法通过源代码拿到密钥。而且这个随机的值在没有设定随机种子的情况下没有办法探测,所以这条路应该是封死了。

app.js

关注以下代码

image.png

我们可以发现,代码的主体的路由使用放在了routes文件夹里面,再对整个app.js进行审计时我们会发现没有特别明显的漏洞点的出现,所以我们应该把目光放在routes文件夹中。

routes/index.js

image.png

对于代码进行分析,我们很容易就可以发现存在一个SSRF漏洞:debug路由。观察执行路径,我们可以知道这里可以通过POST方法发送一次GET请求到本地,然后就可以通过GET方法来进行解析。现在我们来分析GET方法;

首先我们发现需要对于IP黑名单进行绕过。这里我们的需求时发送一个不在IP黑名单却可以请求到本地的地址。根据计算机网络课程所述关于本地环回地址的部分,我们可以知道通过127.0.0.2就可以轻松的绕过第一部分的情况。然后就是经过一层对于单引号和双引号的过滤,这里根据观察我们可以发现在进行过滤后我们使用了url.parse(u).href来获得了真实的URL地址,最后使用命令执行的方法来写入这个地址到log文件中。

这里一般会存在怎样的漏洞呢?我们可以发现,在逻辑过程,我们实际执行的参数并不是经过替换的参数(我们实际执行的参数是经过parse的参数,在parse前我们进行了替换),而是存在一道后处理工序后的参数。正是这样的参数,可能存在一些安全问题。

接下来我们分析url.parse到底干了些啥,以及漏洞可能的存在点。

0x02 代码审计-url.parse

我们先发现,这里我们用到了一个方法,叫做url.parse(u).href。这里我们根据JS的链式执行规则,我们先执行parse再取出href。观察其实际的执行代码,我们可以看到:

node/src/node_url.h

image.png

发现其代码是一个方法,执行操作是用到了SerializeURL
我们来看看parse相关的代码:

image.png

这里我们用到了Parse函数,在执行中相当于我们使用url.parse生成了一个URL对象,然后执行这个href来获得参数。

node/src/node_url.cc

在1359行,我们找到了相关Parse的源代码。

image.png

这里我们经过对于程序的分析发现,里面用到了一些AppendOrEscape的函数。

image.png

这里用到了一个词叫做Percent-Encode,表示对于URL的编码。

但是这些函数实际上在当前程序中已经不可用了(在之前的代码中,使用了req.query.url)的时候已经进行了一次对于URLEncode的解析。所以在当前的情况下,再次使用参数解析就会造成一些意想不到的逃逸。所以,我们通过二次编码就可以解决这个对于URL的过滤问题。

注意这里需要加入@符号才能完成对于二次编码的解析。由于@符号在URL Host中表示用户登录的意思,具体可以观察源代码中1711行:

image.png

这里使用了AppendOrEscape来对usernamepassword进行解析,而我们可以发现这里进行了USERINFO_ENCODE_SET参数的设定,观察这个Set:

image.png

我们可以发现包含二次注入的关键0x250x27。正是百分号和引号造成了注入。为什么我们不能直接在Path中进行注入呢?再次观察另一个SET:PATH_ENCODE_SET

image.png

我们发现在这里面0x250x27是被过滤的对象。虽然在QUERYSET中我们可以发现同样可以二次编码绕过,但是href在提取中只提取了URLHost部分,不会提取QueryString部分。所以,我们需要在Payload里面加上@号来实现逃逸。

0x03 命令执行

我们现在已经绕过了关于引号的问题(也就是我们已经可以造成引号逃逸了)。现在我们所需要的就是进行命令执行。

考虑这里的命令执行和字符串拼接一样,所以我们可以直接尝试执行。由于URLEncode的编码问题,我们来尝试使用Shell特有的一些字符来绕过从而达到空格的目的。这里我们使用${IFS}来当成空格使用。IFS在Shell里面被称为内部字段分隔符,可以起到分割字符串的作用。可以通过%00截断来屏蔽掉后面命令造成的影响。

那么操作方法就呼之欲出了:使用二次编码绕过来执行命令,结合Shell相关知识来进行命令执行拿到flag。由于复现环境的问题,我们要通过0177.1:3000来访问题目环境。

{"url":"http://0177.1:3000/debug?url=http://a%2527@a;cp${IFS}/flag${IFS}/tmp/log%00"}
image.png

0x04 防御思路

对于这些二次解析注入造成的攻击,只要保证我们所执行的东西经过一次解析就可以了。所以应该把replace接在href后面,这样就能完成对于攻击的防御。

It is my final heart.
最后更新于 2022-07-24