HackingBuddyGPT
Concepts
-
Usecase(用例):每个用例是一个完整的攻击方案,在命令行指定 usecase 名称执行对应流程
自定义 usecase 需要继承 UseCase 类,实现两个抽象方法:run 方法是执行入口,get_name 方法获取其名称
-
Agent:借助 LLM 能力实现的智能体,由 usecase 编排调用,即一个 usecase 包含一个或多个 agents
自定义 agent 需要继承 Agent 类,实现抽象方法
- init:初始化
- perform_round:执行具体操作,控制 LLM 执行的轮数
-
Capability:具体的操作实现,比如函数实现、命令执行等等,由 agent 使用
class SSHTestCredential(Capability): conn: SSHConnection def describe(self) -> str: return f"give credentials to be tested by stating `{self.get_name()} username password`" def get_name(self): return "test_credential" def __call__(self, command: str) -> Tuple[str, bool]: cmd_parts = command.split(" ") assert (cmd_parts[0] == "test_credential") if len(cmd_parts) != 3: return "didn't provide username/password", False # now test the credentials. The second return parameter signifies if # the action was able to become the root user return "To be done", True
Code Snippet 1: 示例:SSH连接
自定义扩展
推荐从自定义 Agent 入手构建攻击用例
template_dir = pathlib.Path(__file__).parent
template_next_cmd = Template(filename=str(template_dir / "next_cmd.txt"))
class ExPrivEscLinux(Agent):
# You can define variables for the interaction of the agent. _This does not
# necessarily have to be an ssh-connection as shown in this example._
#
# variables are automatically added as configuration options to the command line
# their sub-options will be taken out of the corresponding class definitions
# which in this case would be out of `SSHConnection`
conn: SSHConnection = None
# variables starting with `_` are not handled by `Configurable` thus not
# auto-configured
_sliding_history: SlidingCliHistory = None
# The `init` method is used to set up and organize different tasks for an object:
def init(self):
# It starts by calling the `init` method of its parent class
# to make sure any necessary setup from the parent class is also performed.
super().init()
# It creates a `SlidingCliHistory` object, which helps
# in managing and recording command history interactions.
# This is a feature of our linux priv-esc agent and might not be needed
# for other agents
self._sliding_history = SlidingCliHistory(self.llm)
# capabilities are actions that can be called by the LLM
# This capability allows the LLM to execute commands via SSH.
self.add_capability(SSHRunCommand(conn=self.conn), default=True)
# This lets the LLM test credentials over an SSH connection.
self.add_capability(SSHTestCredential(conn=self.conn))
# This method counts how many tokens a text query contains by encoding the query
# and then measuring the length of the resulting tokens.
self._template_size = self.llm.count_tokens(template_next_cmd.source)
# Every agent that is based on the `Agent` has to implement the
# `perform_round` function: it outlines the specific actions or behaviors
# the agent will carry out in each round of its operation. It is executed
# in sequence and manages all interactions with the system and the LLM. If
# the method returns `True`, the agent's execution is halted. Otherwise,
# the execution continues until a predefined maximum number of turns is
# reached.
def perform_round(self, turn: int) -> bool:
got_root: bool = False
with self._log.console.status("[bold green]Asking LLM for a new command..."):
# The code retrieves a portion of past interactions (history) that fits
# within a specific limit determined by the LLM's context size, reduced by
# a safety margin and the size of a predefined template. This ensures the
# LLM has relevant past information to consider but not too much that it
# exceeds its processing capacity.
history = self._sliding_history.get_history(self.llm.context_size - llm_util.SAFETY_MARGIN - self._template_size)
# It then sends a request to the LLM, providing it with this history, certain
# capabilities (like additional functions it can perform), and the connection
# information. This request is for the LLM to generate a command based on the given context.
answer = self.llm.get_response(template_next_cmd, capabilities=self.get_capability_block(), history=history, conn=self.conn)
# the LLM's response can be noisy, try to extract the exact command
# from its answer
cmd = llm_util.cmd_output_fixer(answer.result)
with self._log.console.status("[bold green]Executing that command..."):
self._log.console.print(Panel(answer.result, title="[bold cyan]Got command from LLM:"))
# execute the capability identified by the LLM
result, got_root = self.get_capability(cmd.split(" ", 1)[0])(cmd)
# The command and its result are logged in a database
self._log.log_db.add_log_query(self._log.run_id, turn, cmd, result, answer)
# The command and its result are also added to a sliding history of
# commands (`self._sliding_history.add_command`) to maintain a record
# of past interactions.
self._sliding_history.add_command(cmd, result)
self._log.console.print(Panel(result, title=f"[bold cyan]{cmd}"))
# if we got root, we can stop the loop
return got_root
# For seamless integration into the command line interface, create a new UseCase
# based upon the AutonomoousAgentUseCase and mark it with @use_case
@use_case("Showcase Minimal Linux Priv-Escalation")
class MinimalLinuxPrivescUseCase(AutonomousAgentUseCase[MinimalLinuxPrivesc]):
pass
完善:更好的提示词生成
我们的原型使用了相当静态和手动编写的提示。使用 LLM 来生成和优化提示本身(类似于 AutoGPT)可能会提高其有效性。不过,考虑到我们的敏感用例,这些自动生成的提示应该受到人工的密切监控。
另一个研究途径是寻找更好的问题。基于对渗透测试人员工作方式的实证研究, 进一步研究他们在工作中会问自己哪些问题,可以得到更好的提示 ,并更好地理解这个紧密联系的行业。
Linux 提权技巧总结
https://www.geekby.site/2019/10/lin.security%E6%8F%90%E6%9D%83%E9%9D%B6%E5%9C%BA/