Slide 1

Slide 1 text

Inspecting iOS App Traffic with JavaScript @AndyDavies https://www.flickr.com/photos/marc-flores/8367323660

Slide 2

Slide 2 text

WHY?

Slide 3

Slide 3 text

DEMO

Slide 4

Slide 4 text

http://www.delaat.net/rp/2015-2016/p52/report.pdf

Slide 5

Slide 5 text

Inject JavaScript into an App! https://www.frida.re/ App JavaScript VM Script Host Injects script Receives Messages

Slide 6

Slide 6 text

https://opensource.apple.com/source/coreTLS/coreTLS-83.20.8/lib/tls1Callouts.c.auto.html /* * The TLS pseudorandom function, defined in RFC2246, section 5. * This takes as its input a secret block, a label, and a seed, and produces * a caller-specified length of pseudorandom data. * * Optimization TBD: make label optional, avoid malloc and two copies if it's * not there, so callers can take advantage of fixed-size seeds. */ // Note: This is exported as SPI. int tls_handshake_internal_prf( tls_handshake_t ctx, const void *vsecret, size_t secretLen, const void *label, // optional, NULL implies that seed contains // the label size_t labelLen, const void *seed, size_t seedLen, void *vout, // mallocd by caller, length >= outLen size_t outLen) { int serr = errSSLInternal; … Master Secret Client & Server Randoms

Slide 7

Slide 7 text

var hexChar = ["0", "1", "2", "3", "4", "5", "6", "7","8", "9", "A", "B", "C", "D", "E", "F"]; function byteToHex(byte) { return hexChar[(byte >> 4) & 0x0f] + hexChar[byte & 0x0f]; } var f = Module.findExportByName("libsystem_coretls.dylib", "tls_handshake_internal_prf"); Interceptor.attach(f, {onEnter: function (args) { var secretLength = parseInt(args[2], 16); var seedLength = parseInt(args[6], 16); if(secretLength == 48 && (seedLength == 64 || seedLength == 77)) { var secretAddr = new NativePointer(args[1]) var secretBytes = new Uint8Array(Memory.readByteArray(secretAddr, secretLength)); var secret = ""; for(var i = 0; i < secretLength; i++) { secret += byteToHex(secretBytes[i]); } Find function Hook function Extract master secret

Slide 8

Slide 8 text

var seedLength = parseInt(args[6], 16); var seedAddr = new NativePointer(args[5]); var seedBytes = new Uint8Array(Memory.readByteArray(seedAddr, seedLength)); var clientRandom = ""; var serverRandom = ""; if(seedLength == 64) { for(i = 0; i < 32; i++) { clientRandom += byteToHex(seedBytes[i]); } for( ; i < 64; i++) { serverRandom += byteToHex(seedBytes[i]); } } else if(seedLength == 77) { // key expansion var offset = 13; for(i = offset; i < 32 + offset; i++) { serverRandom += byteToHex(seedBytes[i]); } for( ; i < 64 + offset; i++) { clientRandom += byteToHex(seedBytes[i]); } } Extract client and server randoms

Slide 9

Slide 9 text

if(clientRandom !== "") { send("CLIENT_RANDOM "+ clientRandom + " " + secret); } } } }); Send it to the host

Slide 10

Slide 10 text

DEMO

Slide 11

Slide 11 text

Areas that need more work ★ TLS Session Resumption ★ Safari ★ iOS 11

Slide 12

Slide 12 text

So what did I learn? ★ Just like on the web… sometimes we forget to Compress JSON responses Reuse connections Optimise images And a whole bunch of other things

Slide 13

Slide 13 text

https://www.flickr.com/photos/willypayne/3116394973 Thank You & Happy Christmas!