From 424a88897915715c69b3f86f2876277deeb301be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Dec 2022 11:16:07 -0500 Subject: [PATCH 1/3] Bump certifi from 2022.9.24 to 2022.12.7 in /posix (#17) Bumps [certifi](https://github.com/certifi/python-certifi) from 2022.9.24 to 2022.12.7. - [Release notes](https://github.com/certifi/python-certifi/releases) - [Commits](https://github.com/certifi/python-certifi/compare/2022.09.24...2022.12.07) --- updated-dependencies: - dependency-name: certifi dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- posix/Pipfile.lock | 188 +++++++++++++-------------------------------- 1 file changed, 54 insertions(+), 134 deletions(-) diff --git a/posix/Pipfile.lock b/posix/Pipfile.lock index 3b7c296..010a375 100644 --- a/posix/Pipfile.lock +++ b/posix/Pipfile.lock @@ -82,11 +82,11 @@ }, "packaging": { "hashes": [ - "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", - "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" + "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3", + "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3" ], - "markers": "python_version >= '3.6'", - "version": "==21.3" + "markers": "python_version >= '3.7'", + "version": "==22.0" }, "pathspec": { "hashes": [ @@ -98,11 +98,11 @@ }, "platformdirs": { "hashes": [ - "sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7", - "sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10" + "sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca", + "sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e" ], "markers": "python_version >= '3.7'", - "version": "==2.5.4" + "version": "==2.6.0" }, "pluggy": { "hashes": [ @@ -166,14 +166,6 @@ "markers": "python_version >= '3.6'", "version": "==2.13.0" }, - "pyparsing": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "markers": "python_full_version >= '3.6.8'", - "version": "==3.0.9" - }, "pytest": { "hashes": [ "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7", @@ -202,11 +194,11 @@ "develop": { "certifi": { "hashes": [ - "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14", - "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382" + "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3", + "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" ], - "markers": "python_version >= '3.6'", - "version": "==2022.9.24" + "index": "pypi", + "version": "==2022.12.7" }, "charset-normalizer": { "hashes": [ @@ -327,86 +319,19 @@ }, "mkdocs-material-extensions": { "hashes": [ - "sha256:96ca979dae66d65c2099eefe189b49d5ac62f76afb59c38e069ffc7cf3c131ec", - "sha256:bcc2e5fc70c0ec50e59703ee6e639d87c7e664c0c441c014ea84461a90f1e902" + "sha256:9c003da71e2cc2493d910237448c672e00cefc800d3d6ae93d2fc69979e3bd93", + "sha256:e41d9f38e4798b6617ad98ca8f7f1157b1e4385ac1459ca1e4ea219b556df945" ], "markers": "python_version >= '3.7'", - "version": "==1.1" + "version": "==1.1.1" }, "packaging": { "hashes": [ - "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", - "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" + "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3", + "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3" ], - "markers": "python_version >= '3.6'", - "version": "==21.3" - }, - "pillow": { - "hashes": [ - "sha256:03150abd92771742d4a8cd6f2fa6246d847dcd2e332a18d0c15cc75bf6703040", - "sha256:073adb2ae23431d3b9bcbcff3fe698b62ed47211d0716b067385538a1b0f28b8", - "sha256:0b07fffc13f474264c336298d1b4ce01d9c5a011415b79d4ee5527bb69ae6f65", - "sha256:0b7257127d646ff8676ec8a15520013a698d1fdc48bc2a79ba4e53df792526f2", - "sha256:12ce4932caf2ddf3e41d17fc9c02d67126935a44b86df6a206cf0d7161548627", - "sha256:15c42fb9dea42465dfd902fb0ecf584b8848ceb28b41ee2b58f866411be33f07", - "sha256:18498994b29e1cf86d505edcb7edbe814d133d2232d256db8c7a8ceb34d18cef", - "sha256:1c7c8ae3864846fc95f4611c78129301e203aaa2af813b703c55d10cc1628535", - "sha256:22b012ea2d065fd163ca096f4e37e47cd8b59cf4b0fd47bfca6abb93df70b34c", - "sha256:276a5ca930c913f714e372b2591a22c4bd3b81a418c0f6635ba832daec1cbcfc", - "sha256:2e0918e03aa0c72ea56edbb00d4d664294815aa11291a11504a377ea018330d3", - "sha256:3033fbe1feb1b59394615a1cafaee85e49d01b51d54de0cbf6aa8e64182518a1", - "sha256:3168434d303babf495d4ba58fc22d6604f6e2afb97adc6a423e917dab828939c", - "sha256:32a44128c4bdca7f31de5be641187367fe2a450ad83b833ef78910397db491aa", - "sha256:3dd6caf940756101205dffc5367babf288a30043d35f80936f9bfb37f8355b32", - "sha256:40e1ce476a7804b0fb74bcfa80b0a2206ea6a882938eaba917f7a0f004b42502", - "sha256:41e0051336807468be450d52b8edd12ac60bebaa97fe10c8b660f116e50b30e4", - "sha256:4390e9ce199fc1951fcfa65795f239a8a4944117b5935a9317fb320e7767b40f", - "sha256:502526a2cbfa431d9fc2a079bdd9061a2397b842bb6bc4239bb176da00993812", - "sha256:51e0e543a33ed92db9f5ef69a0356e0b1a7a6b6a71b80df99f1d181ae5875636", - "sha256:57751894f6618fd4308ed8e0c36c333e2f5469744c34729a27532b3db106ee20", - "sha256:5d77adcd56a42d00cc1be30843d3426aa4e660cab4a61021dc84467123f7a00c", - "sha256:655a83b0058ba47c7c52e4e2df5ecf484c1b0b0349805896dd350cbc416bdd91", - "sha256:68943d632f1f9e3dce98908e873b3a090f6cba1cbb1b892a9e8d97c938871fbe", - "sha256:6c738585d7a9961d8c2821a1eb3dcb978d14e238be3d70f0a706f7fa9316946b", - "sha256:73bd195e43f3fadecfc50c682f5055ec32ee2c933243cafbfdec69ab1aa87cad", - "sha256:772a91fc0e03eaf922c63badeca75e91baa80fe2f5f87bdaed4280662aad25c9", - "sha256:77ec3e7be99629898c9a6d24a09de089fa5356ee408cdffffe62d67bb75fdd72", - "sha256:7db8b751ad307d7cf238f02101e8e36a128a6cb199326e867d1398067381bff4", - "sha256:801ec82e4188e935c7f5e22e006d01611d6b41661bba9fe45b60e7ac1a8f84de", - "sha256:82409ffe29d70fd733ff3c1025a602abb3e67405d41b9403b00b01debc4c9a29", - "sha256:828989c45c245518065a110434246c44a56a8b2b2f6347d1409c787e6e4651ee", - "sha256:829f97c8e258593b9daa80638aee3789b7df9da5cf1336035016d76f03b8860c", - "sha256:871b72c3643e516db4ecf20efe735deb27fe30ca17800e661d769faab45a18d7", - "sha256:89dca0ce00a2b49024df6325925555d406b14aa3efc2f752dbb5940c52c56b11", - "sha256:90fb88843d3902fe7c9586d439d1e8c05258f41da473952aa8b328d8b907498c", - "sha256:97aabc5c50312afa5e0a2b07c17d4ac5e865b250986f8afe2b02d772567a380c", - "sha256:9aaa107275d8527e9d6e7670b64aabaaa36e5b6bd71a1015ddd21da0d4e06448", - "sha256:9f47eabcd2ded7698106b05c2c338672d16a6f2a485e74481f524e2a23c2794b", - "sha256:a0a06a052c5f37b4ed81c613a455a81f9a3a69429b4fd7bb913c3fa98abefc20", - "sha256:ab388aaa3f6ce52ac1cb8e122c4bd46657c15905904b3120a6248b5b8b0bc228", - "sha256:ad58d27a5b0262c0c19b47d54c5802db9b34d38bbf886665b626aff83c74bacd", - "sha256:ae5331c23ce118c53b172fa64a4c037eb83c9165aba3a7ba9ddd3ec9fa64a699", - "sha256:af0372acb5d3598f36ec0914deed2a63f6bcdb7b606da04dc19a88d31bf0c05b", - "sha256:afa4107d1b306cdf8953edde0534562607fe8811b6c4d9a486298ad31de733b2", - "sha256:b03ae6f1a1878233ac620c98f3459f79fd77c7e3c2b20d460284e1fb370557d4", - "sha256:b0915e734b33a474d76c28e07292f196cdf2a590a0d25bcc06e64e545f2d146c", - "sha256:b4012d06c846dc2b80651b120e2cdd787b013deb39c09f407727ba90015c684f", - "sha256:b472b5ea442148d1c3e2209f20f1e0bb0eb556538690fa70b5e1f79fa0ba8dc2", - "sha256:b59430236b8e58840a0dfb4099a0e8717ffb779c952426a69ae435ca1f57210c", - "sha256:b90f7616ea170e92820775ed47e136208e04c967271c9ef615b6fbd08d9af0e3", - "sha256:b9a65733d103311331875c1dca05cb4606997fd33d6acfed695b1232ba1df193", - "sha256:bac18ab8d2d1e6b4ce25e3424f709aceef668347db8637c2296bcf41acb7cf48", - "sha256:bca31dd6014cb8b0b2db1e46081b0ca7d936f856da3b39744aef499db5d84d02", - "sha256:be55f8457cd1eac957af0c3f5ece7bc3f033f89b114ef30f710882717670b2a8", - "sha256:c7025dce65566eb6e89f56c9509d4f628fddcedb131d9465cacd3d8bac337e7e", - "sha256:c935a22a557a560108d780f9a0fc426dd7459940dc54faa49d83249c8d3e760f", - "sha256:dbb8e7f2abee51cef77673be97760abff1674ed32847ce04b4af90f610144c7b", - "sha256:e6ea6b856a74d560d9326c0f5895ef8050126acfdc7ca08ad703eb0081e82b74", - "sha256:ebf2029c1f464c59b8bdbe5143c79fa2045a581ac53679733d3a91d400ff9efb", - "sha256:f1ff2ee69f10f13a9596480335f406dd1f70c3650349e2be67ca3139280cade0" - ], - "index": "pypi", - "version": "==9.3.0" + "markers": "python_version >= '3.7'", + "version": "==22.0" }, "pygments": { "hashes": [ @@ -418,19 +343,11 @@ }, "pymdown-extensions": { "hashes": [ - "sha256:1bd4a173095ef8c433b831af1f3cb13c10883be0c100ae613560668e594651f7", - "sha256:8e62688a8b1128acd42fa823f3d429d22f4284b5e6dd4d3cd56721559a5a211b" + "sha256:0f8fb7b74a37a61cc34e90b2c91865458b713ec774894ffad64353a5fce85cfc", + "sha256:ac698c15265680db5eb13cd4342abfcde2079ac01e5486028f47a1b41547b859" ], "markers": "python_version >= '3.7'", - "version": "==9.8" - }, - "pyparsing": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "markers": "python_full_version >= '3.6.8'", - "version": "==3.0.9" + "version": "==9.9" }, "python-dateutil": { "hashes": [ @@ -512,42 +429,45 @@ }, "urllib3": { "hashes": [ - "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e", - "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997" + "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc", + "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", - "version": "==1.26.12" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==1.26.13" }, "watchdog": { "hashes": [ - "sha256:083171652584e1b8829581f965b9b7723ca5f9a2cd7e20271edf264cfd7c1412", - "sha256:117ffc6ec261639a0209a3252546b12800670d4bf5f84fbd355957a0595fe654", - "sha256:186f6c55abc5e03872ae14c2f294a153ec7292f807af99f57611acc8caa75306", - "sha256:195fc70c6e41237362ba720e9aaf394f8178bfc7fa68207f112d108edef1af33", - "sha256:226b3c6c468ce72051a4c15a4cc2ef317c32590d82ba0b330403cafd98a62cfd", - "sha256:247dcf1df956daa24828bfea5a138d0e7a7c98b1a47cf1fa5b0c3c16241fcbb7", - "sha256:255bb5758f7e89b1a13c05a5bceccec2219f8995a3a4c4d6968fe1de6a3b2892", - "sha256:43ce20ebb36a51f21fa376f76d1d4692452b2527ccd601950d69ed36b9e21609", - "sha256:4f4e1c4aa54fb86316a62a87b3378c025e228178d55481d30d857c6c438897d6", - "sha256:5952135968519e2447a01875a6f5fc8c03190b24d14ee52b0f4b1682259520b1", - "sha256:64a27aed691408a6abd83394b38503e8176f69031ca25d64131d8d640a307591", - "sha256:6b17d302850c8d412784d9246cfe8d7e3af6bcd45f958abb2d08a6f8bedf695d", - "sha256:70af927aa1613ded6a68089a9262a009fbdf819f46d09c1a908d4b36e1ba2b2d", - "sha256:7a833211f49143c3d336729b0020ffd1274078e94b0ae42e22f596999f50279c", - "sha256:8250546a98388cbc00c3ee3cc5cf96799b5a595270dfcfa855491a64b86ef8c3", - "sha256:97f9752208f5154e9e7b76acc8c4f5a58801b338de2af14e7e181ee3b28a5d39", - "sha256:9f05a5f7c12452f6a27203f76779ae3f46fa30f1dd833037ea8cbc2887c60213", - "sha256:a735a990a1095f75ca4f36ea2ef2752c99e6ee997c46b0de507ba40a09bf7330", - "sha256:ad576a565260d8f99d97f2e64b0f97a48228317095908568a9d5c786c829d428", - "sha256:b530ae007a5f5d50b7fbba96634c7ee21abec70dc3e7f0233339c81943848dc1", - "sha256:bfc4d351e6348d6ec51df007432e6fe80adb53fd41183716017026af03427846", - "sha256:d3dda00aca282b26194bdd0adec21e4c21e916956d972369359ba63ade616153", - "sha256:d9820fe47c20c13e3c9dd544d3706a2a26c02b2b43c993b62fcd8011bcc0adb3", - "sha256:ed80a1628cee19f5cfc6bb74e173f1b4189eb532e705e2a13e3250312a62e0c9", - "sha256:ee3e38a6cc050a8830089f79cbec8a3878ec2fe5160cdb2dc8ccb6def8552658" + "sha256:1893d425ef4fb4f129ee8ef72226836619c2950dd0559bba022b0818c63a7b60", + "sha256:1a410dd4d0adcc86b4c71d1317ba2ea2c92babaf5b83321e4bde2514525544d5", + "sha256:1f2b0665c57358ce9786f06f5475bc083fea9d81ecc0efa4733fd0c320940a37", + "sha256:1f8eca9d294a4f194ce9df0d97d19b5598f310950d3ac3dd6e8d25ae456d4c8a", + "sha256:27e49268735b3c27310883012ab3bd86ea0a96dcab90fe3feb682472e30c90f3", + "sha256:28704c71afdb79c3f215c90231e41c52b056ea880b6be6cee035c6149d658ed1", + "sha256:2ac0bd7c206bb6df78ef9e8ad27cc1346f2b41b1fef610395607319cdab89bc1", + "sha256:2af1a29fd14fc0a87fb6ed762d3e1ae5694dcde22372eebba50e9e5be47af03c", + "sha256:3a048865c828389cb06c0bebf8a883cec3ae58ad3e366bcc38c61d8455a3138f", + "sha256:441024df19253bb108d3a8a5de7a186003d68564084576fecf7333a441271ef7", + "sha256:56fb3f40fc3deecf6e518303c7533f5e2a722e377b12507f6de891583f1b48aa", + "sha256:619d63fa5be69f89ff3a93e165e602c08ed8da402ca42b99cd59a8ec115673e1", + "sha256:74535e955359d79d126885e642d3683616e6d9ab3aae0e7dcccd043bd5a3ff4f", + "sha256:76a2743402b794629a955d96ea2e240bd0e903aa26e02e93cd2d57b33900962b", + "sha256:83cf8bc60d9c613b66a4c018051873d6273d9e45d040eed06d6a96241bd8ec01", + "sha256:920a4bda7daa47545c3201a3292e99300ba81ca26b7569575bd086c865889090", + "sha256:9e99c1713e4436d2563f5828c8910e5ff25abd6ce999e75f15c15d81d41980b6", + "sha256:a5bd9e8656d07cae89ac464ee4bcb6f1b9cecbedc3bf1334683bed3d5afd39ba", + "sha256:ad0150536469fa4b693531e497ffe220d5b6cd76ad2eda474a5e641ee204bbb6", + "sha256:af4b5c7ba60206759a1d99811b5938ca666ea9562a1052b410637bb96ff97512", + "sha256:c7bd98813d34bfa9b464cf8122e7d4bec0a5a427399094d2c17dd5f70d59bc61", + "sha256:ceaa9268d81205876bedb1069f9feab3eccddd4b90d9a45d06a0df592a04cae9", + "sha256:cf05e6ff677b9655c6e9511d02e9cc55e730c4e430b7a54af9c28912294605a4", + "sha256:d0fb5f2b513556c2abb578c1066f5f467d729f2eb689bc2db0739daf81c6bb7e", + "sha256:d6ae890798a3560688b441ef086bb66e87af6b400a92749a18b856a134fc0318", + "sha256:e5aed2a700a18c194c39c266900d41f3db0c1ebe6b8a0834b9995c835d2ca66e", + "sha256:e722755d995035dd32177a9c633d158f2ec604f2a358b545bba5bed53ab25bca", + "sha256:ed91c3ccfc23398e7aa9715abf679d5c163394b8cad994f34f156d57a7c163dc" ], "markers": "python_version >= '3.6'", - "version": "==2.1.9" + "version": "==2.2.0" } } } From b8c2d718b61a708c3b944d8a54eec9e8034f1ff2 Mon Sep 17 00:00:00 2001 From: rc-dbogle Date: Fri, 9 Dec 2022 14:18:46 -0500 Subject: [PATCH 2/3] Ran python formatter on posix tests (#18) --- posix/src/posixath/__main__.py | 14 +- .../posixath/tests/macos/test_T1059_002.py | 225 ++++++++++++------ .../posixath/tests/macos/test_T1059_007.py | 225 ++++++++++++------ posix/src/posixath/utils/common.py | 102 ++++++-- 4 files changed, 400 insertions(+), 166 deletions(-) diff --git a/posix/src/posixath/__main__.py b/posix/src/posixath/__main__.py index 219b76a..b32a20a 100644 --- a/posix/src/posixath/__main__.py +++ b/posix/src/posixath/__main__.py @@ -16,13 +16,16 @@ def get_args() -> tuple[argparse.Namespace, list[str]]: - description = textwrap.dedent(""" + description = textwrap.dedent( + """ PosixATH is the POSIX version of AtomicTestHarness that provides support for macOS and Linux operating systems. AtomicTestHarnesses streamlines the execution of ATT&CK technique variations and validates that the expected telemetry surfaces in the process. - """) - epilog=textwrap.dedent(""" + """ + ) + epilog = textwrap.dedent( + """ Examples: Run all of the variations of ATT&CK technique T1018 for Linux @@ -30,12 +33,13 @@ def get_args() -> tuple[argparse.Namespace, list[str]]: Run all of the variations of ATT&CK technique T1059.002 for macOS python -m posixath macos -t T1059_002 - """) + """ + ) parser: argparse.ArgumentParser = argparse.ArgumentParser( description=description, prog="PosixATH", formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=epilog + epilog=epilog, ) parser.add_argument( "-v", diff --git a/posix/src/posixath/tests/macos/test_T1059_002.py b/posix/src/posixath/tests/macos/test_T1059_002.py index e73e721..5deb9ab 100644 --- a/posix/src/posixath/tests/macos/test_T1059_002.py +++ b/posix/src/posixath/tests/macos/test_T1059_002.py @@ -36,7 +36,9 @@ def osa_shell_path(pytestconfig: pytest.Config): @pytest.mark.macos class TestAppleScript: # Path to the library directory containing supporting resources for this AtomicTestHarness execution. - __LIB_DIR = Path(abspath(getsourcefile(lambda: 0))).parent.joinpath("library", "T1059_002") + __LIB_DIR = Path(abspath(getsourcefile(lambda: 0))).parent.joinpath( + "library", "T1059_002" + ) __CHMOD_744: int = 0o744 def test_nsapplescript(self, osascript_path: Path) -> None: @@ -57,9 +59,14 @@ def test_nsapplescript(self, osascript_path: Path) -> None: - You can infer OSA code execution by watching for ES_EVENT_TYPE_NOTIFY_MMAP events. Processes which map scripting additions / JavaScript / AppleScript components into memory are likely utilizing this API. """ - nsapplescript_source_path: Path = self.__LIB_DIR.joinpath("nsapplescript_example.swift").resolve() - script_path: Path = self.__LIB_DIR.joinpath( - "whoami.scpt").resolve() if osascript_path is None else osascript_path + nsapplescript_source_path: Path = self.__LIB_DIR.joinpath( + "nsapplescript_example.swift" + ).resolve() + script_path: Path = ( + self.__LIB_DIR.joinpath("whoami.scpt").resolve() + if osascript_path is None + else osascript_path + ) if not os.path.isfile(nsapplescript_source_path): assert False, "Swift source code file does not exist!" @@ -68,7 +75,7 @@ def test_nsapplescript(self, osascript_path: Path) -> None: # Load the AppleScript we're going to run and execute the technique try: - with open(script_path, 'r') as script_dir_fd: + with open(script_path, "r") as script_dir_fd: loaded_applescript: str = " ".join(script_dir_fd.readlines()) # Basic check to make sure we have a Swift source file @@ -76,9 +83,14 @@ def test_nsapplescript(self, osascript_path: Path) -> None: assert False, "File is not Swift source code." # Compile the Swift source file. - destination_path: Path = self.__LIB_DIR.joinpath("nsapplescript_example").resolve() - swift_compile_result: StandardizedCompletedProcess = DarwinSTDLib.compile_swift( - nsapplescript_source_path, str(destination_path)) + destination_path: Path = self.__LIB_DIR.joinpath( + "nsapplescript_example" + ).resolve() + swift_compile_result: StandardizedCompletedProcess = ( + DarwinSTDLib.compile_swift( + nsapplescript_source_path, str(destination_path) + ) + ) # Check to make sure compilation was successful returncode: int = swift_compile_result.return_code @@ -94,16 +106,17 @@ def test_nsapplescript(self, osascript_path: Path) -> None: if not DarwinSTDLib.make_file_executable(destination_path): assert False, "Failed to make the NSAppleScript example executable!" - execution_result: dict = self.default_executer([destination_path], str(script_path), - applescript=loaded_applescript) + execution_result: dict = self.default_executer( + [destination_path], str(script_path), applescript=loaded_applescript + ) # Pretty print the results - rich.print_json( - json.dumps(execution_result, indent=4, sort_keys=True) - ) + rich.print_json(json.dumps(execution_result, indent=4, sort_keys=True)) # Was it a success? - assert execution_result is not None and execution_result["result"] == "success" + assert ( + execution_result is not None and execution_result["result"] == "success" + ) except FileNotFoundError as fnfe: assert False, f"File not found:\n{fnfe}" @@ -123,8 +136,11 @@ def test_osascript_cmdl(self, osascript_path: Path) -> None: - Execution of the `osascript` binary will take place and any command line arguments will also be visible. - This is the most trivial case to detect. """ - script_path: Path = self.__LIB_DIR.joinpath( - "whoami.scpt").resolve() if osascript_path is None else osascript_path + script_path: Path = ( + self.__LIB_DIR.joinpath("whoami.scpt").resolve() + if osascript_path is None + else osascript_path + ) if not os.path.isfile(script_path): assert False, "Script does not exist!" osascript_bin_path = Path("/usr/bin/osascript") @@ -132,31 +148,38 @@ def test_osascript_cmdl(self, osascript_path: Path) -> None: # First load the AppleScript we're going to run for the script load try: - with open(script_path, 'r') as script_dir_fd: + with open(script_path, "r") as script_dir_fd: loaded_applescript: str = " ".join(script_dir_fd.readlines()) # 1. Execute the script load technique variation - script_load_result: dict = self.default_executer([osascript_bin_path, str(script_path)], "", - applescript=loaded_applescript) + script_load_result: dict = self.default_executer( + [osascript_bin_path, str(script_path)], + "", + applescript=loaded_applescript, + ) # Pretty print the results from the script load - rich.print_json( - json.dumps(script_load_result, indent=4, sort_keys=True) - ) + rich.print_json(json.dumps(script_load_result, indent=4, sort_keys=True)) # Was it a success? - assert script_load_result is not None and script_load_result["result"] == "success" + assert ( + script_load_result is not None + and script_load_result["result"] == "success" + ) # 2. Execute AppleScript directly at the command line - cmdl_args_result: dict = self.default_executer([osascript_bin_path, "-e", whoami_cmdl], "", - applescript=loaded_applescript) + cmdl_args_result: dict = self.default_executer( + [osascript_bin_path, "-e", whoami_cmdl], + "", + applescript=loaded_applescript, + ) # Pretty print the results from the command line arguments - rich.print_json( - json.dumps(cmdl_args_result, indent=4, sort_keys=True) - ) + rich.print_json(json.dumps(cmdl_args_result, indent=4, sort_keys=True)) # Was it a success? - assert cmdl_args_result is not None and cmdl_args_result["result"] == "success" + assert ( + cmdl_args_result is not None and cmdl_args_result["result"] == "success" + ) except FileNotFoundError as fnfe: assert False, f"File not found:\n{fnfe}" @@ -172,7 +195,11 @@ def test_shell_script(self, osa_shell_path: Path) -> None: - Execution of the `osascript` binary will take place and any command line arguments will also be visible. - The full script content on products supporting script loads """ - script_path: Path = self.__LIB_DIR.joinpath("whoami.sh").resolve() if osa_shell_path is None else osa_shell_path + script_path: Path = ( + self.__LIB_DIR.joinpath("whoami.sh").resolve() + if osa_shell_path is None + else osa_shell_path + ) if not os.path.isfile(script_path): assert False, "Script does not exist!" # Check that it's a valid shell script @@ -190,17 +217,19 @@ def test_shell_script(self, osa_shell_path: Path) -> None: # Load the AppleScript we're going to execute and execute the technique try: - with open(script_path, 'r') as script_fd: + with open(script_path, "r") as script_fd: loaded_applescript: str = " ".join(script_fd.readlines()[1:]) - execution_result: dict = self.default_executer([script_path], "", applescript=loaded_applescript) + execution_result: dict = self.default_executer( + [script_path], "", applescript=loaded_applescript + ) # Pretty print the results - rich.print_json( - json.dumps(execution_result, indent=4, sort_keys=True) - ) + rich.print_json(json.dumps(execution_result, indent=4, sort_keys=True)) # Was it a success? - assert execution_result is not None and execution_result["result"] == "success" + assert ( + execution_result is not None and execution_result["result"] == "success" + ) except FileNotFoundError as fnfe: assert False, f"File not found:\n{fnfe}" @@ -222,21 +251,32 @@ def test_applet(self, osascript_path: Path) -> None: - You can infer OSA code execution by watching for ES_EVENT_TYPE_NOTIFY_MMAP events. Processes which map scripting additions / JavaScript / AppleScript components into memory are applets. """ - applet_source_path: Path = self.__LIB_DIR.joinpath( - "whoami.scpt").resolve() if osascript_path is None else osascript_path + applet_source_path: Path = ( + self.__LIB_DIR.joinpath("whoami.scpt").resolve() + if osascript_path is None + else osascript_path + ) if not os.path.isfile(applet_source_path): assert False, "Script does not exist!" - resulting_applet_path: Path = self.__LIB_DIR.joinpath("example_applet.app").resolve() + resulting_applet_path: Path = self.__LIB_DIR.joinpath( + "example_applet.app" + ).resolve() # Compile the AppleScript source into an applet. - osacompile_proc = DarwinSTDLib.compile_applescript(applet_source_path, resulting_applet_path) + osacompile_proc = DarwinSTDLib.compile_applescript( + applet_source_path, resulting_applet_path + ) if osacompile_proc.return_code != 0: assert False, "Failed to compile AppleScript applet!" # Load the AppleScript we're going to run and execute the technique - loaded_applescript: str = DarwinSTDLib.get_osa_script_from_applet(resulting_applet_path) + loaded_applescript: str = DarwinSTDLib.get_osa_script_from_applet( + resulting_applet_path + ) cmdl = ["/usr/bin/open", str(resulting_applet_path)] - execution_result: dict = self.default_executer(argv=cmdl, stdin="", applescript=loaded_applescript) + execution_result: dict = self.default_executer( + argv=cmdl, stdin="", applescript=loaded_applescript + ) # Pretty print the results rich.print_json(json.dumps(execution_result, indent=4, sort_keys=True)) @@ -253,28 +293,44 @@ def test_stay_open_script(self, osascript_path: Path) -> dict: Arguments: `--osascript-path` """ - stay_open_source_path: Path = self.__LIB_DIR.joinpath( - "whoami.scpt") if osascript_path is None else osascript_path + stay_open_source_path: Path = ( + self.__LIB_DIR.joinpath("whoami.scpt") + if osascript_path is None + else osascript_path + ) if not os.path.isfile(stay_open_source_path): assert False, "Script does not exist!" - resulting_stay_open_path: Path = self.__LIB_DIR.joinpath("example_stay_open.app").resolve() + resulting_stay_open_path: Path = self.__LIB_DIR.joinpath( + "example_stay_open.app" + ).resolve() # Compile the AppleScript source into an applet - osacompile_proc = DarwinSTDLib.compile_applescript(stay_open_source_path, resulting_stay_open_path) + osacompile_proc = DarwinSTDLib.compile_applescript( + stay_open_source_path, resulting_stay_open_path + ) if osacompile_proc.return_code != 0: assert False, "Error compiling the AppleScript stay-open-script!" # Insert the Stay-Open-Script plist key: `OSAAppletStayOpen` plist_path: Path = resulting_stay_open_path.joinpath("Contents", "Info.plist") - plist_result = DarwinSTDLib.insert_plist_key(plist_path=plist_path, key="OSAAppletStayOpen", value_type="-bool", - value="YES") + plist_result = DarwinSTDLib.insert_plist_key( + plist_path=plist_path, + key="OSAAppletStayOpen", + value_type="-bool", + value="YES", + ) if not plist_result: - assert False, "Error inserting the stay-open-key into the AppleScript applet!" + assert ( + False + ), "Error inserting the stay-open-key into the AppleScript applet!" - loaded_applescript: str = DarwinSTDLib.get_osa_script_from_applet(resulting_stay_open_path) + loaded_applescript: str = DarwinSTDLib.get_osa_script_from_applet( + resulting_stay_open_path + ) cmdl = ["/usr/bin/open", str(resulting_stay_open_path)] - execution_result: dict = self.default_executer(argv=cmdl, stdin="", - applescript=loaded_applescript) + execution_result: dict = self.default_executer( + argv=cmdl, stdin="", applescript=loaded_applescript + ) # Pretty print the results rich.print_json(json.dumps(execution_result, indent=4, sort_keys=True)) @@ -302,7 +358,8 @@ def default_executer(self, argv: list, stdin: str, applescript: str) -> dict: return {"result": "failure"} try: # subprocess.run() is blocking which is why we need to use Popen here. - proc: subprocess.Popen = subprocess.Popen(argv, + proc: subprocess.Popen = subprocess.Popen( + argv, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -331,7 +388,7 @@ def default_executer(self, argv: list, stdin: str, applescript: str) -> dict: # 1. The executing binary (e.g. `.app` for Applets, Stay-Open-Scripts, etc.) # 2. The executed script (e.g. `.sh` or `.scpt` for shell and OSA files respectively) executable_path: Path = argv[0] - if argv[0] == Path('/usr/bin/osascript') and argv[1] != "-e": + if argv[0] == Path("/usr/bin/osascript") and argv[1] != "-e": executable_path = argv[1] # The following logic applies to applet and stay-open-script application bundles specific test harness @@ -342,7 +399,9 @@ def default_executer(self, argv: list, stdin: str, applescript: str) -> dict: # Here we're going to get the name of the actual Mach-O binary being executed. These live in the .app's # "MacOS" directory. - execution_dir: Path = Path(argv[1]).joinpath("Contents", "MacOS").resolve() + execution_dir: Path = ( + Path(argv[1]).joinpath("Contents", "MacOS").resolve() + ) app_plist: dict = DarwinSTDLib.get_app_plist(Path(argv[1])) app_binary: str = "" if "CFBundleExecutable" in app_plist: @@ -350,49 +409,73 @@ def default_executer(self, argv: list, stdin: str, applescript: str) -> dict: else: app_binary = os.listdir(execution_dir)[0] - executable_path = Path(argv[1]).joinpath("Contents", "MacOS", app_binary).resolve() + executable_path = ( + Path(argv[1]).joinpath("Contents", "MacOS", app_binary).resolve() + ) # Using the `psutil` library we can get the correct pid for the executed application. # We will first try to find the application by the Mach-O binary name and then we will try with the # `.app` name. # If we still cannot find the pid of the executed binary then it's dead... - app_proc_enumeration: list = list(filter(lambda p: p.name() == app_name, psutil.process_iter())) - if len(app_proc_enumeration) > 0 and app_proc_enumeration[0].pid is not None: + app_proc_enumeration: list = list( + filter(lambda p: p.name() == app_name, psutil.process_iter()) + ) + if ( + len(app_proc_enumeration) > 0 + and app_proc_enumeration[0].pid is not None + ): pid = app_proc_enumeration[0].pid else: app_proc_enumeration: list = list( - filter(lambda p: p.name() == executable_path.stem, psutil.process_iter())) - if len(app_proc_enumeration) > 0 and app_proc_enumeration[0].pid is not None: + filter( + lambda p: p.name() == executable_path.stem, + psutil.process_iter(), + ) + ) + if ( + len(app_proc_enumeration) > 0 + and app_proc_enumeration[0].pid is not None + ): pid = app_proc_enumeration[0].pid # Next we'll attempt to grab the correct ppid try: ppid = psutil.Process(pid).ppid() except psutil.NoSuchProcess as err: - logging.warning("Could not find parent process of pid: {}\n{}".format(pid, err)) + logging.warning( + "Could not find parent process of pid: {}\n{}".format(pid, err) + ) ppid = -1 # This is mostly for Stay-Open-Scripts, but we're going to want to kill the process once we've # collected all the necessary telemetry. For this we're going to use the 'kill' command. if len(app_proc_enumeration) > 0: time.sleep(1) - kill_status: subprocess.CompletedProcess = subprocess.run(["kill", f"{pid}"], capture_output=True) + kill_status: subprocess.CompletedProcess = subprocess.run( + ["kill", f"{pid}"], capture_output=True + ) if kill_status.returncode == 1: # Killing the process will most likely happen for Applets logging.debug( - "Failed to kill process:\nProc Name: {}\nPID: {}".format(executable_path.stem, - pid)) + "Failed to kill process:\nProc Name: {}\nPID: {}".format( + executable_path.stem, pid + ) + ) returncode: int = proc.returncode process_error: bool = False if len(process_stdout_result) != 0 and not ( - "returned:OK" in process_stdout_result or "gave up:true" in process_stdout_result): + "returned:OK" in process_stdout_result + or "gave up:true" in process_stdout_result + ): process_error = True if Path.home().name in process_stdout_result: process_error = False # Fill out the completed process model - completed_process = StandardizedCompletedProcess(result="success", argv=str(proc.args)) + completed_process = StandardizedCompletedProcess( + result="success", argv=str(proc.args) + ) completed_process.attack_id = "T1059.002" completed_process.process_path = str(proc.args[0]) completed_process.return_code = returncode @@ -402,11 +485,17 @@ def default_executer(self, argv: list, stdin: str, applescript: str) -> dict: completed_process.stdout = process_stdout_result completed_process.executable_name = Path(executable_path).name completed_process.md5 = DarwinSTDLib.get_md5(executable_path) - completed_process.executed_applescript = applescript if len(applescript) > 0 else "" + completed_process.executed_applescript = ( + applescript if len(applescript) > 0 else "" + ) if returncode != 0 or process_error: completed_process.result = "failure" return completed_process.__dict__ - except (subprocess.CalledProcessError, FileNotFoundError, PermissionError) as err: + except ( + subprocess.CalledProcessError, + FileNotFoundError, + PermissionError, + ) as err: return {"result": f"Failure: {err}"} diff --git a/posix/src/posixath/tests/macos/test_T1059_007.py b/posix/src/posixath/tests/macos/test_T1059_007.py index 48d95f6..213ccb2 100644 --- a/posix/src/posixath/tests/macos/test_T1059_007.py +++ b/posix/src/posixath/tests/macos/test_T1059_007.py @@ -35,7 +35,9 @@ def osa_shell_path(pytestconfig: pytest.Config): # Several variations are supported: OSAKit, osascript CMDL, shell scripts, applets, and stay-open-scripts. @pytest.mark.macos class TestJXA: - __LIB_DIR = Path(abspath(getsourcefile(lambda: 0))).parent.joinpath("library", "T1059_007") + __LIB_DIR = Path(abspath(getsourcefile(lambda: 0))).parent.joinpath( + "library", "T1059_007" + ) __CHMOD_744: int = 0o744 def test_osakit(self, osascript_path: Path): @@ -57,8 +59,14 @@ def test_osakit(self, osascript_path: Path): - You can infer OSA code execution by watching for ES_EVENT_TYPE_NOTIFY_MMAP events. Processes which map scripting additions / JavaScript / AppleScript components into memory are likely utilizing this API. """ - osakit_source_code_path: Path = self.__LIB_DIR.joinpath("osakit_example.swift").resolve() - script_path = self.__LIB_DIR.joinpath("whoami_jxa.scpt").resolve() if osascript_path is None else osascript_path + osakit_source_code_path: Path = self.__LIB_DIR.joinpath( + "osakit_example.swift" + ).resolve() + script_path = ( + self.__LIB_DIR.joinpath("whoami_jxa.scpt").resolve() + if osascript_path is None + else osascript_path + ) if not os.path.isfile(osakit_source_code_path): assert False, "Swift source code file does not exist!" if not os.path.isfile(script_path): @@ -66,18 +74,23 @@ def test_osakit(self, osascript_path: Path): # Load the JXA we're going to execute and execute the technique try: - with open(script_path, 'r') as script_dir_fd: + with open(script_path, "r") as script_dir_fd: jxa_source: str = " ".join(script_dir_fd.readlines()) # Basic check to make sure we have a Swift source file if osakit_source_code_path.suffix != ".swift": logging.error("File is not Swift source code.") - assert False, f"This is not a valid Swift file: {osakit_source_code_path.name}" + assert ( + False + ), f"This is not a valid Swift file: {osakit_source_code_path.name}" # Compile the Swift source file. - destination_path: str = str(self.__LIB_DIR.joinpath("osakit_example").resolve()) - swift_compile_result: StandardizedCompletedProcess = DarwinSTDLib.compile_swift(osakit_source_code_path, - destination_path) + destination_path: str = str( + self.__LIB_DIR.joinpath("osakit_example").resolve() + ) + swift_compile_result: StandardizedCompletedProcess = ( + DarwinSTDLib.compile_swift(osakit_source_code_path, destination_path) + ) # Check to make sure compilation was successful returncode: int = swift_compile_result.return_code stderr: str = swift_compile_result.stderr @@ -89,20 +102,23 @@ def test_osakit(self, osascript_path: Path): executable: bool = os.access(destination_path, self.__CHMOD_744) if not executable: # Set the executable bit for the user - make_executable_result: bool = DarwinSTDLib.make_file_executable(destination_path) + make_executable_result: bool = DarwinSTDLib.make_file_executable( + destination_path + ) if not make_executable_result: assert False, "Failed to make the NSAppleScript example executable!" - execution_result: dict = self.default_executer([destination_path], str(script_path), - jxa=jxa_source) + execution_result: dict = self.default_executer( + [destination_path], str(script_path), jxa=jxa_source + ) # Pretty print the results - rich.print_json( - json.dumps(execution_result, indent=4, sort_keys=True) - ) + rich.print_json(json.dumps(execution_result, indent=4, sort_keys=True)) # Was it a success? - assert execution_result is not None and execution_result["result"] == "success" + assert ( + execution_result is not None and execution_result["result"] == "success" + ) except FileNotFoundError as fnfe: assert False, f"File not found:\n{fnfe}" @@ -122,43 +138,59 @@ def test_jxa_osascript_cmdl(self, osascript_path: Path): - Execution of the `osascript` binary will take place and any command line arguments will also be visible. - This is the most trivial case to detect. """ - script_path = self.__LIB_DIR.joinpath("whoami_jxa.scpt").resolve() if osascript_path is None else osascript_path + script_path = ( + self.__LIB_DIR.joinpath("whoami_jxa.scpt").resolve() + if osascript_path is None + else osascript_path + ) osascript_bin_path = Path("/usr/bin/osascript") if not os.path.isfile(script_path): assert False, "Script does not exist!" - whoami_cmdl = [osascript_bin_path, "-l", "JavaScript", "-e", "var app = Application.currentApplication();", - "-e", - "app.includeStandardAdditions = true;", "-e", "app.systemInfo().shortUserName;"] + whoami_cmdl = [ + osascript_bin_path, + "-l", + "JavaScript", + "-e", + "var app = Application.currentApplication();", + "-e", + "app.includeStandardAdditions = true;", + "-e", + "app.systemInfo().shortUserName;", + ] try: # Try to load the JXA from the script file - with open(script_path, 'r') as script_dir_fd: + with open(script_path, "r") as script_dir_fd: jxa_source: str = " ".join(script_dir_fd.readlines()) # Execute the script load variation - execution_result: dict = self.default_executer([osascript_bin_path, "-l", "JavaScript", str(script_path)], - "", - jxa=jxa_source) + execution_result: dict = self.default_executer( + [osascript_bin_path, "-l", "JavaScript", str(script_path)], + "", + jxa=jxa_source, + ) # Pretty print the results - rich.print_json( - json.dumps(execution_result, indent=4, sort_keys=True) - ) + rich.print_json(json.dumps(execution_result, indent=4, sort_keys=True)) # Was it a success? - assert execution_result is not None and execution_result["result"] == "success" + assert ( + execution_result is not None and execution_result["result"] == "success" + ) # Execute the line-by-line variation - cmdl_args_result: dict = self.default_executer(whoami_cmdl, "", jxa=jxa_source) + cmdl_args_result: dict = self.default_executer( + whoami_cmdl, "", jxa=jxa_source + ) # Pretty print the results from the command line arguments - rich.print_json( - json.dumps(cmdl_args_result, indent=4, sort_keys=True) - ) + rich.print_json(json.dumps(cmdl_args_result, indent=4, sort_keys=True)) # Was it a success? - assert cmdl_args_result is not None and cmdl_args_result["result"] == "success" + assert ( + cmdl_args_result is not None and cmdl_args_result["result"] == "success" + ) except FileNotFoundError as fnfe: assert False, f"File not found:\n{fnfe}" @@ -174,8 +206,11 @@ def test_jxa_shell_script(self, osa_shell_path: Path): - Execution of the `osascript` binary will take place and any command line arguments will also be visible. - The full script content on products supporting script loads """ - script_path: Path = self.__LIB_DIR.joinpath( - "whoami_jxa.sh").resolve() if osa_shell_path is None else osa_shell_path + script_path: Path = ( + self.__LIB_DIR.joinpath("whoami_jxa.sh").resolve() + if osa_shell_path is None + else osa_shell_path + ) if not os.path.isfile(script_path): assert False, "Shell script does not exist!" @@ -194,17 +229,19 @@ def test_jxa_shell_script(self, osa_shell_path: Path): # Load the JXA we're going to execute and execute the technique try: - with open(script_path, 'r') as script_fd: + with open(script_path, "r") as script_fd: loaded_jxa: str = " ".join(script_fd.readlines()[1:]) - execution_result: dict = self.default_executer([script_path], "", jxa=loaded_jxa) + execution_result: dict = self.default_executer( + [script_path], "", jxa=loaded_jxa + ) # Pretty print the results - rich.print_json( - json.dumps(execution_result, indent=4, sort_keys=True) - ) + rich.print_json(json.dumps(execution_result, indent=4, sort_keys=True)) # Was it a success? - assert execution_result is not None and execution_result["result"] == "success" + assert ( + execution_result is not None and execution_result["result"] == "success" + ) except FileNotFoundError as fnfe: assert False, f"File not found:\n{fnfe}" @@ -227,14 +264,21 @@ def test_jxa_applet(self, osascript_path: Path): - You can infer OSA code execution by watching for ES_EVENT_TYPE_NOTIFY_MMAP events. Processes which map scripting additions / JavaScript / AppleScript components into memory are applets """ - applet_source_path: Path = self.__LIB_DIR.joinpath( - "whoami_jxa.scpt").resolve() if osascript_path is None else osascript_path + applet_source_path: Path = ( + self.__LIB_DIR.joinpath("whoami_jxa.scpt").resolve() + if osascript_path is None + else osascript_path + ) if not os.path.isfile(applet_source_path): assert False, "Script does not exist!" - resulting_applet_path: Path = self.__LIB_DIR.joinpath("example_jxa_applet.app").resolve() + resulting_applet_path: Path = self.__LIB_DIR.joinpath( + "example_jxa_applet.app" + ).resolve() # Compile the JXA source into an applet. - osacompile_proc = DarwinSTDLib.compile_jxa(applet_source_path, resulting_applet_path) + osacompile_proc = DarwinSTDLib.compile_jxa( + applet_source_path, resulting_applet_path + ) if osacompile_proc.return_code != 0: assert False, "Error compiling the JXA applet!" @@ -258,31 +302,42 @@ def test_jxa_stay_open_script(self, osascript_path: Path): Arguments: `--osascript-path` """ - jxa_source_path: Path = self.__LIB_DIR.joinpath("whoami_jxa.scpt") if osascript_path is None else osascript_path + jxa_source_path: Path = ( + self.__LIB_DIR.joinpath("whoami_jxa.scpt") + if osascript_path is None + else osascript_path + ) if not os.path.isfile(jxa_source_path): assert False, "Script does not exist!" - dest_applet_path: Path = self.__LIB_DIR.joinpath("example_stay_open_jxa.app").resolve() + dest_applet_path: Path = self.__LIB_DIR.joinpath( + "example_stay_open_jxa.app" + ).resolve() # Compile the JXA source into an applet - applet_proc: StandardizedCompletedProcess = DarwinSTDLib.compile_jxa(jxa_source_path, dest_applet_path) + applet_proc: StandardizedCompletedProcess = DarwinSTDLib.compile_jxa( + jxa_source_path, dest_applet_path + ) if applet_proc.return_code != 0 or len(applet_proc.stderr) > 0: assert False, "The stay-open-script did not properly execute." plist_path: Path = dest_applet_path.joinpath("Contents", "Info.plist").resolve() - plist_result: bool = DarwinSTDLib.insert_plist_key(plist_path, key="OSAAppletStayOpen", value_type="-bool", - value="YES") + plist_result: bool = DarwinSTDLib.insert_plist_key( + plist_path, key="OSAAppletStayOpen", value_type="-bool", value="YES" + ) if not plist_result: # If there was a problem inserting the stay open plist key - assert False, f"Error adding Property List key: OSAAppletStayOpen to: {plist_path}" + assert ( + False + ), f"Error adding Property List key: OSAAppletStayOpen to: {plist_path}" # Load the JXA we're going to execute and execute the technique loaded_jxa: str = DarwinSTDLib.get_osa_script_from_applet(dest_applet_path) cmdl = ["/usr/bin/open", str(dest_applet_path)] - execution_result: StandardizedCompletedProcess = self.default_executer(argv=cmdl, stdin="", jxa=loaded_jxa) - # Pretty print the results - rich.print_json( - json.dumps(execution_result, indent=4, sort_keys=True) + execution_result: StandardizedCompletedProcess = self.default_executer( + argv=cmdl, stdin="", jxa=loaded_jxa ) + # Pretty print the results + rich.print_json(json.dumps(execution_result, indent=4, sort_keys=True)) # Was it a success? assert execution_result is not None and execution_result["result"] == "success" @@ -341,7 +396,7 @@ def default_executer(argv: list, stdin: str, jxa: str) -> dict | None: # 1. The executing binary (e.g. `.app` for Applets, Stay-Open-Scripts, etc.) # 2. The executed script (e.g. `.sh` or `.scpt` for shell and OSA files respectively) executable_path: Path = argv[0] - if argv[0] == Path('/usr/bin/osascript') and "-e" not in argv: + if argv[0] == Path("/usr/bin/osascript") and "-e" not in argv: if "-l" in argv: executable_path = argv[3] @@ -353,7 +408,9 @@ def default_executer(argv: list, stdin: str, jxa: str) -> dict | None: # Here we're going to get the name of the actual Mach-O binary being executed. These live in the .app's # "MacOS" directory. - execution_dir: Path = Path(argv[1]).joinpath("Contents", "MacOS").resolve() + execution_dir: Path = ( + Path(argv[1]).joinpath("Contents", "MacOS").resolve() + ) app_plist: dict = DarwinSTDLib.get_app_plist(Path(argv[1])) app_binary: str = "" if "CFBundleExecutable" in app_plist: @@ -361,46 +418,74 @@ def default_executer(argv: list, stdin: str, jxa: str) -> dict | None: else: app_binary = os.listdir(execution_dir)[0] - executable_path = Path(argv[1]).joinpath("Contents", "MacOS", app_binary).resolve() + executable_path = ( + Path(argv[1]).joinpath("Contents", "MacOS", app_binary).resolve() + ) # Using the `psutil` library we can get the correct pid for the executed application. # We will first try to find the application by the Mach-O binary name and then we will try with the # `.app` name. # If we still cannot find the pid of the executed binary then it's dead... - app_proc_enumeration: list = list(filter(lambda p: p.name() == app_name, psutil.process_iter())) - if len(app_proc_enumeration) > 0 and app_proc_enumeration[0].pid is not None: + app_proc_enumeration: list = list( + filter(lambda p: p.name() == app_name, psutil.process_iter()) + ) + if ( + len(app_proc_enumeration) > 0 + and app_proc_enumeration[0].pid is not None + ): pid = app_proc_enumeration[0].pid else: app_proc_enumeration: list = list( - filter(lambda p: p.name() == executable_path.stem, psutil.process_iter())) - if len(app_proc_enumeration) > 0 and app_proc_enumeration[0].pid is not None: + filter( + lambda p: p.name() == executable_path.stem, + psutil.process_iter(), + ) + ) + if ( + len(app_proc_enumeration) > 0 + and app_proc_enumeration[0].pid is not None + ): pid = app_proc_enumeration[0].pid try: ppid = psutil.Process(pid).ppid() except psutil.NoSuchProcess as err: - logging.warning("Could not find parent process of pid: {}\n{}".format(pid, err)) + logging.warning( + "Could not find parent process of pid: {}\n{}".format(pid, err) + ) ppid = -1 if len(app_proc_enumeration) > 0: # This is mostly for Stay-Open-Scripts, but we're going to want to kill the process once we've # collected all the necessary telemetry. For this we're going to use the 'kill' command. time.sleep(1) - kill_status: subprocess.CompletedProcess = subprocess.run(["kill", f"{pid}"], capture_output=True) + kill_status: subprocess.CompletedProcess = subprocess.run( + ["kill", f"{pid}"], capture_output=True + ) if kill_status.returncode == 1: # Killing the process will most likely happen for Applets logging.debug( - "Failed to kill process:\nProc Name: {}\nPID: {}".format(executable_path.stem, pid)) + "Failed to kill process:\nProc Name: {}\nPID: {}".format( + executable_path.stem, pid + ) + ) returncode: int = proc.returncode process_error: bool = False if len(process_stdout_result) != 0 and not ( - "returned:OK" in process_stdout_result or "gaveUp:true" in process_stdout_result): + "returned:OK" in process_stdout_result + or "gaveUp:true" in process_stdout_result + ): process_error = True - if Path.home().name in process_stdout_result or "NSAppleEvent" in process_stdout_result: + if ( + Path.home().name in process_stdout_result + or "NSAppleEvent" in process_stdout_result + ): process_error = False # Fill out the completed process model - completed_process = StandardizedCompletedProcess(result="success", argv=str(proc.args)) + completed_process = StandardizedCompletedProcess( + result="success", argv=str(proc.args) + ) completed_process.attack_id = "T1059.007" completed_process.process_path = str(proc.args[0]) completed_process.return_code = returncode @@ -415,5 +500,9 @@ def default_executer(argv: list, stdin: str, jxa: str) -> dict | None: if returncode != 0 or process_error: completed_process.result = "failure" return completed_process.__dict__ - except (subprocess.CalledProcessError, FileNotFoundError, PermissionError) as err: + except ( + subprocess.CalledProcessError, + FileNotFoundError, + PermissionError, + ) as err: assert False, f"Failure: {err}" diff --git a/posix/src/posixath/utils/common.py b/posix/src/posixath/utils/common.py index 15e95b1..1cc8d04 100644 --- a/posix/src/posixath/utils/common.py +++ b/posix/src/posixath/utils/common.py @@ -26,7 +26,7 @@ class StandardizedCompletedProcess: def __init__(self, result: str, argv: str, process_path: str | None = ""): self.activity_at_ts: str = ( - datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z" + datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z" ) self.attack_id: str = "" @@ -156,7 +156,7 @@ def get_executable_md5(file_path: Path) -> str | None: @staticmethod def default_commandline_executer( - argv: list[str], stdin: str | None = None + argv: list[str], stdin: str | None = None ) -> StandardizedCompletedProcess | None: """ Execute a basic command-line and provides back telemetry in the form of a StandardizedCompletedProcess. @@ -224,9 +224,9 @@ def default_commandline_executer( return completed_proc_model except ( - subprocess.CalledProcessError, - FileNotFoundError, - PermissionError, + subprocess.CalledProcessError, + FileNotFoundError, + PermissionError, ) as err: logger.error(err) @@ -240,10 +240,15 @@ def get_file_type(file_path: Path) -> str: """ Attempts to get the "file type" of the specified file. To do this we use the `/usr/bin/file` LOLbin. """ - file_result = DarwinSTDLib.default_commandline_executer(["/usr/bin/file", str(file_path)]) + file_result = DarwinSTDLib.default_commandline_executer( + ["/usr/bin/file", str(file_path)] + ) file_type: str = "UNKNOWN" if file_result.result == "success": - if file_result.stdout is not None and len(file_result.stdout.split(":")) > 0: + if ( + file_result.stdout is not None + and len(file_result.stdout.split(":")) > 0 + ): file_type = file_result.stdout.split(":")[1].strip() return file_type @@ -259,34 +264,54 @@ def make_file_executable(file_path: Path) -> bool: return True @staticmethod - def osacompile(script_path: Path, destination_path: Path, jxa: bool = False) -> StandardizedCompletedProcess: + def osacompile( + script_path: Path, destination_path: Path, jxa: bool = False + ) -> StandardizedCompletedProcess: """ Python wrapper for the `/usr/bin/osascript` LoLBin. Executes either AppleScript or JXA scripts. """ osacompile_bin_path: str = "/usr/bin/osacompile" if jxa: - cmdl: list[str] = [osacompile_bin_path, "-l", "JavaScript", "-o", str(destination_path), str(script_path)] + cmdl: list[str] = [ + osacompile_bin_path, + "-l", + "JavaScript", + "-o", + str(destination_path), + str(script_path), + ] else: - cmdl: list[str] = [osacompile_bin_path, "-o", str(destination_path), str(script_path)] + cmdl: list[str] = [ + osacompile_bin_path, + "-o", + str(destination_path), + str(script_path), + ] return DarwinSTDLib.default_commandline_executer(cmdl) @staticmethod - def compile_applescript(script_path: Path, destination_path: Path) -> StandardizedCompletedProcess: + def compile_applescript( + script_path: Path, destination_path: Path + ) -> StandardizedCompletedProcess: """ Wrapper for the osacompile method specifically for AppleScript. """ return DarwinSTDLib.osacompile(script_path, destination_path) @staticmethod - def compile_jxa(script_path: Path, destination_path: Path) -> StandardizedCompletedProcess: + def compile_jxa( + script_path: Path, destination_path: Path + ) -> StandardizedCompletedProcess: """ Wrapper for the osacompile method specifically for JXA. """ return DarwinSTDLib.osacompile(script_path, destination_path, jxa=True) @staticmethod - def osadecompile(script_path: Path, destination_path: Path | None = None) -> StandardizedCompletedProcess: + def osadecompile( + script_path: Path, destination_path: Path | None = None + ) -> StandardizedCompletedProcess: """ Decompiles a specified OSA script byte-code file and returns the source in the stdout of the returned StandardizedCompletedProcess. @@ -295,7 +320,12 @@ def osadecompile(script_path: Path, destination_path: Path | None = None) -> Sta cmdl: list = [] if destination_path is not None: - cmdl = [osadecompile_bin_path, "-o", str(destination_path), str(script_path)] + cmdl = [ + osadecompile_bin_path, + "-o", + str(destination_path), + str(script_path), + ] else: cmdl = [osadecompile_bin_path, str(script_path)] @@ -309,17 +339,22 @@ def check_and_install_xc_cmdl_tools() -> bool: """ # Check to ensure Xcode command line tools are installed xcode_select_bin_path: str = "/usr/bin/xcode-select" - xcode_select_proc = DarwinSTDLib.default_commandline_executer([xcode_select_bin_path, "-p"]) + xcode_select_proc = DarwinSTDLib.default_commandline_executer( + [xcode_select_bin_path, "-p"] + ) if "error: unable" in xcode_select_proc.stdout: # We need to install Xcode command line tools - cmdl_install_proc = DarwinSTDLib.default_commandline_executer([xcode_select_bin_path, "--install"]) + cmdl_install_proc = DarwinSTDLib.default_commandline_executer( + [xcode_select_bin_path, "--install"] + ) install_rc: int = cmdl_install_proc.returncode install_stderr: str = cmdl_install_proc.stderr install_stdout: str = cmdl_install_proc.stdout if install_rc != 0 or len(install_stderr) > 0: print( - f"Unable to install Xcode command line tools!\nReturn code: {install_rc}\nSTDOUT: {install_stdout}\nSTDERR: {install_stderr}") + f"Unable to install Xcode command line tools!\nReturn code: {install_rc}\nSTDOUT: {install_stdout}\nSTDERR: {install_stderr}" + ) return False else: print("Xcode command line tools are now installed.") @@ -329,7 +364,9 @@ def check_and_install_xc_cmdl_tools() -> bool: return True @staticmethod - def compile_swift(source_path: Path, destination_path: Path) -> StandardizedCompletedProcess: + def compile_swift( + source_path: Path, destination_path: Path + ) -> StandardizedCompletedProcess: """ Compiles a Swift source file at the provided path and outputs the Mach-O to the specified destination path. As a requirement, we first need to check if the Xcode command line tools are installed. @@ -345,8 +382,9 @@ def compile_swift(source_path: Path, destination_path: Path) -> StandardizedComp stdout: str = swift_compile_proc.stdout.decode("utf-8") stderr: str = swift_compile_proc.stderr.decode("utf-8") result: str = "success" if returncode == 0 else "failure" - response_model: StandardizedCompletedProcess = StandardizedCompletedProcess(result=result, argv=str(cmdl), - process_path=swiftc_bin_path) + response_model: StandardizedCompletedProcess = StandardizedCompletedProcess( + result=result, argv=str(cmdl), process_path=swiftc_bin_path + ) response_model.stdout = stdout response_model.stderr = stderr response_model.return_code = returncode @@ -354,7 +392,9 @@ def compile_swift(source_path: Path, destination_path: Path) -> StandardizedComp return response_model @staticmethod - def insert_plist_key(plist_path: Path, key: str, value_type: str, value: any) -> bool: + def insert_plist_key( + plist_path: Path, key: str, value_type: str, value: any + ) -> bool: """ Inserts a key / value pair into the specified property list file. Returning a boolean value indicating success / failure. @@ -363,8 +403,14 @@ def insert_plist_key(plist_path: Path, key: str, value_type: str, value: any) -> insert_plist_key("/Applications/Mail.app/Contents/Info.plist", "OSAAppletStayOpen", "-bool", "YES") """ plutil_bin_path: str = "/usr/bin/plutil" - cmdl: list = [plutil_bin_path, "-insert", key, value_type, value, - str(plist_path)] + cmdl: list = [ + plutil_bin_path, + "-insert", + key, + value_type, + value, + str(plist_path), + ] insert_proc = DarwinSTDLib.default_commandline_executer(cmdl) if "already exists" not in insert_proc.stdout and insert_proc.return_code != 0: return False @@ -379,7 +425,11 @@ def get_osa_script_from_xml(wflow_path: Path) -> str | None: """ if os.path.isfile(wflow_path): try: - elements: list = ET.parse(wflow_path).getroot().findall('dict/array/dict/dict/dict/string') + elements: list = ( + ET.parse(wflow_path) + .getroot() + .findall("dict/array/dict/dict/dict/string") + ) for element in elements: if element.text != "List": return element.text @@ -395,7 +445,9 @@ def get_osa_script_from_applet(applet_path: Path) -> str: 2. The script source will be located in `../Contents/Resources/Scripts/main.scpt` as a compiled OSA script 3. Run-only script files --> not supported. """ - script_path: Path = applet_path.joinpath("Contents", "Resources", "Scripts", "main.scpt").resolve() + script_path: Path = applet_path.joinpath( + "Contents", "Resources", "Scripts", "main.scpt" + ).resolve() wflow_path: Path = applet_path.joinpath("Contents", "document.wflow").resolve() if os.path.isfile(wflow_path): # Automator case From f2d9ce6775e84b5dbed75299d5e165c69239321f Mon Sep 17 00:00:00 2001 From: rc-dbogle Date: Fri, 9 Dec 2022 14:28:05 -0500 Subject: [PATCH 3/3] Add GitHub deploy workflow (#19) * Bump pillow from 9.2.0 to 9.3.0 in /posix Bumps [pillow](https://github.com/python-pillow/Pillow) from 9.2.0 to 9.3.0. - [Release notes](https://github.com/python-pillow/Pillow/releases) - [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) - [Commits](https://github.com/python-pillow/Pillow/compare/9.2.0...9.3.0) --- updated-dependencies: - dependency-name: pillow dependency-type: indirect ... Signed-off-by: dependabot[bot] * Adding github workflows for deploying to powershell gallery and PyPI * Fixed typo in environment variable Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/deploy-posixath.yml | 35 +++++++++++++++++++++++++ .github/workflows/deploy-powershell.yml | 17 ++++++++++++ posix/src/posixath/__about__.py | 2 +- 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/deploy-posixath.yml create mode 100644 .github/workflows/deploy-powershell.yml diff --git a/.github/workflows/deploy-posixath.yml b/.github/workflows/deploy-posixath.yml new file mode 100644 index 0000000..cb877ad --- /dev/null +++ b/.github/workflows/deploy-posixath.yml @@ -0,0 +1,35 @@ +name: deploy-posixath +on: + workflow_dispatch: +jobs: + deploy-posixath: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10"] + defaults: + run: + working-directory: + posix + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install --user pipenv + pipenv install + pipenv install hatch twine + - name: Lint with black + run: | + pipenv run python -m black --check src/posixath/ + - name: Build package + run: pipenv run hatch build + - name: Publish Package to PyPi + run: pipenv run python -m twine upload dist/* + env: + TWINE_USERNAME: ${{secrets.PYPI_USERNAME}} + TWINE_PASSWORD: ${{secrets.PYPI_PASSWORD}} \ No newline at end of file diff --git a/.github/workflows/deploy-powershell.yml b/.github/workflows/deploy-powershell.yml new file mode 100644 index 0000000..704958b --- /dev/null +++ b/.github/workflows/deploy-powershell.yml @@ -0,0 +1,17 @@ +name: Publish PowerShell Module + +on: + workflow_dispatch: + +jobs: + publish-to-gallery: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Build and publish + run: | + Publish-Module -Path '.' -NuGetApiKey "$Env:PGALLERY" + shell: pwsh + env: + PGALLERY: ${{ secrets.PGALLERY }} \ No newline at end of file diff --git a/posix/src/posixath/__about__.py b/posix/src/posixath/__about__.py index f102a9c..27fdca4 100644 --- a/posix/src/posixath/__about__.py +++ b/posix/src/posixath/__about__.py @@ -1 +1 @@ -__version__ = "0.0.1" +__version__ = "0.0.3"