diff --git a/src/rpcclient/rpcclient/darwin/client.py b/src/rpcclient/rpcclient/darwin/client.py index 731a2ab8..41398413 100644 --- a/src/rpcclient/rpcclient/darwin/client.py +++ b/src/rpcclient/rpcclient/darwin/client.py @@ -15,6 +15,7 @@ from rpcclient.darwin.fs import DarwinFs from rpcclient.darwin.hid import Hid from rpcclient.darwin.ioregistry import IORegistry +from rpcclient.darwin.keychain import Keychain from rpcclient.darwin.location import Location from rpcclient.darwin.media import DarwinMedia from rpcclient.darwin.network import DarwinNetwork @@ -69,6 +70,7 @@ def _init_process_specific(self): self.lief = DarwinLief(self) self.bluetooth = Bluetooth(self) self.core_graphics = CoreGraphics(self) + self.keychain = Keychain(self) self.type_decoders = { self.symbols.CFNullGetTypeID(): self._decode_cfnull, self.symbols.CFStringGetTypeID(): self._decode_cfstr, diff --git a/src/rpcclient/rpcclient/darwin/keychain.py b/src/rpcclient/rpcclient/darwin/keychain.py new file mode 100644 index 00000000..05fd2de8 --- /dev/null +++ b/src/rpcclient/rpcclient/darwin/keychain.py @@ -0,0 +1,65 @@ +import logging +from typing import Mapping, List + +from rpcclient.exceptions import BadReturnValueError +from rpcclient.symbol import Symbol + +logger = logging.getLogger(__name__) + + +class Keychain: + """ keychain utils """ + + def __init__(self, client): + self._client = client + + def add_internet_password(self, account: str, server: str, password: str): + attributes = self._client.symbols.objc_getClass('NSMutableDictionary').objc_call('new') + attributes.objc_call('setObject:forKey:', self._client.symbols.kSecClassInternetPassword[0], + self._client.symbols.kSecClass[0]) + attributes.objc_call('setObject:forKey:', self._client.cf(account), self._client.symbols.kSecAttrAccount[0]) + attributes.objc_call('setObject:forKey:', self._client.cf(server), self._client.symbols.kSecAttrServer[0]) + attributes.objc_call('setObject:forKey:', self._client.cf(password), self._client.symbols.kSecValueData[0]) + err = self._client.symbols.SecItemAdd(attributes, 0).c_int32 + if err != 0: + raise BadReturnValueError(f'SecItemAdd() returned: {err}') + + def query_apple_share_passwords(self) -> List[Mapping]: + return self._query(self._client.symbols.kSecClassAppleSharePassword) + + def query_internet_passwords(self) -> List[Mapping]: + return self._query(self._client.symbols.kSecClassInternetPassword) + + def query_generic_passwords(self) -> List[Mapping]: + return self._query(self._client.symbols.kSecClassGenericPassword) + + def query_identities(self) -> List[Mapping]: + return self._query(self._client.symbols.kSecClassIdentity) + + def query_certificates(self) -> List[Mapping]: + return self._query(self._client.symbols.kSecClassCertificate) + + def query_keys(self) -> List[Mapping]: + return self._query(self._client.symbols.kSecClassKey) + + def _query(self, class_type: Symbol) -> List[Mapping]: + with self._client.safe_malloc(8) as p_result: + p_result[0] = 0 + + query = self._client.symbols.objc_getClass('NSMutableDictionary').objc_call('new') + query.objc_call('setObject:forKey:', class_type[0], + self._client.symbols.kSecClass[0]) + query.objc_call('setObject:forKey:', self._client.symbols.kSecMatchLimitAll[0], + self._client.symbols.kSecMatchLimit[0]) + query.objc_call('setObject:forKey:', self._client.symbols.kCFBooleanTrue[0], + self._client.symbols.kSecReturnAttributes[0]) + query.objc_call('setObject:forKey:', self._client.symbols.kCFBooleanTrue[0], + self._client.symbols.kSecReturnRef[0]) + query.objc_call('setObject:forKey:', self._client.symbols.kCFBooleanTrue[0], + self._client.symbols.kSecReturnData[0]) + + err = self._client.symbols.SecItemCopyMatching(query, p_result).c_int32 + if err != 0: + raise BadReturnValueError(f'SecItemCopyMatching() returned: {err}') + + return p_result[0].py() diff --git a/src/rpcserver/ents.plist b/src/rpcserver/ents.plist index c68543ec..b6b1edd8 100644 --- a/src/rpcserver/ents.plist +++ b/src/rpcserver/ents.plist @@ -74,5 +74,9 @@ IOSurfaceRootUserClient + keychain-access-groups + + * +