Closed ChoboSyk closed 2 weeks ago
You're getting that no rows error because until you do the checkin, you don't have a "Callback" as far as Mythic is concerned (no new row in your UI). The CallbackEncryptBytes
and CallbackDecryptBytes
only work on "Callbacks", which is why you're not able to use them before that spot.
I can expand that functionality to allow you to specify any UUID (payload, staging, or callback) along with a C2 Profile name so that this can work more broadly.
I'm also curious about what you're doing with your C2 profile that you need to decrypt/encrypt before handing it off to Mythic. A C2 Profile is supposed to be doing stuff that's more Agent agnostic, and for things that are more agent specific, you should be using a translation container instead (that would allow you to modify data after decryption and before encryption again). That would also prevent the need for all of the extra encrypt/decrypt steps (if possible).
Thanks for the quick answer!
I'm modifying the traffic in the c2 profile to achieve a few things
The profile is meant to be used with slow communicating external c2s. I hope to limit the client side communications to as few requests as possible. Currently the com flow of mythic is mostly 2 http requests for a round trip
I also want to intercept the Download and Upload requests to save the first round trip. Currently when you want to download a file, the implant needs to first confirm the files existence to get a file-id it can use to start transferring the file. This results in an extra round trip before the file ever starts downloading. When your implant only communicates every few hours this can become an issue. I want to intercept those and make the server confirm the file existence (even if it's unsure at this point) so the implant receives a file-id in it's download tasking and can include the file content in it's response directly. If the file doesn't exist I'll return an error in the downloading portion of the exchange. Similar thing with upload.
My approach to all this may be bad lol I'd be all ears if you'd do this differently with Mythic features :)
I think that all makes sense, so hopefully I can help provide a solution that might be a bit easier for you.
You're already familiar with get_tasking
and post_response
"action" fields you can send. What you might not know though is that you can actually send all of your responses
fields in the get_tasking
message as well. https://docs.mythic-c2.net/customizing/payload-type-development/create_tasking/agent-side-coding/action_get_tasking <-- the last helpful hint at the bottom. That means that if you have your sleep cycle end and you need to both get for new tasking and submit some output from tasks, you can do both in the same one message. The main difference between the get_tasking
and post_response
actions is what you get back - in the get_tasking
you can get tasks
back in addition to socks
, rpfwd
, interactive
, and responses
. In a post_response
message, you won't get new tasks back, just the others. This provides some level of flexibility depending on your agent structure, but you don't have to send them as separate message. You can send the following as your agent and be just fine:
{"action": "get_tasking", "responses": [ {"task_id": "uuid", "user_output": "hello" }, {"task_id": "uuid2", "user_output": "helloooo"} ] }
you'd just get back:
{"action": "get_tasking", "tasks": [ {maybe some tasks} ], "responses": [ responses to each message ] }
Hopefully that makes the first part easier.
For uploads and downloads. For upload
(transfer of file from Mythic -> Agent), your agent should already have the file_id it wants to fetch, so it can start fetching from chunk 1 right away. https://docs.mythic-c2.net/customizing/hooking-features/action-upload#example-agent-pull-down. So, you specify your file_id and chunk number, and you start fetching. I'm not sure what extra round trip you're referring to here.
For downloads (transfer of file from Agent -> Mythic), you're right. There's an extra round trip to get the initial file_id so you can start transferring data. That said, now that you point that out, https://github.com/its-a-feature/Mythic/actions/runs/10675952576 <-- once that finishes, you can pull down the latest Mythic, run make
and sudo ./mythic-cli build mythic_server
to rebuild to get the latest server component, and I added a new feature in there for you. When you send your first message, you can send the following:
"download": {
"total_chunks": 4,
"full_path": "/test/test2/test3.file", // optional full path to the file downloaded
"host": "hostname the file is downloaded from", // optional
"filename": "filename for Mythic/operator if full_path doesn't make sense", // optional
"is_screenshot": false, //indicate if this is a file or screenshot (default is false)
"chunk_size": 512000, // indicate chunk size if intending to send chunks out of order or parallelized
"chunk_num": 1,
"chunk_data": "base64_blob==",
}
^ notice that you're saying how many total_chunks
there will be, and, you're sending chunk_num
with chunk_data
. You'd get back your normal:
{
"status": "success",
"file_id": "UUID Here"
"task_id": "task uuid here",
}
This will allow you to cut out that one extra round trip and start sending data with the initial registration message.
Thoughts?
For the download feature the fix is perfect thank you!
I have Download implemented already but not Upload and just re-read the documentation and missed the part about using create_tasking so the first request includes file data directly instead of just file_id and file_path. I thought that the implant had to first send a request to Mythic specifying which chunk it wants with a file ID. Implying that the first round trip would just be mythic telling the implant to start the upload process. With the create_tasking trick I think the issue is solved and I can just tell my implant to start its upload requests at chunk n.2 as the first one came with the tasking itself.
For the fusion of get_tasking and post_response I didn't know that feature existed and it's almost perfect but still doesn't resolve my need to decrypt messages on the fly. One issue I find of External C2 configurations is that when an operator sends a new command during the sleep time of a beacon, the get_tasking message has already been posted and thus the operator needs to wait for an extra callback for the task to be fetched. If I use the feature the following happens:
With C2s that communicate only a few times a day this can be pretty frustrating.
What I'm hoping to do, is receive the response + get-tasking at the same time. Post the response so the operator can see the result of his previous task, but hold on making the get-tasking request for as long as I know the beacon will be sleeping. The server will then make it ~1 min before the beacon is planned to check for it's next frame and push that fresh get-tasking so an operator doesn't have to wait the extra round trip. This is a an issue proper to extC2s but I've had to work/write a few for CS and it would tilt me every time lol. With CS I'd write a utility script that would simulate the beacon sending a null frame forcing CS to push the new tasking in it's socket connection. Always found that pretty wonky and I'm trying to make something more seamless with Mythic. I don't really see a way to keep that logic separate from my c2 profile though.
So, if I'm understanding your flow correctly, you have:
Yup exactly. When the agents sleeps hours at a time this can become blocking instead of a small inconvenience when agents communicate fairly frequently.
ok, i know this C2 is communicating very infrequently, but how important is message size?
Mythic has the idea of PushC2 and PushC2OneToMany. In both cases, the C2 Profile has a held-open gRPC connection to Mythic and is getting a stream of messages destined for the callbacks at the other end. You can still send get_tasking and post_response messages like normal, but this would allow your C2 Profile to collect tasks as they come in and send them off to your dead drop 3rd party service. Your agent would still reach out to the dead drop as necessary and fetch tasking. The only complication is that each new task would be a new message, so I'm not sure if the agent could get multiple messages from the dead drop at once or if that would break what you're doing here
I'm trying to limit the amount of requests made by the agent. With a simple channel like GraphApi + Onedrive you'll have a max size of ~200mb per frame so you'll naturally want to stuff all the tasks into a singular frame to limit the traffic of the agent to a single download. With a more throttled channel like a messaging system or wtv you'll often have frame limits of a few kb. In those cases I'd still prefer to have Mythic generate a single frame that is bigger than the max channel size and have the C2 profile handle the chunking on an encrypted blob. If I end up uploading a new frame everytime an operator pushes a new task, I could end up with the agent making multiple download requests for what could actually fit in a single.
So yeah I'd like Mythic to create the least amount of frames to be exchanged possible so the c2 can do the chunking in an optimal way and not on a per command basis.
In a way I'm trying to simulate a Push server that pushes based on a timer that is synced with the beacon sleep patterns. Making sure the server only pushes its tasks frames right before the beacon wakes up to fetch it.
From your C2, how do you know an agent is about to wake up and fetch tasking?
When the beacon sends the get-tasking + responses frame it appends it's next sleep duration to the message. The server side generally has a lot less request throttling restriction so it'll get the frame relatively in real time and be able to calculate to a few minutes off when the beacon will wake up.
Ok, I think I'm tracking now. I appreciate you providing some context! I think you're right in that Mythic's current feature set doesn't quite cover this case. Of the following two, which do you think is the most beneficial to your work:
Any of these solutions would work for me. The RPC calls would allow me not to have useless crypto code in the C2 code so probably the better solution if it's not too much trouble on your end. No key exchange as of now.
Thank you so much for taking the time to help with such a funky edge case. The tool you built is amazing and the support you give is honestly ridiculous for something free.
I'll try to get that added this week for you.
Ok, you'll need to fetch the latest Mythic and the latest either PyPi or Go package that you're using for the C2 Profile that communicates with Mythic, but I think I got it updated where you specify the payload, staging, or callback id and the c2 profile name and you should be good to go
Hey, I just tried the RPC change and I'm still having an SQL error when providing the profile name with a payloads UUID. I'm probably forgetting to update something on my end. This is my setup.
Would you have any idea what the issue might be?
if you click the hamburger icon in the top left of Mythic's UI, what does it show for the Mythic server version and the UI version? Should be:
Mythic Version: v3.3.1-rc6
UI Version: v0.2.40
Also, which version of the PyPi package did you install? v0.5.10? Did you make sure to rebuild that container too (sudo ./mythic-cli build [c2 name]
)?
I have these version and the docker has been rebuilt:
Mythic Version: v3.3.1-rc6
UI Version: v0.2.38
For PyPi do you mean which version of MythicContainer is used in the c2 profile server code?
For the actual web server doing the RPC call it's using
The mythic_go_service binary is using the previous version still . Would that be the issue?
Where are you seeing the mythic_go_service binary? That shouldn't be in use in what you're doing. If your compiled code that you're using to make the RPC call is using 1.4.5 though then I think that should be fine. I'll have to look into this more. I don't see where you're getting that sql error anymore.
The server code making the RPC is 1.4.5. I added changes to the logs to be 100% and the build would break otherwise I believe as the C2Profile param of the json object wouldn't exist. The mythic_go_service runs in the docker in parallel to the actual webserver. It contains the code to register the c2 with mythic from what I understand.
Service Binary
Web server
Request
What is current running the docker:
Hopefully this isn't some dumb mistake on my end lol thanks for looking into it
Can you try one more thing for me - run:
sudo ./mythic-cli config set debug_level debug
sudo ./mythic-cli build mythic_server
sudo ./mythic-cli logs mythic_server -f <-- this will hang and stream output from the mythic_server container
then re-run your example and see if any errors are getting logged that look like they're related to this
Getting the same error without much more info in the logs
I did some tests and hardcoded the payload UUID instead of using the Callback's present in the messages and the decryption works for all messages after the check-in one. The issue only happens for the check-in message and the server's answer to it. At first I thought it was because the row in the DB didn't exist yet for the payload when the check-in comes in but even if I replay the check-in after successfully decrypting a get-tasking with the same payload UUID I still get the SQL error. Not sure what is different about the check-in. The server accepts it successfully so the crypto should be good.
Ok, your mythic server isn't updated. You did a git pull
on the master
branch right? Then a sudo make
and a sudo ./mythic-cli build mythic_server
? I'm confident that it's not quite updated because that error in your screenshot points to line 113:
https://github.com/its-a-feature/Mythic/blob/bec5422883b3c87cb0c30967e3305b5826c623ba/mythic-docker/src/rabbitmq/recv_mythic_rpc_callback_decrypt_bytes.go#L113
^ in the old code, that makes sense
In the latest master branch though, https://github.com/its-a-feature/Mythic/blob/master/mythic-docker/src/rabbitmq/recv_mythic_rpc_callback_decrypt_bytes.go#L113, it's not a logging message
I really thought I did but clearly not. I had the latest version pulled but I somehow failed to rebuild I guess. It works now. Sorry about that!
Thanks again for all the help :)
Glad it's working now!
Hi,
I'm building an C2 profile that needs to decrypt/re-encrypt messages for some modifications as they come in. I've been using SendMythicRPCCallbackDecryptBytes and SendMythicRPCCallbackEncryptBytes. I currently get an error using these methods when providing the implants default UUID before it is changed after the check-in. This means that the check-in messages cannot be decrypted.
When the original UUID is provided with a message for decryption I get the following error: "sql: no rows in result set". Following messages once the UUID is changed can be decrypted without issue.
Would it be possible to add that when a payload is created, an entry is made in the database matching it's base UUID and it's crypto information?
I was also wondering if there was a way to get the actual base64 key value via the RPC methods instead of delegating the actual encrypt/decrypt functionalities to the RPC functions.
Thank you!