[FR] Add new macOS RTAs for Endpoint Rules (#2632)
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from pathlib import Path
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="9e87748e-9866-4b6b-832d-5cba4dda14e8",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "Potential Default Application Hijacking",
|
||||
"rule_id": "5d2c3833-a36a-483a-acea-5bf8cf363a81",
|
||||
}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1574"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
app_dir = Path("/Applications/test/Contents/")
|
||||
app_dir.mkdir(parents=True, exist_ok=True)
|
||||
masquerade = str(app_dir / "hijack")
|
||||
common.create_macos_masquerade(masquerade)
|
||||
masquerade2 = "/tmp/open"
|
||||
common.create_macos_masquerade(masquerade2)
|
||||
|
||||
# Execute command
|
||||
common.log("Launching fake open commands to mimic hijacking applications")
|
||||
command = f"{masquerade2} -a /System/Applications/*"
|
||||
common.execute([masquerade, "childprocess", command], timeout=10, kill=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_directory(str(app_dir))
|
||||
common.remove_file(masquerade2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="084c5d8f-2578-4fe0-bc6f-f6c44205804a",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "At Job Creation or Modification by an Unusual Process",
|
||||
"rule_id": "779f18ce-1457-457c-80e1-3a5d146c2dc0",
|
||||
}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1053", "T1053.002"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
common.log("Executing file creation on /private/var/at/jobs/test.")
|
||||
common.temporary_file_helper("testing", file_name="/private/var/at/jobs/test")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,36 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="fa2bbba7-66f4-4fd6-9c81-599d58fe67e8",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{"rule_name": "Background Process Execution via Shell", "rule_id": "603ac59e-9cca-4c48-9750-e38399079043"}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1059", "T1059.004"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
masquerade = "/tmp/sh"
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
common.log("Executing background processes via sh from tmp directory.")
|
||||
command = 'bash -c "/* &"'
|
||||
common.execute([masquerade, "childprocess", command], shell=True, timeout=5, kill=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_file(masquerade)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,40 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="631a211d-bdaa-4b9d-a786-31d84d7bc070",
|
||||
platforms=["linux", "macos"],
|
||||
endpoint=[
|
||||
{"rule_id": "31da6564-b3d3-4fc8-9a96-75ad0b364363", "rule_name": "Tampering of Bash Command-Line History"}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1070", "T1070.003"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
masquerade = "/tmp/history"
|
||||
|
||||
if common.CURRENT_OS == "linux":
|
||||
source = common.get_path("bin", "linux.ditto_and_spawn")
|
||||
common.copy_file(source, masquerade)
|
||||
else:
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
# Execute command
|
||||
common.log("Launching fake builtin commands for tampering of bash command line history")
|
||||
command = "-c"
|
||||
common.execute([masquerade, command], timeout=10, kill=True, shell=True)
|
||||
# cleanup
|
||||
common.remove_file(masquerade)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -0,0 +1,32 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="e85f7e39-da36-4ed4-be00-c5b29f4d763c",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "Cron Tab Creation or Modification by an Unusual Process",
|
||||
"rule_id": "e5fc1285-d312-4b45-9e6b-e6c037276c17",
|
||||
}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1053", "T1053.003"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
common.log("Executing file creation on /private/var/at/tabs/test.")
|
||||
common.temporary_file_helper("testing", file_name="/private/var/at/tabs/test")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,40 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="aec658cc-a5df-42e8-8e09-810b484b9ef2",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "MacOS Potential Data Exfiltration via Curl",
|
||||
"rule_id": "192ec591-1d00-4c16-a717-8a7481038d23",
|
||||
}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1048"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
# create masquerades
|
||||
masquerade = "/tmp/curl"
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
# Execute command
|
||||
common.log("Launching fake curl commands to simulate data exfil")
|
||||
common.execute([masquerade, "-F", "*@*.zip", "http*"], timeout=10, kill=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_file(masquerade)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,39 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="bf7645b2-d0cf-428d-a158-b1479160e60c",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "Payload Downloaded by Process Running in Suspicious Directory",
|
||||
"rule_id": "8c42c8bd-c282-44ca-b308-92e4267b6244",
|
||||
}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1105"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
masquerade = "/tmp/curl"
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
# Execute command
|
||||
common.log("Launching fake curl commands to download payload")
|
||||
common.execute([masquerade, "childprocess", "curl", "-k", "http://portquiz.net/"], timeout=5, kill=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_file(masquerade)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,43 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="4743705e-bf41-404a-b2f3-9f8f067516e6",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "Suspicious DMG File Creation in Temp Directory",
|
||||
"rule_id": "fdb0e7ed-4210-4b71-be47-d0b0d9458fa7",
|
||||
}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1211", "T1059", "T1059.004"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
tmp_dir = Path("/tmp/TestDMGDir")
|
||||
tmp_dmg = "/tmp/TestDMG.dmg"
|
||||
tmp_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Execute command
|
||||
common.log("Launching hdiutil commands to create a dmg in tmp directory")
|
||||
common.execute(["hdiutil", "create", "-size", "50m", "-volname", str(tmp_dir), "-ov", tmp_dmg], kill=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_directory(str(tmp_dir))
|
||||
common.remove_file(tmp_dmg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,47 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
import platform
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="f1321e5c-101d-4b03-8f0c-6cf8bda174ec",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "Collect DIAG Dylib Load Event",
|
||||
"rule_id": "2df75424-4106-43c5-8fea-f115e18588da",
|
||||
},
|
||||
{
|
||||
"rule_name": "Dylib Injection via Process Environment Variables",
|
||||
"rule_id": "246741d4-3eee-4fbb-beec-53ef562c62c3",
|
||||
},
|
||||
{
|
||||
"rule_name": "Potential Binary Masquerading via Invalid Code Signature",
|
||||
"rule_id": "4154c8ce-c718-4641-80db-a6a52276f1a4",
|
||||
},
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1574", "T1574.006"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
if platform.processor() == "arm":
|
||||
name = "com.apple.sleep_arm"
|
||||
dylib = "inject_arm.dylib"
|
||||
else:
|
||||
name = "com.apple.sleep_intel"
|
||||
dylib = "inject_intel.dylib"
|
||||
target_bin = common.get_path("bin", name)
|
||||
common.execute([f"DYLD_INSERT_LIBRARIES={dylib}", target_bin, "5"], kill=True, shell=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,42 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="2182f7e5-fc4b-4476-86c3-e7128dfcaa7a",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "Suspicious File Overwrite and Modification via Echo",
|
||||
"rule_id": "cd3a06dc-58c3-4d57-a03a-0d8991f237e7",
|
||||
}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1027", "T1059", "T1059.004"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
file_path = "/tmp/test"
|
||||
masquerade = "/tmp/testbin"
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
# Execute command
|
||||
common.log("Launching fake bash commands to abnormal echo shell commands")
|
||||
command = f"bash -c 'echo* > {file_path}'"
|
||||
common.execute([masquerade, "childprocess", command], timeout=10, kill=True, shell=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_file(masquerade)
|
||||
common.remove_file(file_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,30 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="0630610d-a9ae-47df-9e2f-e7f393972f1e",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{"rule_name": "Execution of Non-Executable File via Shell", "rule_id": "c0770406-7ede-4049-a7a1-999c15fb60bd"}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1036", "T1059", "T1059.004"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
common.log("Executing bash on unexecutable file.")
|
||||
with common.temporary_file("testing", "/*.txt"):
|
||||
common.execute(["/bin/bash", "/*.txt"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,36 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="21d1d048-b8c9-4b6d-9748-44f8af1b444d",
|
||||
platforms=["macos"],
|
||||
endpoint=[],
|
||||
siem=[
|
||||
{
|
||||
"rule_name": "Shell Script Execution from abnormal Volume Mount Path",
|
||||
"rule_id": "87def154-004d-4d3a-8224-591e41804454",
|
||||
}
|
||||
],
|
||||
techniques=["T1059", "T1059.004"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
masquerade = "/Volumes/bash"
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
# Execute command
|
||||
common.log("Launching bash commands to simulate execution from mounted volume")
|
||||
common.execute([masquerade, "/Volumes/*/Contents/*"], timeout=10, kill=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,41 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="54041e42-7a4b-417e-ac40-cd50c7085e48",
|
||||
platforms=["macos"],
|
||||
endpoint=[],
|
||||
siem=[
|
||||
{
|
||||
"rule_name": "Suspicious Python Package Child Process Execution",
|
||||
"rule_id": "d8cbba0d-7275-4bcd-be22-79ee6fea2951",
|
||||
}
|
||||
],
|
||||
techniques=["T1059", "T1059.004", "T1059.006"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
# test_file = "/tmp/test.txt"
|
||||
masquerade = "/tmp/bash"
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
# Execute command
|
||||
common.log("Launching bash commands to mimic python package execution")
|
||||
parent_args = "*/lib/python*/site-packages/*"
|
||||
common.execute([masquerade, "childprocess", parent_args, "-c"], timeout=10, kill=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_file(masquerade)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,40 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="1b681241-d9f1-4239-a9e7-650ebc0c38a4",
|
||||
platforms=["macos"],
|
||||
endpoint=[],
|
||||
siem=[
|
||||
{
|
||||
"rule_name": "Suspicious Terminal Child Process Execution",
|
||||
"rule_id": "8e88d216-af7a-4f5c-8155-fa7d2be03987",
|
||||
}
|
||||
],
|
||||
techniques=["T1059", "T1059.004"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
masquerade = "/tmp/terminal"
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
# Execute command
|
||||
command = f"bash -c '/tmp/*'"
|
||||
common.log("Launching bash commands to mimic terminal activity")
|
||||
common.execute([masquerade, "childprocess", command], timeout=10, kill=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_file(masquerade)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,42 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="b2faa842-ffc9-41c6-baed-8008c9749a52",
|
||||
platforms=["macos"],
|
||||
endpoint=[],
|
||||
siem=[
|
||||
{
|
||||
"rule_name": "Suspicious Nohup Execution",
|
||||
"rule_id": "3f18726c-4897-41dc-8426-15da95b8482f",
|
||||
}
|
||||
],
|
||||
techniques=["T1059", "T1059.004", "T1564", "T1564.003"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
test_file = "/tmp/test.txt"
|
||||
masquerade = "/tmp/bash"
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
# Execute command
|
||||
command = f"nohup {test_file}"
|
||||
common.log("Launching bash commands to mimic suspicious nohup execution")
|
||||
with common.temporary_file("testing", test_file):
|
||||
common.execute([masquerade, "childprocess", command, "&"], timeout=10, kill=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_file(masquerade)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,43 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from pathlib import Path
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="aac863d1-8306-463e-b81f-3d97ba925a44",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "Suspicious PrivilegedHelperTool Activity",
|
||||
"rule_id": "900fdb84-2a81-4a6d-88db-b48a0fafd79e",
|
||||
}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1068"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
tools = Path("/Library/PrivilegedHelperTools")
|
||||
tools.mkdir(parents=True, exist_ok=True)
|
||||
masquerade = str(tools / "testbin")
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
# Execute command
|
||||
common.log("Launching fake bash commands to abnormal echo shell commands")
|
||||
command = f"bash -c '/tmp/*'"
|
||||
common.execute([masquerade, "childprocess", command], timeout=10, kill=True, shell=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_directory(str(tools))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,35 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="9332cece-38b7-49e1-9f8d-e879913ffdfb",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "Tclsh execution followed by immediate network connection",
|
||||
"rule_id": "ac1eaed8-2aee-48d7-9824-2be1f00eda0e",
|
||||
}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1059"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
masquerade = "/tmp/tclsh"
|
||||
common.copy_file("/usr/bin/curl", masquerade)
|
||||
|
||||
common.log("Executing commands to mimic network activity from tclsh")
|
||||
common.execute([masquerade, url], shell=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,33 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="dbbfda7f-376d-482d-b7ea-3bb1e8918584",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "File Made Executable by Suspicious Parent Process",
|
||||
"rule_id": "42ab2c0f-b10d-467d-8c6d-def890cf3f68",
|
||||
}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1222", "T1222.002", "T1564"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
common.log("Executing chmod on tmp files.")
|
||||
with common.temporary_file("testing", "/tmp/test.txt"):
|
||||
common.execute(["chmod", "+x", "/tmp/test.txt"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,41 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from pathlib import Path
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="1d7ff305-03b5-4917-b32c-d0267018063c",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{"rule_name": "MacOS Hidden File Mounted", "rule_id": "c5f219ca-4bda-461b-bc54-246c0bb48143"},
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1211", "T1059", "T1059.004"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
mount_dir = "/tmp/.exploit"
|
||||
disk_file = "disk.dmg"
|
||||
|
||||
# create disk image
|
||||
common.execute(["hdiutil", "create", "-size", "50b", "-volname", ".exploit", "-ov", disk_file], kill=True)
|
||||
|
||||
# attach disk image to mount point
|
||||
common.log("Launching hdutil commands to mount dummy dmg")
|
||||
common.execute(["hdiutil", "attach", "-mountpoint", mount_dir, disk_file], kill=True)
|
||||
|
||||
# cleanup
|
||||
common.execute(["hdiutil", "eject", "/tmp/.exploit"], timeout=10, kill=True)
|
||||
common.remove_file(disk_file)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="6df524fe-6a1a-417f-8f70-d6140ef739e2",
|
||||
platforms=["macos"],
|
||||
endpoint=[{"rule_name": "Persistence via a Hidden Plist Filename", "rule_id": "4090fed3-8ac4-45bf-8545-bae448fd38d4"}],
|
||||
siem=[{
|
||||
'rule_id': '092b068f-84ac-485d-8a55-7dd9e006715f',
|
||||
'rule_name': 'Creation of Hidden Launch Agent or Daemon'
|
||||
}],
|
||||
techniques=["T1547", "T1547.011", "T1543", "T1543.001", "T1564", "T1564.001"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
plist_path = f"/Library/LaunchAgents/.test.plist"
|
||||
common.log(f"Executing hidden plist creation on {plist_path}")
|
||||
common.temporary_file_helper("testing", plist_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,41 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="c4ac8740-3dca-4550-831b-e03d21de581d",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "New System Kext File and Immediate Load via KextLoad",
|
||||
"rule_id": "de869aa1-c63a-451e-a953-7069ec39ba60",
|
||||
}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1547", "T1547.006", "T1059", "T1059.004"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
# create masquerades
|
||||
masquerade = "/tmp/mv"
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
# Execute command"
|
||||
common.log("Launching fake commands load Kext file.")
|
||||
common.execute([masquerade, "/System/Library/Extensions/*.kext"], timeout=10, kill=True)
|
||||
common.execute(["kextload", "test.kext"], timeout=10, kill=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_file(masquerade)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -10,14 +10,19 @@ from . import RtaMetadata
|
||||
metadata = RtaMetadata(
|
||||
uuid="f158a6dc-1974-4b98-a3e7-466f6f1afe01",
|
||||
platforms=["macos"],
|
||||
endpoint=[],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "Keychain Dump via native Security tool",
|
||||
"rule_id": "549344d6-aaef-4495-9ca2-7a0b849bf571",
|
||||
}
|
||||
],
|
||||
siem=[
|
||||
{
|
||||
"rule_name": "Dumping of Keychain Content via Security Command",
|
||||
"rule_id": "565d6ca5-75ba-4c82-9b13-add25353471c",
|
||||
}
|
||||
],
|
||||
techniques=["T1555"],
|
||||
techniques=["T1555", "T1555.001"],
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from pathlib import Path
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="17c710a6-9070-4448-b68c-a3694657552e",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "Persistence via Suspicious Launch Agent or Launch Daemon",
|
||||
"rule_id": "c6037fad-ad13-46a6-9f7f-4deeef5ac69b",
|
||||
},
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1547", "T1547.011", "T1543", "T1543.001", "T1543.004"],
|
||||
)
|
||||
|
||||
plist = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.example.myapp</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>bash</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
"""
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
plist_name = "com.test.plist"
|
||||
daemon_dir = Path("/", "Library", "LaunchDaemons").expanduser()
|
||||
daemon_dir.mkdir(parents=True, exist_ok=True)
|
||||
plist_path = str(daemon_dir / plist_name)
|
||||
|
||||
# with common.temporary_file(plist, file_name=plist_path):
|
||||
with open(plist_path, "w") as f:
|
||||
f.write(plist)
|
||||
common.execute(["launchctl", "load", plist_path], kill=True)
|
||||
common.execute(["launchctl", "unload", plist_path], kill=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,37 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="eb5834cf-fcd8-4318-a656-5315a664e61d",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{"rule_name": "Link Creation to Temp Directory", "rule_id": "ccca5e9f-2625-4b95-9b15-d5d8fc56df2c"},
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1222", "T1222.002"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
masquerade = "/tmp/ln"
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
# Execute command
|
||||
common.log("Launching fake ln commands to link to temp directory")
|
||||
with common.temporary_file("testing", "/tmp/test.txt"):
|
||||
common.execute([masquerade, "-s", "/tmp/test.txt"], timeout=10, kill=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_file(masquerade)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,41 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="2c2c75c0-28cc-4828-b8a4-6b33e027a80a",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "Execution of a File Dropped by OpenSSL",
|
||||
"rule_id": "d2017990-b448-4617-8d4a-55aa45abe354",
|
||||
}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1027", "T1140", "T1204", "T1204.002"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
masquerade = "/tmp/testbin"
|
||||
|
||||
# Execute command
|
||||
common.log("Launching bash commands for file creation via openssl")
|
||||
common.execute(["openssl", "rand", "-base64", 2, "-out", masquerade], timeout=10, kill=True)
|
||||
|
||||
common.create_macos_masquerade(masquerade)
|
||||
common.execute([masquerade, "ls"], timeout=10, kill=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_file(masquerade)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,40 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="7343a543-c2f6-4215-a21c-04eb8c764656",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "Potential Masquerading as System Binary",
|
||||
"rule_id": "bb1de0c7-3504-4b31-8d3e-928aa3acf64f",
|
||||
}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1036", "T1036.004", "T1059", "T1059.004"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
masquerade = "/tmp/bash"
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
# Execute command
|
||||
common.log("Launching fake bash commands to mimic passing a path to system bin")
|
||||
command = f"exec -a /System/Applications/test {masquerade}"
|
||||
common.execute([masquerade, "childprocess", command], timeout=5, kill=True, shell=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_file(masquerade)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,61 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="caa6feb7-cc17-425f-996f-b1b69efa93e2",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{"rule_name": "File Made Executable via Pkg Install Script", "rule_id": "75f5d51a-218f-4d5b-80e5-eb74e498fde4"},
|
||||
{
|
||||
"rule_name": "File Made Executable by Suspicious Parent Process",
|
||||
"rule_id": "42ab2c0f-b10d-467d-8c6d-def890cf3f68",
|
||||
},
|
||||
{
|
||||
"rule_name": "Suspicious File Create via Pkg Install Script",
|
||||
"rule_id": "f06d9987-33f8-44b7-b815-c1f66fb39d25",
|
||||
},
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1222", "T1222.002", "T1564", "T1546", "T1546.016"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
dest_file = "/tmp/test.py"
|
||||
source_file = "/tmp/test.txt"
|
||||
masquerade = "/Users/bash"
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
# Execute command
|
||||
command = f"chmod +x {source_file}"
|
||||
common.log("Launching fake bash commands to execute chmod on file via pkg install")
|
||||
with common.temporary_file("testing", source_file):
|
||||
common.execute(
|
||||
[
|
||||
masquerade,
|
||||
"childprocess",
|
||||
command,
|
||||
"childprocess",
|
||||
f"cp {source_file} {dest_file}",
|
||||
"childprocess",
|
||||
"/tmp/PKInstallSandbox.*/Scripts/*/postinstall",
|
||||
],
|
||||
timeout=10,
|
||||
kill=True,
|
||||
)
|
||||
|
||||
# cleanup
|
||||
common.remove_file(masquerade)
|
||||
common.remove_file(dest_file)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,36 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="1796555f-921a-459f-9661-0d94cf90fe81",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{"rule_name": "Potential SIP Bypass via the ShoveService", "rule_id": "7dea8cfc-92db-4081-9a5d-85ead8cedd5f"}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1068"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
masquerade = "/tmp/sh"
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
common.log("Executing shove processes to mimic sip bypass.")
|
||||
command = "/System/Library/PrivateFrameworks/PackageKit.framework/Versions/A/Resources/shove -x"
|
||||
common.execute([masquerade, "childprocess", command], shell=True, timeout=5, kill=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_file(masquerade)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,88 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
char *combine_argv(int argc, char **argv, int start, int end)
|
||||
{
|
||||
int total_size = 0;
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
total_size += strlen(argv[i]);
|
||||
}
|
||||
// Provides space for ' ' after each argument and a '\0' terminator.
|
||||
char *ret = malloc(total_size + end - start + 1);
|
||||
if (ret == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error: memory allocation failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
int j = 0;
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
strcat(ret + j, argv[i]);
|
||||
j += strlen(argv[i]);
|
||||
ret[j++] = ' ';
|
||||
}
|
||||
ret[j - 1] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
void spawn_child_processes(int argc, char **argv)
|
||||
{
|
||||
int start = 1;
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
if (strcmp(argv[i], "childprocess") == 0)
|
||||
{
|
||||
char *command = combine_argv(argc, argv, start, i);
|
||||
printf("Spawning child process %s\n", command);
|
||||
if (system(command) == -1)
|
||||
{
|
||||
fprintf(stderr, "Error: failed to spawn child process\n");
|
||||
free(command);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
free(command);
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start < argc)
|
||||
{
|
||||
char *command = combine_argv(argc, argv, start, argc);
|
||||
printf("Spawning child process %s\n", command);
|
||||
if (system(command) == -1)
|
||||
{
|
||||
fprintf(stderr, "Error: failed to spawn child process\n");
|
||||
free(command);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
free(command);
|
||||
}
|
||||
}
|
||||
|
||||
void validate_input(int argc, char **argv)
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
fprintf(stderr, "Error: invalid number of arguments\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
else if (strcmp(argv[1], "childprocess") == 0 && argc < 3)
|
||||
{
|
||||
fprintf(stderr, "Error: invalid argument format. Expected childprocess <name of command>\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
validate_input(argc, argv);
|
||||
spawn_child_processes(argc, argv);
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
printf("argv[%2d]: %s\n", i, argv[i]);
|
||||
}
|
||||
system("/bin/bash");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc == 2)
|
||||
{
|
||||
printf("Sleeping for %s seconds.\n", argv[1]);
|
||||
sleep(atoi(argv[1]));
|
||||
}
|
||||
else if (argc > 2)
|
||||
{
|
||||
printf("Too many arguments supplied.\n");
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("One argument expected.\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/error.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <pthread.h>
|
||||
#include <mach/mach_vm.h>
|
||||
|
||||
#define STACK_SIZE 65536
|
||||
#define CODE_SIZE 128
|
||||
|
||||
/* This shellcode is just an infinite loop */
|
||||
char injectedCode[] =
|
||||
"\x90"
|
||||
"\x90"
|
||||
"\xeb\xfe"
|
||||
"\x90"
|
||||
"\x90"
|
||||
"\x90";
|
||||
|
||||
int inject(pid_t pid)
|
||||
{
|
||||
task_t remoteTask;
|
||||
mach_error_t kr = 0;
|
||||
|
||||
/**
|
||||
* Second - the critical part - we need task_for_pid in order to get the task port of the target
|
||||
* pid. This is our do-or-die: If we get the port, we can do *ANYTHING* we want. If we don't, we're
|
||||
* #$%#$%.
|
||||
*/
|
||||
|
||||
kr = task_for_pid(mach_task_self(), pid, &remoteTask);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Unable to call task_for_pid on pid %d: %s. Cannot continue!\n", pid, mach_error_string(kr));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
mach_vm_address_t remoteStack64 = (vm_address_t)NULL;
|
||||
mach_vm_address_t remoteCode64 = (vm_address_t)NULL;
|
||||
kr = mach_vm_allocate(remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Unable to allocate memory for remote stack in thread: Error %s\n", mach_error_string(kr));
|
||||
return (-2);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Allocated remote stack @0x%llx\n", remoteStack64);
|
||||
}
|
||||
/**
|
||||
* Then we allocate the memory for the thread
|
||||
*/
|
||||
remoteCode64 = (vm_address_t)NULL;
|
||||
kr = mach_vm_allocate(remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Unable to allocate memory for remote code in thread: Error %s\n", mach_error_string(kr));
|
||||
return (-2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the (now patched) code
|
||||
*/
|
||||
kr = mach_vm_write(remoteTask, // Task port
|
||||
remoteCode64, // Virtual Address (Destination)
|
||||
(vm_address_t)injectedCode, // Source
|
||||
sizeof(injectedCode)); // Length of the source
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Unable to write remote thread memory: Error %s\n", mach_error_string(kr));
|
||||
return (-3);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark code as executable - This also requires a workaround on iOS, btw.
|
||||
*/
|
||||
kr = vm_protect(remoteTask, remoteCode64, sizeof(injectedCode), FALSE, VM_PROT_READ | VM_PROT_EXECUTE);
|
||||
|
||||
/*
|
||||
* Mark stack as writable - not really necessary
|
||||
*/
|
||||
kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Unable to set memory permissions for remote thread: Error %s\n", mach_error_string(kr));
|
||||
return (-4);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create thread - This is obviously hardware specific.
|
||||
*/
|
||||
x86_thread_state64_t remoteThreadState64;
|
||||
|
||||
thread_act_t remoteThread;
|
||||
|
||||
memset(&remoteThreadState64, '\0', sizeof(remoteThreadState64));
|
||||
|
||||
remoteStack64 += (STACK_SIZE / 2); // this is the real stack
|
||||
//remoteStack64 -= 8; // need alignment of 16
|
||||
|
||||
const char *p = (const char *)remoteCode64;
|
||||
|
||||
remoteThreadState64.__rip = (u_int64_t)(vm_address_t)remoteCode64;
|
||||
|
||||
// set remote Stack Pointer
|
||||
remoteThreadState64.__rsp = (u_int64_t)remoteStack64;
|
||||
remoteThreadState64.__rbp = (u_int64_t)remoteStack64;
|
||||
|
||||
printf("Remote Stack 64 0x%llx, Remote code is %p\n", remoteStack64, p);
|
||||
|
||||
/*
|
||||
* create thread and launch it in one go
|
||||
*/
|
||||
|
||||
kr = thread_create_running(remoteTask, x86_THREAD_STATE64,
|
||||
(thread_state_t)&remoteThreadState64, x86_THREAD_STATE64_COUNT, &remoteThread);
|
||||
|
||||
//kr = thread_create(remoteTask, &remoteThread);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Unable to create remote thread: error %s", mach_error_string(kr));
|
||||
return (-3);
|
||||
}
|
||||
|
||||
// Wait for mach thread to finish
|
||||
/* mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT;
|
||||
for (;;) {
|
||||
kr = thread_get_state(remoteThread, x86_THREAD_STATE64, (thread_state_t)&remoteThreadState64, &thread_state_count);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Error getting stub thread state: error %s", mach_error_string(kr));
|
||||
break;
|
||||
}
|
||||
|
||||
if (remoteThreadState64.__rax == 0xD13) {
|
||||
printf("Stub thread finished\n");
|
||||
kr = thread_terminate(remoteThread);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Error terminating stub thread: error %s", mach_error_string(kr));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
|
||||
sleep(5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: %s _pid_ \n", argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
pid_t pid = atoi(argv[1]);
|
||||
inject(pid);
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/error.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <pthread.h>
|
||||
#include <mach/mach_vm.h>
|
||||
|
||||
#define STACK_SIZE 65536
|
||||
#define CODE_SIZE 128
|
||||
|
||||
/* This shellcode is just an infinite loop */
|
||||
char injectedCode[] =
|
||||
"\x00\x00\x00\x14";
|
||||
|
||||
int inject(pid_t pid)
|
||||
{
|
||||
task_t remoteTask;
|
||||
mach_error_t kr = 0;
|
||||
|
||||
/**
|
||||
* Second - the critical part - we need task_for_pid in order to get the task port of the target
|
||||
* pid. This is our do-or-die: If we get the port, we can do *ANYTHING* we want. If we don't, we're
|
||||
* #$%#$%.
|
||||
*/
|
||||
|
||||
kr = task_for_pid(mach_task_self(), pid, &remoteTask);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Unable to call task_for_pid on pid %d: %s. Cannot continue!\n", pid, mach_error_string(kr));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
mach_vm_address_t remoteStack64 = (vm_address_t)NULL;
|
||||
mach_vm_address_t remoteCode64 = (vm_address_t)NULL;
|
||||
kr = mach_vm_allocate(remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Unable to allocate memory for remote stack in thread: Error %s\n", mach_error_string(kr));
|
||||
return (-2);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Allocated remote stack @0x%llx\n", remoteStack64);
|
||||
}
|
||||
/**
|
||||
* Then we allocate the memory for the thread
|
||||
*/
|
||||
remoteCode64 = (vm_address_t)NULL;
|
||||
kr = mach_vm_allocate(remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Unable to allocate memory for remote code in thread: Error %s\n", mach_error_string(kr));
|
||||
return (-2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the (now patched) code
|
||||
*/
|
||||
kr = mach_vm_write(remoteTask, // Task port
|
||||
remoteCode64, // Virtual Address (Destination)
|
||||
(vm_address_t)injectedCode, // Source
|
||||
sizeof(injectedCode)); // Length of the source
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Unable to write remote thread memory: Error %s\n", mach_error_string(kr));
|
||||
return (-3);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark code as executable - This also requires a workaround on iOS, btw.
|
||||
*/
|
||||
kr = vm_protect(remoteTask, remoteCode64, sizeof(injectedCode), FALSE, VM_PROT_READ | VM_PROT_EXECUTE);
|
||||
|
||||
/*
|
||||
* Mark stack as writable - not really necessary
|
||||
*/
|
||||
kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Unable to set memory permissions for remote thread: Error %s\n", mach_error_string(kr));
|
||||
return (-4);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create thread - This is obviously hardware specific.
|
||||
*/
|
||||
arm_thread_state64_t remoteThreadState64;
|
||||
|
||||
thread_act_t remoteThread;
|
||||
|
||||
memset(&remoteThreadState64, '\0', sizeof(remoteThreadState64));
|
||||
|
||||
remoteStack64 += (STACK_SIZE / 2); // this is the real stack
|
||||
//remoteStack64 -= 8; // need alignment of 16
|
||||
|
||||
const char *p = (const char *)remoteCode64;
|
||||
|
||||
remoteThreadState64.__pc = (u_int64_t)(vm_address_t)remoteCode64;
|
||||
remoteThreadState64.__lr = (u_int64_t)(vm_address_t)remoteCode64;
|
||||
// set remote Stack Pointer
|
||||
remoteThreadState64.__sp = (u_int64_t)remoteStack64;
|
||||
|
||||
printf("Remote Stack 64 0x%llx, Remote code is %p\n", remoteStack64, p);
|
||||
|
||||
/*
|
||||
* create thread and launch it in one go
|
||||
*/
|
||||
|
||||
kr = thread_create_running(remoteTask, ARM_THREAD_STATE64,
|
||||
(thread_state_t)&remoteThreadState64, ARM_THREAD_STATE64_COUNT, &remoteThread);
|
||||
|
||||
//kr = thread_create(remoteTask, &remoteThread);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Unable to create remote thread: error %s", mach_error_string(kr));
|
||||
return (-3);
|
||||
}
|
||||
|
||||
// Wait for mach thread to finish
|
||||
/* mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT;
|
||||
for (;;) {
|
||||
kr = thread_get_state(remoteThread, x86_THREAD_STATE64, (thread_state_t)&remoteThreadState64, &thread_state_count);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Error getting stub thread state: error %s", mach_error_string(kr));
|
||||
break;
|
||||
}
|
||||
|
||||
if (remoteThreadState64.__rax == 0xD13) {
|
||||
printf("Stub thread finished\n");
|
||||
kr = thread_terminate(remoteThread);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr, "Error terminating stub thread: error %s", mach_error_string(kr));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
|
||||
sleep(5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: %s _pid_ \n", argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
pid_t pid = atoi(argv[1]);
|
||||
inject(pid);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="a56d07b3-c459-4a72-adab-b93bbe008f0f",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{
|
||||
"rule_name": "Non-Native Dylib Extracted into New Directory",
|
||||
"rule_id": "62cc9cf4-5440-4237-aa5b-ea8db83deb3d",
|
||||
}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1059", "T1059.004"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
# Execute command"
|
||||
common.log("Launching commands to tar tmp dir.")
|
||||
common.execute(["mkdir"], timeout=10, kill=True)
|
||||
|
||||
with common.temporary_file("testing", "/tmp/test.txt"):
|
||||
common.execute(["tar", "-cf", "test.dylib", "/tmp/test.txt"], timeout=10, kill=True)
|
||||
|
||||
# cleanup
|
||||
common.remove_file("test.dylib")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
@@ -0,0 +1,34 @@
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License
|
||||
# 2.0; you may not use this file except in compliance with the Elastic License
|
||||
# 2.0.
|
||||
|
||||
from . import common
|
||||
from . import RtaMetadata
|
||||
|
||||
|
||||
metadata = RtaMetadata(
|
||||
uuid="de7e28b2-c01d-4cd7-abb7-ddb64bce5f45",
|
||||
platforms=["macos"],
|
||||
endpoint=[
|
||||
{"rule_name": "Compressed File Extracted to Temp Directory", "rule_id": "24fa0f80-7e3a-4b27-801a-30ef53f190bf"}
|
||||
],
|
||||
siem=[],
|
||||
techniques=["T1059", "T1059.004"],
|
||||
)
|
||||
|
||||
|
||||
@common.requires_os(metadata.platforms)
|
||||
def main():
|
||||
|
||||
masquerade = "/Users/bash"
|
||||
common.create_macos_masquerade(masquerade)
|
||||
|
||||
command = 'bash -c "unzip * /tmp/* -d *"'
|
||||
|
||||
common.log("Executing unzip to tmp directory.")
|
||||
common.execute([masquerade, "childprocess", command], shell=True, timeout=5, kill=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
Reference in New Issue
Block a user