its-a-feature / Mythic

A collaborative, multi-platform, red teaming framework
Other
3.29k stars 432 forks source link

CallbackToken not working #406

Closed Eliotsehr closed 2 months ago

Eliotsehr commented 2 months ago

Hello,

I'm messing with the Callback Token feature and I can't make the SendMythicRPCCallbackTokenCreate call work. No matter what I supply in the arguments I always get the same error:

{"level":"error","error":"sql: no rows in result set","func":"github.com/its-a-feature/Mythic/rabbitmq.handleAgentMessagePostResponseCallbackTokens","line":823,"time":"2024-09-08T14:22:31Z","message":"Failed to find token to add to callback"}

The SendMythicRPCTokenCreate works fine but when trying to associate the Token with a Callback it doesn't work. Moreover, when providing a MythicRPCCallbackTokenData in the SendMythicRPCCallbackTokenCreate RPC call, the addToken function in the util_agent_message_actions_post_response.go file never seems to get called.

Here is the python code in my command for reference (I also tried giving a Token in the TokenInfo parameter):

async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse:
    resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True)

    callback_token = MythicRPCCallbackTokenData(
                                                Action="add",
                                                Host=task.Callback.Host,
                                                TokenId=9 # dummy ID
                                            )

    status = await SendMythicRPCCallbackTokenCreate( MythicRPCCallbackTokenCreateMessage(task.Task.ID, CallbackTokens=[callback_token]))

    # ....
its-a-feature commented 2 months ago

Hello, great question! Let me see if I can shed some light into what's going on here.

When dealing with Tokens, there's two components that Mythic tracks - Tokens and CallbackTokens. Realistically, a CallbackToken is nothing more than a table associating Tokens with Callbacks. As part of this, as part of your normal agent messages back to Mythic, you can set the tokens field (just like you would with user_output, file_browser, etc). This is simply telling Mythic "hey, here's some tokens I've seen on the system. My callback isn't necessarily tracking that I'm using/can use any of them, just letting you know they exist". When you send information back via the callback_tokens keyword, you're saying "here's a token, and my callback is tracking a way for me to leverage it, so if you want to use it with a task, just let me know". If you don't want to do this via your agent's normal messages, there's the MythicRPC Calls like you're seeing there to achieve the same thing.

However, you might be wondering - "wouldn't that mean if i had a token that I wanted to use with my callback, then I'd need to send two messages? one to register the token with Mythic, and one to tell Mythic that my callback can use it?". Yes, but also no. Mythic allows you to combine these into one Message. Instead of sending a tokens message and then a callback_tokens message, you can simply send a callback_tokens message and include in it information about the token. In your example of not seeing the addToken function getting called in your CallbackTokenCreate RPC Call, it's because you're not trying to also register a new token, you're trying to associate an existing token with your callback. For reference:

if callbackToken.Action == "add" {
// we want to associate a new token with the callback (one that already exists or create one)
if callbackToken.TokenInfo != nil {
// we'll create a new token and associate it with this callback
if databaseID, err := addToken(task, *callbackToken.TokenInfo); err != nil {
    continue
} else {
    databaseToken.ID = databaseID
}

But you're doing this in two separate calls, so we skip past that piece and do a lookup:

if err := database.DB.Get(&databaseToken, `SELECT id FROM token WHERE
    token_id=$1 AND host=$2 AND operation_id=$3`,
databaseToken.TokenID, databaseToken.Host, task.OperationID); err != nil {
logging.LogError(err, "Failed to find token to add to callback")
continue
}

All that said, I was doing some testing while writing this and in the PyPi version there was actually a typo in the JSON field when communicating with Mythic. If you update your mythic-container PyPi version to v0.5.12, then you should see that your example works

Eliotsehr commented 2 months ago

Thanks for your answer I confirm that the package update worked !