回归最本质的信息安全

Brida:将frida与burp结合进行移动app渗透测试

2017年8月1日发布

32,492
1
8

导语:Brida是Burp Suite一个插件,它可以将Burp和Frida结合起来使用,这样就可以根据你的需求修改移动端app与服务器的通信流量。

简介

Brida是Burp Suite一个插件,它可以将Burp和Frida结合起来使用,这样就可以根据你的需求修改移动端app与服务器的通信流量。当分析移动app时,无法截取通信数据包,无法获得数据包一些隐藏的内容,所以就有想法写一个能够截取移动数据包的软件,于是Brida就产生了。

Frida是什么?

Frida是一个动态的代码工具包,它是一款基于python + javascript 的hook与调试框架,相比xposed和substrace cydia更加便捷,可以向Windows,Mac,Linux,iOS和Android上的app注入javascript代码片段,并完全访问程序的内存。因为本文不是主要介绍Frida,所以只会提及其中简单的功能。你可以在https://frida.re/docs/home/查找到详细信息。在阅读这篇文章之前,希望您能够了解到Frida的一些关键概念以及常用功能。

(译者注)4hou传送门:http://www.4hou.com/technology/4675.html

依赖

这个工具是为了让渗透测试人员提高工作效率,读者可能需要了解一些基础的应用程序操作系统的概念,逆向工程和渗透测试知识等等。同时需要了解Burp Suite的功能。下列软件在本次文章中需要用到:

1. Burp Suite Pro 1.7.25
2. Frida 10.11
3. Pyro 4.60 (pip install pyro4)
4. Python 2.7
5. Java 1.8
6. 顺手的反编译软件

我们采用Pyro4作为Burp和Frida的接口,以便python和java能够兼容使用。

Brida可以在https://github.com/federicodotta/Brida下载,稍后几天会在Burp插件商店推出。

常见情况

在你需要对本机应用程序(Android,IOS或者其他平台)做一个完整的了解,这就意味着你需要对它内部与服务器进行的通信进行分析。因为你每天都在做这一件事情,我们此时假设你可以重定向应用的流量,我们此时还假设你已经绕过了ssl固定机制,以及反root机制。所以现在我们就可以用burp查看数据包内部究竟发生了什么。

Schermata-2017-07-27-alle-16.38.03-768x274.png

目前就有点尴尬了,这一个应用程序看起来是使用某种编码或者加密程序,以便向后端服务器发送的数据不被窃听。

更进一步说,应用程序的逻辑可以基于密码令牌,也可以很使用复杂的响应算法等等。那么我们如何修改这些数据呢?大多数的时候唯一可行的方法就是对应用程序进行反编译,然后识别加密解密方法,然后对截取的内容进行解密。

这一方法很明显会浪费很多时间,并且不会每个程序都可以逆向成功,因为产生密钥以及加密程序是存储在设备保护区域,所以不能直接逆向得到。此时Brida就会派上用场,它不会尝试破解密钥或者证书以及查找加密解密程序源码,那么为什么不让他为我们做一些有趣的事情呢?

它是如何工作的?

Brida由以下三部分组成:
1.Brida.jar为Burp插件
2.bridaServicePyro是用于Frida适配到burp上的python脚本,这一部分存储在插件中,在执行brida过程中复制到缓存文件夹中。
3.script.js是要注入到目标应用程序的javascript脚本,它会通过Frida带有的rpc.exports功能将信息返回到拓展程序中。

'use strict';

// 1 - FRIDA EXPORTS

rpc.exports = {

    exportedFunction: function() {

        // Do stuff...  
        // This functions can be called from custom plugins or from Brida "Execute method" dedicated tab

    },

    // Function executed when executed Brida contextual menu option 1.
    // Input is passed from Brida encoded in ASCII HEX and must be returned in ASCII HEX (because Brida will decode the output
    // from ASCII HEX). Use auxiliary functions for the conversions.
    contextcustom1: function(message) {
        return "6566";
    },

    // Function executed when executed Brida contextual menu option 2.
    // Input is passed from Brida encoded in ASCII HEX and must be returned in ASCII HEX (because Brida will decode the output
    // from ASCII HEX). Use auxiliary functions for the conversions.
    contextcustom2: function(message) {
        return "6768";
    },

    // Function executed when executed Brida contextual menu option 3.
    // Input is passed from Brida encoded in ASCII HEX and must be returned in ASCII HEX (because Brida will decode the output
    // from ASCII HEX). Use auxiliary functions for the conversions.
    contextcustom3: function(message) {
        return "6768";
    },

    // Function executed when executed Brida contextual menu option 4.
    // Input is passed from Brida encoded in ASCII HEX and must be returned in ASCII HEX (because Brida will decode the output
    // from ASCII HEX). Use auxiliary functions for the conversions.
    contextcustom4: function(message) {
        return "6768";
    }

}

// 2 - AUXILIARY FUNCTIONS

// Convert a hex string to a byte array
function hexToBytes(hex) {
    for (var bytes = [], c = 0; c < hex.length; c += 2)
    bytes.push(parseInt(hex.substr(c, 2), 16));
    return bytes;
}

// Convert a ASCII string to a hex string
function stringToHex(str) {
    return str.split("").map(function(c) {
        return ("0" + c.charCodeAt(0).toString(16)).slice(-2);
    }).join("");
}

// Convert a hex string to a ASCII string
function hexToString(hexStr) {
    var hex = hexStr.toString();//force conversion
    var str = '';
    for (var i = 0; i < hex.length; i += 2)
        str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
    return str;
}

// Convert a byte array to a hex string
function bytesToHex(bytes) {
    for (var hex = [], i = 0; i < bytes.length; i++) {
        hex.push((bytes[i] >>> 4).toString(16));
        hex.push((bytes[i] & 0xF).toString(16));
    }
    return hex.join("");
}

// 3 - FRIDA HOOKS (if needed)

if(ObjC.available) {

    // Insert here Frida interception methods, if needed 
    // (es. Bypass Pinning, save values, etc.)

}

以上是一个script.js的框架:你可以根据定义导出你想要的函数,其中4个方法还可以简单的添加到burp右键菜单中。

请注意,JS中所有实现的方法必须是小写(Pyro有一些限制)。

Brida提供了三种不同的操作:
1.使用自定义参数调用函数
2.结合右键菜单功能
3.自定义插件存根生成

这篇文章中我们通过IOS10的Signal进行测试,我们已经得到了这个软件的源码.虽然这篇文章是采用的ios,但是在安卓,linux,windows的平台上测试步骤是完全一样的。

使用方法

首先,我们需要将Briada.jar添加到burp插件中。Jpython在这里是不需要的,但是我们需要一个合适版本的python环境。

Schermata-2017-07-25-alle-09.43.01.png

步骤:

Extender -> Add -> Extension file (.jar)

设置一些选项:

Schermata-2017-07-27-alle-16.17.31.png

1. Python binary path:python可执行程序路径,用于启动Pyro服务。
2. Pyro host, Pyro port:Pyro 服务的主机以及端口。可以保持默认。
3. Frida JS file path:需要注入的Frida脚本存放的位置
4. Application ID:比如org.whispersystems.signal

现在选项已经设置完毕,让我们看下这些按钮的作用:

1. Start server: 在Burp和Frida之间启动桥接服务器(它在后台运行python或者Pyro RPC服务)
2. kill server:关闭桥接服务器
3. Spawn application:在设备上启动应用程序,并且在其中注入FridaJS脚本。
4. Reload JS:在不重启程序的情况下重新加载js脚本
5. Java Stub:打印一个Java Stub为你的插件使用Brida
6. Python Stub:打印一个Python Stub为你的插件使用Brida
7. Save settings to file:将设置保存到文件中
8. Load settings from file:从指定文件中导入设置
9. Execute Method:执行"execute method"方法。

开始搞吧~先点击”start server”,然后点击”Spawn application”

示例一:自定义参数,直接方法调用

使用brida通过自定义参数执行一个app方法,这是一个很简单的例子,在scriptSignal.js中,我们定义了一个简单的Objc方法,它会使用NSString实现对字符串大写处理:

touppercase: function(message) {
 var a1 = ObjC.classes.NSString.stringWithString_(message);
 var a2 = a1.uppercaseString();
 return a2.toString();
}

然后我们设置所有参数,来配置”direct method”调用的一部分:

brida1.png

点击”Execute Method”,Brida将通过Pyro调用JS中定义的方法,该方法将在iOS上执行,结果将显示在“Output”部分。

示例二:结合右键菜单功能

我们在burp右键菜单添加了默认几个选项,方便你重新定义这些选项的功能以便更好的使用Brida。对于一些基础的模块比如加密解密,你可以直接写一个JS脚本,而不用重写一个插件。

Brida Custom1:通过右键菜单进行访问,它会调用contextcustom1 JS脚本
Brida Custom2:通过右键菜单进行访问,它会调用contextcustom2 JS脚本
Brida Custom3:通过右键菜单进行访问,它会调用contextcustom3 JS脚本
Brida Custom4:通过右键菜单进行访问,它会调用contextcustom4 JS脚本

默认情况下,为了管理二进制数据,Brida将输入以及输出都进行了16进制编码。转换的辅助功能都在JS文件中提供。

brida2.png

在可编辑视图的情况下,将直接替换为JS执行的结果。在不可编辑的视图上,它将生成一个带有结果的消息框。

brida3.png

在这个示例JS中,我们实现了以下方法:
contextcustom1将创建所选字符串的小写形式:

contextcustom1: function(message) {
 var a1 = ObjC.classes.NSString.stringWithString_(hexToString(message));
 var a2 = a1.lowercaseString();
 return stringToHex(a2.toString());
}

contextcustom2将创建所选文本的base64编码形式:

contextcustom2: function(message) {
 var inputByte = hexToBytes(message);
 var ptrMessage = Memory.alloc(inputByte.length);
 Memory.writeByteArray(ptrMessage,inputByte);
 var objMessage = ObjC.classes.NSData.alloc().initWithBytes_length_(ptrMessage,inputByte.length);
 var encodedMessage = objMessage.base64EncodedString();
 return stringToHex(encodedMessage.toString());
}

contextcustom3将创建所选文本的大写形式:

contextcustom3: function(message) {
 var a1 = ObjC.classes.NSString.stringWithString_(hexToString(message));
 var a2 = a1.uppercaseString();
 return stringToHex(a2.toString());
}

contextcustom4将解码所选文本的base64:

contextcustom4: function(message) {
 var a2 = ObjC.classes.NSString.stringWithString_(hexToString(message));
 var encodedString = ObjC.classes.NSData.dataFromBase64String_(a2);
 var ptrBytesReturned = encodedString.bytes();
 var ptrBytesLength = encodedString.length();
 var bytesReturneded = Memory.readByteArray(ptrBytesReturned, ptrBytesLength);
 return bytesToHex(bytesReturneded);
}

示例三:自定义插件-Signal(iOS)修改传输中的加密邮件

Brida真正的威力就在于此。我们将为特定目的编写一个自定义的Burp插件,简单的说就是我们希望burp在消息加密前进行捕获,然后将消息替换为我们伪造的信息。所有实现代码地址为:https://github.com/federicodotta/Brida/tree/master/examples.

这一程序具有python和java版本。为了简单起见,我们将仅在文章中解释Python版本。您将需要在Burp中加载Brida插件,然后加载Signal应用程序的python或java插件。

您可以使用”Java Stub”或”Python Stub”Brida功能轻松生成通过Pyro4连接到Brida的有效代码,并执行Frida导出的功能。

首先我们先写出我们将要注入的js脚本:

scriptSignal.js

脚本的最后一部分会包含插件使用的Frida hooks,sendMessage:recipient:thread:attempts:success:failure:这一hook是用于获取最后一条信息的目的地号码,然后执行生成新加密信息的功能。此值存储在JS中的局部变量中,并将在我们的自定义函数中使用。

var hooksendMessage = ObjC.classes.OWSMessageSender["- sendMessage:recipient:thread:attempts:success:failure:"];
 Interceptor.attach(hooksendMessage.implementation, {
  onEnter: function(args) { 
   var obj2 = ObjC.Object(args[3]);
   destNum = obj2.recipientId().toString();
  }
});

然后,我们必须绕过SSL验证,以便能够通过Burp Proxy拦截传输中的数据。

JavaScript

changemessage: function(message) {

    var env = ObjC.classes.Environment.getCurrent();

    var messageSender = env.messageSender();
    var signalRecipient = ObjC.classes.SignalRecipient.alloc().initWithTextSecureIdentifier_relay_(destNum,null);
    var contactThread = ObjC.classes.TSContactThread.alloc().initWithContactId_(destNum);

    var mex = ObjC.classes.TSOutgoingMessage.alloc().initWithTimestamp_inThread_messageBody_(Math.round(+new Date()/1000),null,message);

    var retVal = messageSender.deviceMessages_forRecipient_inThread_(mex,signalRecipient,contactThread);                

    var retValMessage = retVal.objectAtIndex_(0);

    return retValMessage.toString();

}

BurpBridaSignal.py

这是Burp Suite插件文件,它使用Brida生成新消息,并用新的消息替换发送的消息。第一部分使用Burp Suite功能来分析所有请求并检查指定特定请求包含加密发送消息的特定字符串(destinationRegistrationId)。

if messageIsRequest:

 # Get request bytes                
 request = messageInfo.getRequest()

 # Get a IRequestInfo object, useful to work with the request
 analyzedRequest = self.helpers.analyzeRequest(request)

 headers = list(analyzedRequest.getHeaders())

 bodyOffset = int(analyzedRequest.getBodyOffset())

 body = request[bodyOffset:]
 bodyString = "".join(map(chr,body))

 if "destinationRegistrationId" in bodyString:

第二部分是插件核心,它使用Brida来通过pp.callexportfunction(’changemessage’,args)请求生成在scriptSignal.js中定义的更新信函的新消息。基本上它会生成一个新的消息,存放在名为“pwned”的文本中。

jsonBody = json.loads(bodyString)

uri = 'PYRO:BridaServicePyro@localhost:9999'
pp = Pyro4.Proxy(uri)
args = []
args.append("pwned")
newMessage = pp.callexportfunction('changemessage',args)
pp._pyroRelease()

然后它用刚刚通过Brida生成的新消息替换原始消息。

  m = re.search(".*content = "(.*?)".*", newMessage)
  if m:
   newMessage = m.group(1)
   jsonBody["messages"][0]["content"] = newMessage
   newBodyString = json.dumps(jsonBody)
   newBodyString = newBodyString.replace("/", "/")
   newRequest = self.helpers.buildHttpMessage(headers, self.helpers.stringToBytes(newBodyString))
   messageInfo.setRequest(newRequest)
本文翻译自:https://techblog.mediaservice.net/2017/07/brida-advanced-mobile-application-penetration-testing-with-frida/,如若转载,请注明来源于嘶吼: http://www.4hou.com/penetration/6916.html

点赞 8
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

xnianq

xnianq

HFUT,信息安全

发私信

发表评论

    高坂穗乃果
    高坂穗乃果 2017-08-02 17:57

    作者辛苦了…这东西我自己研究顶多研究到第一层