Skip to content

Commit ec8d605

Browse files
author
zerosum0x0
committed
initial commit
1 parent b7fb20a commit ec8d605

File tree

123 files changed

+5479
-203
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

123 files changed

+5479
-203
lines changed

LICENSE

+49-201
Large diffs are not rendered by default.

README.md

+49-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,49 @@
1-
# koadic
2-
Koadic C3 COM Command & Control - JScript RAT
1+
# Koadic
2+
Koadic, or COM Command & Control, is a Windows post-exploitation rootkit similar to other penetration testing tools such as Meterpreter and Powershell Empire. The major difference is that Koadic does most of its operations using Windows Script Host (a.k.a. JScript/VBScript), with compatibility in the core to support a default installation of Windows 2000 with no service packs (and potentially even versions of NT4) all the way through Windows 10.
3+
4+
It is possible to serve payloads completely in memory from stage 0 to beyond, as well as use cryptographically secure communications over SSL and TLS (depending on what the victim OS has enabled).
5+
6+
Koadic also attempts to be compatible with both Python 2 and Python 3.
7+
8+
### Demo
9+
10+
[![Koadic Demo](http://img.youtube.com/vi/hLdXp2WPvTs/0.jpg)](http://www.youtube.com/watch?v=hLdXp2WPvTs "Koadic Demo")
11+
12+
1. Hooks a zombie
13+
2. Elevates integrity (UAC Bypass)
14+
3. Dumps SAM/SECURITY hive for passwords
15+
4. Scans local network for open SMB
16+
5. Pivots to another machine
17+
18+
### Stagers
19+
Stagers hook target zombies and allow you to use implants.
20+
21+
Module | Description
22+
--------|------------
23+
stager/js/mshta | serves payloads in memory using MSHTA.exe HTML Applications
24+
stager/js/regsvr | serves payloads in memory using regsvr32.exe COM+ scriptlets
25+
stager/js/rundll32_js | serves payloads in memory using rundll32.exe
26+
stager/js/disk | serves payloads using files on disk
27+
28+
### Implants
29+
Implants start jobs on zombies.
30+
31+
Module | Description
32+
--------|------------
33+
implant/elevate/bypassuac_eventvwr | Uses enigma0x3's eventvwr.exe exploit to bypass UAC on Windows 7, 8, and 10.
34+
implant/elevate/bypassuac_sdclt | Uses enigma0x3's sdclt.exe exploit to bypass UAC on Windows 10.
35+
implant/fun/thunderstruck | Maxes volume and opens a URL in a hidden window (AC/DC YouTube).
36+
implant/fun/voice | Plays a message over text-to-speech.
37+
implant/gather/clipboard | Retrieves the current content of the user clipboard.
38+
implant/gather/hashdump_sam | Retrieves hashed passwords from the SAM hive.
39+
implant/gather/hashdump_dc | Domain controller hashes from the NTDS.dit file.
40+
implant/inject/reflectdll_excel | Injects a reflective-loaded DLL (if Excel is installed).
41+
implant/inject/shellcode_excel | Runs arbitrary shellcode payload (if Excel is installed).
42+
implant/manage/enable_rdesktop | Enables remote desktop on the target.
43+
implant/manage/exec_cmd | Run an arbitrary command on the target, and optionally receive the output.
44+
implant/manage/killav | iterate known antivirus processes, and attempt to end their execution.
45+
implant/pivot/exec_wmi | Run a command on another machine using WMI.
46+
implant/pivot/exec_psexec | Run a command on another machine using psexec.
47+
implant/scan/tcp | Uses HTTP to scan open TCP ports on the target zombie LAN.
48+
implant/utils/download_file | Downloads a file from the target zombie.
49+
implant/utils/upload_file | Uploads a file from the listening server to the target zombies.

core/__init__.py

Whitespace-only changes.

core/cidr.py

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Parts of this file are:
2+
# Copyright (c) 2007 Brandon Sterne
3+
# 2015 Updates by Rafe Colburn
4+
# Licensed under the MIT license.
5+
# http://brandon.sternefamily.net/files/mit-license.txt
6+
# CIDR Block Converter - 2007
7+
8+
def ip2bin(ip):
9+
b = ""
10+
inQuads = ip.split(".")
11+
outQuads = 4
12+
for q in inQuads:
13+
if q != "":
14+
b += dec2bin(int(q),8)
15+
outQuads -= 1
16+
while outQuads > 0:
17+
b += "00000000"
18+
outQuads -= 1
19+
return b
20+
21+
def dec2bin(n,d=None):
22+
s = ""
23+
while n>0:
24+
if n&1:
25+
s = "1"+s
26+
else:
27+
s = "0"+s
28+
n >>= 1
29+
if d is not None:
30+
while len(s)<d:
31+
s = "0"+s
32+
if s == "": s = "0"
33+
return s
34+
35+
def bin2ip(b):
36+
ip = ""
37+
for i in range(0,len(b),8):
38+
ip += str(int(b[i:i+8],2))+"."
39+
return ip[:-1]
40+
41+
def parse_cidr(str):
42+
splitted = str.split("/")
43+
44+
if len(splitted) > 2:
45+
raise ValueError
46+
47+
if len(splitted) == 1:
48+
subnet = 32
49+
else:
50+
subnet = int(splitted[1])
51+
52+
if subnet > 32:
53+
raise ValueError
54+
55+
str_ip = splitted[0]
56+
ip = str_ip.split(".")
57+
if len(ip) != 4:
58+
raise ValueError
59+
60+
for i in ip:
61+
if int(i) < 0 or int(i) > 255:
62+
raise ValueError
63+
64+
if subnet == 32:
65+
return [str_ip]
66+
67+
bin_ip = ip2bin(str_ip)
68+
69+
ip_prefix = bin_ip[:-(32-subnet)]
70+
71+
ret = []
72+
for i in range(2**(32-subnet)):
73+
ret.append(bin2ip(ip_prefix+dec2bin(i, (32-subnet))))
74+
75+
return ret
76+
77+
def get_ports(str):
78+
ports = []
79+
for x in str.split(","):
80+
x = x.strip()
81+
if "-" in x:
82+
x = x.split("-")
83+
if len(x) > 2:
84+
raise ValueError
85+
86+
if int(x[0]) < 0 or int(x[0]) > 65535:
87+
raise ValueError
88+
if int(x[1]) < 0 or int(x[1]) + 1 > 65535:
89+
raise ValueError
90+
if int(x[0]) > int(x[1]) + 1:
91+
raise ValueError
92+
93+
ports += range(int(x[0]), int(x[1]) + 1)
94+
else:
95+
if int(x) < 0 or int(x) > 65535:
96+
raise ValueError
97+
ports.append(int(x))
98+
99+
return ports
100+
101+
def get_ips(str):
102+
ips = []
103+
for x in str.split(","):
104+
ips += parse_cidr(x.strip())
105+
return ips
106+
107+
if __name__ == "__main__":
108+
ips = get_ips("127.0.0.1,192.168.0.0/24,10.0.0.40/28")
109+
print(ips)
110+
ports = get_ports("4444,1-100,5432")
111+
print(ports)

core/colors.py

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
class Colors(object):
2+
def __init__(self):
3+
# http://ozzmaker.com/add-colour-to-text-in-python/
4+
self.ENDC = '\033[0m'
5+
6+
self.RED = '31'
7+
self.GREEN = '32'
8+
self.YELLOW = '33'
9+
self.BLUE = '34'
10+
self.CYAN = '36'
11+
12+
self.NORMAL = '0'
13+
self.BOLD = '1'
14+
self.UNDERLINE = '2'
15+
16+
def error(self, text):
17+
return self.colorize(text, [self.RED, self.BOLD])
18+
19+
def warning(self, text):
20+
return self.colorize(text, [self.YELLOW, self.BOLD])
21+
22+
def good(self, text):
23+
return self.colorize(text, [self.GREEN, self.BOLD])
24+
25+
def status(self, text):
26+
return self.colorize(text, [self.BLUE, self.BOLD])
27+
28+
def colorize(self, text, options, readline=False):
29+
start = ""
30+
if readline:
31+
start += "\001"
32+
start += '\033['
33+
start += ";".join(options)
34+
start += "m"
35+
if readline:
36+
start += "\002"
37+
end = "\001" + self.ENDC + "\002"
38+
else:
39+
end = self.ENDC
40+
41+
return start + text + end
42+
43+
def get_prompt(self, state, isreadline = True):
44+
import os
45+
glyph = "#" if os.geteuid() == 0 else "$"
46+
return "%s%s: %s%s" % (self.colorize("(", [self.GREEN], isreadline),
47+
self.colorize("koadic", [self.BOLD], isreadline),
48+
self.colorize(state, [self.CYAN], isreadline),
49+
self.colorize(")" + glyph + " ", [self.GREEN], isreadline))

core/commands/__init__.py

Whitespace-only changes.

core/commands/cmdshell.py

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
DESCRIPTION = "command shell to interact with a zombie"
2+
3+
def autocomplete(shell, line, text, state):
4+
if len(line.split(" ")) >= 2:
5+
return None
6+
7+
options = []
8+
9+
for server in shell.stagers:
10+
for session in server.sessions:
11+
options.append(str(session.id))
12+
13+
try:
14+
return options[state]
15+
except:
16+
return None
17+
18+
def help(shell):
19+
pass
20+
21+
def get_prompt(shell, id, ip, isreadline = True):
22+
return "%s%s: %s%s" % (shell.colors.colorize("[", [shell.colors.NORMAL], isreadline),
23+
shell.colors.colorize("koadic", [shell.colors.BOLD], isreadline),
24+
shell.colors.colorize("ZOMBIE %s (%s)" % (id, ip), [shell.colors.CYAN], isreadline),
25+
shell.colors.colorize(" - cmd.exe]> ", [shell.colors.NORMAL], isreadline))
26+
27+
def run_cmdshell(shell, session):
28+
import copy
29+
30+
exec_cmd_name = 'implant/manage/exec_cmd'
31+
# this won't work, Error: "can't pickle module objects"
32+
#plugin = copy.deepcopy(shell.plugins['implant/manage/exec_cmd'])
33+
plugin = shell.plugins[exec_cmd_name]
34+
35+
# copy (hacky shit)
36+
old_prompt = shell.prompt
37+
old_clean_prompt = shell.clean_prompt
38+
old_state = shell.state
39+
40+
old_zombie = plugin.options.get("ZOMBIE")
41+
old_cmd = plugin.options.get("CMD")
42+
43+
id = str(session.id)
44+
ip = session.ip
45+
46+
while True:
47+
shell.state = exec_cmd_name
48+
shell.prompt = get_prompt(shell, id, ip, True)
49+
shell.clean_prompt = get_prompt(shell, id, ip, False)
50+
plugin.options.set("ZOMBIE", id)
51+
52+
try:
53+
import readline
54+
readline.set_completer(None)
55+
cmd = shell.get_command(shell.prompt)
56+
57+
if len(cmd) > 0:
58+
if cmd == 'exit':
59+
return
60+
61+
plugin.options.set("CMD", cmd)
62+
plugin.run()
63+
except KeyboardInterrupt:
64+
shell.print_plain(shell.prompt)
65+
return
66+
finally:
67+
plugin.options.set("ZOMBIE", old_zombie)
68+
plugin.options.set("cmd", old_cmd)
69+
70+
shell.prompt = old_prompt
71+
shell.clean_prompt = old_clean_prompt
72+
shell.state = old_state
73+
74+
75+
def execute(shell, cmd):
76+
splitted = cmd.split(" ")
77+
if len(splitted) >= 2:
78+
target = splitted[1]
79+
80+
for server in shell.stagers:
81+
for session in server.sessions:
82+
if target == str(session.id):
83+
run_cmdshell(shell, session)
84+
return
85+
86+
shell.print_error("Zombie #%s not found." % (target))
87+
else:
88+
shell.print_error("You must provide a zombie number as an argument.")

core/commands/exit.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
DESCRIPTION = "exits the program"
2+
3+
def autocomplete(shell, line, text, state):
4+
return None
5+
6+
def help(shell):
7+
pass
8+
9+
def execute(shell, cmd):
10+
import sys
11+
sys.exit(0)

core/commands/help.py

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
DESCRIPTION = "displays help info for a command"
2+
3+
4+
def autocomplete(shell, line, text, state):
5+
6+
# should never go this big...
7+
if len(line.split(" ")) >= 3:
8+
return None
9+
10+
options = [x + " " for x in shell.actions.keys() if x.startswith(text)]
11+
12+
try:
13+
return options[state]
14+
except:
15+
return None
16+
17+
18+
def help(shell):
19+
shell.print_plain("")
20+
shell.print_plain("You definitely need help")
21+
shell.print_plain("")
22+
23+
def execute(shell, cmd):
24+
25+
splitted = cmd.split(" ")
26+
27+
if len(splitted) == 1:
28+
return help_all(shell)
29+
30+
if len(splitted) >= 2:
31+
return help_command(shell, splitted[1])
32+
33+
34+
def help_command(shell, command):
35+
if command not in shell.actions:
36+
shell.print_error("No command named %s" % command)
37+
return
38+
39+
shell.actions[command].help(shell)
40+
41+
42+
def help_all(shell):
43+
formats = '\t{0:<12}{1:<16}'
44+
45+
shell.print_plain("")
46+
shell.print_plain(formats.format("COMMAND", "DESCRIPTION"))
47+
shell.print_plain(formats.format("---------", "-------------"))
48+
49+
for key, env in shell.actions.items():
50+
shell.print_plain(formats.format(key, env.DESCRIPTION))
51+
52+
shell.print_plain("")
53+
shell.print_plain('Use "help %s" to find more info about a command.' %
54+
shell.colors.colorize("command", [shell.colors.BOLD]))
55+
shell.print_plain("")

0 commit comments

Comments
 (0)