pushC2 and proxy updates

This commit is contained in:
its-a-feature
2026-02-18 13:53:14 -06:00
parent 652234d7e1
commit 3bb93efa72
27 changed files with 392 additions and 355 deletions
+10
View File
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.4.24] - 2026-02-18
### Changed
- Updated PushC2 to funnel traffic into one channel instead of two
- Updated socks proxy reads to do a basic burst for read sizes to reduce number of concurrent waiting messages
- this increases throughput for socks traffic with a bit more bursty memory usage
- Updated key reflection in agent messages to omit cwd and impersonation_context fields
- Updated empty BFS paths to be cached as well so non-existent paths aren't recomputed a bunch
## [3.4.23] - 2026-02-08
### Changed
+7
View File
@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.3.100] - 2026-02-18
### Changed
- Updated the eventing page to only stream in updates that match the current filter
- Updated payload parameter array type to start empty
## [0.3.99] - 2026-02-10
### Changed
@@ -17,7 +17,11 @@ import MythicTextField from "../../MythicComponents/MythicTextField";
export function CallbacksTableEditTriggerOnCheckinDialog(props) {
const [comment, setComment] = React.useState(0);
const onChange = (name, value, error) => {
setComment(parseInt(value));
if(isNaN(parseInt(value))){
setComment(0);
} else {
setComment(parseInt(value));
}
}
useEffect( () => {
setComment(props.trigger_on_checkin_after_time);
@@ -29,8 +33,14 @@ export function CallbacksTableEditTriggerOnCheckinDialog(props) {
<React.Fragment>
<DialogTitle id="form-dialog-title">{"Adjust this callback's trigger threshold"}</DialogTitle>
<DialogContent dividers={true} style={{height: "100%"}}>
<Typography>
{"This adjusts how long, in minutes, this callback must not checkin before finally checking in to trigger an eventing workflow (trigger is callback_checkin). A zero value means never trigger."}
<Typography >
This adjusts how long, in minutes, this callback must <b>not</b> checkin before finally checking in to trigger an <b>eventing workflow</b> (trigger is callback_checkin).
</Typography>
<Typography >
A zero value means never trigger.
</Typography>
<Typography >
If no eventing workflow for <b>callback_checkin</b> that matches the right payload_types and supported_os restrictions, then nothing will happen.
</Typography>
<MythicTextField autoFocus={true} onChange={onChange} type={"number"} value={comment} onEnter={onSubmit} name={"trigger threshold in minutes"} showLabel={false} />
</DialogContent>
@@ -59,7 +59,7 @@ export function CreatePayloadParameter({onChange, parameter_type, default_value,
const [dictSelectOptions, setDictSelectOptions] = React.useState([]);
const [dictSelectOptionsChoice, setDictSelectOptionsChoice] = React.useState("");
const [dateValue, setDateValue] = React.useState(dayjs(new Date()));
const [arrayValue, setArrayValue] = React.useState([""]);
const [arrayValue, setArrayValue] = React.useState([]);
const [typedArrayValue, setTypedArrayValue] = React.useState([]);
const [fileValue, setFileValue] = React.useState({name: ""});
const [fileMultValue, setFileMultValue] = React.useState([]);
@@ -93,6 +93,9 @@ mutation UpdateLevelOperationEventLog($id: Int!) {
}
}
`;
export const levelOptions = [
"All Levels", "warning (unresolved)", "warning (resolved)", "info", "debug", "api", "auth", "agent"
];
export function EventFeed({}){
const [pageData, setPageData] = React.useState({
"totalCount": 0,
@@ -112,7 +115,30 @@ export function EventFeed({}){
updatingPrev[indx] = cur;
return [...updatingPrev];
}
return [...prev, cur];
// only add this if this msg fits into the right current view
switch(level){
case "All Levels":
return [...prev, cur];
case "warning (unresolved)":
if( (cur.warning || cur.level === 'warning') && cur.resolved === false){
return [...prev, cur]
} else {
return [...prev];
}
case "warning (resolved)":
if( (cur.warning || cur.level === 'warning') && cur.resolved === true){
return [...prev, cur];
} else {
return [...prev];
}
default:
if(cur.level === level && !cur.warning){
return [...prev, cur];
} else {
return [...prev];
}
}
//return [...prev, cur];
}, [...operationeventlog]);
newEvents.sort((a,b) => (a.id > b.id) ? -1 : ((b.id > a.id) ? 1 : 0));
setOperationEventLog(newEvents);
@@ -14,6 +14,7 @@ import MenuItem from '@mui/material/MenuItem';
import Select from '@mui/material/Select';
import Grid from '@mui/material/Grid';
import {alertCount} from "../../../cache";
import {levelOptions} from "./EventFeed";
const EventList = ({onUpdateLevel, onUpdateResolution, operationeventlog}) => {
return (
@@ -31,9 +32,7 @@ export function EventFeedTable(props){
const theme = useTheme();
const [search, setSearch] = React.useState("");
const [level, setLevel] = React.useState("info");
const levelOptions = [
"All Levels", "warning (unresolved)", "warning (resolved)", "info", "debug", "api", "auth", "agent"
];
const handleSearchValueChange = (name, value, error) => {
setSearch(value);
+1 -1
View File
@@ -15,7 +15,7 @@ import {jwtDecode} from 'jwt-decode';
import {meState} from './cache';
import {getSkewedNow} from "./components/utilities/Time";
export const mythicUIVersion = "0.3.99";
export const mythicUIVersion = "0.3.100";
let fetchingNewToken = false;
+1 -1
View File
@@ -1 +1 @@
3.4.23
3.4.24
-2
View File
@@ -60,9 +60,7 @@ require (
golang.org/x/arch v0.23.0 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.32.0 // indirect
golang.org/x/tools v0.40.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect
)
+13 -91
View File
@@ -2,12 +2,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w=
github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc=
github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
@@ -20,18 +16,12 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
github.com/go-co-op/gocron/v2 v2.16.6 h1:zI2Ya9sqvuLcgqJgV79LwoJXM8h20Z/drtB7ATbpRWo=
github.com/go-co-op/gocron/v2 v2.16.6/go.mod h1:zAfC/GFQ668qHxOVl/D68Jh5Ce7sDqX6TJnSQyRkRBc=
github.com/go-co-op/gocron/v2 v2.17.0 h1:e/oj6fcAM8vOOKZxv2Cgfmjo+s8AXC46po5ZPtaSea4=
github.com/go-co-op/gocron/v2 v2.17.0/go.mod h1:Zii6he+Zfgy5W9B+JKk/KwejFOW0kZTFvHtwIpR4aBI=
github.com/go-co-op/gocron/v2 v2.19.0 h1:OKf2y6LXPs/BgBI2fl8PxUpNAI1DA9Mg+hSeGOS38OU=
github.com/go-co-op/gocron/v2 v2.19.0/go.mod h1:5lEiCKk1oVJV39Zg7/YG10OnaVrDAV5GGR6O0663k6U=
github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs=
@@ -46,10 +36,6 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
github.com/go-playground/validator/v10 v10.30.0 h1:5YBPNs273uzsZJD1I8uiB4Aqg9sN6sMDVX3s6LxmhWU=
github.com/go-playground/validator/v10 v10.30.0/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
@@ -58,8 +44,6 @@ github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9L
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/goccy/go-yaml v1.19.1 h1:3rG3+v8pkhRqoQ/88NYNMHYVGYztCOCIZ7UQhu7H+NE=
github.com/goccy/go-yaml v1.19.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -111,28 +95,19 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY=
github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.54.1 h1:4ZAWm0AhCb6+hE+l5Q1NAL0iRn/ZrMwqHRGQiFwj2eg=
github.com/quic-go/quic-go v0.54.1/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U=
github.com/quic-go/quic-go v0.58.0 h1:ggY2pvZaVdB9EyojxL1p+5mptkuHyX5MOSv4dgWF4Ug=
github.com/quic-go/quic-go v0.58.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/rubenv/sql-migrate v1.8.0 h1:dXnYiJk9k3wetp7GfQbKJcPHjVJL6YK19tKj8t2Ns0o=
github.com/rubenv/sql-migrate v1.8.0/go.mod h1:F2bGFBwCU+pnmbtNYDeKvSuvL6lBVtXDXUUv5t+u1qw=
github.com/rubenv/sql-migrate v1.8.1 h1:EPNwCvjAowHI3TnZ+4fQu3a915OpnQoPAjTXCGOy2U0=
github.com/rubenv/sql-migrate v1.8.1/go.mod h1:BTIKBORjzyxZDS6dzoiw6eAFYJ1iNlGAtjn4LGeVjS8=
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
@@ -152,7 +127,6 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
@@ -161,101 +135,49 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/arch v0.21.0 h1:iTC9o7+wP6cPWpDWkivCvQFGAHDQ59SrSxsLPcnkArw=
golang.org/x/arch v0.21.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU=
golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9 h1:V1jCN2HBa8sySkR5vLcCSqJSTMv093Rw9EJefhQGP7M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+8 -7
View File
@@ -3,20 +3,21 @@ package grpc
import (
"errors"
"fmt"
"math"
"net"
"sync"
"time"
"github.com/its-a-feature/Mythic/database/enums/PushC2Connections"
"github.com/its-a-feature/Mythic/grpc/services"
"github.com/its-a-feature/Mythic/logging"
"github.com/its-a-feature/Mythic/utils"
"google.golang.org/grpc"
"math"
"net"
"sync"
"time"
)
const (
connectionTimeoutSeconds = 10
channelSendTimeoutSeconds = 10
channelSendTimeoutSeconds = 1
)
type translationContainerServer struct {
@@ -203,7 +204,7 @@ func (t *pushC2Server) addNewPushC2Client(CallbackID int, callbackUUID string, b
t.Lock()
if _, ok := t.clients[CallbackID]; !ok {
t.clients[CallbackID] = &grpcPushC2ClientConnections{}
t.clients[CallbackID].pushC2MessageFromMythic = make(chan services.PushC2MessageFromMythic, 1000)
t.clients[CallbackID].pushC2MessageFromMythic = make(chan services.PushC2MessageFromMythic, 2000)
}
fromMythic := t.clients[CallbackID].pushC2MessageFromMythic
t.clients[CallbackID].connected = true
@@ -356,7 +357,7 @@ func Initialize(connectNotification chan int, disconnectNotification chan int) {
// initial for push c2 servers
PushC2Server.clients = make(map[int]*grpcPushC2ClientConnections)
PushC2Server.clientsOneToMany = make(map[string]*grpcPushC2ClientConnections)
PushC2Server.rabbitmqProcessPushC2AgentConnection = make(chan PushC2ServerConnected, 20)
PushC2Server.rabbitmqProcessPushC2AgentConnection = make(chan PushC2ServerConnected, 200)
PushC2Server.connectionTimeout = connectionTimeoutSeconds * time.Second
PushC2Server.channelSendTimeout = channelSendTimeoutSeconds * time.Second
connectString = fmt.Sprintf("0.0.0.0:%d", utils.MythicConfig.ServerGRPCPort)
@@ -37,7 +37,8 @@ func (t *pushC2Server) StartPushC2StreamingOneToMany(stream services.PushC2_Star
if err == io.EOF {
logging.LogDebug("Client closed before ever sending anything, err is EOF")
return nil // the client closed before ever sending anything
} else if err != nil {
}
if err != nil {
logging.LogError(err, "Client ran into an error before sending anything")
return err
}
@@ -55,6 +56,7 @@ func (t *pushC2Server) StartPushC2StreamingOneToMany(stream services.PushC2_Star
}
logging.LogDebug("Got new push c2 for one-to-many", "c2", c2ProfileName)
failedReadFromAgent := make(chan bool)
var streamSendMu sync.Mutex
go func() {
// continue to read new messages from the agent, these should realistically only be
// responses and streamed data based on what we push to the agent, but doesn't matter
@@ -64,7 +66,8 @@ func (t *pushC2Server) StartPushC2StreamingOneToMany(stream services.PushC2_Star
logging.LogDebug("Client closed before ever sending anything, err is EOF")
failedReadFromAgent <- true // the client closed before ever sending anything
return
} else if err != nil {
}
if err != nil {
logging.LogError(err, "Client ran into an error before sending anything")
failedReadFromAgent <- true
return
@@ -111,12 +114,14 @@ func (t *pushC2Server) StartPushC2StreamingOneToMany(stream services.PushC2_Star
go updatePushC2OneToManyLastCheckinConnectTimestamp(fromMythicResponse, len(fromAgent.Base64Message) > 0, c2ProfileName)
if fromMythicResponse.Err != nil {
// mythic encountered an error with the first message from the agent, return error and wait for the next
streamSendMu.Lock()
err = stream.Send(&services.PushC2MessageFromMythic{
Success: false,
Error: fromMythicResponse.Err.Error(),
Message: nil,
TrackingID: fromMythicResponse.TrackingID,
})
streamSendMu.Unlock()
if err != nil {
// we failed to send the message back to the agent, bail
// not tracking any full listening callback yet, so nothing to mark closed
@@ -126,12 +131,14 @@ func (t *pushC2Server) StartPushC2StreamingOneToMany(stream services.PushC2_Star
// successfully sent our error message, re-loop waiting for next message
continue
}
streamSendMu.Lock()
err = stream.Send(&services.PushC2MessageFromMythic{
Success: true,
Error: "",
Message: fromMythicResponse.Message,
TrackingID: fromMythicResponse.TrackingID,
})
streamSendMu.Unlock()
if err != nil {
// we failed to send the message back to the agent, bail
// not tracking any full listening callback yet, so nothing to mark closed
@@ -165,7 +172,9 @@ func (t *pushC2Server) StartPushC2StreamingOneToMany(stream services.PushC2_Star
// msgToSend.Message should be in the form:
// UUID[encrypted bytes or unencrypted data]
//logging.LogDebug("sending message from Mythic to agent")
streamSendMu.Lock()
err = stream.Send(&msgToSend)
streamSendMu.Unlock()
if err != nil {
logging.LogError(err, "Failed to send message through stream to push c2")
t.SetPushC2OneToManyChannelExited(c2ProfileName)
@@ -14,13 +14,14 @@ import (
)
type RabbitMQProcessAgentMessageFromPushC2 struct {
C2Profile string
RawMessage *[]byte
Base64Message *[]byte
RemoteIP string
UpdateCheckinTime bool
ResponseChannel chan RabbitMQProcessAgentMessageFromPushC2Response
TrackingID string
C2Profile string
RawMessage *[]byte
Base64Message *[]byte
RemoteIP string
UpdateCheckinTime bool
ResponseChannel chan RabbitMQProcessAgentMessageFromPushC2Response
DefaultResponseChannel chan services.PushC2MessageFromMythic
TrackingID string
}
type RabbitMQProcessAgentMessageFromPushC2Response struct {
Message []byte
@@ -36,8 +37,8 @@ func (t *pushC2Server) StartPushC2Streaming(stream services.PushC2_StartPushC2St
var c2ProfileName string
var rawMessage *[]byte
var base64Message *[]byte
rabbitmqProcessAgentMessageResponseChannel := make(chan RabbitMQProcessAgentMessageFromPushC2Response, 2)
rabbitmqProcessAgentMessageToMythicChannel := make(chan RabbitMQProcessAgentMessageFromPushC2, 1)
rabbitmqProcessAgentMessageResponseChannel := make(chan RabbitMQProcessAgentMessageFromPushC2Response, 100)
rabbitmqProcessAgentMessageToMythicChannel := make(chan RabbitMQProcessAgentMessageFromPushC2, 10)
disconnectChannel := make(chan bool)
newPushConnectionChannel := t.GetRabbitMqProcessAgentMessageChannel()
// make channel with 2 in case something happens and disconnects the client before mythic responds
@@ -164,8 +165,7 @@ func (t *pushC2Server) StartPushC2Streaming(stream services.PushC2_StartPushC2St
return err
}
logging.LogDebug("Got new push c2 agent", "agent id", callbackUUID)
failedReadFromAgent := make(chan bool)
var streamSendMu sync.Mutex
failedReadFromAgent := make(chan bool, 4)
go func() {
// continue to read new messages from the agent, these should realistically only be
// responses and streamed data based on what we push to the agent, but doesn't matter
@@ -192,63 +192,23 @@ func (t *pushC2Server) StartPushC2Streaming(stream services.PushC2_StartPushC2St
base64Message = nil
}
// send the agent message along to Mythic for processing and catch response
//logging.LogDebug("got message from agent to Mythic")
select {
case rabbitmqProcessAgentMessageToMythicChannel <- RabbitMQProcessAgentMessageFromPushC2{
C2Profile: c2ProfileName,
ResponseChannel: rabbitmqProcessAgentMessageResponseChannel,
RawMessage: rawMessage,
Base64Message: base64Message,
RemoteIP: fromAgent.GetRemoteIP(),
TrackingID: fromAgent.TrackingID,
C2Profile: c2ProfileName,
//ResponseChannel: rabbitmqProcessAgentMessageResponseChannel,
DefaultResponseChannel: newPushMessageFromMythicChannel,
RawMessage: rawMessage,
Base64Message: base64Message,
RemoteIP: fromAgent.GetRemoteIP(),
TrackingID: fromAgent.TrackingID,
}:
case <-time.After(t.GetChannelTimeout()):
case <-failedReadFromAgent:
err = errors.New("timeout sending to rabbitmqProcessAgentMessageChannel")
logging.LogError(err, "gRPC stream connection needs to exit due to timeouts")
failedReadFromAgent <- true
return
}
var fromMythicResponse RabbitMQProcessAgentMessageFromPushC2Response
select {
case fromMythicResponse = <-rabbitmqProcessAgentMessageResponseChannel:
case <-time.After(t.GetChannelTimeout()):
err = errors.New("timeout receiving msg from mythic to channel")
logging.LogError(err, "gRPC stream connection needs to exit due to timeouts")
failedReadFromAgent <- true
return
}
if fromMythicResponse.Err != nil {
// mythic encountered an error with the first message from the agent, return error and wait for the next
streamSendMu.Lock()
err = stream.Send(&services.PushC2MessageFromMythic{
Success: false,
Error: fromMythicResponse.Err.Error(),
Message: nil,
TrackingID: fromMythicResponse.TrackingID,
})
streamSendMu.Unlock()
if err != nil {
// we failed to send the message back to the agent, bail
// not tracking any full listening callback yet, so nothing to mark closed
failedReadFromAgent <- true
return
}
// successfully sent our error message, re-loop waiting for next message
continue
}
streamSendMu.Lock()
err = stream.Send(&services.PushC2MessageFromMythic{
Success: true,
Error: "",
Message: fromMythicResponse.Message,
TrackingID: fromMythicResponse.TrackingID,
})
streamSendMu.Unlock()
if err != nil {
// we failed to send the message back to the agent, bail
// not tracking any full listening callback yet, so nothing to mark closed
failedReadFromAgent <- true
return
}
}
}()
for {
@@ -266,6 +226,46 @@ func (t *pushC2Server) StartPushC2Streaming(stream services.PushC2_StartPushC2St
t.SetPushC2ChannelExited(callback.ID)
//go updatePushC2LastCheckinDisconnectTimestamp(callback.ID, c2ProfileName)
return errors.New(fmt.Sprintf("client disconnected: %s", callbackUUID))
case fromMythicResponse, ok := <-rabbitmqProcessAgentMessageResponseChannel:
if !ok {
logging.LogError(nil, "got !ok from fromMythicResponse, channel was closed for push c2")
t.SetPushC2ChannelExited(callback.ID)
//go updatePushC2LastCheckinDisconnectTimestamp(callback.ID, c2ProfileName)
return nil
}
if fromMythicResponse.Err != nil {
// mythic encountered an error with the first message from the agent, return error and wait for the next
err = stream.Send(&services.PushC2MessageFromMythic{
Success: false,
Error: fromMythicResponse.Err.Error(),
Message: nil,
TrackingID: fromMythicResponse.TrackingID,
})
if err != nil {
logging.LogError(err, "Failed to send message through stream to push c2")
// we failed to send the message back to the agent, bail
// not tracking any full listening callback yet, so nothing to mark closed
failedReadFromAgent <- true
t.SetPushC2ChannelExited(callback.ID)
return err
}
// successfully sent our error message, re-loop waiting for next message
continue
}
err = stream.Send(&services.PushC2MessageFromMythic{
Success: true,
Error: "",
Message: fromMythicResponse.Message,
TrackingID: fromMythicResponse.TrackingID,
})
if err != nil {
logging.LogError(err, "Failed to send message through stream to push c2")
// we failed to send the message back to the agent, bail
// not tracking any full listening callback yet, so nothing to mark closed
failedReadFromAgent <- true
t.SetPushC2ChannelExited(callback.ID)
return err
}
case msgToSend, ok := <-newPushMessageFromMythicChannel:
if !ok {
logging.LogError(nil, "got !ok from messageToSend, channel was closed for push c2")
@@ -276,9 +276,7 @@ func (t *pushC2Server) StartPushC2Streaming(stream services.PushC2_StartPushC2St
// msgToSend.Message should be in the form:
// UUID[encrypted bytes or unencrypted data]
//logging.LogDebug("sending message from Mythic to agent")
streamSendMu.Lock()
err = stream.Send(&msgToSend)
streamSendMu.Unlock()
if err != nil {
logging.LogError(err, "Failed to send message through stream to push c2")
t.SetPushC2ChannelExited(callback.ID)
@@ -286,6 +284,7 @@ func (t *pushC2Server) StartPushC2Streaming(stream services.PushC2_StartPushC2St
return err
}
}
logging.LogInfo("sent data to agent", "newPushMessageFromMythicChannel", len(newPushMessageFromMythicChannel))
}
}
}
@@ -294,6 +293,7 @@ var c2ProfileMap = make(map[string]int)
var c2ProfileMapPushC2MapLock sync.RWMutex
func updatePushC2LastCheckinDisconnectTimestamp(callbackId int, c2ProfileName string) {
logging.LogInfo("pushc2 disconnected")
c2ProfileId := -1
c2ProfileMapPushC2MapLock.Lock()
if _, ok := c2ProfileMap[c2ProfileName]; ok {
@@ -344,7 +344,7 @@ func updatePushC2LastCheckinConnectTimestamp(callbackId int, c2ProfileName strin
err := database.DB.Get(&currentEdge, `SELECT id FROM callbackgraphedge WHERE
source_id=$1 and destination_id=$2 and c2_profile_id=$3 and end_timestamp IS NULL`,
callbackId, callbackId, c2ProfileId)
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
// no active edges, so add one
_, err = database.DB.Exec(`INSERT INTO callbackgraphedge
(source_id, destination_id, c2_profile_id, operation_id)
@@ -355,6 +355,8 @@ func updatePushC2LastCheckinConnectTimestamp(callbackId int, c2ProfileName strin
} else {
logging.LogInfo("Added new callbackgraph edge in pushC2", "c2", c2ProfileId, "callback", callbackId)
}
} else if err != nil {
logging.LogError(err, "failed to get callbackgraph edges")
}
select {
case pushC2StreamingConnectNotification <- callbackId:
@@ -308,6 +308,7 @@ func processAgentMessageContent(agentMessageInput *AgentMessageRawInput, uuidInf
instanceResponse.Err = errors.New(errorMessage)
return response
}
//logging.LogDebug("Parsing message from agent", "step 3", decryptedMessage)
if utils.MythicConfig.DebugAgentMessage {
stringMsg, err := json.Marshal(decryptedMessage)
if err != nil {
@@ -546,6 +547,7 @@ func processAgentMessageContent(agentMessageInput *AgentMessageRawInput, uuidInf
if uuidInfo.UUIDType == "callback" {
instanceResponse.OuterUuidIsCallback = true
}
//logging.LogDebug("message mythic back to agent", "response", response)
if utils.MythicConfig.DebugAgentMessage {
stringMsg, err := json.Marshal(response)
if err != nil {
@@ -721,7 +723,7 @@ func ProcessAgentMessage(agentMessageInput *AgentMessageRawInput) ([]byte, error
}
func LookupEncryptionData(c2profile string, messageUUID string, updateCheckinTime bool) (*cachedUUIDInfo, error) {
//logging.LogTrace("Looking up information for new message", "uuid", messageUUID)
//logging.LogDebug("Looking up encryption information for message", "uuid", messageUUID, "updateCheckinTime", updateCheckinTime, "c2profile", c2profile)
//logging.LogDebug("Getting encryption data", "cachemap", cachedUUIDInfoMap)
newCache := cachedUUIDInfo{}
@@ -1144,69 +1146,74 @@ func RecursivelyEncryptMessage(path []cbGraphAdjMatrixEntry, message map[string]
//logging.LogDebug("recursively encrypting message", "path", path)
for i := 0; i < len(path)-1; i++ {
logging.LogDebug("Recursively encrypting and prepping tasks for delegates", "target_id", path[i].DestinationId, "c2", path[i].C2ProfileName)
if targetUuidInfo, err := LookupEncryptionData(path[i].C2ProfileName, path[i].DestinationAgentId, true); err != nil {
targetUuidInfo, err := LookupEncryptionData(path[i].C2ProfileName, path[i].DestinationAgentId, updateCheckinTime)
if err != nil {
logging.LogError(err, "Failed to lookup encryption data for target", "target", path[i].DestinationAgentId, "target_id", path[i].DestinationId)
return nil, err
} else if encryptedBytes, err := EncryptMessage(targetUuidInfo, path[i].DestinationAgentId, currentMessage, true); err != nil {
}
encryptedBytes, err := EncryptMessage(targetUuidInfo, path[i].DestinationAgentId, currentMessage, true)
if err != nil {
logging.LogError(err, "Failed to encrypt message when trying to prep tasks for delegates")
return nil, err
} else {
currentMessage = map[string]interface{}{
"action": "get_tasking",
"tasks": []string{},
"delegates": []map[string]interface{}{
{
"message": string(encryptedBytes),
"c2_profile": path[i].C2ProfileName,
"uuid": path[i].DestinationAgentId,
},
}
currentMessage = map[string]interface{}{
"action": "get_tasking",
"tasks": []string{},
"delegates": []map[string]interface{}{
{
"message": string(encryptedBytes),
"c2_profile": path[i].C2ProfileName,
"uuid": path[i].DestinationAgentId,
},
}
},
}
}
// final encrypt of the message
i := len(path) - 1
if i >= 0 {
if targetUuidInfo, err := LookupEncryptionData(path[i].C2ProfileName, path[i].DestinationAgentId, updateCheckinTime); err != nil {
targetUuidInfo, err := LookupEncryptionData(path[i].C2ProfileName, path[i].DestinationAgentId, updateCheckinTime)
if err != nil {
logging.LogError(err, "Failed to lookup encryption data for target", "target", path[i].DestinationAgentId, "target_id", path[i].DestinationId)
return nil, err
} else if encryptedBytes, err := EncryptMessage(targetUuidInfo, path[i].DestinationAgentId, currentMessage, true); err != nil {
}
encryptedBytes, err := EncryptMessage(targetUuidInfo, path[i].DestinationAgentId, currentMessage, true)
if err != nil {
logging.LogError(err, "Failed to encrypt message when trying to prep tasks for delegates")
return nil, err
} else {
return encryptedBytes, nil
}
} else {
logging.LogError(nil, "Can't encrypt final time for delegate task wrapping", "index", i)
return nil, errors.New("failed to do final encrypt")
return encryptedBytes, nil
}
logging.LogError(nil, "Can't encrypt final time for delegate task wrapping", "index", i)
return nil, errors.New("failed to do final encrypt")
}
func reflectBackOtherKeys(response *map[string]interface{}, other *map[string]interface{}) {
reservedOtherKeys := map[string]int{
"socks": 1,
"edges": 1,
"delegates": 1,
"responses": 1,
"action": 1,
"rpfwd": 1,
"alerts": 1,
"ips": 1,
"os": 1,
"user": 1,
"architecture": 1,
"domain": 1,
"external_ip": 1,
"ip": 1,
"host": 1,
"pid": 1,
"process_name": 1,
"extra_info": 1,
"sleep_info": 1,
"integrity_level": 1,
"uuid": 1,
"interactive": 1,
"file_id": 1,
"socks": 1,
"edges": 1,
"delegates": 1,
"responses": 1,
"action": 1,
"rpfwd": 1,
"alerts": 1,
"ips": 1,
"os": 1,
"user": 1,
"architecture": 1,
"domain": 1,
"external_ip": 1,
"ip": 1,
"host": 1,
"pid": 1,
"process_name": 1,
"extra_info": 1,
"sleep_info": 1,
"integrity_level": 1,
"uuid": 1,
"interactive": 1,
"file_id": 1,
"cwd": 1,
"impersonation_context": 1,
}
//logging.LogInfo("other keys", "other", *other)
for key, val := range *other {
@@ -1243,7 +1250,8 @@ func updateCheckinTimeEverySecond() {
callbackGraph.Add(callbackStruct, callbackStruct, callbackInfo.C2ProfileName, false)
i++
}
updateTimes(time.Now().UTC(), callbackIDs)
//logging.LogInfo("updating checkin times", "callbacks", callbackIDs)
//updateTimes(time.Now().UTC(), callbackIDs)
callbackIDMap = make(map[int]*cachedUUIDInfo)
}
}
@@ -1265,17 +1273,6 @@ func UpdateCallbackEdgesAndCheckinTime(uuidInfo *cachedUUIDInfo) {
// only bother updating the last checkin time if it's been more than one second
if callback.LastCheckin.Sub(uuidInfo.LastCheckinTime).Seconds() > 1 {
updateCheckinTimeChannel <- uuidInfo
/*
_, err := database.DB.NamedExec(`UPDATE callback SET
last_checkin=:last_checkin, dead=false
WHERE id=:id`, callback)
if err != nil {
logging.LogError(err, "Failed to update last_checkin time", "callback", uuidInfo.UUID)
return
}
callbackGraph.Add(callback, callback, uuidInfo.C2ProfileName, false)
*/
//callbackGraph.AddByAgentIds(callback.AgentCallbackID, callback.AgentCallbackID, uuidInfo.C2ProfileName)
if uuidInfo.EdgeId == 0 {
err := database.DB.Get(&uuidInfo.EdgeId, `SELECT id FROM callbackgraphedge
@@ -25,8 +25,8 @@ type agentMessageCheckin struct {
ProcessName string `json:"process_name" mapstructure:"process_name"`
EncKey *[]byte `json:"enc_key" mapstructure:"enc_key"`
DecKey *[]byte `json:"dec_key" mapstructure:"dec_key"`
Cwd string `json:"cwd" mapstructure:"cwd"`
ImpersonationContext string `json:"impersonation_context" mapstructure:"impersonation_context"`
Cwd *string `json:"cwd,omitempty" mapstructure:"cwd,omitempty"`
ImpersonationContext *string `json:"impersonation_context,omitempty" mapstructure:"impersonation_context,omitempty"`
Other map[string]interface{} `json:"-" mapstructure:",remain"` // capture any 'other' keys that were passed in so we can reply back with them
}
@@ -119,7 +119,7 @@ func getDelegateTaskMessages(callbackID int, agentUUIDLength int, updateCheckinT
if callbackIds := submittedTasksAwaitingFetching.getOtherCallbackIds(callbackID); len(callbackIds) > 0 {
// check if there's a route between our callback and the callback with a task
for _, targetCallbackId := range callbackIds {
if routablePath := callbackGraph.GetBFSPath(callbackID, targetCallbackId); routablePath != nil {
if routablePath := callbackGraph.GetBFSPath(callbackID, targetCallbackId); routablePath != nil && len(routablePath) > 0 {
// there's a route between our callback and the target callback for some sort of task
logging.LogDebug("task exists for callback we can route to")
currentTasks := []databaseStructs.Task{}
@@ -206,7 +206,7 @@ func getDelegateProxyMessages(callbackID int, agentUUIDLength int, updateCheckin
if callbackIds := proxyPorts.GetOtherCallbackIds(callbackID); len(callbackIds) > 0 {
// check if there's a route between our callback and the callback with a task
for _, targetCallbackId := range callbackIds {
if routablePath := callbackGraph.GetBFSPath(callbackID, targetCallbackId); routablePath != nil {
if routablePath := callbackGraph.GetBFSPath(callbackID, targetCallbackId); routablePath != nil && len(routablePath) > 0 {
// there's a route between our callback and the target callback for some sort of proxy data
if messages, err := proxyPorts.GetDataForCallbackId(targetCallbackId, CALLBACK_PORT_TYPE_SOCKS); err != nil {
logging.LogError(err, "Failed to get socks proxy data for routable callback")
@@ -91,11 +91,17 @@ func handleAgentMessageUpdateInfo(incoming *map[string]interface{}, uUIDInfo *ca
if agentMessage.Architecture != "" {
callback.Architecture = agentMessage.Architecture
}
if agentMessage.Cwd != nil {
callback.Cwd = *agentMessage.Cwd
}
if agentMessage.ImpersonationContext != nil {
callback.ImpersonationContext = *agentMessage.ImpersonationContext
}
if _, err := database.DB.NamedExec(`UPDATE callback SET
"user"=:user, host=:host, pid=:pid, extra_info=:extra_info, sleep_info=:sleep_info,
enc_key=:enc_key, dec_key=:dec_key, process_name=:process_name, ip=:ip, external_ip=:external_ip,
integrity_level=:integrity_level, "domain"=:domain, os=:os, architecture=:architecture,
process_short_name=:process_short_name
process_short_name=:process_short_name, cwd=:cwd, impersonation_context=:impersonation_context
WHERE id=:id`, callback); err != nil {
response["status"] = "error"
response["error"] = err.Error()
@@ -35,20 +35,40 @@ func processAgentMessageFromPushC2() {
TrackingID: agentMessage.TrackingID,
})
//logging.LogDebug("finished processing message, about to send response back to grpc")
select {
case agentMessage.ResponseChannel <- grpc.RabbitMQProcessAgentMessageFromPushC2Response{
Message: messageResponse.Message,
NewCallbackUUID: messageResponse.NewCallbackUUID,
OuterUuid: messageResponse.OuterUuid,
OuterUuidIsCallback: messageResponse.OuterUuidIsCallback,
Err: messageResponse.Err,
TrackingID: messageResponse.TrackingID,
AgentUUIDSize: messageResponse.AgentUUIDSize,
}:
case <-time.After(grpc.PushC2Server.GetChannelTimeout()):
err := errors.New("timeout sending agent message response back to agentMessageToProcess.ResponseChannel")
logging.LogError(err, "not sending response back")
if agentMessage.DefaultResponseChannel != nil {
//logging.LogDebug("got response message from Mythic back to Agent for PushC2 via agentMessage.DefaultResponseChannel")
select {
case agentMessage.DefaultResponseChannel <- services.PushC2MessageFromMythic{
Success: true,
Error: "",
Message: messageResponse.Message,
TrackingID: messageResponse.TrackingID,
}:
case <-newConnection.DisconnectProcessingChan:
err := errors.New("client disconnected trying to send back to agentMessageToProcess.ResponseChannel")
logging.LogError(err, "not sending response back")
return
}
}
if agentMessage.ResponseChannel != nil {
//logging.LogDebug("Sending response back to ResponseChannel for PushC2")
select {
case agentMessage.ResponseChannel <- grpc.RabbitMQProcessAgentMessageFromPushC2Response{
Message: messageResponse.Message,
NewCallbackUUID: messageResponse.NewCallbackUUID,
OuterUuid: messageResponse.OuterUuid,
OuterUuidIsCallback: messageResponse.OuterUuidIsCallback,
Err: messageResponse.Err,
TrackingID: messageResponse.TrackingID,
AgentUUIDSize: messageResponse.AgentUUIDSize,
}:
case <-newConnection.DisconnectProcessingChan:
err := errors.New("client disconnected trying to send back to agentMessageToProcess.ResponseChannel")
logging.LogError(err, "not sending response back")
return
}
}
case <-newConnection.DisconnectProcessingChan:
logging.LogInfo("PushC2 client disconnected, exiting routine processing messages")
return
@@ -58,13 +78,13 @@ func processAgentMessageFromPushC2() {
}(agentMessageToProcess)
}
}
func sendMessageToDirectPushC2(callbackID int, message map[string]interface{}, updateCheckinTime bool) error {
func sendMessageToDirectPushC2(callbackID int, message map[string]interface{}) error {
responseChan, callbackUUID, base64Encoded, c2ProfileName, trackingID, _, err := grpc.PushC2Server.GetPushC2ClientInfo(callbackID)
if err != nil {
logging.LogError(err, "Failed to get push c2 client info")
return err
}
uUIDInfo, err := LookupEncryptionData(c2ProfileName, callbackUUID, updateCheckinTime)
uUIDInfo, err := LookupEncryptionData(c2ProfileName, callbackUUID, true)
if err != nil {
logging.LogError(err, "Failed to find encryption data for callback")
return err
@@ -86,8 +106,9 @@ func sendMessageToDirectPushC2(callbackID int, message map[string]interface{}, u
//logging.LogDebug("Sent message back to responseChan")
return nil
default:
logging.LogError(nil, "push channel full, dropping proxy message", "callbackID", callbackID)
return errors.New("push channel full, dropping proxy message")
err = errors.New("push channel full")
logging.LogError(err, "message dropped", "callbackID", callbackID)
return err
}
}
@@ -120,20 +141,20 @@ func interceptProxyDataToAgentForPushC2() {
_ = sendMessageToDirectPushC2(msg.CallbackID, map[string]interface{}{
"action": "post_response",
msg.ProxyType: []interface{}{msg.InteractiveMessage},
}, false)
})
case CALLBACK_PORT_TYPE_SOCKS:
fallthrough
case CALLBACK_PORT_TYPE_RPORTFWD:
_ = sendMessageToDirectPushC2(msg.CallbackID, map[string]interface{}{
"action": "post_response",
msg.ProxyType: []interface{}{msg.Message},
}, false)
})
}
attemptedToSend = true
} else {
connectedPushClient := grpc.PushC2Server.GetConnectedClients()
for _, clientID := range connectedPushClient {
if routablePath := callbackGraph.GetBFSPath(clientID, msg.CallbackID); routablePath != nil {
connectedPushClients := grpc.PushC2Server.GetConnectedClients()
for _, clientID := range connectedPushClients {
if routablePath := callbackGraph.GetBFSPath(clientID, msg.CallbackID); routablePath != nil && len(routablePath) > 0 {
var delegateMessages interface{}
switch msg.ProxyType {
case CALLBACK_PORT_TYPE_INTERACTIVE:
@@ -149,7 +170,11 @@ func interceptProxyDataToAgentForPushC2() {
"action": "post_response",
"delegates": delegateMessages,
}
_ = sendMessageToDirectPushC2(clientID, newTaskMsg, false)
//logging.LogDebug("about to call sendMessageToDirectPushC2")
err := sendMessageToDirectPushC2(clientID, newTaskMsg)
if err != nil {
logging.LogError(err, "failed to send to push c2 delegate messages")
}
attemptedToSend = true
break
}
@@ -384,7 +409,7 @@ func pushC2AgentGetDelegateTaskMessages(taskId int, callbackId int, routablePath
}
}
if wrappedMessage, err := RecursivelyEncryptMessage(routablePath, newTask, true); err != nil {
if wrappedMessage, err := RecursivelyEncryptMessage(routablePath, newTask, false); err != nil {
logging.LogError(err, "Failed to recursively encrypt message")
} else {
submittedTasksAwaitingFetching.removeTask(currentTasks[i].ID)
@@ -405,15 +430,15 @@ func pushC2AgentGetDelegateProxyMessages(messages []interface{}, portType string
"action": "get_tasking",
portType: messages,
}
if wrappedMessage, err := RecursivelyEncryptMessage(routablePath, newTask, true); err != nil {
wrappedMessage, err := RecursivelyEncryptMessage(routablePath, newTask, false)
if err != nil {
logging.LogError(err, "Failed to recursively encrypt message")
return nil
} else {
delegateMessages = append(delegateMessages, delegateMessageResponse{
Message: string(wrappedMessage),
SuppliedUuid: routablePath[len(routablePath)-1].DestinationAgentId,
C2ProfileName: routablePath[len(routablePath)-1].C2ProfileName,
})
}
delegateMessages = append(delegateMessages, delegateMessageResponse{
Message: string(wrappedMessage),
SuppliedUuid: routablePath[len(routablePath)-1].DestinationAgentId,
C2ProfileName: routablePath[len(routablePath)-1].C2ProfileName,
})
return delegateMessages
}
@@ -60,7 +60,7 @@ func (c *bfsCache) GetPath(sourceId int, destinationId int) []cbGraphAdjMatrixEn
if _, sourceExists := c.cache[sourceId]; sourceExists {
if _, destinationExists := c.cache[sourceId][destinationId]; destinationExists {
if len(c.cache[sourceId][destinationId]) > 0 {
logging.LogInfo("Got path from cache", "source", sourceId, "destination", destinationId, "path", c.cache[sourceId][destinationId][0])
//logging.LogInfo("Got path from cache", "source", sourceId, "destination", destinationId, "path", c.cache[sourceId][destinationId][0])
return c.cache[sourceId][destinationId][0]
}
return nil
@@ -113,7 +113,7 @@ func (g *cbGraph) getAllChildIDs(callbackId int) []int {
continue
}
for i, _ := range immediateChildren {
if immediateChildren[i].SourceId == immediateChildren[i].DestinationId {
if immediateChildren[i].SourceId == immediateChildren[i].DestinationId && immediateChildren[i].SourceId != callbackId {
//logging.LogInfo("found egress connection", "id", immediateChildren[i])
callbacksWithEgress = append(callbacksWithEgress, immediateChildren[i].SourceId)
}
@@ -143,7 +143,7 @@ func (g *cbGraph) getAllChildIDs(callbackId int) []int {
finalCallbackIDsToUpdate := []int{}
for _, callbackIdToUpdate := range callbackIDsToUpdate {
//logging.LogInfo("checking if should update", "callbackIdToUpdate", callbackIdToUpdate)
if slices.Contains(callbacksWithEgress, callbackIdToUpdate) {
if slices.Contains(callbacksWithEgress, callbackIdToUpdate) || slices.Contains(finalCallbackIDsToUpdate, callbackIdToUpdate) {
//logging.LogInfo("checking if should update", "its egress", true, "callbackIdToUpdate", callbackIdToUpdate)
continue
}
@@ -236,7 +236,7 @@ func (g *cbGraph) Add(source databaseStructs.Callback, destination databaseStruc
if dest.DestinationId == destination.ID && dest.C2ProfileName == c2profileName {
g.lock.Unlock()
//logging.LogDebug("Found existing connection, not adding new one to memory", "source", source.ID, "destination", destination.ID, "c2 profile", c2profileName)
if initializing {
if initializing || c2.IsP2p {
// don't update callback times when initializing, this is when the Mythic server starts up
return
}
@@ -246,6 +246,7 @@ func (g *cbGraph) Add(source databaseStructs.Callback, destination databaseStruc
}
callbackIDs := g.getAllChildIDs(source.ID)
if len(callbackIDs) > 0 {
//logging.LogInfo("about to call updateTimes in Add", "callbackIDs", callbackIDs, "c2", c2profileName, "source", source.ID, "destination", destination.ID)
updateTimes(updateTime, callbackIDs)
}
return
@@ -418,7 +419,9 @@ func (g *cbGraph) GetBFSPath(sourceId int, destinationId int) []cbGraphAdjMatrix
}
}
}
return nil
// add a known "empty" so we don't keep looking
BFSCache.Add(sourceId, destinationId, []cbGraphAdjMatrixEntry{})
return []cbGraphAdjMatrixEntry{}
}
func (g *cbGraph) Print() {
g.lock.RLock()
@@ -431,6 +434,7 @@ func (g *cbGraph) Print() {
}
func RemoveEdgeByIds(sourceId int, destinationId int, c2profileName string) error {
//logging.LogInfo("removing edges", "sourceID", sourceId, "destinationID", destinationId, "c2ProfileName", c2profileName)
currentEdges := []databaseStructs.Callbackgraphedge{}
err := database.DB.Select(&currentEdges, `SELECT
id FROM callbackgraphedge WHERE
@@ -510,7 +514,7 @@ func getC2ProfileForName(c2profileName string) databaseStructs.C2profile {
return c2
}
c2profile := databaseStructs.C2profile{}
err := database.DB.Get(&c2profile, `SELECT id, is_p2p FROM c2profile WHERE "name"=$1 AND deleted=false`,
err := database.DB.Get(&c2profile, `SELECT id, is_p2p, "name" FROM c2profile WHERE "name"=$1 AND deleted=false`,
c2profileName)
if errors.Is(err, sql.ErrNoRows) {
return databaseStructs.C2profile{}
+53 -70
View File
@@ -106,80 +106,63 @@ func (s *submittedTasksForAgents) addTask(task databaseStructs.Task) {
if err != nil {
logging.LogError(err, "failed to generate pushc2 task message")
}
err = sendMessageToDirectPushC2(task.CallbackID, newTaskMsg, false)
err = sendMessageToDirectPushC2(task.CallbackID, newTaskMsg)
if err != nil {
logging.LogError(err, "failed to send pushc2 task message")
} else {
_, err = database.DB.Exec(`UPDATE callback SET last_checkin=$1
WHERE id=$2`, time.UnixMicro(0), task.CallbackID)
if err != nil {
logging.LogError(err, "Failed to update callback last checkin time")
}
EventingChannel <- EventNotification{
Trigger: eventing.TriggerTaskStart,
OperationID: task.OperationID,
OperatorID: task.OperatorID,
TaskID: task.ID,
}
return
}
} else {
// check if the task is for a linked agent of a push c2 style client
connectedPushClient := grpc.PushC2Server.GetConnectedClients()
for _, clientID := range connectedPushClient {
if routablePath := callbackGraph.GetBFSPath(clientID, task.CallbackID); routablePath != nil {
// we have a p2p path from callbackID to task.CallbackID
delegateMessages := pushC2AgentGetDelegateTaskMessages(task.ID, task.CallbackID, routablePath)
if delegateMessages != nil {
newTaskMsg := map[string]interface{}{
"action": "get_tasking",
"delegates": delegateMessages,
}
/*
err := sendMessageToDirectPushC2(clientID, newTaskMsg, false)
if err != nil {
logging.LogError(err, "failed to send pushc2 delegate task message")
} else {
return
}
*/
responseChan, callbackUUID, base64Encoded, c2ProfileName, trackingID, _, err := grpc.PushC2Server.GetPushC2ClientInfo(clientID)
logging.LogDebug("new msg for push c2", "task", newTaskMsg)
uUIDInfo, err := LookupEncryptionData(c2ProfileName, callbackUUID, false)
if err != nil {
logging.LogError(err, "Failed to find encryption data for callback")
break
}
responseBytes, err := EncryptMessage(uUIDInfo, callbackUUID, newTaskMsg, base64Encoded)
if err != nil {
logging.LogError(err, "Failed to encrypt message")
break
}
//logging.LogDebug("new encrypted msg for push c2", "enc", string(responseBytes))
select {
case responseChan <- services.PushC2MessageFromMythic{
Message: responseBytes,
Success: true,
Error: "",
TrackingID: trackingID,
}:
// everything went ok, return from this
logging.LogDebug("Sent message back to responseChan")
case <-time.After(grpc.PushC2Server.GetChannelTimeout()):
logging.LogError(nil, "timeout trying to send to responseChannel")
}
EventingChannel <- EventNotification{
Trigger: eventing.TriggerTaskStart,
OperationID: task.OperationID,
OperatorID: task.OperatorID,
TaskID: task.ID,
}
return
EventingChannel <- EventNotification{
Trigger: eventing.TriggerTaskStart,
OperationID: task.OperationID,
OperatorID: task.OperatorID,
TaskID: task.ID,
}
return
}
// check if the task is for a linked agent of a push c2 style client
connectedPushClient := grpc.PushC2Server.GetConnectedClients()
for _, clientID := range connectedPushClient {
if routablePath := callbackGraph.GetBFSPath(clientID, task.CallbackID); routablePath != nil && len(routablePath) > 0 {
// we have a p2p path from callbackID to task.CallbackID
delegateMessages := pushC2AgentGetDelegateTaskMessages(task.ID, task.CallbackID, routablePath)
if delegateMessages != nil {
newTaskMsg := map[string]interface{}{
"action": "get_tasking",
"delegates": delegateMessages,
}
responseChan, callbackUUID, base64Encoded, c2ProfileName, trackingID, _, err := grpc.PushC2Server.GetPushC2ClientInfo(clientID)
//logging.LogDebug("new msg for push c2", "task", newTaskMsg)
uUIDInfo, err := LookupEncryptionData(c2ProfileName, callbackUUID, false)
if err != nil {
logging.LogError(err, "Failed to find encryption data for callback")
break
}
responseBytes, err := EncryptMessage(uUIDInfo, callbackUUID, newTaskMsg, base64Encoded)
if err != nil {
logging.LogError(err, "Failed to encrypt message")
break
}
//logging.LogDebug("new encrypted msg for push c2", "enc", string(responseBytes))
select {
case responseChan <- services.PushC2MessageFromMythic{
Message: responseBytes,
Success: true,
Error: "",
TrackingID: trackingID,
}:
// everything went ok, return from this
logging.LogDebug("Sent message to PushC2 Channel")
case <-time.After(grpc.PushC2Server.GetChannelTimeout()):
logging.LogError(nil, "timeout trying to send to responseChannel")
}
EventingChannel <- EventNotification{
Trigger: eventing.TriggerTaskStart,
OperationID: task.OperationID,
OperatorID: task.OperatorID,
TaskID: task.ID,
}
return
}
}
}
@@ -1110,6 +1110,34 @@ var Socks5UnrecognizedAddrType = fmt.Errorf("unrecognized address type")
var Socks5StartNoAuth = []byte{'\x05', '\x00'}
var Socks5StartUsernamePasswordAuth = []byte{'\x05', '\x02'}
// maxAllocSize is 100KB
const maxAllocSize = 1024 * 100
const minAllocSize = 1024
func (p *callbackPortUsage) burstAdjustReadSize(lastReads []int, currentLimit int) int {
if lastReads[0] == 0 {
return currentLimit
}
if lastReads[0] == lastReads[1] && lastReads[1] == lastReads[2] && lastReads[0] == currentLimit {
newLimit := currentLimit * 2
if newLimit > maxAllocSize {
newLimit = maxAllocSize
}
return newLimit
}
if lastReads[0] == lastReads[1] && lastReads[1] > lastReads[2] {
return currentLimit
}
if lastReads[0] > lastReads[1] && lastReads[0] > lastReads[2] {
newLimit := currentLimit / 2
if newLimit < minAllocSize {
newLimit = minAllocSize
}
return newLimit
}
return currentLimit
}
// ***** start section from https://github.com/armon/go-socks5 ********
type AddrSpec struct {
FQDN string
@@ -1310,11 +1338,19 @@ func (p *callbackPortUsage) handleSocksConnections() {
go func(conn net.Conn) {
// function for reading from Mythic's connections to send to agents
firstRead := true
lastReadSizes := []int{0, 0, 0}
tempBufSize := minAllocSize
for {
buf := make([]byte, 4096)
logging.LogDebug("looping to read from connection again", "server_id", newConnection.ServerID)
tempBufSize = p.burstAdjustReadSize(lastReadSizes, tempBufSize)
buf := make([]byte, tempBufSize)
//logging.LogDebug("looping to read from connection again", "server_id", newConnection.ServerID)
// add some sleep here to keep things from getting overwhelmed
time.Sleep(time.Duration(20) * time.Millisecond)
length, err := conn.Read(buf)
if length > 0 {
lastReadSizes[0] = lastReadSizes[1]
lastReadSizes[1] = lastReadSizes[2]
lastReadSizes[2] = length
if firstRead {
firstRead = false
// the first message that gets sent has connection information
@@ -1510,6 +1546,7 @@ func (p *callbackPortUsage) handleSocksConnections() {
}
// track it so we can get the output back to the right addr
p.newConnectionChannel <- &newUDPConnection
//logging.LogDebug("got new UDP connection, sending to intercept proxy")
interceptProxyToAgentMessageChan <- interceptProxyToAgentMessage{
Message: proxyToAgentMessage{
Message: newUDPConnectionBuf[:newUDPConnectionBufLength],
@@ -1563,6 +1600,7 @@ func (p *callbackPortUsage) handleSocksConnections() {
p.removeConnectionsChannel <- &newUDPConnection
return
}
//logging.LogDebug("new udp message saying to remove")
interceptProxyToAgentMessageChan <- interceptProxyToAgentMessage{
Message: proxyToAgentMessage{
Message: nil,
@@ -1583,7 +1621,7 @@ func (p *callbackPortUsage) handleSocksConnections() {
default:
}
}
//logging.LogDebug("Message received from proxychains", "serverID", newConnection.ServerID, "size", length)
//logging.LogDebug("Message received from proxychains", "serverID", newConnection.ServerID, "size", length, "sizeLimit", tempBufSize)
interceptProxyToAgentMessageChan <- interceptProxyToAgentMessage{
Message: proxyToAgentMessage{
Message: buf[:length],
+1 -1
View File
@@ -11,7 +11,7 @@ import (
"github.com/spf13/viper"
)
const mythicServerVersion = "3.4.23"
const mythicServerVersion = "3.4.24"
type Config struct {
// server configuration
@@ -1,17 +1,17 @@
{
"files": {
"main.css": "/new/static/css/main.262ce320.css",
"main.js": "/new/static/js/main.7903191a.js",
"main.js": "/new/static/js/main.962a1f64.js",
"sql-wasm.wasm": "/new/sql-wasm-7f26ab750fcdafabbe18c6525a66a0bd.wasm",
"static/media/mythic-red.png": "/new/static/media/mythic-red.203468a4e5240d239aa0.png",
"static/media/graphql.png": "/new/static/media/graphql.8f15978b39b0870a9f0e.png",
"static/media/Mythic_Logo.svg": "/new/static/media/Mythic_Logo.6842c911bebe36d6f83fc7ced4a2cd99.svg",
"index.html": "/new/index.html",
"main.262ce320.css.map": "/new/static/css/main.262ce320.css.map",
"main.7903191a.js.map": "/new/static/js/main.7903191a.js.map"
"main.962a1f64.js.map": "/new/static/js/main.962a1f64.js.map"
},
"entrypoints": [
"static/css/main.262ce320.css",
"static/js/main.7903191a.js"
"static/js/main.962a1f64.js"
]
}
+1 -1
View File
@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/new/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="apple-touch-icon" href="/new/logo192.png"/><link rel="manifest" href="/new/manifest.json"/><title>Mythic</title><script defer="defer" src="/new/static/js/main.7903191a.js"></script><link href="/new/static/css/main.262ce320.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/new/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="apple-touch-icon" href="/new/logo192.png"/><link rel="manifest" href="/new/manifest.json"/><title>Mythic</title><script defer="defer" src="/new/static/js/main.962a1f64.js"></script><link href="/new/static/css/main.262ce320.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>