CTIgor MCP for OpenCTI

Building on my earlier posts about CTIgor, I’ve implemented a new MCP server and framework that can interact with the OpenCTI Threat Intelligence Platform. Capabilities exposed through the MCP server help to allow the agent to yield more insights about CTI findings through retrieval augmented generation (RAG), using an OpenCTI database as a RAG source. There are a lot of considerations that need to be addressed in the design: Simply exposing raw server JSON responses to the agent doesn’t suffice for many reasons. I will discuss these issues and explain my mitigations along the way.

Introduction

This post will build upon my earlier CTIgor framework posts, leveraging the most recent one to offer up an OpenCTI MCP Server that the agent can leverage. For reference, here are the earlier posts if you’d like to catch up:

This post will assume the reader is familiar with these earlier posts, including how an MCP server is built, as well as how to use the ctigor_summarize.py tool and the uv Python project tool that the repositories are based on.

There is a separate repository for my OpenCTI MCP tool, as it is intended to be compatible with any MCP client. This project will be updated as time goes on, so the latest code in it may not exactly match that discussed in this blog post. Thus, I am including a link to the exact commit this post was built from:

Usage With CTIgor

Starting up the server is straightforward. Unlike the mock servers built in Adding MCP to CTIgor, this one written for OpenCTI handles setting up the listener as part of its main execution code. Therefore, it doesn’t need mcp to run and can serve entirely by itself. In order to work, however, it needs the URL and an API key for an OpenCTI instance populated in pycti_tools/local_settings.py. There’s a sample file checked into the repository to assist.

OpenCTI Access Configuration

Copy the sample file over to the proper location:

cp pycti_tools/local_settings.py.sample pycti_tools/local_settings.py

Then, edit the pycti_tools/local_settings.py file (not the .py.sample one) and add in the credentials from OpenCTI:

api_url = ""       # Put the URL for your OpenCTI server here
api_token = ""     # Put the API token to log into OpenCTI here

This file is located in the pycti_tools/ subdirectory due to the fact that all of the code that interfaces with OpenCTI is located there, rather than in the top-level package.

As a standard “good practice” it’s ideal to create a dedicated user in OpenCTI for the MCP server to authenticate as. This will help differentiate MCP Server activity from interactive users or automation activity within the platform, and also will enable access control limits to be implemented on the MCP Server, independent of other automation or users of the platform.

In this screenshot is a portion of the administrative view of a functional User (in this case, a demo account). The Token field is masked and using the “copy” icon in it, the API key can be copied to the clipboard (to be pasted into the pycti_tools/local_settings.py file:

Admin view of a User in OpenCTI, showing masked API token

Starting the MCP Server for OpenCTI

It can now be started by running the following command, which will listen on port 8002 by default:

uv run ./mcp_server_octi.py

If a different listen port is desired, there’s a CLI argument to specify it:

usage: mcp_server_octi.py [-h] [-p PORT]

Execute the OpenCTI MCP Server

options:
  -h, --help       show this help message and exit
  -p, --port PORT  TCP port to listen on (default 8002)

Starting it up, the following status text will be displayed to the screen:

Added <class 'pycti_tools.lookup_observables.ToolSpec'> to MCP
Added <class 'pycti_tools.lookup_adversary.ToolSpec'> to MCP
INFO:     Started server process [876891]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8002 (Press CTRL+C to quit)

The first two lines document it loading two tools from the pycti_tools/ folder within the project, and attaching them to the MCP system. More on the structure of this later, but the way the server is written, it will attempt to discover tools that are written into Python scripts in the pycti_tools/ folder, and registered to auto-load in pycti_tools/__init__.py. More on the structure of this later.

Connect ctigor_summarize.py With pycti-mcp

While the above is running, in another terminal, start up the ctigor_summarize.py tool, and connect it to the OpenCTI MCP server with the appropriate --mcp CLI argument. Make sure to match the port chosen from earlier, if a different TCP port was used to listen. Note that the code here is being run from inside the ctigor repository, and not the pycti-mcp one. For a short refresh on how to use it: Adding MCP to CTIgor

uv run ctigor_summarize.py -i --mcp http://localhost:8002

You should be presented with the CTIgor> chat prompt, which can then be asked to explain what tools it has available:

CTIgor> Hi, what tools do you have available?

If successful, a response such as this one, with an explanation about specific tools and the functionality they offer, should be provided. The wording and format will vary from run to run, and even more if models other than gpt-4.1-nano are being used:

=======
I have access to the functions in the 'functions' namespace, which include tools for looking up
observables and threat adversaries in OpenCTI. How can I assist you today?
=======

The above explains that there are some tools available. For this project, the implemented ones are the looking up observables and the looking up threat adversaries.

Inquiring About a Threat Adversary

For this demonstration, I’ll inquire about the Marbled Dust adversary, which is a name used by Microsoft.

CTIgor> What can you tell me about the adversary named "marbled dust", using OpenCTI?

The response:

The adversary named "Marbled Dust" is also known as Teal Kurma, Cosmic Wolf, SILICON, and UNC1326.

Based on the OpenCTI data, it is linked to the threat group "Sea Turtle," which has been active since
at least 2017 and is associated with espionage activities targeting victims across Asia, Europe, and
North America.

Sea Turtle is notable for its operations involving DNS hijacking and complex DNS-based intrusions,
often compromising DNS providers to hijack DNS resolution for targeted victims. Its operations are
linked to Turkey and have been involved in regional espionage.

Recent reports indicate that Marbled Dust leverages a zero-day vulnerability in Output Messenger for
regional espionage activities.

For further details, you can refer to the following sources:

- [Talos Sea Turtle 2019](https://blog.talosintelligence.com/seaturtle/)
- [PWC Sea Turtle 2023](https://www.pwc.com/gx/en/issues/cybersecurity/cyber-threat-intelligence/tortoise-and-malwahare.html)
- [Microsoft Digital Defense 2021 report](https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RWMFIi?id=101738)
- Additional references on threat activities and analyses are provided in the retrieved data.

Would you like more detailed information on any specific aspect?

Below is a screenshot of the Intrusion Set page for the Sea Turtle adversary the LLM is working off of:

Sea Turtle Intrusion Set Entity Page in OpenCTI

The following screenshot is a list of the external reports, who’s titles, URLs, authors, and other metadata were included in the JSON response the LLM got from the PyCTI MCP server:

Sea Turtle Intrusion Set List of External Reports

The summary provided above appears to incorporate elements from the titles and metadata of the reports in the second image, with the description and some other metadata from the first screenshot.

Pivoting and Aggregating From OpenCTI

In order to support retrieval-augmented-generation that includes both the externally-hosted and OpenCTI-hosted information, I followed the steps in Adding MCP to CTIgor to run the basic MCP server that contained the tools for loading content from the web or text files on disk (by default, this server listens on port 8001).

Then I re-executed ctigor_summarize.py to connect it to both MCP servers:

uv run ctigor_summarize.py -i --mcp http://localhost:8002/sse http://localhost:8001/sse

When I asked it for an exhaustive list of the external references, it complied:

CTIgor> Show me a unique list of the external references
Here is a unique list of external references related to Marbled Dust:

1. [Talos Sea Turtle 2019](https://blog.talosintelligence.com/seaturtle/)
2. [Talos Sea Turtle 2019_2](https://blog.talosintelligence.com/sea-turtle-keeps-on-swimming/)
3. [Microsoft Digital Defense 2021](https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RWMFIi?id=101738)
4. [PwC Sea Turtle 2023](https://www.pwc.com/gx/en/issues/cybersecurity/cyber-threat-intelligence/tortoise-and-malwahare.html)
5. [Turkish espionage campaigns in the Netherlands](https://www.huntandhackett.com/blog/turkish-espionage-campaigns)
6. [Hunt Sea Turtle 2024](https://www.huntandhackett.com/blog/turkish-espionage-campaigns)
7. [AlienVault OtX Pulse on Turkish espionage](https://otx.alienvault.com/pulse/659ff606e4082e547653006b)
8. [AlienVault OtX Pulse on Indicators](https://otx.alienvault.com/pulse/65a0740fefe93d8593b812af)
9. [MITRE ATT&CK - Sea Turtle (G1041)](https://attack.mitre.org/groups/G1041)
10. [Microsoft Security Blog on Marbled Dust zero-day](https://www.microsoft.com/en-us/security/blog/2025/05/12/marbled-dust-leverages-zero-day-in-output-messenger-for-regional-espionage)
11. [Microsoft OtX Pulse](https://otx.alienvault.com/pulse/68221f112932c07e56cb0096)
12. [SentinelOne Cybersecurity Blog](https://www.sentinelone.com/blog/the-good-the-bad-and-the-ugly-in-cybersecurity-week-20-6)
13. [Mandiant Blog on DNS Hijacking](https://www.mandiant.com/resources/blog/global-dns-hijacking-campaign-dns-record-manipulation-at-scale)
14. [Threat Intelligence on Sea Turtle](https://threatintel.eu/2020/02/25/on-sea-turtle-campaign-targeting-greek-governmental-organisations-timeline)
15. [DomainTools Blog on Passive DNS](https://www.domaintools.com/resources/blog/finding-additional-indicators-with-passive-dns-within-domaintools-iris)
16. [ICANN Collaboration](https://community.icann.org/download/attachments/109483867/Cybersecurity%20and%20the%20ICANN%20Ecosystem.pdf)
17. [Reuters on Cyberattacks in Turkey](https://www.reuters.com/article/us-cyber-attack-hijack-exclusive/exclusive-hackers-acting-in-turkeys-interests-believed-to-be-behind-recent-cyberattacks-sources-idUSKBN1ZQ10X)
18. [YouTube Analysis of Sea Turtle](https://www.youtube.com/watch?v=ws1k44ZhJ3g)
19. [Virus Bulletin Paper](https://www.virusbulletin.com/uploads/pdf/magazine/2019/VB2019-Mercer-Rascagneres.pdf)
20. [CrowdStrike 2022 Threat Report](https://go.crowdstrike.com/rs/281-OBQ-266/images/Report2022GTR.pdf)
21. [Passive DNS Bitcoin](https://www.reuters.com/article/us-cyber-attack-hijack-exclusive/exclusive-hackers-acting-in-turkeys-interests-believed-to-be-behind-recent-cyberattacks-sources-idUSKBN1ZQ10X)

Would you like to explore any of these references further?

That is a long list of reports, so I wanted to have the agent read them all and summarize them for me. However, I know that the otx.alienvault.com content will be inaccessible to the agent without an API credential, and as well wouldn’t really have a lot of explanatory information. So, to save LLM usage, I built a prompt to have it read and summarize these without attempting to use the AlienVault content:

CTIgor> Fetch, read, and summarize each of these external references for me, except for the one at otx.alienvault.com

The following response was returned, after some time. The load_from_web tool in the MCP server has been instrumented to report what URLs are fetched, so I was able to follow the activity and verify it performed a fetch of each of the pages:

[06/01/25 21:20:06] INFO     Fetching url: https://blog.talosintelligence.com/sea-turtle-keeps-on-swimming/                          mcp_server.py:33
                    INFO     Fetching url: https://go.crowdstrike.com/rs/281-OBQ-266/images/Report2022GTR.pdf                        mcp_server.py:33
                    INFO     Fetching url:                                                                                           mcp_server.py:33
                             https://www.mandiant.com/resources/blog/global-dns-hijacking-campaign-dns-record-manipulation-at-scale
                    INFO     Fetching url: https://blog.talosintelligence.com/seaturtle/                                             mcp_server.py:33
                    INFO     Fetching url: https://www.virusbulletin.com/uploads/pdf/magazine/2019/VB2019-Mercer-Rascagneres.pdf     mcp_server.py:33
                    INFO     Fetching url: https://www.youtube.com/watch?v=ws1k44ZhJ3g                                               mcp_server.py:33
                    INFO     Fetching url: https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RWMFIi?id=101738                mcp_server.py:33
                    INFO     Fetching url:                                                                                           mcp_server.py:33
                             https://www.pwc.com/gx/en/issues/cybersecurity/cyber-threat-intelligence/tortoise-and-malwahare.html
                    INFO     Fetching url:                                                                                           mcp_server.py:33
                             https://threatintel.eu/2020/02/25/on-sea-turtle-campaign-targeting-greek-governmental-organisations-tim
                             eline
                    INFO     Fetching url: https://www.huntandhackett.com/blog/turkish-espionage-campaigns                           mcp_server.py:33
                    INFO     Fetching url:                                                                                           mcp_server.py:33
                             https://www.reuters.com/article/us-cyber-attack-hijack-exclusive/exclusive-hackers-acting-in-turkeys-in
                             terests-believed-to-be-behind-recent-cyberattacks-sources-idUSKBN1ZQ10X
                    INFO     Fetching url: https://attack.mitre.org/groups/G1041                                                     mcp_server.py:33
                    INFO     Fetching url:                                                                                           mcp_server.py:33
                             https://www.microsoft.com/en-us/security/blog/2025/05/12/marbled-dust-leverages-zero-day-in-output-mess
                             enger-for-regional-espionage
                    INFO     Fetching url:                                                                                           mcp_server.py:33
                             https://community.icann.org/download/attachments/109483867/Cybersecurity%20and%20the%20ICANN%20Ecosyste
                             m.pdf
                    INFO     Fetching url: https://www.huntandhackett.com/blog/turkish-espionage-campaigns                           mcp_server.py:33
                    INFO     Fetching url:                                                                                           mcp_server.py:33
                             https://www.domaintools.com/resources/blog/finding-additional-indicators-with-passive-dns-within-domain
                             tools-iris
                    INFO     Fetching url: https://www.sentinelone.com/blog/the-good-the-bad-and-the-ugly-in-cybersecurity-week-20-6 mcp_server.py:33

And, the response returned by the agent:

Here is a summary of each external reference about "Marbled Dust" and related threat campaigns:

1. **Cisco Talos Blog (2019)**:
   - Discusses the DNS hijacking campaign known as "Sea Turtle," which targeted multiple organizations mainly in the Middle East and North Africa.
   - The attackers exploited DNS registry weaknesses, compromising DNS providers to redirect traffic, harvest credentials, and perform man-in-the-middle attacks.
   - The campaign used techniques like DNS record manipulation, TLS certificate acquisition, and web shells to maintain persistence.
   - It revealed the high capability of the threat actors, likely state-sponsored, with activities ongoing since 2017.

2. **British CERT (2020)**:
   - Details the ongoing activities of Sea Turtle targeting Greek government agencies, including the Prime Minister's Office and ministries.
   - Describes techniques such as DNS hijacking, DNS record change, and credential theft.
   - Highlights the importance of protecting DNS infrastructure and advises on mitigation strategies like DNSSEC, registrar locks, and multi-factor authentication.
   - Explains the use of DNS manipulation at scale, with threats likely linked to Iran or similar state actors.

3. **Microsoft Security Blog (2025)**:
   - Reports that Marbled Dust exploits a zero-day vulnerability in Output Messenger, a messaging app, to conduct regional espionage, especially targeting Kurdish military and organizations in Iraq.
   - The attack involves directory traversal vulnerability CVE-2025-27920, allowing the upload of malicious files for persistence and data exfiltration.
   - Demonstrates exploitation techniques like credential theft, code injection, and communication with command-and-control servers.
   - Emphasizes increased technical sophistication by the threat actor, possibly indicating escalation in operational capabilities.

4. **Microsoft Threat Intelligence (2025)**:
   - Confirms that Marbled Dust is a Turkey-affiliated espionage actor that targets Europe, the Middle East, and North Africa.
   - Uses techniques like DNS hijacking, compromised DNS registries, and zero-day exploits for targeted espionage and intelligence gathering.
   - Focuses on government, telecommunication, NGO, and media sectors.
   - The recent use of a zero-day indicates a higher level of operational sophistication.

5. **SentinelOne Blog & Resources**:
   - Provides recent updates and detailed reports on campaigns attributed to Marbled Dust, including its use of reverse shells, DNS hijacking, credential theft, and zero-day exploits.
   - Highlights that the threat actor has continued evolving tactics and maintains persistence through credential compromise and sophisticated malware.

Please let me know if you want more detailed information on specific articles or threats.

Without any specific prompting, it organized the content according to sourcing. It also appears to have only produced summaries for content from 5 sources.

The above content can also be reformatted through successive prompts to the agent:

CTIgor> Give me that exact same response, but reformat it such that no line is longer than 60 characters
...

1. **Cisco Talos Blog (2019)**:

- Discusses the DNS hijacking campaign known as "Sea Turtle," which  
  targeted multiple organizations mainly in the Middle East and North  
  Africa.
- The attackers exploited DNS registry weaknesses, compromising DNS  
  providers to redirect traffic, harvest credentials, and perform man-in-the-  
  middle attacks.
- The campaign used techniques like DNS record manipulation, TLS  
  certificate acquisition, and web shells to maintain persistence.
- It revealed the high capability of the threat actors, likely state-  
  sponsored, with activities ongoing since 2017.

...
CTIgor> actually, format it with 80-character lines. make sure that the successive lines of each bullet point are
indented to match the start of the text of the first line of the bullet point
...

1. **Cisco Talos Blog (2019)**:

- Discusses the DNS hijacking campaign known as "Sea Turtle," which targeted  
  multiple organizations mainly in the Middle East and North Africa.
- The attackers exploited DNS registry weaknesses, compromising DNS providers  
  to redirect traffic, harvest credentials, and perform man-in-the-middle attacks.
- The campaign used techniques like DNS record manipulation, TLS certificate  
  acquisition, and web shells to maintain persistence.
- It revealed the high capability of the threat actors, likely state-sponsored, with  
  activities ongoing since 2017.

...

(omitting most response content for brevity)

Asking it to produce a summary for one of the sources that was omitted in the original summary list it provided:

CTIgor> You didn't generate a summary for the reporting at `www.huntandhackett.com`. Please generate a summary block for that too.
Here is a summary of the reporting from www.huntandhackett.com, formatted with 80-character lines:

- Hunt & Hackett has observed campaigns by the Sea Turtle threat actor in the  
  Netherlands, using tactics similar to those previously reported by other sources.
- Their operations include the use of reverse shells, DNS hijacking, credential  
  theft, and malware deployment like SnappyTCP.
- They have identified compromises of cPanel accounts and the use of command-  
  and-control channels targeting multiple organizations, often involving VPN  
  connections for operational security.
- The group demonstrates defense evasion techniques, such as log overwriting and  
  history clearing, to avoid detection.
- Recent activity suggests increased sophistication, including the use of a zero-day  
  vulnerability in Output Messenger for regional espionage targeting Kurdish military  
  and organizations in Iraq.
- The group mainly targets government, telecommunication, NGO, and media sectors,  
  with a focus on Middle East and North Africa regions.
- Their operations indicate a state-supportive backing, with activities aligned with  
  Turkish interests.

Let me know if you'd like further details from this source.

Conclusion

This entry helped demonstrate a bit more of the capability available by implementing an MCP server that can interact with OpenCTI. It is a helpful way to add arbitrary tools to an agent. In this case, I have attempted to provide a mechanism for the agent to query OpenCTI as well as some transformation of the responses from the server in order to provide more context or more completeness in-line with what the end user might be asking. A big reason for doing this is that a big feature of this agent is to provide answers from OpenCTI. However, we do not want to require the end user to have detailed knowledge about OpenCTI’s internals and data model in order to use the agent effectively. The agent is far more useful if the MCP bridge sitting between it and the OpenCTI instance can operate with a better understanding of the user’s wants. Additionally, the raw data model inside of OpenCTI can be fairly verbose. As models are billed by token counts, and even have upper limitations also measured in token counts, additional effort was made to make the responses from the MCP server more concise than their database-native counterparts.