0%

frida-trace扩展信息到burp

frida-trace扩展信息到burp

[TOC]

1. 简介

参考原文Tracing API calls in Burp with Frida

利用frida实现一个与burp结合实时修改二进制函数参数返回值插件。

  1. 可以burp查看frida hook的值,burp有很多非常人性化的界面可以参考。
  2. 可以burp修改hook的参数,注入测试
    1. NSString*
    2. NSData*
    3. byte * or char*

2. 配置

2.1 frida-trace 传输到代理 127.0.0.1:26080

  1. python3 tracer.py -m “+[EncryptUtils AESEncrypt:key:]” -Uf me.pandamac.iOSPentest
  2. python3 tracer.py -m “+[EncryptUtils AESEncrypt:key:]” -f me.pandamac.iOSPentest -D c27808502c1f74e1659074e5154256360fe17f648
  3. python3 tracer.py -m “-[EncryptUtils ret_NSData_by_bytes:byNSString:]” -f me.pandamac.iOSPentest -D c7808502c1f74e1659074e5154256360fe17f648
# coding:utf-8

from frida_tools import tracer
import json
import requests

BURP_HOST = 'localhost'
BURP_PORT = 26080

def frida_process_message(self, message, data, ui):
handled = False
if message['type'] == 'input':
handled = True
elif message['type'] == 'send':
stanza = message['payload']
if stanza['from'] == '/request':
req_data = stanza['payload']
print(req_data)
# orig_json_data = json.loads(req_data)
# orig_request_url = orig_json_data.pop(u'orig_request_url')
orig_request_url = 'execute'
req = requests.request('REQUEST',
'http://%s:%d/' % (BURP_HOST, BURP_PORT),
headers={
'content-type': 'text/plain',
'ORIG_REQUEST_URI': orig_request_url
},
data=req_data)
return_content = req.content.decode('utf-8')
# req = requests.request('REQUEST', 'http://%s:%d/' % (BURP_HOST, BURP_PORT),
# headers={'content-type':'text/plain', 'ORIG_REQUEST_URI': orig_request_url},
# data=json.dumps(orig_json_data))
self._script.post({'type':'input', 'payload': return_content})
handled = True
elif stanza['from'] == '/response':
req_data = stanza['payload'].encode('utf-8')
req = requests.request('RESPONSE', 'http://%s:%d/' % (BURP_HOST, BURP_PORT),
headers={'content-type': 'text/plain'},
data=req_data)
self._script.post({'type': 'output', 'payload': req.content.decode('utf-8')})
handled = True
if not handled:
self.__process_message(message, data, ui)

tracer.Tracer.__process_message = tracer.Tracer._process_message
tracer.Tracer._process_message = frida_process_message

if __name__ == '__main__':
tracer.main()

2.2 127.0.0.1:26080 重定位到 127.0.0.1:27080

-w674

2.3 回显server接受信息,返回信息

  • 如果没有这个,将返回数据为 burp的代理信息给js代码,并不是burp修改后的数据。
  • 相当于将数据修改后发送到127.0.0.1:27080,然后通过requests.request 返回的数据为回显server回显出来的数据。
# coding: utf-8
import platform

if int(platform.python_version_tuple()[0]) < 3:
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
else:
from http.server import HTTPServer, BaseHTTPRequestHandler
from optparse import OptionParser
ECHO_PORT = 27080
class RequestHandler(BaseHTTPRequestHandler):
def do_REQUEST(self):
request_path = self.path
print('Recving request connction...')
request_headers = self.headers
content_length = request_headers.getheaders('content-length')
length = int(content_length[0]) if content_length else 0

self.send_response(200)
self.end_headers()
self.wfile.write(self.rfile.read(length))
def do_RESPONSE(self):
request_path = self.path
print('Recving Response connction...')
request_headers = self.headers
content_length = request_headers.getheaders('content-length')
length = int(content_length[0]) if content_length else 0

self.send_response(200)
self.end_headers()

self.wfile.write(self.rfile.read(length))
def main():
print('Listening on localhost: %d' % ECHO_PORT)
server = HTTPServer(('', ECHO_PORT), RequestHandler)
server.serve_forever()
if __name__ == '__main__':
print('Staring echo server on port %d' % ECHO_PORT)
main()

2.4 trace js 测试代码,需改参数和返回值

{
/**
* Called synchronously when about to call +[EncryptUtils AESEncrypt:key:].
*
* @this {object} - Object allowing you to store state for use in onLeave.
* @param {function} log - Call this function with a string to be presented to the user.
* @param {array} args - Function arguments represented as an array of NativePointer objects.
* For example use args[0].readUtf8String() if the first argument is a pointer to a C string encoded as UTF-8.
* It is also possible to modify arguments by assigning a NativePointer object to an element of this array.
* @param {object} state - Object allowing you to keep state across function calls.
* Only one JavaScript function will execute at a time, so do not worry about race-conditions.
* However, do not use this to store function arguments across onEnter/onLeave, but instead
* use "this" which is an object for keeping state local to an invocation.
*/
onEnter: function (log, args, state) {
log('+[EncryptUtils AESEncrypt:' + args[2] + ' key:' + args[3] + ']');
this.args0=args[0];
this.args1=args[1];
this.args2=args[2];
this.args3=args[3];

var func_name_nstring = ObjC.selectorAsString(this.args1);
send_data=JSON.stringify({
FH_FUNC_NAME: func_name_nstring,
FH_ARG_COUNT: 3,
FH_DETAIL_OBJECT: ObjC.Object(this.args0).toString(),
FH_DETAIL_ARG1: ObjC.Object(this.args2).toString(),
FH_DETAIL_ARG2: ObjC.Object(this.args3).toString(),
// FH_DETAIL_RET: ObjC.Object(retval).toString(),
}, null, 2);

send({from:'/request', payload: send_data});

var op = recv('input', function onMessage(value){
log("Waiting Input payload");
// log(value.payload);
// log("Waiting Input recv_data start");
recv_data = JSON.parse(value.payload)
log(recv_data)
// log("Waiting Input recv_data end");

var NSString = ObjC.classes.NSString;
args[2] = NSString.alloc().initWithString_(recv_data["FH_DETAIL_ARG1"]);
args[3] = NSString.alloc().initWithString_(recv_data["FH_DETAIL_ARG2"]);
});
op.wait();
},

/**
* Called synchronously when about to return from +[EncryptUtils AESEncrypt:key:].
*
* See onEnter for details.
*
* @this {object} - Object allowing you to access state stored in onEnter.
* @param {function} log - Call this function with a string to be presented to the user.
* @param {NativePointer} retval - Return value represented as a NativePointer object.
* @param {object} state - Object allowing you to keep state across function calls.
*/
onLeave: function (log, retval, state) {
console.log(retval);

var func_name_nstring = ObjC.selectorAsString(this.args1);
send_data=JSON.stringify({
FH_FUNC_NAME: func_name_nstring,
FH_DETAIL_RET: ObjC.Object(retval).toString(),
}, null, 2);

send({from:'/response', payload: send_data});

var op = recv('output', function onMessage(value){
log("Waiting output payload");
// log(value.payload);
// log("Waiting output recv_data start");
recv_data = JSON.parse(value.payload)
log(recv_data)
// log("Waiting output recv_data end");

var NSString = ObjC.classes.NSString;
var tmp_retval = NSString.alloc().initWithString_(recv_data["FH_DETAIL_RET"]);
retval.replace(tmp_retval);
});
op.wait();
}
}

3.测试效果

3.1 burp接受函数参数包

-w468
可以进行修改,然后点击Forward

3.2 burp接受函数返回包

-w504
可以进行修改,然后点击Forward

4.总结

  • 直接测试所有二进制的函数进出返回值,用于测试工作,剩下工作就是编写合适的 js 代码,将trace的函数用于通用。当然也可以不用trace,自己写hook 框架,达到任意修改函数返回值的效果。
  • 如果函数有时间校验的话,可能会出错,这时候用代码实现修改即可。