Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated plugin documentation with hooking mechanism overview #129

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions sphinx-docs/How-to-Build-Plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,74 @@ You should see a new "abilities" tab at the top, clicking on this should navigat
## Adding documentation

Any Markdown or reStructured text in the plugin's `docs/` directory will appear in the documentation generated by the fieldmanual plugin. Any resources, such as images and videos, will be added as well.

## Caldera Plugin Hooks

Caldera provides plugins the ability to hook into the runtime when a link is added to any operation. This is facilated through a dictionary object in each executor ```executor.HOOKS```. The values in this dictionary contain function pointers to be called before the server queues the ability for execution.

To leverage this capability, plugins need to add their function pointer to the hook dictionary object. An example of how this can be accomplished is described below.

1. Modify your plugin hook.py to await a new service function that will add our executor hooks. This is done in the expansion method as the abilities files have been loaded by this point during server startup.
``` python
#hook.py
async def expansion(services):
await services.get('myplugin_svc').initialize_code_hook_functions()
```
2. Update your plugin service script (e.g., myplugin_svc.py) to parse ability files and their executors. Add logic to hook into the executor's you are interested in modifying.
``` python
#myplugin_svc.py
async def initialize_code_hook_functions(self):
self.log.debug('searching for abilities')
ab_count = 0
ex_count = 0
hooks = 0
for ability in await self.data_svc.locate('abilities'):
ab_count += 1
for executor in ability.executors:
ex_count += 1
"""add your logic here"""
if ability.plugin == "myplugin":
self.log.debug(f'{ability.ability_id} is being hooked.')
hooks +=1
"""Make the key unique to your plugin"""
executor.HOOKS['myuniquekey'] = self.myplugin_hook
self.log.debug(f'parsed {ab_count} abilities, {ex_count} executors, {hooks} hooks added.')

async def myplugin_hook(self, ability, executor):
try:
"""Change the command"""
executor.command = "my new command"

"""Adding a new payload"""
executor.payloads.append("/tmp/mynewpayload.exe")

except Exception as e:
self.log.debug(f'Error while performing hook function: {e}')

```
In the example above, we hook each caldera ability with our unique plugin function that meets the following condition:
``` python
#myplugin_svc.py -> see add your logic here
ability.plugin == "myplugin"
```
Consequently, the ability yaml file we are targeting would need to have the plugin defined as "myplugin"
```yaml
# 1811b7f2-3a73-11eb-adc1-0242ac120102.yml
- id: 1811b7f2-3a73-11eb-adc1-0242ac120102
name: my awesome ability
plugin: myplugin
```
You can use a myriad of criteria to determine which abilities or specific executors you are hooking into. In the example above we use the plugin name, but you could just as easily use the ability id, or a custom symbol you add to the ability or executor 'additional_info'. See below.

``` python
#myplugin_svc.py -> see add your logic here
ability.additional_info['hook'] == "myspecialhook"
```

```yaml
# 1811b7f2-3a73-11eb-adc1-0242ac120103.yml
- id: 1811b7f2-3a73-11eb-adc1-0242ac120103
name: my awesome ability
plugin: myplugin
hook: myspecialhook
```