Projects STRLCPY routeros-scanner Commits ed66a424
🤬
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■
    .gitignore
     1 +.idea/
     2 + 
     3 +# Created by https://www.gitignore.io/api/macos,linux,django,python,pycharm
     4 + 
     5 +### Django ###
     6 +*.log
     7 +*.pot
     8 +*.pyc
     9 +__pycache__/
     10 +local_settings.py
     11 +db.sqlite3
     12 +media
     13 + 
     14 +### Linux ###
     15 +*~
     16 + 
     17 +# temporary files which can be created if a process still has a handle open of a deleted file
     18 +.fuse_hidden*
     19 + 
     20 +# KDE directory preferences
     21 +.directory
     22 + 
     23 +# Linux trash folder which might appear on any partition or disk
     24 +.Trash-*
     25 + 
     26 +# .nfs files are created when an open file is removed but is still being accessed
     27 +.nfs*
     28 + 
     29 +### macOS ###
     30 +*.DS_Store
     31 +.AppleDouble
     32 +.LSOverride
     33 + 
     34 +# Icon must end with two \r
     35 +Icon
     36 + 
     37 +# Thumbnails
     38 +._*
     39 + 
     40 +# Files that might appear in the root of a volume
     41 +.DocumentRevisions-V100
     42 +.fseventsd
     43 +.Spotlight-V100
     44 +.TemporaryItems
     45 +.Trashes
     46 +.VolumeIcon.icns
     47 +.com.apple.timemachine.donotpresent
     48 + 
     49 +# Directories potentially created on remote AFP share
     50 +.AppleDB
     51 +.AppleDesktop
     52 +Network Trash Folder
     53 +Temporary Items
     54 +.apdisk
     55 + 
     56 +### PyCharm ###
     57 +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
     58 +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
     59 + 
     60 +# User-specific stuff:
     61 +.idea/**/workspace.xml
     62 +.idea/**/tasks.xml
     63 +.idea/dictionaries
     64 + 
     65 +# Sensitive or high-churn files:
     66 +.idea/**/dataSources/
     67 +.idea/**/dataSources.ids
     68 +.idea/**/dataSources.xml
     69 +.idea/**/dataSources.local.xml
     70 +.idea/**/sqlDataSources.xml
     71 +.idea/**/dynamic.xml
     72 +.idea/**/uiDesigner.xml
     73 + 
     74 +# Gradle:
     75 +.idea/**/gradle.xml
     76 +.idea/**/libraries
     77 + 
     78 +# CMake
     79 +cmake-build-debug/
     80 + 
     81 +# Mongo Explorer plugin:
     82 +.idea/**/mongoSettings.xml
     83 + 
     84 +## File-based project format:
     85 +*.iws
     86 + 
     87 +## Plugin-specific files:
     88 + 
     89 +# IntelliJ
     90 +/out/
     91 + 
     92 +# mpeltonen/sbt-idea plugin
     93 +.idea_modules/
     94 + 
     95 +# JIRA plugin
     96 +atlassian-ide-plugin.xml
     97 + 
     98 +# Cursive Clojure plugin
     99 +.idea/replstate.xml
     100 + 
     101 +# Crashlytics plugin (for Android Studio and IntelliJ)
     102 +com_crashlytics_export_strings.xml
     103 +crashlytics.properties
     104 +crashlytics-build.properties
     105 +fabric.properties
     106 + 
     107 +### PyCharm Patch ###
     108 +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
     109 + 
     110 +# *.iml
     111 +# modules.xml
     112 +# .idea/misc.xml
     113 +# *.ipr
     114 + 
     115 +# Sonarlint plugin
     116 +.idea/sonarlint
     117 + 
     118 +### Python ###
    1 119  # Byte-compiled / optimized / DLL files
    2  -__pycache__/
    3 120  *.py[cod]
    4 121  *$py.class
    5 122   
    skipped 2 lines
    8 125   
    9 126  # Distribution / packaging
    10 127  .Python
     128 +env/
    11 129  build/
    12 130  develop-eggs/
    13 131  dist/
    skipped 6 lines
    20 138  sdist/
    21 139  var/
    22 140  wheels/
    23  -pip-wheel-metadata/
    24  -share/python-wheels/
    25 141  *.egg-info/
    26 142  .installed.cfg
    27 143  *.egg
    28  -MANIFEST
    29 144   
    30 145  # PyInstaller
    31 146  # Usually these files are written by a python script from a template
    skipped 8 lines
    40 155  # Unit test / coverage reports
    41 156  htmlcov/
    42 157  .tox/
    43  -.nox/
    44 158  .coverage
    45 159  .coverage.*
    46 160  .cache
    47 161  nosetests.xml
    48 162  coverage.xml
    49  -*.cover
    50  -*.py,cover
     163 +*,cover
    51 164  .hypothesis/
    52  -.pytest_cache/
    53 165   
    54 166  # Translations
    55 167  *.mo
    56  -*.pot
    57 168   
    58 169  # Django stuff:
    59  -*.log
    60  -local_settings.py
    61  -db.sqlite3
    62  -db.sqlite3-journal
    63 170   
    64 171  # Flask stuff:
    65 172  instance/
    skipped 11 lines
    77 184  # Jupyter Notebook
    78 185  .ipynb_checkpoints
    79 186   
    80  -# IPython
    81  -profile_default/
    82  -ipython_config.py
    83  - 
    84 187  # pyenv
    85 188  .python-version
    86 189   
    87  -# pipenv
    88  -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
    89  -# However, in case of collaboration, if having platform-specific dependencies or dependencies
    90  -# having no cross-platform support, pipenv may install dependencies that don't work, or not
    91  -# install all needed dependencies.
    92  -#Pipfile.lock
    93  - 
    94  -# PEP 582; used by e.g. github.com/David-OConnor/pyflow
    95  -__pypackages__/
    96  - 
    97  -# Celery stuff
     190 +# celery beat schedule file
    98 191  celerybeat-schedule
    99  -celerybeat.pid
    100 192   
    101 193  # SageMath parsed files
    102 194  *.sage.py
    103 195   
    104  -# Environments
     196 +# dotenv
    105 197  .env
     198 + 
     199 +# virtualenv
    106 200  .venv
    107  -env/
    108 201  venv/
    109 202  ENV/
    110  -env.bak/
    111  -venv.bak/
    112 203   
    113 204  # Spyder project settings
    114 205  .spyderproject
    skipped 5 lines
    120 211  # mkdocs documentation
    121 212  /site
    122 213   
    123  -# mypy
    124  -.mypy_cache/
    125  -.dmypy.json
    126  -dmypy.json
    127  - 
    128  -# Pyre type checker
    129  -.pyre/
     214 +# End of https://www.gitignore.io/api/macos,linux,django,python,pycharm
    130 215   
  • ■ ■ ■ ■ ■ ■
    src/assets/mikrotik_cpe_match.json
     1 +{
     2 + "CVE-2020-20219": [
     3 + {
     4 + "exact": "6.44.6"
     5 + }
     6 + ],
     7 + "CVE-2015-2350": [
     8 + {
     9 + "end_including": "5.0"
     10 + }
     11 + ],
     12 + "CVE-2012-6050": [
     13 + {
     14 + "exact": "5.15"
     15 + }
     16 + ],
     17 + "CVE-2020-20262": [
     18 + {
     19 + "end_excluding": "6.47"
     20 + }
     21 + ],
     22 + "CVE-2020-20215": [
     23 + {
     24 + "exact": "6.44.6"
     25 + }
     26 + ],
     27 + "CVE-2020-20254": [
     28 + {
     29 + "end_excluding": "6.47"
     30 + }
     31 + ],
     32 + "CVE-2018-1157": [
     33 + {
     34 + "end_excluding": "6.40.9"
     35 + },
     36 + {
     37 + "end_excluding": "6.42.7"
     38 + }
     39 + ],
     40 + "CVE-2018-1156": [
     41 + {
     42 + "end_excluding": "6.40.9"
     43 + },
     44 + {
     45 + "end_excluding": "6.42.7"
     46 + }
     47 + ],
     48 + "CVE-2020-20214": [
     49 + {
     50 + "exact": "6.44.6"
     51 + }
     52 + ],
     53 + "CVE-2020-20222": [
     54 + {
     55 + "exact": "6.44.6"
     56 + }
     57 + ],
     58 + "CVE-2020-20218": [
     59 + {
     60 + "exact": "6.44.6"
     61 + }
     62 + ],
     63 + "CVE-2020-20213": [
     64 + {
     65 + "exact": "6.44.5"
     66 + }
     67 + ],
     68 + "CVE-2020-20252": [
     69 + {
     70 + "end_excluding": "6.47"
     71 + }
     72 + ],
     73 + "CVE-2020-22845": [
     74 + {
     75 + "exact": "6.47"
     76 + }
     77 + ],
     78 + "CVE-2017-6297": [
     79 + {
     80 + "exact": "6.37.4"
     81 + },
     82 + {
     83 + "exact": "6.83.3"
     84 + }
     85 + ],
     86 + "CVE-2019-3977": [
     87 + {
     88 + "end_including": "6.44.5"
     89 + },
     90 + {
     91 + "end_including": "6.45.6"
     92 + }
     93 + ],
     94 + "CVE-2020-20248": [
     95 + {
     96 + "exact": "6.47"
     97 + }
     98 + ],
     99 + "CVE-2020-20225": [
     100 + {
     101 + "end_excluding": "6.47"
     102 + }
     103 + ],
     104 + "CVE-2020-20264": [
     105 + {
     106 + "end_excluding": "6.47"
     107 + }
     108 + ],
     109 + "CVE-2017-8338": [
     110 + {
     111 + "exact": "6.38.5"
     112 + }
     113 + ],
     114 + "CVE-2020-20265": [
     115 + {
     116 + "end_excluding": "6.47"
     117 + }
     118 + ],
     119 + "CVE-2008-6976": [
     120 + {
     121 + "start_including": "2.0",
     122 + "end_including": "2.9.51"
     123 + },
     124 + {
     125 + "start_including": "3.0",
     126 + "end_including": "3.13"
     127 + }
     128 + ],
     129 + "CVE-2020-20249": [
     130 + {
     131 + "end_excluding": "6.47"
     132 + }
     133 + ],
     134 + "CVE-2019-3976": [
     135 + {
     136 + "end_including": "6.45.6"
     137 + },
     138 + {
     139 + "end_including": "6.44.5"
     140 + }
     141 + ],
     142 + "CVE-2020-22844": [
     143 + {
     144 + "exact": "6.47"
     145 + }
     146 + ],
     147 + "CVE-2020-20253": [
     148 + {
     149 + "end_excluding": "6.47"
     150 + }
     151 + ],
     152 + "CVE-2020-20212": [
     153 + {
     154 + "exact": "6.44.5"
     155 + }
     156 + ],
     157 + "CVE-2020-20245": [
     158 + {
     159 + "exact": "6.46.3"
     160 + }
     161 + ],
     162 + "CVE-2020-20250": [
     163 + {
     164 + "end_excluding": "6.47"
     165 + }
     166 + ],
     167 + "CVE-2019-16160": [
     168 + {
     169 + "end_excluding": "6.45.5"
     170 + }
     171 + ],
     172 + "CVE-2020-20211": [
     173 + {
     174 + "exact": "6.44.5"
     175 + }
     176 + ],
     177 + "CVE-2020-20246": [
     178 + {
     179 + "exact": "6.46.3"
     180 + }
     181 + ],
     182 + "CVE-2020-20231": [
     183 + {
     184 + "start_including": "6.44.6",
     185 + "end_including": "6.48.3"
     186 + }
     187 + ],
     188 + "CVE-2020-20266": [
     189 + {
     190 + "end_excluding": "6.47"
     191 + }
     192 + ],
     193 + "CVE-2019-3979": [
     194 + {
     195 + "end_including": "6.44.5"
     196 + },
     197 + {
     198 + "end_including": "6.45.6"
     199 + }
     200 + ],
     201 + "CVE-2020-20227": [
     202 + {
     203 + "exact": "6.47"
     204 + }
     205 + ],
     206 + "CVE-2019-3943": [
     207 + {
     208 + "end_including": "6.42.12"
     209 + },
     210 + {
     211 + "end_including": "6.43.12"
     212 + },
     213 + {
     214 + "exact": "6.41"
     215 + },
     216 + {
     217 + "exact": "6.41"
     218 + },
     219 + {
     220 + "exact": "6.41"
     221 + },
     222 + {
     223 + "exact": "6.41"
     224 + },
     225 + {
     226 + "exact": "6.41"
     227 + },
     228 + {
     229 + "exact": "6.41"
     230 + },
     231 + {
     232 + "exact": "6.41"
     233 + },
     234 + {
     235 + "exact": "6.41"
     236 + },
     237 + {
     238 + "exact": "6.41"
     239 + },
     240 + {
     241 + "exact": "6.41"
     242 + },
     243 + {
     244 + "exact": "6.41"
     245 + },
     246 + {
     247 + "exact": "6.41"
     248 + },
     249 + {
     250 + "exact": "6.42"
     251 + },
     252 + {
     253 + "exact": "6.42"
     254 + },
     255 + {
     256 + "exact": "6.42"
     257 + },
     258 + {
     259 + "exact": "6.42"
     260 + },
     261 + {
     262 + "exact": "6.42"
     263 + },
     264 + {
     265 + "exact": "6.42"
     266 + },
     267 + {
     268 + "exact": "6.42"
     269 + },
     270 + {
     271 + "exact": "6.42"
     272 + },
     273 + {
     274 + "exact": "6.42"
     275 + },
     276 + {
     277 + "exact": "6.42"
     278 + },
     279 + {
     280 + "exact": "6.42"
     281 + },
     282 + {
     283 + "exact": "6.42"
     284 + },
     285 + {
     286 + "exact": "6.42"
     287 + },
     288 + {
     289 + "exact": "6.42"
     290 + },
     291 + {
     292 + "exact": "6.42"
     293 + },
     294 + {
     295 + "exact": "6.42"
     296 + },
     297 + {
     298 + "exact": "6.42"
     299 + },
     300 + {
     301 + "exact": "6.42"
     302 + },
     303 + {
     304 + "exact": "6.42"
     305 + },
     306 + {
     307 + "exact": "6.42"
     308 + },
     309 + {
     310 + "exact": "6.42"
     311 + },
     312 + {
     313 + "exact": "6.42"
     314 + },
     315 + {
     316 + "exact": "6.42"
     317 + },
     318 + {
     319 + "exact": "6.42"
     320 + },
     321 + {
     322 + "exact": "6.42"
     323 + },
     324 + {
     325 + "exact": "6.43"
     326 + },
     327 + {
     328 + "exact": "6.43"
     329 + },
     330 + {
     331 + "exact": "6.43"
     332 + },
     333 + {
     334 + "exact": "6.43"
     335 + },
     336 + {
     337 + "exact": "6.43"
     338 + },
     339 + {
     340 + "exact": "6.43"
     341 + },
     342 + {
     343 + "exact": "6.43"
     344 + },
     345 + {
     346 + "exact": "6.43"
     347 + },
     348 + {
     349 + "exact": "6.43"
     350 + },
     351 + {
     352 + "exact": "6.43"
     353 + },
     354 + {
     355 + "exact": "6.43"
     356 + },
     357 + {
     358 + "exact": "6.43"
     359 + },
     360 + {
     361 + "exact": "6.43"
     362 + },
     363 + {
     364 + "exact": "6.43"
     365 + },
     366 + {
     367 + "exact": "6.43"
     368 + },
     369 + {
     370 + "exact": "6.43"
     371 + },
     372 + {
     373 + "exact": "6.43"
     374 + },
     375 + {
     376 + "exact": "6.43"
     377 + },
     378 + {
     379 + "exact": "6.43"
     380 + },
     381 + {
     382 + "exact": "6.43"
     383 + },
     384 + {
     385 + "exact": "6.43"
     386 + },
     387 + {
     388 + "exact": "6.43"
     389 + },
     390 + {
     391 + "exact": "6.43"
     392 + },
     393 + {
     394 + "exact": "6.43"
     395 + },
     396 + {
     397 + "exact": "6.44"
     398 + },
     399 + {
     400 + "exact": "6.44"
     401 + },
     402 + {
     403 + "exact": "6.44"
     404 + },
     405 + {
     406 + "exact": "6.44"
     407 + },
     408 + {
     409 + "exact": "6.44"
     410 + },
     411 + {
     412 + "exact": "6.44"
     413 + },
     414 + {
     415 + "exact": "6.44"
     416 + },
     417 + {
     418 + "exact": "6.44"
     419 + },
     420 + {
     421 + "exact": "6.44"
     422 + },
     423 + {
     424 + "exact": "6.44"
     425 + },
     426 + {
     427 + "exact": "6.44"
     428 + },
     429 + {
     430 + "exact": "6.44"
     431 + }
     432 + ],
     433 + "CVE-2019-3978": [
     434 + {
     435 + "end_including": "6.44.5"
     436 + },
     437 + {
     438 + "end_including": "6.45.6"
     439 + }
     440 + ],
     441 + "CVE-2019-3981": [
     442 + {
     443 + "end_excluding": "6.43"
     444 + },
     445 + {
     446 + "end_excluding": "3.20"
     447 + }
     448 + ],
     449 + "CVE-2020-20267": [
     450 + {
     451 + "end_excluding": "6.47"
     452 + }
     453 + ],
     454 + "CVE-2019-15055": [
     455 + {
     456 + "start_including": "6.45",
     457 + "end_including": "6.45.3"
     458 + },
     459 + {
     460 + "end_including": "6.44.5"
     461 + }
     462 + ],
     463 + "CVE-2020-20230": [
     464 + {
     465 + "end_excluding": "6.47"
     466 + }
     467 + ],
     468 + "CVE-2020-20247": [
     469 + {
     470 + "end_excluding": "6.46.5"
     471 + }
     472 + ],
     473 + "CVE-2020-20237": [
     474 + {
     475 + "exact": "6.46.3"
     476 + }
     477 + ],
     478 + "CVE-2018-1159": [
     479 + {
     480 + "end_excluding": "6.40.9"
     481 + },
     482 + {
     483 + "end_excluding": "6.42.7"
     484 + }
     485 + ],
     486 + "CVE-2020-11881": [
     487 + {
     488 + "start_including": "6.41.3",
     489 + "end_including": "6.46.5"
     490 + },
     491 + {
     492 + "exact": "7.0"
     493 + },
     494 + {
     495 + "exact": "7.0"
     496 + },
     497 + {
     498 + "exact": "7.0"
     499 + }
     500 + ],
     501 + "CVE-2020-20221": [
     502 + {
     503 + "end_excluding": "6.44.6"
     504 + }
     505 + ],
     506 + "CVE-2018-14847": [
     507 + {
     508 + "end_including": "6.42"
     509 + }
     510 + ],
     511 + "CVE-2019-13954": [
     512 + {
     513 + "exact": "6.45"
     514 + },
     515 + {
     516 + "end_excluding": "6.44.5"
     517 + }
     518 + ],
     519 + "CVE-2019-3924": [
     520 + {
     521 + "end_excluding": "6.42.12"
     522 + },
     523 + {
     524 + "end_excluding": "6.43.12"
     525 + }
     526 + ],
     527 + "CVE-2018-7445": [
     528 + {
     529 + "end_excluding": "6.41.3"
     530 + },
     531 + {
     532 + "exact": "6.4.2"
     533 + },
     534 + {
     535 + "exact": "6.4.2"
     536 + },
     537 + {
     538 + "exact": "6.4.2"
     539 + },
     540 + {
     541 + "exact": "6.4.2"
     542 + },
     543 + {
     544 + "exact": "6.4.2"
     545 + },
     546 + {
     547 + "exact": "6.4.2"
     548 + },
     549 + {
     550 + "exact": "6.4.2"
     551 + },
     552 + {
     553 + "exact": "6.4.2"
     554 + },
     555 + {
     556 + "exact": "6.4.2"
     557 + },
     558 + {
     559 + "exact": "6.4.2"
     560 + },
     561 + {
     562 + "exact": "6.4.2"
     563 + },
     564 + {
     565 + "exact": "6.4.2"
     566 + },
     567 + {
     568 + "exact": "6.4.2"
     569 + }
     570 + ],
     571 + "CVE-2020-20217": [
     572 + {
     573 + "end_excluding": "6.47"
     574 + }
     575 + ],
     576 + "CVE-2017-7285": [
     577 + {
     578 + "exact": "6.38.5"
     579 + }
     580 + ],
     581 + "CVE-2020-20216": [
     582 + {
     583 + "exact": "6.44.6"
     584 + }
     585 + ],
     586 + "CVE-2019-13955": [
     587 + {
     588 + "end_excluding": "6.44.5"
     589 + },
     590 + {
     591 + "exact": "6.45"
     592 + }
     593 + ],
     594 + "CVE-2020-20220": [
     595 + {
     596 + "end_excluding": "6.47"
     597 + }
     598 + ],
     599 + "CVE-2018-1158": [
     600 + {
     601 + "end_excluding": "6.40.9"
     602 + },
     603 + {
     604 + "end_excluding": "6.42.7"
     605 + }
     606 + ],
     607 + "CVE-2021-27221": [
     608 + {
     609 + "exact": "6.47.9"
     610 + }
     611 + ],
     612 + "CVE-2020-20236": [
     613 + {
     614 + "exact": "6.46.3"
     615 + }
     616 + ]
     617 +}
  • ■ ■ ■ ■ ■ ■
    src/commands/basecommand.py
     1 +class BaseCommand(object):
     2 + def _ssh_data(self, sshc, command):
     3 + stdin, stdout, stderr = sshc.exec_command(command)
     4 + 
     5 + return str(stdout.read())
     6 + 
     7 + def _ssh_data_with_header(self, sshc, command):
     8 + data = self._ssh_data(sshc, command)
     9 + res = []
     10 + 
     11 + if ' 0 ' in data:
     12 + res = data.partition(' 0 ')[2].split('\\r\\n\\r\\n')[:-1]
     13 + res = list(map(lambda y: self._parse_data(y), res))
     14 + 
     15 + return res
     16 + 
     17 + def _parse_data(self, data):
     18 + split_data = data.replace(' \\r\\n ', '').replace('\'', '').split('=')
     19 + return dict(zip(list(map(lambda x: x.rpartition(' ')[-1].strip().replace('\"', ''), split_data[:-1])), \
     20 + list(map(lambda x: x.rpartition(' ')[0].strip().replace('\"', ''), \
     21 + split_data[1:-1])) + [split_data[-1].strip().replace('\"', '')]))
     22 + 
     23 + def run_ssh(self, data):
     24 + raise NotImplementedError
     25 + 
  • ■ ■ ■ ■ ■ ■
    src/commands/dns.py
     1 +from commands.basecommand import BaseCommand
     2 + 
     3 + 
     4 +class DNS(BaseCommand):
     5 + def __init__(self):
     6 + self.__name__ = 'DNS Cache'
     7 + 
     8 + def run_ssh(self, sshc):
     9 + data = self._ssh_data(sshc, '/ip dns print')
     10 + enabled = 'allow-remote-requests: yes' in data.lower()
     11 + 
     12 + res = self._ssh_data_with_header(sshc, '/ip dns cache print detail')
     13 + sus_dns, recommendation = self.check_results_ssh(res, enabled)
     14 + 
     15 + return {'raw_data': res,
     16 + 'suspicious': sus_dns,
     17 + 'recommendation': recommendation}
     18 + 
     19 + def check_results_ssh(self, res, enabled):
     20 + sus_dns = []
     21 + recommendation = []
     22 + 
     23 + for item in res:
     24 + if int(item['ttl'].partition('s')[0]) > 200000:
     25 + sus_dns.append(f'Domain name: {item["name"]} with ip {item["address"]}: might be DNS poisoning- '
     26 + f'severity: high')
     27 + 
     28 + if enabled:
     29 + recommendation.append('In case DNS cache is not required on your router - disable it')
     30 + 
     31 + return sus_dns, recommendation
     32 + 
     33 + 
     34 + 
     35 + 
     36 + 
     37 + 
     38 + 
     39 + 
  • ■ ■ ■ ■ ■ ■
    src/commands/files.py
     1 +from commands.basecommand import BaseCommand
     2 +import re
     3 + 
     4 + 
     5 +class Files(BaseCommand):
     6 + def __init__(self):
     7 + self.__name__ = 'Files'
     8 + 
     9 + def run_ssh(self, sshc):
     10 + res = self._ssh_data_with_header(sshc, '/file print detail')
     11 + sus_dns, recommendation = self.check_results_ssh(res)
     12 + 
     13 + return {'raw_data': res,
     14 + 'suspicious': sus_dns,
     15 + 'recommendation': recommendation}
     16 + 
     17 + def check_results_ssh(self, res):
     18 + sus_files = []
     19 + recommendation = []
     20 + 
     21 + for item in res:
     22 + if 'contents' in item:
     23 + if ('/tool fetch' in item['contents']) or ('http://' in item['contents']):
     24 + sus_files.append(f'File name: {item["name"]}, content: {item["contents"]} - severity: high')
     25 + 
     26 + return sus_files, recommendation
     27 + 
     28 + 
     29 + 
     30 + 
     31 + 
     32 + 
  • ■ ■ ■ ■ ■ ■
    src/commands/fwnat.py
     1 +from commands.basecommand import BaseCommand
     2 +from ipaddress import ip_address
     3 + 
     4 + 
     5 +class FWNat(BaseCommand):
     6 + def __init__(self):
     7 + self.__name__ = 'FW Nat'
     8 + 
     9 + def run_ssh(self, sshc):
     10 + res = self._ssh_data_with_header(sshc, '/ip firewall nat print detail')
     11 + sus_dns, recommendation = self.check_results_ssh(res)
     12 + 
     13 + return {'raw_data': res,
     14 + 'suspicious': sus_dns,
     15 + 'recommendation': recommendation}
     16 + 
     17 + def check_results_ssh(self, res):
     18 + sus_nat = []
     19 + recommendation = []
     20 + 
     21 + for item in res:
     22 + if (item['action'] == 'dst-nat') and ('dst-address' in item) and ('to-address' in item):
     23 + if (not ip_address(item['dst-address']).is_private) and (not ip_address(item['to-address']).is_private):
     24 + sus_nat.append(f'dst-nat rule from {item["dst-address"]} to {item["to-address"]}: both are public '
     25 + f'IPs, might used for malicious activity - severity: high')
     26 + 
     27 + return sus_nat, recommendation
     28 + 
     29 + 
     30 + 
     31 + 
     32 + 
     33 + 
     34 + 
     35 + 
     36 + 
     37 + 
  • ■ ■ ■ ■ ■ ■
    src/commands/fwrules.py
     1 +from commands.basecommand import BaseCommand
     2 + 
     3 + 
     4 +class FW(BaseCommand):
     5 + def __init__(self):
     6 + self.__name__ = 'FW Rules'
     7 + 
     8 + def run_ssh(self, sshc):
     9 + res = self._ssh_data_with_header(sshc, '/ip firewall filter print detail')
     10 + 
     11 + return {'raw_data': res,
     12 + 'suspicious': [],
     13 + 'recommendation': []}
     14 + 
     15 + 
     16 + 
     17 + 
     18 + 
     19 + 
     20 + 
     21 + 
     22 + 
  • ■ ■ ■ ■ ■ ■
    src/commands/ports.py
     1 +from commands.basecommand import BaseCommand
     2 + 
     3 + 
     4 +class Ports(BaseCommand):
     5 + def __init__(self):
     6 + self.__name__ = 'Ports'
     7 + 
     8 + def run_ssh(self, sshc):
     9 + res = self._ssh_data_with_header(sshc, '/ip service print detail')
     10 + sus_dns, recommendation = self.check_results_ssh(res)
     11 + 
     12 + return {'raw_data': res,
     13 + 'suspicious': sus_dns,
     14 + 'recommendation': recommendation}
     15 + 
     16 + def check_results_ssh(self, res):
     17 + sus_ports = []
     18 + recommendation = []
     19 + def_ports = {'telnet': 23, 'ftp': 21, 'www': 80, 'ssh': 22, 'www-ssl': 443, 'api': 8728, 'winbox': 8291,
     20 + 'api-ssl': 8729}
     21 + 
     22 + for item in res:
     23 + service = item['name']
     24 + if def_ports[service] != int(item['port']):
     25 + sus_ports.append(f'The port for {service}, has changed from {def_ports[service]} to {item["port"]} - '
     26 + f'severity: low')
     27 + 
     28 + if (service == 'ssh') and (int(item['port']) == 22):
     29 + recommendation.append('The port for ssh protocol is as ssh default port (22)- Mikrotik company '
     30 + 'recommended to change it')
     31 + 
     32 + return sus_ports, recommendation
     33 + 
     34 + 
     35 + 
     36 + 
     37 + 
     38 + 
     39 + 
     40 + 
     41 + 
     42 + 
     43 + 
  • ■ ■ ■ ■ ■ ■
    src/commands/proxy.py
     1 +from commands.basecommand import BaseCommand
     2 +import re
     3 + 
     4 + 
     5 +class Proxy(BaseCommand):
     6 + def __init__(self):
     7 + self.__name__ = 'Proxy'
     8 + 
     9 + def run_ssh(self, sshc):
     10 + data = self._ssh_data(sshc, '/ip proxy print')
     11 + enabled = 'enabled: yes' in data.lower()
     12 + 
     13 + res = self._ssh_data_with_header(sshc, '/ip proxy access print detail')
     14 + sus_dns, recommendation = self.check_results_ssh(res, enabled)
     15 + 
     16 + return {'raw_data': res,
     17 + 'suspicious': sus_dns,
     18 + 'recommendation': recommendation}
     19 + 
     20 + def check_results_ssh(self, res, enabled):
     21 + sus_proxy = []
     22 + recommendation = []
     23 + 
     24 + if enabled:
     25 + recommendation.append('Proxy detected. In case you don\'t need it - disable it')
     26 + 
     27 + return sus_proxy, recommendation
     28 + 
     29 + 
     30 + 
     31 + 
     32 + 
     33 + 
     34 + 
     35 + 
     36 + 
  • ■ ■ ■ ■ ■ ■
    src/commands/scheduler.py
     1 +from commands.basecommand import BaseCommand
     2 +import re
     3 + 
     4 + 
     5 +class Scheduler(BaseCommand):
     6 + def __init__(self):
     7 + self.__name__ = 'Scheduler'
     8 + 
     9 + def run_ssh(self, sshc):
     10 + res = self._ssh_data_with_header(sshc, '/system scheduler print detail')
     11 + sus_dns, recommendation = self.check_results_ssh(res)
     12 + 
     13 + return {'raw_data': res,
     14 + 'suspicious': sus_dns,
     15 + 'recommendation': recommendation}
     16 + 
     17 + def check_results_ssh(self, res):
     18 + sus_tasks = []
     19 + recommendation = []
     20 + 
     21 + for item in res:
     22 + if (re.match(r'u\d+$', item['name'].lower())) or (('/tool fetch' in item['on-event']) or
     23 + ('url' in item['on-event']) or ('http' in item['on-event'])):
     24 + sus_tasks.append(f'Task name: {item["name"]}, executes: {item["on-event"]} - severity: high')
     25 + 
     26 + return sus_tasks, recommendation
     27 + 
     28 + 
     29 + 
     30 + 
     31 + 
     32 + 
     33 + 
     34 + 
     35 + 
     36 + 
  • ■ ■ ■ ■ ■ ■
    src/commands/socks.py
     1 +from commands.basecommand import BaseCommand
     2 +import re
     3 + 
     4 + 
     5 +class Socks(BaseCommand):
     6 + def __init__(self):
     7 + self.__name__ = 'Socks'
     8 + 
     9 + def run_ssh(self, sshc):
     10 + data = self._ssh_data(sshc, '/ip socks print')
     11 + enabled = 'enabled: yes' in data.lower()
     12 + 
     13 + res = self._ssh_data_with_header(sshc, '/ip socks access print detail')
     14 + sus_dns, recommendation = self.check_results_ssh(res, enabled)
     15 + 
     16 + return {'raw_data': res,
     17 + 'suspicious': sus_dns,
     18 + 'recommendation': recommendation}
     19 + 
     20 + def check_results_ssh(self, res, enabled):
     21 + sus_socks = []
     22 + recommendation = []
     23 + 
     24 + if enabled:
     25 + recommendation.append('Socks detected. In case you don\'t need it - disable it')
     26 + 
     27 + return sus_socks, recommendation
     28 + 
     29 + 
     30 + 
     31 + 
     32 + 
  • ■ ■ ■ ■ ■ ■
    src/commands/users.py
     1 +from commands.basecommand import BaseCommand
     2 + 
     3 + 
     4 +class Users(BaseCommand):
     5 + def __init__(self):
     6 + self.__name__ = 'Users'
     7 + 
     8 + def run_ssh(self, sshc):
     9 + res = self._ssh_data_with_header(sshc, '/user print detail')
     10 + sus_dns, recommendation = self.check_results_ssh(res)
     11 + 
     12 + return {'raw_data': res,
     13 + 'suspicious': sus_dns,
     14 + 'recommendation': recommendation}
     15 + 
     16 + def check_results_ssh(self, res):
     17 + sus_users = []
     18 + recommendation = []
     19 + 
     20 + for item in res:
     21 + if (item['name'] == 'admin') and (item['group'] == 'full'):
     22 + recommendation.append(
     23 + 'You are using the default "admin" user name- create new user in "full" group with a unique name, '
     24 + 'and delete the admin user')
     25 + if item['address'] == '':
     26 + recommendation.append(f'Add allowed ip address to user: {item["name"]}, '
     27 + f'to be the only address it can login from')
     28 + 
     29 + return sus_users, recommendation
     30 + 
     31 + 
     32 + 
     33 + 
     34 + 
     35 + 
     36 + 
     37 + 
  • ■ ■ ■ ■ ■ ■
    src/commands/version.py
     1 +from commands.basecommand import BaseCommand
     2 +from nvd import CVEValidator
     3 +import re
     4 + 
     5 + 
     6 +class Version(BaseCommand):
     7 + def __init__(self):
     8 + self.__name__ = 'Version'
     9 + 
     10 + def run_ssh(self, sshc):
     11 + version = ''
     12 + res = ''
     13 + data = self._ssh_data(sshc, '/system resource print')
     14 + version_reg = re.search(r'version: ([\d\.]+)', data)
     15 + 
     16 + if version_reg:
     17 + version = version_reg.group(1)
     18 + res = f'The Mikrotik version: {version}'
     19 + 
     20 + sus_dns, recommendation = self.check_results_ssh(version)
     21 + 
     22 + return {'raw_data': res,
     23 + 'suspicious': sus_dns,
     24 + 'recommendation': recommendation}
     25 + 
     26 + def check_results_ssh(self, res):
     27 + sus_version = []
     28 + recommendation = []
     29 + 
     30 + if res:
     31 + cve = CVEValidator('./assets/mikrotik_cpe_match.json')
     32 + ver_cves = cve.check_version(res)
     33 + if ver_cves:
     34 + sus_version = ver_cves
     35 + 
     36 + return sus_version, recommendation
     37 + 
     38 + 
     39 + 
     40 + 
  • ■ ■ ■ ■ ■ ■
    src/main.py
     1 +import argparse
     2 +import json
     3 +import paramiko
     4 + 
     5 +from commands.fwnat import FWNat
     6 +from commands.dns import DNS
     7 +from commands.files import Files
     8 +from commands.fwrules import FW
     9 +from commands.ports import Ports
     10 +from commands.proxy import Proxy
     11 +from commands.scheduler import Scheduler
     12 +from commands.socks import Socks
     13 +from commands.users import Users
     14 +from commands.version import Version
     15 + 
     16 + 
     17 +def main(args):
     18 + all_data = {}
     19 + commands = [Version(), Scheduler(), Files(), FWNat(), Proxy(), Socks(), DNS(), Users(), Ports(), FW()]
     20 + 
     21 + print(f'Mikrotik ip address: {args.ip}\n')
     22 + 
     23 + with paramiko.SSHClient() as ssh_client:
     24 + ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
     25 + ssh_client.connect(hostname=args.ip, port=args.port, username=args.userName, password=args.password)
     26 + 
     27 + for command in commands:
     28 + res = command.run_ssh(ssh_client)
     29 + all_data[command.__name__] = res
     30 + 
     31 + if args.J:
     32 + print(json.dumps(all_data, indent=4))
     33 + else:
     34 + print_txt_results(all_data)
     35 + 
     36 + 
     37 +def print_txt_results(res):
     38 + for command in res:
     39 + print(f'{command}:')
     40 + for item in res[command]:
     41 + if res[command][item]:
     42 + print(f'\t{item}:')
     43 + if type(res[command][item]) == list:
     44 + data = '\n\t\t'.join(json.dumps(i) for i in res[command][item])
     45 + else:
     46 + data = res[command][item]
     47 + print(f'\t\t{data}')
     48 + 
     49 + 
     50 +if __name__ == '__main__':
     51 + parser = argparse.ArgumentParser()
     52 + parser.add_argument('-i', '--ip', help='The tested Mikrotik IP address', required=True)
     53 + parser.add_argument('-p', '--port', help='The tested Mikrotik SSH port', required=True)
     54 + parser.add_argument('-u', '--userName', help='User name of user with admin Permissions', required=True)
     55 + parser.add_argument('-ps', '--password', help='the password of the given user name', default='')
     56 + parser.add_argument('-J', help='print the results as json format', action='store_true')
     57 + args = parser.parse_args()
     58 + 
     59 + main(args)
     60 + 
  • ■ ■ ■ ■ ■ ■
    src/nvd.py
     1 +#!/usr/bin/env python3
     2 +# -*- coding: utf-8 -*-
     3 +"""
     4 +Created on Thu Mar 10 17:50:49 2022
     5 + 
     6 +@author: datch
     7 +"""
     8 +import json
     9 + 
     10 +class Comparison(object):
     11 + SMALLER = -1
     12 + SAME = 0
     13 + BIGGER = 1
     14 +
     15 + 
     16 +class CVEValidator(object):
     17 + def __init__(self, jsonfname):
     18 + with open(jsonfname, 'r') as fjson:
     19 + self._all_cpe_match_data = json.loads(fjson.read())
     20 +
     21 + def _compare_3_section_version(self, version, version_to_compare_to):
     22 + va_splitted = version.split('.')
     23 + vb_splitted = version_to_compare_to.split('.')
     24 +
     25 + comparison = Comparison.SAME
     26 +
     27 + for index in range(3):
     28 + a = 0
     29 +
     30 + if len(va_splitted) > index:
     31 + a = int(va_splitted[index])
     32 +
     33 + b = 0
     34 +
     35 + if len(vb_splitted) > index:
     36 + b = int(vb_splitted[index])
     37 +
     38 + if a == b:
     39 + continue
     40 + elif a < b:
     41 + comparison = Comparison.SMALLER
     42 + break
     43 + else:
     44 + comparison = Comparison.BIGGER
     45 + break
     46 +
     47 + return comparison
     48 +
     49 + def check_version(self, version):
     50 + res = []
     51 +
     52 + for cve in self._all_cpe_match_data:
     53 + for match_ranges in self._all_cpe_match_data[cve]:
     54 + if 'start_including' in match_ranges:
     55 + if self._compare_3_section_version(version, \
     56 + match_ranges['start_including']) >= Comparison.SAME:
     57 + if 'end_including' in match_ranges:
     58 + if self._compare_3_section_version(version, \
     59 + match_ranges['end_including']) <= Comparison.SAME:
     60 + res.append(cve)
     61 + elif 'end_excluding' in match_ranges:
     62 + if self._compare_3_section_version(version, \
     63 + match_ranges['end_excluding']) < Comparison.SAME:
     64 + res.append(cve)
     65 + else:
     66 + res.append(cve)
     67 + elif 'end_including' in match_ranges:
     68 + if self._compare_3_section_version(version, \
     69 + match_ranges['end_including']) <= Comparison.SAME:
     70 + res.append(cve)
     71 + elif 'start_excluding' in match_ranges:
     72 + if self._compare_3_section_version(version, \
     73 + match_ranges['start_excluding']) > Comparison.SAME:
     74 + if 'end_including' in match_ranges:
     75 + if self._compare_3_section_version(version, \
     76 + match_ranges['end_including']) <= Comparison.SAME:
     77 + res.append(cve)
     78 + elif 'end_excluding' in match_ranges:
     79 + if self._compare_3_section_version(version, \
     80 + match_ranges['end_excluding']) < Comparison.SAME:
     81 + res.append(cve)
     82 + else:
     83 + res.append(cve)
     84 + elif 'end_excluding' in match_ranges:
     85 + if self._compare_3_section_version(version, \
     86 + match_ranges['end_excluding']) < Comparison.SAME:
     87 + res.append(cve)
     88 + elif 'exact' in match_ranges:
     89 + if self._compare_3_section_version(version, \
     90 + match_ranges['exact']) == Comparison.SAME:
     91 + res.append(cve)
     92 +
     93 + return list(set(res))
Please wait...
Page is in error, reload to recover