MobSF / Mobile-Security-Framework-MobSF

Mobile Security Framework (MobSF) is an automated, all-in-one mobile application (Android/iOS/Windows) pen-testing, malware analysis and security assessment framework capable of performing static and dynamic analysis.
https://opensecurity.in
GNU General Public License v3.0
17.49k stars 3.24k forks source link

SYMBOLS STRIPPED False Negative #2233

Closed Karmaz95 closed 1 year ago

Karmaz95 commented 1 year ago

Hello, I am working on a project about Mach-O binaries for ARM64. I was researching the idea of stripping symbols in Mach-O binaries, and I found a False Negative scenario for this code: https://github.com/MobSF/Mobile-Security-Framework-MobSF/blob/28b7fdeca5a806a3d429593c8eec012373fe2b24/mobsf/StaticAnalyzer/views/ios/macho_analysis.py#L226

We got two Objective-C simple programs. First is not stripped:

❯ dsymutil -s 'arc_example'
----------------------------------------------------------------------
Symbol table for: 'arc_example' (arm64)
----------------------------------------------------------------------
Index    n_strx   n_type             n_sect n_desc n_value
======== -------- ------------------ ------ ------ ----------------
[     0] 00000179 0e (     SECT    ) 01     0000   0000000100003d18 '-[Person name]'
[     1] 00000188 0e (     SECT    ) 01     0000   0000000100003d40 '-[Person setName:]'
[     2] 0000019b 0e (     SECT    ) 01     0000   0000000100003d78 '-[Person .cxx_destruct]'
[     3] 000001b3 1e (PEXT SECT    ) 03     0000   0000000100003ec0 '_objc_msgSend$name'
[     4] 000001c6 1e (PEXT SECT    ) 03     0000   0000000100003ee0 '_objc_msgSend$setName:'
[     5] 000001dd 0e (     SECT    ) 04     0000   0000000100003f00 '__OBJC_$_INSTANCE_METHODS_Person'
[     6] 000001fe 0e (     SECT    ) 0e     0000   0000000100008000 '__OBJC_METACLASS_RO_$_Person'
[     7] 0000021b 0e (     SECT    ) 0e     0000   0000000100008048 '__OBJC_$_INSTANCE_VARIABLES_Person'
[     8] 0000023e 0e (     SECT    ) 0e     0000   0000000100008070 '__OBJC_$_PROP_LIST_Person'
[     9] 00000258 0e (     SECT    ) 0e     0000   0000000100008088 '__OBJC_CLASS_RO_$_Person'
[    10] 00000271 1e (PEXT SECT    ) 11     0000   00000001000080f0 '_OBJC_IVAR_$_Person._name'
[    11] 00000002 0f (     SECT EXT) 12     0000   0000000100008120 '_OBJC_CLASS_$_Person'
[    12] 00000017 0f (     SECT EXT) 12     0000   00000001000080f8 '_OBJC_METACLASS_$_Person'
[    13] 00000030 0f (     SECT EXT) 01     0010   0000000100000000 '__mh_execute_header'
[    14] 00000044 0f (     SECT EXT) 01     0000   0000000100003da8 '_main'
[    15] 0000004a 01 (     UNDF EXT) 00     0100   0000000000000000 '_NSLog'
[    16] 00000051 01 (     UNDF EXT) 00     0200   0000000000000000 '_OBJC_CLASS_$_NSObject'
[    17] 00000068 01 (     UNDF EXT) 00     0200   0000000000000000 '_OBJC_METACLASS_$_NSObject'
[    18] 00000083 01 (     UNDF EXT) 00     0400   0000000000000000 '___CFConstantStringClassReference'
[    19] 000000a5 01 (     UNDF EXT) 00     0200   0000000000000000 '__objc_empty_cache'
[    20] 000000b8 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_alloc_init'
[    21] 000000c9 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_autoreleasePoolPop'
[    22] 000000e2 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_autoreleasePoolPush'
[    23] 000000fc 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_getProperty'
[    24] 0000010e 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_msgSend'
[    25] 0000011c 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_release'
[    26] 0000012a 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_retainAutoreleasedReturnValue'
[    27] 0000014e 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_setProperty_atomic'
[    28] 00000167 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_storeStrong'

The second is stripped (strip arc_example -o ss_example):

----------------------------------------------------------------------
Symbol table for: 'ss_example' (arm64)
----------------------------------------------------------------------
Index    n_strx   n_type             n_sect n_desc n_value
======== -------- ------------------ ------ ------ ----------------
[     0] 00000147 3c (N_OPT        ) 00     0000   0000000005614542 'radr://5614542'
[     1] 00000004 0f (     SECT EXT) 01     0010   0000000100000000 '__mh_execute_header'
[     2] 00000018 01 (     UNDF EXT) 00     0100   0000000000000000 '_NSLog'
[     3] 0000001f 01 (     UNDF EXT) 00     0200   0000000000000000 '_OBJC_CLASS_$_NSObject'
[     4] 00000036 01 (     UNDF EXT) 00     0200   0000000000000000 '_OBJC_METACLASS_$_NSObject'
[     5] 00000051 01 (     UNDF EXT) 00     0400   0000000000000000 '___CFConstantStringClassReference'
[     6] 00000073 01 (     UNDF EXT) 00     0200   0000000000000000 '__objc_empty_cache'
[     7] 00000086 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_alloc_init'
[     8] 00000097 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_autoreleasePoolPop'
[     9] 000000b0 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_autoreleasePoolPush'
[    10] 000000ca 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_getProperty'
[    11] 000000dc 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_msgSend'
[    12] 000000ea 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_release'
[    13] 000000f8 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_retainAutoreleasedReturnValue'
[    14] 0000011c 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_setProperty_atomic'
[    15] 00000135 01 (     UNDF EXT) 00     0200   0000000000000000 '_objc_storeStrong'

Then we run your function on these binaries, and we can see that it returns a True for both of them:

image

To sum up, there is a problem with symbol.type & 0x0e > 0 check, which omits the types:

>>> 0x1e & 0xe0
0
>>> 0x0e & 0xe0
0
>>> 0x0f & 0xe0
0

I came to the below solutions for this problem:

  1. Check for any of the above types.
  2. Check if the symbol name is not __mh_execute_header
    filter_symbols = ['radr://5614542', '__mh_execute_header']
    if ((symbol.type & 0xe0) > 0 or (symbol.type in [0x0e,0x1e,0x0f])) and symbol.name.lower().strip() not in filter_symbols:

So the final code would look like this:

    def is_symbols_stripped(self):
        filter_symbols = ['radr://5614542', '__mh_execute_header']
        for i in self.macho.symbols:
            if ((i.type & 0xe0) > 0 or (i.type in [0x0e,0x1e,0x0f])) and i.name.lower().strip() not in filter_symbols:
                return False
        return True

These changes made the function more accurate and coped with all cases during my tests. The following screenshot shows it on arc_example(not stripped) and ss_example(stripped) sample

image

I created a pull request for this issue.

github-actions[bot] commented 1 year ago

👋 @Karmaz95 Issues is only for reporting a bug/feature request. For limited support, questions, and discussions, please join MobSF Slack channel Please include all the requested and relevant information when opening a bug report. Improper reports will be closed without any response.

ajinabraham commented 1 year ago

Addressed in #2234