Skip to content

Commit

Permalink
Merge pull request #14 from dmdhrumilmistry/android-shell-pinning
Browse files Browse the repository at this point in the history
Android application certificate pinning
  • Loading branch information
dmdhrumilmistry authored Jul 17, 2022
2 parents 424ec5c + 1c91336 commit 2ec52c7
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 13 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,5 @@ cython_debug/
*.exe
*.build
*.dist
*exectuables*
*exectuables*
*temp*
16 changes: 16 additions & 0 deletions examples/Android/intercept-using-burp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from pyhtools.attackers.Android.mitm.cert_pin import PinCertificate

pinner = PinCertificate(
apk_path=r'/home/hacker/apks/com.application.name.apk', # application package path
package_name=r'com.application.name', # package name of target application
cert_path=r'burp_cert.der', # burpsuite/custom CA certificate
frida_binary_path=r'frida-server-15.1.28-android-x86', # download and update path, (https://github.com/frida/frida/releases)
frida_script_path=r'script.js', # download from frida examples (https://codeshare.frida.re/@pcipolloni/universal-android-ssl-pinning-bypass-with-frida/)
device_name='emulator-5554', # device name from adb
host='127.0.0.1', # adb host
port=5037, # adb port
)

pinner.pin_certificate()

# once certificate is pinned you can exit python script using ctrl+c then return key
Empty file.
Empty file.
150 changes: 150 additions & 0 deletions pyhtools/attackers/Android/mitm/cert_pin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
from ppadb.client import Client
from ppadb.device import Device
from os.path import isfile, basename
from os import system
from . import utils

import asyncio
import frida
import logging
import threading
logging.basicConfig(level=logging.DEBUG,
format='[%(asctime)s] [%(levelname)s] - %(message)s')


class PinCertificateExceptions:
'''
Pin Certificate Exception Class
'''
class ServerNotRunning(Exception):
pass

class NoDevicesFound(Exception):
pass


class PinCertificate:
def __init__(self, apk_path: str, package_name: str, cert_path: str, frida_binary_path: str, frida_script_path: str, device_name: str, host: str = '127.0.0.1', port: int = 5037,):
# check data types
assert type(apk_path) == str
assert type(package_name) == str
assert type(cert_path) == str
assert type(device_name) == str
assert type(frida_binary_path) == str
assert type(frida_script_path) == str
assert type(host) == str
assert type(port) == int

# check if files are available at their paths
if not isfile(apk_path):
raise FileNotFoundError(f'{apk_path} APK file not found')

if not isfile(cert_path):
raise FileNotFoundError(f'{cert_path} Certificate file not found')

if not isfile(frida_binary_path):
raise FileNotFoundError(
f'{frida_binary_path} Frida binary file not found')

if not isfile(frida_script_path):
raise FileNotFoundError(
f'{frida_binary_path} Frida binary file not found')

# assign values
self.__device_name = device_name
self.__apk_path = apk_path
self.__package_name = package_name
self.__cert_path = cert_path
self.__frida_path = frida_binary_path
self.__frida_script_path = frida_script_path

# connect to adb server
self._adb = Client(
host=host,
port=port
)

# set initial values
self.device = self.get_device()

def get_device(self):
_ = self.get_adb_devices()
device: Device = self._adb.device(self.__device_name)
return device

def get_adb_devices(self):
try:
devices: list[Device] = self._adb.devices()
if len(devices) == 0:
raise PinCertificateExceptions.NoDevicesFound(
"No ADB Devices Attached")

return devices
except RuntimeError:
raise PinCertificateExceptions.ServerNotRunning(
"ADB Server is not running, start using `adb start-server`")

def get_frida_devices(self):
devices = frida.enumerate_devices()
if len(devices) == 0:
raise PinCertificateExceptions.NoDevicesFound(
"No Frida Devices Found")

return devices

def install_apk(self, force_install: bool = True):
if self.device.is_installed(self.__package_name) and force_install:
self.device.uninstall(self.__package_name)

self.device.install(self.__apk_path)

if self.device.is_installed(self.__package_name):
return True
return False

def start_frida(self):
asyncio.run(utils.run(f'adb shell /data/local/tmp/frida-server &'))

def pin_certificate(self):
logging.info("Starting Certificate Pinning Procedure..")

# get device
self.device: Device = self.get_device()
logging.info(f'Connected to {self.__device_name} device')

# install apk
logging.info(f'Installing package')
if self.install_apk():
logging.info(
f'{basename(self.__apk_path)} APK installation completed successfully')
else:
logging.error(
f'{basename(self.__apk_path)} APK installation failed!')

# push certificate to the device
self.device.push(
src=self.__cert_path,
dest=r'/data/local/tmp/cert-der.crt',
mode=644
)
logging.info(
f'{self.__cert_path} certificate pushed to /data/local/tmp/cert-der.crt')

# push frida binary to the device
self.device.push(
src=self.__frida_path,
dest=r'/data/local/tmp/frida-server',
mode=555
)
logging.info(
f'{self.__frida_path} frida binary pushed to /data/local/tmp/frida-server')

# start frida server in different thread
logging.info("Starting Frida server")
frida_thread = threading.Thread(target=self.start_frida)
frida_thread.start()
# self.device.shell('su /data/local/tmp/frida-server &')

# Start SSL pinning
system(
f'frida -U -l {self.__frida_script_path} --no-paus -f {self.__package_name}')
16 changes: 16 additions & 0 deletions pyhtools/attackers/Android/mitm/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import asyncio


async def run(cmd):
proc = await asyncio.create_subprocess_shell(
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE)

stdout, stderr = await proc.communicate()

# print(f'[{cmd!r} exited with {proc.returncode}]')
if stdout:
print(f'[stdout]\n{stdout.decode()}')
if stderr:
print(f'[stderr]\n{stderr.decode()}')
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
beautifulsoup4>=4.9.3
colorama>=0.4.4
frida-tools>=10.8.0
#netfilterqueue (for linux devices only): sudo pip3 install --upgrade -U git+https://github.com/kti/python-netfilterqueue
kamene>=0.32
nuitka
pure-python-adb
pyfiglet>=0.8.post1
pynput>=1.7.3
pytelegrambotapi>=4.0.1
Expand Down
26 changes: 14 additions & 12 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,25 @@
packages=find_packages(),
include_package_data=True,
install_requires=[
'beautifulsoup4',
'colorama',
# 'netfilterqueue', # (for linux devices only): sudo pip3 install -U git+https://github.com/kti/python-netfilterqueue
'beautifulsoup4>=4.9.3',
'colorama>=0.4.4',
'frida-tools>=10.8.0',
# 'netfilterqueue', #(for linux devices only): sudo pip3 install --upgrade -U git+https://github.com/kti/python-netfilterqueue,
'kamene>=0.32',
'nuitka',
'kamene',
'scapy',
'psutil',
'prettytable',
'pynput',
'pyfiglet',
'pytelegrambotapi',
'pure-python-adb',
'pyfiglet>=0.8.post1',
'pynput>=1.7.3',
'pytelegrambotapi>=4.0.1',
'prettytable>=2.1.0',
'psutil>=5.8.0',
'pyinstaller',
'requests',
'requests>=2.25.1',
'scapy>=2.4.5',
# 'wmi', # for windows process management
'zstandard',
],
classifiers=[
'Development Status :: 4 - Beta',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
Expand Down

0 comments on commit 2ec52c7

Please sign in to comment.