page-loader

In part 1 of the Cowrie Honeypot Series I covered the configuring of the HoneyPot in Azure, and setting up everything needed in Splunk to work with the logs. In today’s blog post, I’m going to be expanding on my Alert Framework and creating a new simple playbook to block SSH connections from attackers. My intent in this small project is to block any attacker’s IP address that successfully connects to the honeypot with credentials, as once I know they’re out there and have some good credentials, I don’t really need to gather any more information from them.

Alert Framework Modifications

When I created my Alert Framework, I knew there would be more to add to it in the future. Somehow I didn’t consider calling of various Ansible playbooks as the result of searches finding results, so that’s what I’m fixing now. All of this is currently contained within the core alert_actions.py script, however if it ends up being cumbersome to maintain, I’ll look to break it out into a separate script.

Function: split_lookup_into_params(lookup_entry)

First on the modification block is the function that parses the lookup entry containing the static configs of the searches.

def split_lookup_into_params(lookup_entry):
	alert_actions = lookup_entry.split('|')[1]
	### ... ###
	# Specific Action Flags - Ansible
	ansible_playbook, playbook_name = False, ''
	### ... ###
	if 'ansible_playbook=' in alert_actions:
		ansible_playbook = True
		match = re.search(r'playbook_name=(?P<playbook_name>.+?)(,|$|")', alert_actions)
		if match:
			playbook_name = match.group('playbook_name')
	return send_email, send_text, create_ticket, email_include_attachment, email_include_table, ticket_urgency, ansible_playbook, playbook_name

Like the other types of actions, we’re going to define some default values for whether to execute an Ansible playbook, and create an empty string for the playbook’s name. Then, we check to see whether we want to trigger a playbook, and use the ‘re’ module to extract the name of the playbook.

Function: main():

Down in the main function, we have a fairly straightforward set of steps to run through in order to execute the proper playbook.

	if ansible_playbook:
		if playbook_name == 'Cowrie_Block_IP':
			if 'src_ip' in search_results[0].keys():
				command = 'ansible-playbook /etc/ansible/playbooks/Cowrie_Block_IP/main.yml --extra-vars "src_ip=' + str(search_results[0]['src_ip']) + '"'
				CompletedProcess = subprocess.run(command, shell=True, check=True, stdout=subprocess.PIPE)
				logger.info(str(CompletedProcess.stdout))
			else:
				logger.error('No src_ip found in search results')
	else:
		logging.error('playbook_name specified does not match any known playbook name')

Each Ansible playbook is segmented into its own if block which handles that playbook’s specific requirements. This one simply checks to make sure that the src_ip field is included in the search results, and then passes the IP address on to the Cowrie_Block_IP playbook through the use of extra-vars. At the end of the playbook running we log the results, and we do some standard error logging in case anything were to go wrong.

Ansible Playbook – Cowrie_Block_IP

Ansible has a great ufw module that made building this playbook a breeze.

- name: Block IP in Cowrie with ufw - {{src_ip}}
  hosts: 104.211.52.137
  tasks:
    - name: Block IP in Cowrie
      ufw:
        rule: deny
        proto: tcp
        src: "{{src_ip}}"
        port: 2222
        insert: 1
        comment: Block SSH access - set by Ansible
      become: yes
...

In this playbook I’m simply targeting the Honeypot, and adding a deny for the IP address on tcp/2222. Port 2222 is used as that is the port I actually have the SSH redirected to. Attackers still attempt to connect to port 22 like normal, and the redirection is handled behind the scenes. I’m also throwing a comment on there that this block was added through an Ansible Playbook.

Note that we’re specifying the IP to block through the use of extra variables passed in at the time the playbook is called. In the same manner, we could very easily pass in a port, more specific comment, or anything desired.

Splunk Configs

On the Splunk side of the shop, we have very little to configure. First, we’ll need a new entry added to the alert_action_config.csv file that controls which actions are run:

Cowrie – Block Successful Attackers|ansible_playbook=true, playbook_name=Cowrie_Block_IP

Then, we’ll need a simple search to find successful attackers:

host=azure001 sourcetype=cowrie action=success
| stats c by src_ip

This search is set up to run every 5 minutes, and to trigger the script alert action for each result that’s found. You might have noticed that when the playbook is called, we’re only passing the first src_ip value to it. That’s not an issue though, if we run the playbook for every result that’s found:

Search In Action

With everything configured, its time to give it a test. First I started by connecting to the honeypot from my home network, monkeyed around on the file system for a bit, and disconnected.

Sure enough, my IP address was picked up by the Splunk search, and sent off to the Ansible playbook for blocking:

Taking a look at the blocks on the honeypot over a real SSH connection, we can see the automated results from the playbook in action:

Checking out my traffic, you can see consistent events coming in until after 3:50 when the playbook ran. After that time, I disconnected from the honeypot, and am unable to reconnect.

Conclusion

Overall this project came out exactly as I wanted it to. I’m continuously impressed at how easy it is to create Ansible playbooks and get them linked up to Splunk as the orchestration platform. For the time being I don’t have any plans for enhancements to this mini-project, but if anyone has some ideas, please feel free to share.

Thanks for reading!

Leave a Reply

Your email address will not be published. Required fields are marked *