Friday, February 27, 2015

BASEBAND ... Stalking you and dating you

So I have numerously given hints and tutorials on how to attack basebands, now for this little trick (hack) research, I will require a mobile phone (after all which or what is the best way to attack a baseband [read do research])

PS: most of the research is collaboration from I and a very good researcher Fabien Ansglard whose done most of the coding you see here)

Now as I explained theres 3 computers (systems) on a 'smart' phone , well

-----The Smart (read in this context ANDROID/iOS)
-----The Phone (read in this context Baseband)
-----The SIM (duuuh)

now, superbly we have two phones that we love to break into (kernel level) by rooting and jailbreaking that is android and iOS respectively,

so we go for iOS first, we break down the structures:





A kernel module exposes the serial line over an UNIX pseudo-terminal in the /dev folder. On Androids there is only one pseudo terminal: /dev/smd0 but on iPhone the UART is divided by a kernel module and several pseudo-terminals are exposed: /dev/mux.h5-baseband.reg/dev/dlci.h5-baseband.call or/dev/dlci.h5-baseband.sms. The user land process can then open any terminal and perform I/O commands with simple read and write.


then here we go to android:




now this is the method most unlocks rely on,  you get inside the baseband and check how a specific model allows certain MNC & MCCs , now, since we are not really going for that, we just wanna play MITM between the OS and the modem OS control what goes into the phone and into the OS :)

How libraries are loaded (theory)



In MacOS X/Linux, programs are stored on hard-drive with missing parts referencing methods and functions from bigger libraries (libc,zlib,...). Only when they are started the missing symbols are linked, it is called dynamic linking and it's done by ld on Linux and dyld on MacOS X:


  1. After a new process is forked an execv occurs, the program is read from hard-drive and different sections are loaded in RAM, .data is loaded into "read only" pages while .text and a stack is created with "read and write" pages.
  2. Note: In the drawing .data's pages are mapped starting at 0x00000000 but in reality this is reserved so you get a nice "segmentation fault" upon de-refencing a null pointer.
  3. Once the program's different section are all in pages, the kernel reads which loader should be used and integrate it in the process address space. The loader is usually already resident in memory somewhere on the system so it is not loaded from hard-drive but mapped by adding an entry in the progress's page table. Execution control is them transferred to the loader, passing via parameters where the different program sections are.
  4. The loader reads the missing symbols names and search for them in the default libraries.
  5. The libraries are usually also resident in memory so there is no need to read them from the hard-drive. They are mapped in the process address space via the process's page table and symbols resolution occurs. If all symbols are resolved, execution of the program can begin.
Note that the loader is performing symbols resolution (intercepted by our library) at launchtime. The interceptor library then uses the loader to create a hook atruntime.

It is not complicated to do:
  • Write a library with function prototype matching the one you want to intercept (let's say: void *malloc(size_t size))
  • Compile as a shared library
  • Instruct the loader to lookup this library before anything else:
  • On linux this is done via the LD_PRELOAD environment variable.
  • On MacOS X it is slighty different because lyld uses a two-level namespaces ( a symbol not only features a method's name but also the name of the library) hence you have to specify the name of your interceptor library via DYLD_INSERT_LIBRARIES but also instruct lyld to use a flat lookup system via DYLD_FORCE_FLAT_NAMESPACE

Of course to make the program run you also have to get a hold on the "real" function via dlsym(RTLD_NEXT, "malloc") and relay the call so everything is transparent to the program.

Dummy source code (malloc_interposer.c):
 #include <stdio.h>
   #include <dlfcn.h>
   #include <sys/types.h>
   #include <sys/stat.h>



   void *malloc(size_t size)
   {
      static void * (*func)();

  
      if(!func)
         func =  dlsym(RTLD_NEXT, "malloc");


      printf("malloc(%d) is called\n", size);     
      return(func(size));
   } 



Compilation :
   $: gcc -D_GNU_SOURCE -rdynamic -shared malloc_interposer.c -o /lib/malloc_interposer.so.1.0 -ldl

 

Execution :
   $: LD_PRELOAD=/lib/malloc_interposer.so.1.0 cat /dev/null

   malloc(20) is called

 

Library preloading & Method interposition: Practice

While the method described previously works very well on Linux, MacOS X tend to behave poorly when you flatten the lookup system of lyld. Luckily there is an other way to place a hook on MacOS X and this method is described in Amit Singh's gem: MacOS X Internals as method interposition:

By placing a special sub-section __interpose in the data portion of the executable, dyld will perform all the interceptions automatically. Here is an example hooking open,close,write and read.
  
 static const interposer_t interposers[] __attribute__ ((section("__DATA, __interpose")))=
 {
  { (void*)my_open, (void*)open },
  { (void*)my_close, (void*)close},
  { (void*)my_read, (void*)read},
  { (void*)my_write, (void*)write},
 };

 
 int my_open  (const char* path, int flags, mode_t mode){..}
 int my_close (int d){..}
 int my_read  (int  handle,  void  *buffer,  int  nbyte ){..}
 int my_write (int  handle,  void  *buffer,  int  nbyte  ){..}


 
With this trick, it was easy to identify the pseudo-terminals used by placing a hook on open and close. Then hook read and write. The tracing is performed by maintaining a mapping between file descriptor returned by <fcntl.h> and FILE*'s . Here is the resulting source code: fdinterceptor.c and a zip containing a plist and the script to inject: inject.zip.


Toolchain in action:
   // Build the tools
 $ cd /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin
 $ gcc-4.2 -arch armv6 -dynamiclib -isysroot ../../SDKs/iPhoneOS3.1.3.sdk -o fdinterceptor.dylib fdinterceptor.c


 // Send the tools
 $ scp fdinterceptor.dylib injectCommCenter.sh com.apple.CommCenter.plist root@192.168.1.103:/tmp


 // Jump in and inject
  $ ssh -l root 192.168.1.103
 # cd /tmp
 # ./injectCommCenter.sh


Notice that injection is performed via a script injectCommCenter.sh:
 cd /System/Library/LaunchDaemons/
 cp com.apple.CommCenter.plist com.apple.CommCenter.plist.vanilla
 cp /tmp/com.apple.CommCenter.plist /System/Library/LaunchDaemons/com.apple.CommCenter.plist

 launchctl unload -w /System/Library/LaunchDaemons/com.apple.CommCenter.plist
 launchctl load -w /System/Library/LaunchDaemons/com.apple.CommCenter.plist

 cp com.apple.CommCenter.plist.vanilla com.apple.CommCenter.plist


The launchctl lines are not very interesting as they merely unload and reload the CommCenter deamon. But what is done before and afer is a bit more worth mentioning: Because the CommCenter not only handles the modem but also the WIFI connection, once you unload the CommCenter your SSH terminal will HANG. You are literally sawing off the branch you are sitting on. It is hence a necessity to script the re-loading....but there is more:

Because we modified the plist and it is saved on hard-drive: if we have a bug in our interceptor library we may potentially brick the device and require a full DFU restore ! So in order to take into account a worse case scenario the script also remove the interceptor library from the plist, this way the device can restart safely: This is just an idiot proof security.


Results:


Booting
 [send] at   # Modem Are you there ?
 [send] at
 [send] at
 [send] at
 [recv] AT   # Yes I am !

 [send] ate0   # Set modem to "no echo" mode
 [recv] ate0 OK

 [send] at+cmee=1  # Require error code to be returned as code (opposed to verbose at+cmee=2)
 [recv] OK

 [send] at+ipr=750000  # Set the terminal speed
 [recv] OK

 [send] at+xdrv=0,41,25  # Call method 41 on device 0 (speakers)
 [recv] +XDRV: 0,41,1,0
 [recv] OK
     RV: 0,41,1,0
 
 [send] at+xtransportmode # Switch to binary code instead of commands
 [recv] OK

 [send] at+cscs="HEX"  # Set the TE character set to HEX
 [recv] OK

 [send] at+xthumb?
 [recv] +XTHUMB: "1E2834B6CE739AB36EF9454B7997FCD30208398C","E93B43F3EF6DAED516A2D4B9BAD5494DC81E92D3"
 [recv] OK

 [send] at+xgendata          # Request modem's firmware description
 [recv] +XGENDATA: "","DEV_ICE_MODEM_04.05.04_G","EEP_VERSION:208","EEP_REVISION:1","BOOTLOADER_VERSION:3.9_M3S2"
 [recv] OK

 [send] at+xdrv=10,2   # Call a function for a device, format is at+xdrv:deviceId,functionId,params ...
 [recv] :+XDRV: 10,2,0
 [recv] OK

 [send] at+xl1set="psvon"

 [send] at+cmux=0,0,0,1500 # Set the multiplexing mode 
 [recv] OK

 [open] '/dev/dlci.h5-baseband.call'
 [open] '/dev/dlci.h5-baseband.reg'
 [open] '/dev/dlci.h5-baseband.sms'
 [open] '/dev/dlci.h5-baseband.low'
 [open] '/dev/dlci.h5-baseband.pdp_ctl'
 [open] '/dev/dlci.h5-baseband.chatty'
 [open] '/dev/dlci.h5-baseband.pdp_0'
 [open] '/dev/dlci.h5-baseband.pdp_1'


 # The rest of the registration occurs in /dev/dlci.h5-baseband.reg
 # The two main used pseudo terminal after this are of course /dev/dlci.h5-baseband.call 
 # and /dev/dlci.h5-baseband.sms
 
Receiving a (fictional) SMS:
 

 # Receiving an unsollicited text message (AT+CMT). 

 [recv] AT+CMT=10307919127163385F901000B914161387976F0000066C8721E640C8B592090F28D76838661793B3C5E83D
      0657959079AD2D36C3628EDA697E5E539BD4C06A5DD203A3A3D07C1DFF3343DFD76837E202ABA0E92C1E86850339C0
      7C96031180846D3C9642ED80C046F8350C72675154B01


Text message are PDU encoded, you can find plenty of online decoder. Here is the plain text version
 SMSC:   +19726133589
 Receiver:  +1416839XXXX
 Payload:  Hey Fab,  John Carmack here: Still interested in this position ? Thu 20th May 2010 04:22.03PM



Receiving a call :
 
 
 [recv] RING     # Trigger the phone to ring
 [recv] +CLIP: "",128,,,,2   # No caller ID :/ !
 [recv] +XCALLSTAT: 1,4
 [recv] RING[recv] +CLIP: "",128,,,,2
 [send] ata     # Local user decided to accept the incoming call
 [recv] +XCALLSTAT: 1,0    # Reporting call status is enabled (1), voice is active (0)
 [recv] OK
 [recv] +XCALLSTAT: 1,6    # Reporting call status is enabled (1), voice is disconnected (6)
 [send] at+ceer     # Local user hang up
 [recv] NO CARRIER    # Connection is indeed terminated from the other hand
 [recv] +CEER: "Release","Normal call clearing" #Collect informations on call
 [recv] OK

Note : I was surprised to find RING command notifications note only in the call channel but also in the sms channel but it actually makes a lot of sense when connect to EDGE/GPRS: Since text message and call are not supported simultaneously the sms pseudo-terminal must remain silent during a call.

now i will put up part2 where i will give the code capable of MITM

No comments:

Post a Comment

Dynamic Binary Instrumentation (pt2)

Quick how to: After install of Frida on your machine, you will need to install your server agent on your (use case is phone) iphone/andro...