Skip to content

Commit

Permalink
Merge pull request #3 from andreashappe/web-3
Browse files Browse the repository at this point in the history
Update homepage
  • Loading branch information
andreashappe authored Oct 16, 2024
2 parents 7d4be40 + 86ad2eb commit 3215230
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 32 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# LangGraph x PenTesting Playground

When I originally wrote `hackingBuddyGPT` around Feb/March 2023, LLM tooling was a bit sparse. Let's see what happens if we try to re-use some of hackingbuddyGPT's concepts with a modern framework.
When I originally wrote [hackingBuddyGPT](https://github.com/ipa-lab/hackingBuddyGPT) around Feb/March 2023, LLM tooling was a bit sparse. Let's see what happens if we try to re-use some of hackingbuddyGPT's concepts with a modern framework.

Documentation and examples can be found at [https://offensivegraphs.ai].

# Disclaimers
## Disclaimers

### Disclaimer 1

This project is an experimental application and is provided "as-is" without any warranty, express or implied. By using this software, you agree to assume all risks associated with its use, including but not limited to data loss, system failure, or any other issues that may arise.

The developers and contributors of this project do not accept any responsibility or liability for any losses, damages, or other consequences that may occur as a result of using this software. You are solely responsible for any decisions and actions taken based on the information provided by this project.
The developers and contributors of this project do not accept any responsibility or liability for any losses, damages, or other consequences that may occur as a result of using this software. You are solely responsible for any decisions and actions taken based on the information provided by this project.

**Please note that the use of any OpenAI language model can be expensive due to its token usage.** By utilizing this project, you acknowledge that you are responsible for monitoring and managing your own token usage and the associated costs. It is highly recommended to check your OpenAI API usage regularly and set up any necessary limits or alerts to prevent unexpected charges.

Expand Down
12 changes: 4 additions & 8 deletions docs/blog/posts/2024-10-10-first-steps-and-initial-version.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ categories:
---
# First Steps and Initial Version

This started when [Jürgen]() contacted [Andreas]() as he needed an automated attacker emulation for one of his defensive projects. Andreas wrote the initial version of [hackingBuddyGPT](https://hackingbuddy.ai) in March 2023 (roughly 18 months ago) and much of the codebase was written for older, less-sophisticated, LLMs. Juergen had experience with [LangGraph](https://www.langchain.com/langgraph), so we decided to play around with using langgraph for offensive security.
This started when [Jürgen](https://github.com/brandl) contacted [Andreas](https://github.com/andreashappe) as he needed an automated attacker emulation for one of his defensive projects. Andreas wrote the initial version of [hackingBuddyGPT](https://hackingbuddy.ai) in March 2023 (roughly 18 months ago) and much of the codebase was written for older, less-sophisticated, LLMs. Juergen had experience with [LangGraph](https://www.langchain.com/langgraph), so we decided to play around with using langgraph for offensive security.

## Scencario and Starting Situation

Expand All @@ -30,7 +30,7 @@ So as a starting point we want to replicate the functonality of [hackingBuddyGPT

You have a vulnerable VM that allows for the execution of arbitrary commands via SSH. We want to use a LLM (OpenAI GPT4o in this example) to internally think of a strategy and execute commands until our goal of becoming root is reached, in which case we terminate.

This prototype source code can be found [in the github history](https://github.com/andreashappe/offensive-langraph/tree/64ae8a080c5aa5e7255e1cb00c8ddb5adc6d1a20). If you look into the current `main` branch, the current source code will look differently.
This prototype source code can be found [in the github history](https://github.com/andreashappe/offensivegraphs/tree/64ae8a080c5aa5e7255e1cb00c8ddb5adc6d1a20). If you look into the current `main` branch, the current source code will look differently.

We split the functionality into two files: `ssh.py` for all the SSH callouts performed by the prototype, and `initial_version.py` containg the actual prototype logic.

Expand Down Expand Up @@ -111,7 +111,6 @@ llm_with_tools = llm.bind_tools(tools)

Now we create a connection to the LLM (`gpt-4o` in our case) and register some tools that the LLM is allowed to use. As tool, we are using the `ssh_execute_command` function we described before.


```python title="initial_version.py: Begin with our Graph" linenums="31"
class State(TypedDict):
messages: Annotated[list, add_messages]
Expand All @@ -125,7 +124,6 @@ The information passed between nodes is stored in the `State`. In our case, we j

Finally, we create our graph (named `graph_builder`). We tell it that `State` will be used to pass messages.


```python title="initial_version.py: Our first node, the LLM call" linenums="38"
def chatbot(state: State):
return {"messages": [llm_with_tools.invoke(state["messages"])]}
Expand Down Expand Up @@ -166,7 +164,6 @@ def route_tools(state: State):

This was copied verbose from the tutorial. In summary, it checks if there's a message within the state. If it has a message and the message is a `tool_calls` message, i.e., we want to execute an external tool, the next node/action will be our tool node. Otherwise we go to the `END` node and finish our graph. Why does this work? Well, as long as there are tool calls, the tools will be executed and the results appended to the end of the message log. As soon as the LLM can find an answer, it will send out the answer (not a `tool_call`), thus the `route_tools` method will go to the `END` node.


```python title="initial_version.py: finalize the graph!" linenums="66"
memory = MemorySaver()
graph = graph_builder.compile(checkpointer=memory)
Expand All @@ -176,7 +173,6 @@ Now we create the graph. What is `MemorySaver`? This stores our state between in

Now, what to use as our initial message (this will be the first message within our `state['messages'] list and task our LLM to "do stuff"). We're using an adapted prompt from our hackingBuddyGPT prototype:


```python title="initial_version.py: The initial user question for the LLM" linenums="70"
template = Template("""
You are a low-privilege user ${username} with password ${password} on a linux system and your goal is to become the root user by executing commands on the remote system.
Expand Down Expand Up @@ -218,12 +214,12 @@ You can use `graph.get_graph().draw_mermaid()` to get a mermaid diagram with our

![Our Graph in its full Glory](initial_prototype.svg)

## Execute it!
## Execute it

Now you can start the prototype with `python initial_version.py` (I've cut out some noise, also the output will become better over time):

```bash
(venv) andy@cargocult:~/offensive-langgraph/src$ python initial_version.py
(venv) andy@cargocult:~/offensivegraphs/src$ python initial_version.py
================================ Human Message =================================


Expand Down
14 changes: 7 additions & 7 deletions docs/blog/posts/2024-10-11-configuration-for-tool-calls.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ categories:
---
# Improving Configuration Handling, esp. for Tools

While being quite happy that the [initial prototype]() worked within hours, its code was very prototype-y, i.e., much of its configuration was hard-coded. In a second step, we want to fix this by making our target information (the SSH connection) configurable and remvoe all hard-coded credentials from the code.
While being quite happy that the [initial prototype](2024-10-10-first-steps-and-initial-version.md) worked within hours, its code was very prototype-y, i.e., much of its configuration was hard-coded. In a second step, we want to fix this by making our target information (the SSH connection) configurable and remove all hard-coded credentials from the code.

## Big Picture

Expand All @@ -26,13 +26,13 @@ The prototype will read this for configuration data. With this, the initial part

After looking into the [@tool annotation](https://api.python.langchain.com/en/latest/tools/langchain_core.tools.tool.html) for functions, this did not look like the perfect approach. Instead we opted towards subclassing [BaseTool](https://api.python.langchain.com/en/latest/core/tools/langchain_core.tools.base.BaseTool.html). This allows us to configure our tool-class through its standard constructor, i.e., pass the `SSHConnection` into it, and then use the connection when the tool sis called by the LLM through its `_run()` method.

You can find the resulting source code in [this github version](https://github.com/andreashappe/offensive-langgraph/tree/26c02488e7da504cade55fda0094225bac055f01). Please note, that I had a bug initially ([fixed here](https://github.com/andreashappe/offensive-langgraph/commit/576105f2a358c7aa6877d3bcf0395a5ec2997e7f)). I wilkl use the fixed source code within this post to keep things easier to read.
You can find the resulting source code in [this github version](https://github.com/andreashappe/offensivegraphs/tree/26c02488e7da504cade55fda0094225bac055f01). Please note, that I had a bug initially ([fixed here](https://github.com/andreashappe/offensivegraphs/commit/576105f2a358c7aa6877d3bcf0395a5ec2997e7f)). I wilkl use the fixed source code within this post to keep things easier to read.

Let's start with our updated tool that will be configurable:

## Making our Tool configurable by switching to `BaseTool`

You can find the full source code at [within github](https://github.com/andreashappe/offensive-langgraph/blob/26c02488e7da504cade55fda0094225bac055f01/src/ssh.py). This change was pretty straight-forward.
You can find the full source code at [within github](https://github.com/andreashappe/offensivegraphs/blob/26c02488e7da504cade55fda0094225bac055f01/src/ssh.py). This change was pretty straight-forward.

Instead of writing a function, we now create a class for each tool. We have to subclass [BaseTool](https://api.python.langchain.com/en/latest/tools/langchain_core.tools.BaseTool.html), the parameters for our tool are now defined in a separate class which is a subclass of `BaseModel`:

Expand Down Expand Up @@ -100,7 +100,7 @@ Next step is wiring everything up within our prototype code.

## Improving the Configuration Handling

We now have a tool that's configurable whileall needed configuration is in the `.env` file. Let's connect them! First, we introduce a simple helper function that receives an environmental variable or throws an error otherwise:
We now have a tool that's configurable while all needed configuration is in the `.env` file. Let's connect them! First, we introduce a simple helper function that receives an environmental variable or throws an error otherwise:

```python title="initial_version.py: environment variable helper" linenums="16"
def get_or_fail(name: str) -> str:
Expand All @@ -123,15 +123,15 @@ def get_ssh_connection_from_env() -> SSHConnection:
return SSHConnection(host=host, hostname=hostname, username=username, password=password)
```

Finally, we can wire everything up within our [prototype](https://github.com/andreashappe/offensive-langgraph/blob/26c02488e7da504cade55fda0094225bac055f01/src/initial_version.py):
Finally, we can wire everything up within our [prototype](https://github.com/andreashappe/offensivegraphs/blob/26c02488e7da504cade55fda0094225bac055f01/src/initial_version.py):

```python title="initial_version.py: retrieving configuration data" linenums="24"
load_dotenv()
conn = get_ssh_connection_from_env()
get_or_fail("OPENAI_API_KEY") # langgraph will use this env variable itself
```

Note that we now have a configured SSH connection within `conn`. When creating the tools for our LLMs, instead of passing the functions (as we did with `@tool`), we now pass in the instanciated tool-classes which receive the configured SSH connection through their constructor parameters (line 33, we also added a second tool `SSHTestCredentialsTool` for credential checking):
Note that we now have a configured SSH connection within `conn`. When creating the tools for our LLMs, instead of passing the functions (as we did with `@tool`), we now pass in the instantiated tool-classes which receive the configured SSH connection through their constructor parameters (line 33, we also added a second tool `SSHTestCredentialsTool` for credential checking):

```python title="initial_version.py: Getting all configuration from the env" linenums="32"
llm = ChatOpenAI(model="gpt-4o", temperature=0)
Expand All @@ -149,4 +149,4 @@ Do not repeat already tried escalation attacks. You should focus upon enumeratio
""").render(username=conn.username, password=conn.password)
```

And that's it.
And that's it.
10 changes: 5 additions & 5 deletions docs/blog/posts/2024-10-12-create_react_agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ categories:

LangGraph has some amazing [Prebuilt Components](https://langchain-ai.github.io/langgraph/reference/prebuilt/), one of them is the [`create_react_agent` function](https://langchain-ai.github.io/langgraph/reference/prebuilt/#langgraph.prebuilt.chat_agent_executor.create_react_agent) that allows you to hughely simplify creating new tool-using agents.

The full source code can be found [within our github history](https://github.com/andreashappe/offensive-langgraph/blob/b806dbc2196434137393cbc411ab7c879c70c7a9/src/switch-to-react.py).
The full source code can be found [within our github history](https://github.com/andreashappe/offensivegraphs/blob/b806dbc2196434137393cbc411ab7c879c70c7a9/src/switch-to-react.py).

## The simplified version

This willb e based upon our [recent configuration-improved version](). Similar to that version, we start by reading the configuration data, setting up our LLM, connecting to the target system via SSH, and configuring tools for usage through LLMs:
This willb e based upon our [recent configuration-improved version](2024-10-11-configuration-for-tool-calls.md). Similar to that version, we start by reading the configuration data, setting up our LLM, connecting to the target system via SSH, and configuring tools for usage through LLMs:

```python title="Initial Configuration" linenums="10"
# setup configuration from environment variables
Expand All @@ -35,9 +35,9 @@ Now we can use the `create_react_agent` method to create a new agent graph based
agent_executor = create_react_agent(llm, tools)
```

All that's left is to create the initial message (as detailed in our [initial blog post]()) and start the agent by calling `stream` on it while passing the mentioned initial message.
All that's left is to create the initial message (as detailed in our [initial blog post](2024-10-10-first-steps-and-initial-version.md)) and start the agent by calling `stream` on it while passing the mentioned initial message.

Again we are using `events` to output all tool calls and decisions that our agent is making.
Again we are using `events` to output all tool calls and decisions that our agent is making.

```python title="Starting the agent and output it's messages" linenums="26"
template = PromptTemplate.from_template("""
Expand All @@ -63,4 +63,4 @@ for event in events:

And that's it! Pretty amazing, when you think about it.

The `node`/`edge` graph is exactly the same as in [our initial hand-written version]().
The `node`/`edge` graph is exactly the same as in [our initial hand-written version](2024-10-10-first-steps-and-initial-version.md).
6 changes: 3 additions & 3 deletions docs/blog/posts/2024-10-14-plan-and-exec.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ categories:
---
# Adding Plan-and-Execute Planner

All sources can be found in [our github history](https://github.com/andreashappe/offensive-langgraph/tree/dbe5ae76d044e6dc876dcb86029f853a30bac565).
All sources can be found in [our github history](https://github.com/andreashappe/offensivegraphs/tree/dbe5ae76d044e6dc876dcb86029f853a30bac565).

When using LLMs for complex tasks like hacking, a common problem is that they become hyper-focused upon a single attack vector and ignore all others. They go down a "depth-first" rabbit hole and never leave it. This was experienced by [me](https://arxiv.org/abs/2310.11409) and [others](https://arxiv.org/abs/2308.06782).

Expand Down Expand Up @@ -216,7 +216,7 @@ def execute_step(state: PlanExecute):
}
```

We are reusing our [initial simple agent]() as executor on line 46. On lines 29-31 we are creating a new connection to OpenAI and configure some SSH-based tools (as mentioned in the original post) for our executor agent. This fully separated the LLM connection, graph history and supported tools from the LLM-configuration used by the plan-and-execute graph and would allow for using different LLMs for the `planner` and `executor` respectively.
We are reusing our [initial simple agent](2024-10-10-first-steps-and-initial-version.md) as executor on line 46. On lines 29-31 we are creating a new connection to OpenAI and configure some SSH-based tools (as mentioned in the original post) for our executor agent. This fully separated the LLM connection, graph history and supported tools from the LLM-configuration used by the plan-and-execute graph and would allow for using different LLMs for the `planner` and `executor` respectively.

Starting on line 49 , we execute our sub-agent and output its steps before returning the final step on line 59 as `past_steps`. This will append our agent's output (which includes a generated summary of its results) to `past_steps` within our shared state (which will subsequently be used by the `replanner` agent to refine future planning steps).

Expand Down Expand Up @@ -252,4 +252,4 @@ And that's it! Enjoy your multi-agent driven plan-and-execute architecture!

## Improvement Ideas

Before we move further with our exploration of offensive graphs,, we might want to investigate logging and tracing options. As we are now starting subgraphs (or might even run subgraphs/agents in-parallel), traditional console output becomes confusing to follow. Stay tuned!
Before we move further with our exploration of offensive graphs,, we might want to investigate logging and tracing options. As we are now starting subgraphs (or might even run subgraphs/agents in-parallel), traditional console output becomes confusing to follow. Stay tuned!
10 changes: 6 additions & 4 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ We see this as a benefit, we learn using LLMs and offensive security.

| Example | Domain | Summary | Further Documentation |
| -- | -- | -- | -- |
| initial example | linux priv-esc | good first example | [initial post](blog/2024/10/10/first-steps-and-initial-version/#the-first-prototype), [tools and configuration](blog/2024/10/11/improving-configuration-handling-esp-for-tools/) |
| react agent | linux priv-esc | use langgraph to reduce code | [Using `create_react_agent`](blog/2024/10/12/simplify-our-tool-calling-agent-through-create_react_agent/) |
| plan-and-execute | linux priv-esc | multi-layer planing | [Adding Plan-and-Execute Planner](blog/2024/10/14/adding-plan-and-execute-planner/) |
| [initial example](https://github.com/andreashappe/offensivegraphs/blob/main/src/initial_version.py) | linux priv-esc | good first example | [initial post](blog/posts/2024-10-10-first-steps-and-initial-version.md), [tools and configuration](blog/posts/2024-10-11-configuration-for-tool-calls.md) |
| [react agent](https://github.com/andreashappe/offensivegraphs/blob/main/src/switch-to-react.py) | linux priv-esc | use langgraph to reduce code | [Using `create_react_agent`](blog/posts/2024-10-12-create_react_agent.md) |
| [plan-and-execute](https://github.com/andreashappe/offensivegraphs/blob/main/src/plan_and_execute.py) | linux priv-esc | multi-layer planing | [Adding Plan-and-Execute Planner](blog/posts/2024-10-14-plan-and-exec.md) |

## How did we get there?

Expand Down Expand Up @@ -64,4 +64,6 @@ $ mv env.example .env

## How to contribute

- link to github and github issues
We try to keep all development open on github at [https://github.com/andreashappe/offensivegraphs] with the exemption that security-critical research might only be released after responsible disclosure with the respective targets.

We're happy to accept contributions [through github pull-requests](https://github.com/andreashappe/offensivegraphs/pulls) as well as bug-reports/ideas at [github's issue tracker](https://github.com/andreashappe/offensivegraphs/issues). Feel free to contact [Andreas](mailto:andreas@offensive.one) in case of questions/ideas.
Loading

0 comments on commit 3215230

Please sign in to comment.