avaScript 引擎负责运行开发编写的 JS 代码。它由很多部分组成,对我们而言最重要的是 JIT 编译器,它的作用是当有些函数变得很热门被调用很多次时,它会把这个函数编译成机器代码来改进它的性能。JIT 编译器还负责优化代码。它具有很多针对代码的假设,它不希望这些假设崩溃。我们随后会讲讲 JIT 编译器中出现的相关漏洞问题。
来了解下 JavaScript 的基础知识,它是一种动态输入语言,可读性尚可。JS 对象具有“原型”,用于从其它对象中继承各种功能,它在漏洞发现过程中很重要。它可通过 _proto_ 属性更改对象的原型进行修改。 Proxy 是可用于重新定义基础操作的对象。我们可以通过这些基础操作,将调用限制在 getter 和 setter 等函数中。
来看下 ChakraCore。JavaScript 具有数组,而 ChakraCore 具有类型数组。我们来看看第一种类型 JavascriptNativeIntArray。它用于存储整数,每个元素具有四个字节。(举例: varint_arr=[1])另外一种类型是 JavascriptNativeFloatArray,它用于存储浮点数,和C语言不同,它的每个元素具有8个字节(举例 varfloat_arr=[13.37])。 JavascriptArray 用于存储对象(主要是指针),每个元素具有8个字节(举例: varobject_arr=[{}])。
我们来看下如何转换这些类型。
其中最后一种 (也就是 aray2._proto_=array1 中的 array1 直接转换为 JavascriptArray) 转换在 JavaScript 引擎很少见,但对于我们今天讲的主题很重要。当我们有两个数组,并将其中一个设置为另外一个的原型,那么充当原型的这个数组就会被直接转换为 JavascriptArray。这一点我们稍后再着重讲。
我们再来看看这些数组在内存中是什么样的。举个例子:
我们来看看实际在内存中,当调试如下样本代码时,可以看到我们刚才提到的字段状态 (vararr=[0xaaaaaa,0x31377];)。
红框部分是 JavascriptArray 属性,我们能看到数组的初始值,也就是 ArrayFlags 的值。绿框部分是实际的片段属性,它有长度、大小。蓝框是片段的内存布局(包括元素,下图的地址是 pArr->head)。图中右下角我们定义了两个片段。那么什么是 ArrayFlags ?它们是提示数组的某些东西的一些 flag。在这个案例中,它被定义为一个枚举类型。我们感兴趣的字段是 JavascriptArray 的 arrayFlags 字段 HasNoMissingValues(如图)。
在我们的例子中,被我们定义为 ArrayFlags 的 InitialArrayValue 实际上由两个不同的 flag 组成:一个是 ObjectArrayFlagsTag flag,它和我们讲的内容不重要就不讲了;我们将重点看第二个 HasNoMissingValues flag,它说明数组并不存在缺失的值,也就是说数组中不存在任何洞 (holes)。那么,“洞”是什么意思?我们创建一个数组,元素之间有一些值。在 ChakraCore 案例中,它有三个元素,但中间的一个元素是缺失的。
放在这里的值,它们在内存中表示为这些常数。我举这个例子是因为这样我们更容易地能在内存布局中发现它们。
像这里(如图)就存在一个“洞”,它并没有开启 HasNoMissingValues,也就是说数组中存在洞,数组中确实存在缺失的值。这看似很合理,但当我们查看内存布局时,我们会发现一些奇怪之处。我们来看下这些所谓的“缺失的值”是如何在内存中表示的。
这(图)是片段的内存转储,红色部分是数组的元素。我们看到 deadbeef deadbeef ,但在中间即“缺失的值”(“洞”)的位置,我们看到了一些 Magic 常数。 0xfff80002fff80002是从哪里来的?这些常数代表的是“缺失的值”或者说数组中的“洞”似乎能说得通。但如之前所述,我们已经知道有一些东西能代表“缺失的值”,就是没有 HasNoMissingValues flag。而现在我们似乎发现了另外一种表示方法,就是数组的内容(上面提到的 Magic 常量)。
这很奇怪,也引发了很多问题:数组的 flag 和数组的内容不匹配怎么办? HasNoMissingValues 设为真,那么就意味着不存在任何“洞”;但是 数组中实际上存在一个“缺失的值”。另外,我们在某种程度上把“数据”和“元数据”混为一谈了,因为如果把常量当作控制流,那么我们如果能够伪造它的话就很有意思了。
事实证明,我们真的能够伪造这个“缺失的值”。这是由 @s0rryMybad 和 @lokihardt 发现的漏洞(如上图),获得了CVE 编号 CVE-2018-8505。他们就是把我们之前看到的常量转换为浮点数数组,从而伪造了“缺失的值”,进而发现了漏洞。缓解这个漏洞的方法有很多,可以通过不断更改这个Magic值常量或增加更多的检查加固安全性。
渗透爬虫软件 资源采集 劫持域名 提取抓料子 六合·色站·彩票·博彩·金融 接渗透拿站,各大网站数据-拖库-提权联系 域名劫持 DNS 入侵网站 联系:@hacker_HouZiM
来了解下 JavaScript 的基础知识,它是一种动态输入语言,可读性尚可。JS 对象具有“原型”,用于从其它对象中继承各种功能,它在漏洞发现过程中很重要。它可通过 _proto_ 属性更改对象的原型进行修改。 Proxy 是可用于重新定义基础操作的对象。我们可以通过这些基础操作,将调用限制在 getter 和 setter 等函数中。
来看下 ChakraCore。JavaScript 具有数组,而 ChakraCore 具有类型数组。我们来看看第一种类型 JavascriptNativeIntArray。它用于存储整数,每个元素具有四个字节。(举例: varint_arr=[1])另外一种类型是 JavascriptNativeFloatArray,它用于存储浮点数,和C语言不同,它的每个元素具有8个字节(举例 varfloat_arr=[13.37])。 JavascriptArray 用于存储对象(主要是指针),每个元素具有8个字节(举例: varobject_arr=[{}])。
我们来看下如何转换这些类型。
其中最后一种 (也就是 aray2._proto_=array1 中的 array1 直接转换为 JavascriptArray) 转换在 JavaScript 引擎很少见,但对于我们今天讲的主题很重要。当我们有两个数组,并将其中一个设置为另外一个的原型,那么充当原型的这个数组就会被直接转换为 JavascriptArray。这一点我们稍后再着重讲。
我们再来看看这些数组在内存中是什么样的。举个例子:
我们来看看实际在内存中,当调试如下样本代码时,可以看到我们刚才提到的字段状态 (vararr=[0xaaaaaa,0x31377];)。
红框部分是 JavascriptArray 属性,我们能看到数组的初始值,也就是 ArrayFlags 的值。绿框部分是实际的片段属性,它有长度、大小。蓝框是片段的内存布局(包括元素,下图的地址是 pArr->head)。图中右下角我们定义了两个片段。那么什么是 ArrayFlags ?它们是提示数组的某些东西的一些 flag。在这个案例中,它被定义为一个枚举类型。我们感兴趣的字段是 JavascriptArray 的 arrayFlags 字段 HasNoMissingValues(如图)。
在我们的例子中,被我们定义为 ArrayFlags 的 InitialArrayValue 实际上由两个不同的 flag 组成:一个是 ObjectArrayFlagsTag flag,它和我们讲的内容不重要就不讲了;我们将重点看第二个 HasNoMissingValues flag,它说明数组并不存在缺失的值,也就是说数组中不存在任何洞 (holes)。那么,“洞”是什么意思?我们创建一个数组,元素之间有一些值。在 ChakraCore 案例中,它有三个元素,但中间的一个元素是缺失的。
放在这里的值,它们在内存中表示为这些常数。我举这个例子是因为这样我们更容易地能在内存布局中发现它们。
像这里(如图)就存在一个“洞”,它并没有开启 HasNoMissingValues,也就是说数组中存在洞,数组中确实存在缺失的值。这看似很合理,但当我们查看内存布局时,我们会发现一些奇怪之处。我们来看下这些所谓的“缺失的值”是如何在内存中表示的。
这(图)是片段的内存转储,红色部分是数组的元素。我们看到 deadbeef deadbeef ,但在中间即“缺失的值”(“洞”)的位置,我们看到了一些 Magic 常数。 0xfff80002fff80002是从哪里来的?这些常数代表的是“缺失的值”或者说数组中的“洞”似乎能说得通。但如之前所述,我们已经知道有一些东西能代表“缺失的值”,就是没有 HasNoMissingValues flag。而现在我们似乎发现了另外一种表示方法,就是数组的内容(上面提到的 Magic 常量)。
这很奇怪,也引发了很多问题:数组的 flag 和数组的内容不匹配怎么办? HasNoMissingValues 设为真,那么就意味着不存在任何“洞”;但是 数组中实际上存在一个“缺失的值”。另外,我们在某种程度上把“数据”和“元数据”混为一谈了,因为如果把常量当作控制流,那么我们如果能够伪造它的话就很有意思了。
事实证明,我们真的能够伪造这个“缺失的值”。这是由 @s0rryMybad 和 @lokihardt 发现的漏洞(如上图),获得了CVE 编号 CVE-2018-8505。他们就是把我们之前看到的常量转换为浮点数数组,从而伪造了“缺失的值”,进而发现了漏洞。缓解这个漏洞的方法有很多,可以通过不断更改这个Magic值常量或增加更多的检查加固安全性。
渗透爬虫软件 资源采集 劫持域名 提取抓料子 六合·色站·彩票·博彩·金融 接渗透拿站,各大网站数据-拖库-提权联系 域名劫持 DNS 入侵网站 联系:@hacker_HouZiM
我通过检查并提取所有相关代码发现该应用程序是用 Django 构建的
在 CGI 协议中,有一些变量,包括要执行的脚本路径。我认为 uWSGI 协议也应该有相同的机制。
以下是从nginx代理到uwsgi套接字的示例数据包:
请注意,查询 uwsgi 套接字需要一些变量。经过一些尝试,我找到了几个重要的变量
所以,如果我能使用以下变量查询 uwsgi 套接字,就足以执行任意脚本:
我需要发送任意 TCP 数据包来查询 uwsgi,因为他们使用的是特殊的二进制协议。您可以在uwsgi 文档中找到 uwsgi 协议的详细信息。它非常简单,就像这样
渗透爬虫软件 资源采集 劫持域名 提取抓料子 六合·色站·彩票·博彩·金融 接渗透拿站,各大网站数据-拖库-提权联系 域名劫持 DNS 入侵网站 联系:@hacker_HouZiM
/proc/self/cmdline。首先,我尝试使用SECRET_KEY`from`获取 shell settings.py,但没有找到序列化相关的代码。接下来,我发现 uWSGI 配置使用 TCP 套接字作为 Nginx 代理的后端服务,所以我尝试利用这一点。在 CGI 协议中,有一些变量,包括要执行的脚本路径。我认为 uWSGI 协议也应该有相同的机制。
以下是从nginx代理到uwsgi套接字的示例数据包:
请注意,查询 uwsgi 套接字需要一些变量。经过一些尝试,我找到了几个重要的变量
UWSGI_FILE,并SCRIPT_NAME用它们来指定要执行的脚本路径。 UWSGI_FILE其中一个是脚本路径,SCRIPT_NAME另一个与要触发的函数名称有关(我不知道具体是什么,但并不重要)。所以,如果我能使用以下变量查询 uwsgi 套接字,就足以执行任意脚本:
我需要发送任意 TCP 数据包来查询 uwsgi,因为他们使用的是特殊的二进制协议。您可以在uwsgi 文档中找到 uwsgi 协议的详细信息。它非常简单,就像这样
2 byte (length) + n byte (data)。渗透爬虫软件 资源采集 劫持域名 提取抓料子 六合·色站·彩票·博彩·金融 接渗透拿站,各大网站数据-拖库-提权联系 域名劫持 DNS 入侵网站 联系:@hacker_HouZiM
S2-029漏洞的问题是OGNL表达式二次执行,对应struts2版本为Struts 2.0.0 - Struts 2.3.24.1(2.3.20.3除外)官方对这个漏洞的简介是会对分配给某些标记的属性值进行二次执行,因此可以传入一个值,该值在rendered tags attribute会再次执行。
我们可以观察一下struts2是如何处理html的id标签,在看UIBean类之前,先讲一下OGNL二次执行的点,首先是这样的。
我在example.jsp的代码是这样的,接收参数的值用${value}包含了起来,就像这样。
那么我存进去的表达式,就会产生OGNL表达式执行,再看看后端的处理方式。
findString方法执行了一次表达式,随后findStringIfAltSyntax又解析了一次表达式
只要html标签的id值可控,就可以任意代码执行,name属性的值同样。
在struts2 2.3.24版本后官方增加了一些黑名单类列表以及一些包名,在struts-default.xml。
安全管理器也增加了包检查器,类检查器和成员类检查器,关于这些检查器的实现,都是从定义好的排除类名迭代循环匹配
结合POC之所以能够执行命令,是因为OGNL上下文一个安全检查方法的逻辑判断出错了,导致现有安全机制失效。关注一下ObjectPropertyAccessor类的setPossibleProperty方法,这个方法是对表达式完成赋值操作的。
渗透爬虫软件 资源采集 劫持域名 提取抓料子 六合·色站·彩票·博彩·金融 接渗透拿站,各大网站数据-拖库-提权联系 域名劫持 DNS 入侵网站 联系:@hacker_HouZiM
我们可以观察一下struts2是如何处理html的id标签,在看UIBean类之前,先讲一下OGNL二次执行的点,首先是这样的。
我在example.jsp的代码是这样的,接收参数的值用${value}包含了起来,就像这样。
那么我存进去的表达式,就会产生OGNL表达式执行,再看看后端的处理方式。
findString方法执行了一次表达式,随后findStringIfAltSyntax又解析了一次表达式
只要html标签的id值可控,就可以任意代码执行,name属性的值同样。
在struts2 2.3.24版本后官方增加了一些黑名单类列表以及一些包名,在struts-default.xml。
安全管理器也增加了包检查器,类检查器和成员类检查器,关于这些检查器的实现,都是从定义好的排除类名迭代循环匹配
结合POC之所以能够执行命令,是因为OGNL上下文一个安全检查方法的逻辑判断出错了,导致现有安全机制失效。关注一下ObjectPropertyAccessor类的setPossibleProperty方法,这个方法是对表达式完成赋值操作的。
渗透爬虫软件 资源采集 劫持域名 提取抓料子 六合·色站·彩票·博彩·金融 接渗透拿站,各大网站数据-拖库-提权联系 域名劫持 DNS 入侵网站 联系:@hacker_HouZiM
先调用一下这个模块看一下有哪些options是属于用户输入范畴的。
可以看到msf的模块也支持自定义checkurl,并且提供自定义测试标记,即响应码和匹配模式,并且还支持是否验证CONNECT通道代理方法。所以可以发现从用户自定义角度来说,msf相对nmap会更加人性化。不过基本上也是和nmap有异曲同工之妙,为什么这么说,因为,emmm……
然后对模块的方法实现进行一个分析,首先是一个初始化方法,其实就是默认值的赋值,和刚刚在options选项中看到的基本一样。
第二个方法是run_host(),这个方法就是自定义用户输入的内容。直接看一下执行主体的verify_target()方法,脚本里写的代码很简单。就是调用了send_request_cgi()方法获取到response,然后对response处理分析来判断是否存在proxy打开的情况。判断一共分3中,没有response判断没有proxy情况,判断普通http代理就通过判断响应码或者响应数据中的匹配模式,而判断CONNECT通道代理就是只判断响应码是否符合。
其实重点在于这个send_request_cgi()方法,定位一下这个方法地址,先看一下全局include,可以确定为includeMsf::Exploit::Remote::HttpClient,找到这个方法的实现脚本地址为/opt/metasploit-framework/embedded/framework/lib/msf/core/exploit/http/client.rb,其实如果看不懂的话可以用grep一键找。
理一下send_request_cgi()方法主体,执行方法主要是下面这一段,所以接下来要定位一下connect()方法,来弄清楚request_cgi的具体执行参数的表达含义。
connect()方法首先会判断一下ssl协议,然后开始连接http server,可以看到这边的参数opt其实就是制定远程主机rhost的值,实际上就是check_url传入的值。
追踪到/opt/metasploit-framework/embedded/framework/lib/rex/proto/http/client.rb的request_cgi()方法,其实也就是基本的定义参数,然后进行client客户端去连接。
渗透爬虫软件 资源采集 劫持域名 提取抓料子 六合·色站·彩票·博彩·金融 接渗透拿站,各大网站数据-拖库-提权联系 域名劫持 DNS 入侵网站 联系:@hacker_HouZiM
可以看到msf的模块也支持自定义checkurl,并且提供自定义测试标记,即响应码和匹配模式,并且还支持是否验证CONNECT通道代理方法。所以可以发现从用户自定义角度来说,msf相对nmap会更加人性化。不过基本上也是和nmap有异曲同工之妙,为什么这么说,因为,emmm……
然后对模块的方法实现进行一个分析,首先是一个初始化方法,其实就是默认值的赋值,和刚刚在options选项中看到的基本一样。
第二个方法是run_host(),这个方法就是自定义用户输入的内容。直接看一下执行主体的verify_target()方法,脚本里写的代码很简单。就是调用了send_request_cgi()方法获取到response,然后对response处理分析来判断是否存在proxy打开的情况。判断一共分3中,没有response判断没有proxy情况,判断普通http代理就通过判断响应码或者响应数据中的匹配模式,而判断CONNECT通道代理就是只判断响应码是否符合。
其实重点在于这个send_request_cgi()方法,定位一下这个方法地址,先看一下全局include,可以确定为includeMsf::Exploit::Remote::HttpClient,找到这个方法的实现脚本地址为/opt/metasploit-framework/embedded/framework/lib/msf/core/exploit/http/client.rb,其实如果看不懂的话可以用grep一键找。
理一下send_request_cgi()方法主体,执行方法主要是下面这一段,所以接下来要定位一下connect()方法,来弄清楚request_cgi的具体执行参数的表达含义。
connect()方法首先会判断一下ssl协议,然后开始连接http server,可以看到这边的参数opt其实就是制定远程主机rhost的值,实际上就是check_url传入的值。
追踪到/opt/metasploit-framework/embedded/framework/lib/rex/proto/http/client.rb的request_cgi()方法,其实也就是基本的定义参数,然后进行client客户端去连接。
渗透爬虫软件 资源采集 劫持域名 提取抓料子 六合·色站·彩票·博彩·金融 接渗透拿站,各大网站数据-拖库-提权联系 域名劫持 DNS 入侵网站 联系:@hacker_HouZiM
通过使用 dnSpy,我们可以从内存中导出电路板数据,并编写一些 Python 脚本来提取这些单元格。以下是它们的位置(行,列):
一开始我尝试点击那些单元格,结果发现棋盘的高度超过了屏幕高度,导致我无法点击单元格[24, 28]……🤨
总之,我们仍然可以使用 dnSpy 中的“设置下一条语句”功能来劫持程序流程。这里我让程序跳转到那一行。
直接修改
渗透爬虫软件 资源采集 劫持域名 提取抓料子 六合·色站·彩票·博彩·金融 接渗透拿站,各大网站数据-拖库-提权联系 域名劫持 DNS 入侵网站 联系:@hacker_HouZiM
1. [20, 7]
2. [24, 28]
3. [7, 28]
一开始我尝试点击那些单元格,结果发现棋盘的高度超过了屏幕高度,导致我无法点击单元格[24, 28]……🤨
总之,我们仍然可以使用 dnSpy 中的“设置下一条语句”功能来劫持程序流程。这里我让程序跳转到那一行。
1
this.RevealedCells.Add(row * MainForm.VALLOC_NODE_LIMIT + column);
直接修改
row&col变量(在 dnSpy 中按 F2 键)到那些非地雷单元格的位置。然后跳转到GetKey获取标志的函数:渗透爬虫软件 资源采集 劫持域名 提取抓料子 六合·色站·彩票·博彩·金融 接渗透拿站,各大网站数据-拖库-提权联系 域名劫持 DNS 入侵网站 联系:@hacker_HouZiM