由于我的挖洞团队『大哥们别挖了孩子要哭惹』,经过上一篇hook前端的文章的影响后,发现其实——只要hook前端的危险生成函数,然后一般而言,危险函数会和过滤函数存在同一个或者相近的作用域下,简单来说:
let filter_data = filter(raw_data)
generate(a)//危险函数
所以用之前想到的hook方法hook住这个generate函数并找到与他同级或者上级的过滤函数这个是很必要的,那么我们要怎么找呢?
我们发现其实js在每个函数作用域下提供一个arguments的变量,而这个变量中的callee.caller则指向了上层函数,但是这里存在一个问题:当使用js内部作用域下使用'use strict'或者"use strict"时,则会导致callee.caller 会触发异常(当然运气不好的话可能是null,为什么出现null是运气不好?因为没异常你也不知道怎么回事,网上解释说是顶层调用才会出现null,而且我第一时间也认为,出异常的话才会是use strict的错(因为我在用油猴脚本hook执行的时候,他会在console.log中红字提示,去掉"use strict"即可),所以忽略了这个问题,导致我和朋友查了一个半小时,最后看了控制台对null输出的解释才知道原来是use strict的原因)
所以回到我们的正题,思前想后为了过滤use strict标签,就想到了用bp来进行拦截修改,这时候就想到bp的插件:
然后通过乌云几篇留下的插件贴和burpsuite 官方上example后就差不多懂内容,关键难的是配置:python环境,因为需要jython,而新版的pycharm不兼容,eclipse也不兼容。 java环境打包runable jar 给bp执行 执行又告诉你缺包,但是拿jadx打开又看到里面已经都被打包进去了,整个过程让我暴躁的不行。(这个流程至少从昨天5点开始到夜里2点,至少6个小时在配置环境!!!!!!!,最后放弃了只能用jython的方式,唯一缺点就是没有语法联想)。
接下来来进入到正题:burpsuite的插件(jython版)
api可以在bp里找(这个api界面是我们要实时切换回来看的)
首先第一步配置环境:
在bp中将python Environment配置为jython的路径
第二部 创建一个入口类(继承,这里应该叫实现IBurpExtenderCallbacks接口):
实现他的接口对应的接口:
实现IBurpExtender
也就是说加载把插件加载进去bp会通过反射来找对应实现了IBurpExtender接口的对象,然后new出实例对象,再调用实例对象的registerExtenderCallback,从而导致我们实现的registerExtenderCallback被触发(也可以直接把他想成是main函数)。
还有关键点就是IBurpExtenderCallbacks这个对象了,可以直接参考api:
IBurpExtenderCallbacks_api
由于篇幅较长,我们这里就简单得将该api中重点(与本篇内容有关的)的api讲解一下:
getHelpers
getStdout
*registerHttpListener
getParameters
getHeaders
(ps:这个api是根据下啦顺序来的,所以可能我们后面的顺序可能不是依赖这个来讲的。)
首先重申我们的需求-》访问网站的js-》bp自动将js中的'use strict'和"use strict"删去-》返回数据给页
所以再这个过程中我们肯定需要监听他的http请求的过程,所以要注册一个HTTP请求的监听事件(registerHttpListener,所以我们也理所应当需要一个IHttpLinstener的实现)
IHttpListener
最简单的方式就是同一个类进行实现
class Somebody(这个是我的hacker名...)Extender(IBurpExtender, IHttpListener):
因为继承(实现了IburpExtender和IHttpListener,所以需要对内部方法registerExtenderCallbacks和processHttpMessage进行实现)
def registerExtenderCallbacks(self, callbacks): self._callbacks = callbacks self._callbacks.egisterHttpListener(this)#注册监听 def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): pass
processHttpMessage三个参数分别是:
toolFlag->来区分插件的一个标号 int
messageIsRequest 是来区分触发该事件的是不是一个Request boolean
messageInfo 则是数据的内容 他的类型是IHttpRequestResponse
接着我们又要去介绍我们的新朋友*IHttpRequestResponse
IHttpRequestResponse
他是一个容器,里面包含一个会话(session)相当于一个request<->response。
但是需要注意的是当触发httplistener的时候如果messageIsRequest==1的话,是仅仅在请求阶段-》那么自然也就get不到Response了。但是在Response阶段你不仅可以getResponse,还能getRequest(会话并不冲突)。
所以我们也可以得到:
def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): if not messageIsRequest:#非Request时
接着我们需要的是“将js文件中的use strict删除掉”,所以我们要知道请求的是一个js文件,这里我们要用到Request头部的信息,所以要用callbacks中的headers
他返回的是一个String数组,内容就是中的每一段(行)内容。
所以其实只要获取第一行判断内容是不是存在.js就行了
request_headers = self._callbacks.getHeaders(messageInfo.getRequest()) if ".js" in request_header[0]:
接着就是对函数内部的过滤了,上面提到了callbacks方法中getParameters函数,但其实这个函数并不是适合response,一开始我使用的时候很正常,后面发现他会将body中的内容=号去掉(这里你可能就理解了),并且他返回的是string[][]。所以其实他更像是request的方法(而且文档说了是给request使用的)(所以这里就淘汰他,我因为不知道这个规则,后面过滤完之后,发现内容js内容老是执行错误,看了下是少了很多等号,然后排查,才排查出是这个函数的问题)。
所以这里我们要怎么办呢?
这里要用到callbacks中的getHelpers方法他会返回一个IExtensionHelpers对象:
helper = self._callbacks.getHelpers()
这个对象篇幅也比较大,所以我就列举几个关键的函数:
IResponseInfo解析response的内容-》IResponseInfo对象
str->bytes,bytes->str,因为python可能涉及编码的问题,所以最好使用它的api
然后我们就可以通过analyzeResponse方法生成一个分析对象-》
response_info = helper.analyzeResponse(messageInfo.getResponse())
接着我们再看看这个生成的对象的接口:
然后其实就可以看到他有个我们需要的方法getBodyOffset:
他是获取我们body段位置的偏移,那我们怎么利用他呢?很简单
response_raw = messageInfo.getResponse()
response_body = response_raw[response_info.getBodyOffset():]#切片到最后
接着的内容就简单了无非是先将bytes转成string-> 切掉内部的use strict->转成bytes 设置回去
所以这里又用到我们的helper了:
return_js_content=helper.bytesToString(response_body).replace('\'use strict\'', '').replace('"use strict"', '')
还有重要的一点就是当body内容变了的时候在response的header里的Content-Length也要跟着变:
response_header = self._callbacks.getHeaders(messageInfo.getResponse()) header_content = '' for header in response_headers: if "Content-Length:" in header: header_content = header_content + 'Content-Length: '+str(len(return_js_content)) +'\n' else: header_content = header_content + header + "\n"
最后一步(拼接传回):
entire_response = header_content + '\n' +return_js_content
messageInfo.setResponse(entire_response)
回想起来忘了关键的一步其实需要将api中的包导出来和对应的py同级:
还有 关键我的代码比较乱,所以上面内容的代码是我重新打了一遍的。
导包也要导正确..因为我当时用的好像是一个example 所以也没有去掉很多没必要的包,还有就是因为我的pycharm 不兼容jython,所以也没办法正确知道导入啥。
最后测试一下结果:
如今去除use strict的效果
还有一点:
不排除js动态生成的use strict..后期可能还要想个能彻底解除的方法..
最后抱怨一句:
严格模式nmsl